コード例 #1
0
class GetFrames:
    def __init__(self, root):
        self.root = root
        self.img_dir = ""
        self.perSec = -1
        self.params_json = ""
        self.diff_dirs = ["", ""]
        self.track_json = ""
        self.contour_json = ""

        self.window = tk.Toplevel(self.root)
        self.window.geometry("350x200")
        self.l0 = tk.Label(
            self.window,
            text="Select the video to process into frames,\n"
            "entering the number of frames per second to extract\n"
            "(default 0 means extract all frames available)\n"
            "-or- select the directory containing frames:")
        self.e0 = tk.Entry(self.window, width=4)
        self.e0.insert(0, "0")
        self.b0 = tk.Button(self.window,
                            text="Select video",
                            command=self.getFrames)
        self.l1 = tk.Label(self.window, text="-or-")
        self.b1 = tk.Button(self.window,
                            text="Select folder",
                            command=self.getFolder)
        self.prog = Progress(self.window)

        self.l0.pack()
        self.e0.pack()
        self.b0.pack()
        self.l1.pack()
        self.b1.pack()

    def getFrames(self):
        f = filedialog.askopenfilename()
        if isinstance(f, str) and self.e0.get().isdigit():
            self.img_dir = f
            video = cv2.VideoCapture(f)
            progress, frame = video.read()
            (i, frameCount) = (0, 0)
            fps = video.get(cv2.CAP_PROP_FPS)
            self.perSec = min(int(self.e0.get()), fps)
            ratio = int(fps // int(self.perSec)) if self.perSec else 1
            length_of_vid = int(video.get(cv2.CAP_PROP_FRAME_COUNT) // ratio)

            self.l0.config(
                text=
                f"~{length_of_vid} frames to convert for video. Video fps: {fps}. "
                f"\nRatio of frames extracted per total: {ratio}.")
            self.e0.pack_forget()
            self.b0.pack_forget()
            self.l1.pack_forget()
            self.b1.pack_forget()
            self.prog.pack()
            self.window.update()

            self.img_dir = f[:f.rfind(".")] + " sequence"
            makedir(self.img_dir)

            if len(str(length_of_vid)) < len(str(length_of_vid + 5)):
                length_of_vid = length_of_vid + 5

            form = "0>" + str(len(str(length_of_vid)))
            self.prog.config(length=length_of_vid / 10)

            while progress:
                if i % ratio == 0:
                    cv2.imwrite(
                        f"{self.img_dir}/image{format(frameCount, form)}.jpg",
                        cv2.UMat(frame))
                    frameCount += 1
                i += 1
                progress, frame = video.read()
                if i % (ratio * 10) == 0:
                    self.prog.increase()

            video.release()
            self.params_json = self.img_dir + params
            if os.path.exists(self.params_json):
                f = open(self.params_json)
                temp = json.load(f)
                f.close()
            else:
                temp = params_json
            temp["duration"] = ratio / fps
            f = open(self.params_json, "w")
            json.dump(temp, f)
            f.close()
            self.exit()
        else:
            messagebox.showerror(title="Bad ratio",
                                 message="Please type a non-negative integer "
                                 "for the frame ratio entry box.")

    def getFolder(self):
        self.img_dir = filedialog.askdirectory()
        if self.img_dir:
            self.params_json = self.img_dir + params
            self.diff_dirs = [self.img_dir + diff1, self.img_dir + diff2]
            self.track_json = self.img_dir + track
            self.contour_json = self.img_dir + contour
            if not os.path.exists(self.params_json):
                self.params_json = ""
            if not (os.path.exists(self.diff_dirs[0]) and os.path.exists(
                    self.diff_dirs[1]) and len(os.listdir(self.diff_dirs[0]))
                    and len(os.listdir(self.diff_dirs[1]))):
                self.diff_dirs = ["", ""]
            if not os.path.exists(self.contour_json):
                self.contour_json = ""
            if not os.path.exists(self.track_json):
                self.track_json = ""
            self.exit()

    def exit(self):
        self.window.destroy()

    def getFiles(self):
        return self.img_dir, self.params_json, self.diff_dirs, self.track_json, self.contour_json
コード例 #2
0
class GetThresh:
    def __init__(self, root, folder):
        self.root = root
        self.folder = folder
        self.window = tk.Toplevel(self.root)
        self.done = False
        self.thresh = 10
        self.img = None
        self.picSize = (0, 0)
        self.hist = None
        self.histTk = None
        self.display = None
        self.displayTk = None
        self.imgs = sorted(os.listdir(self.folder))
        for entry in range(len(self.imgs) - 1, -1, -1):
            temp = self.imgs[entry].rsplit(".")
            if len(temp) < 2 or temp[-1] not in extension:
                self.imgs.pop(entry)

        # Make folders necessary for tool
        self.diff1_p = self.folder + diff1
        self.diff2_p = self.folder + diff2
        self.contour = ""
        self.params = self.folder + params
        if os.path.exists(self.params):
            params_f = open(self.params)
            self.params_json = json.load(params_f)
            params_f.close()
        else:
            self.params_json = params_json
        makedir(self.diff1_p)
        makedir(self.diff2_p)

        # Advanced parameters
        self.canny_lower = self.params_json["canny_lower"]
        self.canny_upper = self.params_json["canny_upper"]
        self.kernel = self.params_json["kernel"]
        self.dilate_it1 = self.params_json["dilate_it1"]
        self.erode_it1 = self.params_json["erode_it1"]
        self.dilate_it2 = self.params_json["dilate_it2"]
        self.erode_it2 = self.params_json["erode_it2"]
        self.area_lower = self.params_json["area_lower"]
        self.area_upper = self.params_json["area_upper"]

        self.setup()

        # Make widgets to be placed in
        self.l0 = tk.Label(self.window, image=self.displayTk)
        self.l1 = tk.Label(self.window, image=self.histTk)
        self.slider = tk.Scale(self.window,
                               from_=0,
                               to=50,
                               orient=tk.HORIZONTAL,
                               length=297)
        self.slider.set(self.thresh)
        self.l2 = tk.Label(self.window, text="Histogram of Pixel Intensity")
        self.b0 = tk.Button(self.window, text="Show", command=self.getPic)
        self.b1 = tk.Button(self.window,
                            text="Set & Run",
                            command=self.setAndRun)
        self.b2 = tk.Button(self.window,
                            text="Advanced",
                            command=self.advanced)
        self.prog = Progress(self.window)

        self.getPic()
        self.l0.grid(row=1, column=1, rowspan=5)
        self.l1.grid(row=2, column=2, columnspan=3)
        self.l2.grid(row=1, column=2, columnspan=3)
        self.slider.grid(row=3, column=2, columnspan=3)
        self.b0.grid(row=4, column=2)
        self.b1.grid(row=4, column=3)
        self.b2.grid(row=4, column=4)

    def setup(self):
        """Gets the desired threshold for cut-off from user."""
        current2 = cv2.imread(self.folder + "/" + self.imgs[0])
        for indx in range(1, len(
                self.imgs)):  # Find a good difference example for user
            current1 = current2
            current2 = cv2.imread(self.folder + "/" + self.imgs[indx])
            diff = cv2.absdiff(current1, current2)
            if self.hasContent(diff):
                self.img = diff
                break

        vals = list(range(0, 50))
        freq = [
            i[0] for i in cv2.calcHist([self.img], [0], None, [256], [0, 256])
        ][:50]

        plt.figure(figsize=(3, 1)).add_axes((0, 0, 1, 1))
        plt.bar(vals, freq, color='#0504aa')
        plt.axis("off")
        buf = io.BytesIO()
        plt.savefig(buf, format='png')
        buf.seek(0)
        self.hist = ImageOps.expand(Image.open(buf), 2, "black")
        self.histTk = ImageTk.PhotoImage(self.hist)
        self.size()

    def hasContent(self, image):
        """Returns true if an image has content above given threshold. False if otherwise."""
        _, baw = cv2.threshold(cv2.cvtColor(image, cv2.COLOR_BGR2GRAY),
                               self.thresh, 255, cv2.THRESH_BINARY)
        return bool(np.amax(baw))

    def advanced(self):
        temp = Advanced(self.window, self.canny_lower, self.canny_upper,
                        self.kernel, self.dilate_it1, self.erode_it1,
                        self.dilate_it2, self.erode_it2, self.area_lower,
                        self.area_upper)
        self.window.wait_window(temp.window)
        self.canny_lower, self.canny_upper, self.kernel, self.dilate_it1, self.erode_it1, \
            self.dilate_it2, self.erode_it2, self.area_lower, self.area_upper = temp.getInfo()

    def size(self, scalar=500):
        w = int(self.img.shape[1])
        h = int(self.img.shape[0])
        scale_temp = scalar / w if w > h else scalar / h
        self.img = cv2.resize(self.img,
                              (int(w * scale_temp), int(h * scale_temp)))
        self.picSize = (self.img.shape[1], self.img.shape[0])

    def getPic(self):
        """Make a new threshold picture based off of the current threshold and put it in the window"""
        self.thresh = int(self.slider.get())
        (_, baw) = cv2.threshold(cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY),
                                 self.thresh, 255, cv2.THRESH_BINARY)
        self.display = cvToPil(baw)
        self.displayTk = ImageTk.PhotoImage(self.display)
        self.l0 = tk.Label(self.window, image=self.displayTk)
        self.l0.grid(row=1, column=1, rowspan=5)

    def selectContours(self, contours):
        temp = []
        for entry in contours:
            if (self.area_upper <= 0 or cv2.contourArea(entry) <= self.area_upper) and \
                    self.area_lower <= cv2.contourArea(entry):
                temp.append(entry)
        return temp

    def setAndRun(self):
        """Sets threshold based off of slider value and runs the difference"""
        if self.thresh != int(self.slider.get()):
            self.getPic()
        else:
            # window configuration
            self.b0.config(state=tk.DISABLED)
            self.b1.config(state=tk.DISABLED)
            self.b2.config(state=tk.DISABLED)
            self.prog.grid(row=5, column=2, columnspan=3)
            self.prog.config(length=len(self.imgs) * 2 + 10)

            # diff 1 files
            indx = 1
            name2 = self.imgs[0]
            current2 = cv2.imread(self.folder + "/" + name2)
            length = len(self.imgs)

            while indx < length:
                name1 = name2
                name2 = self.imgs[indx]
                current1 = current2
                current2 = cv2.imread(self.folder + "/" + name2)
                while not self.hasContent(cv2.absdiff(
                        current1,
                        current2)):  # skip frames w/o significant difference
                    indx += 1
                    self.prog.increase()
                    name2 = self.imgs[indx]
                    current2 = cv2.imread(self.folder + "/" + name2)
                temp = cv2.absdiff(current1,
                                   current2)  # difference between pictures
                temp = cv2.cvtColor(temp,
                                    cv2.COLOR_BGR2GRAY)  # convert to grayscale
                temp = cv2.threshold(
                    temp, self.thresh, 255,
                    cv2.THRESH_TOZERO)[1]  # cut off all pixels below thresh
                temp = cv2.GaussianBlur(
                    temp, (5, 5), 1)  # blur to get rid of remaining noise
                temp = cv2.Canny(
                    temp, self.canny_lower,
                    self.canny_upper)  # find edges with Canny algorithm
                cv2.imwrite(
                    self.diff1_p + "/" + name1, temp
                )  # name is the name of the earlier image in the difference
                indx += 1
                self.prog.increase()

            # diff 2 files
            diff1_imgs = sorted(os.listdir(self.diff1_p))
            indx = 1
            self.contour = self.folder + contour
            contour_dict = {}
            kernel = np.ones((self.kernel, self.kernel))
            current2 = cv2.imread(self.diff1_p + "/" + diff1_imgs.pop(0))
            current2 = cv2.dilate(current2, kernel, iterations=self.dilate_it1)
            current2 = cv2.erode(current2, kernel, iterations=self.erode_it1)

            for image in diff1_imgs:
                current1 = current2
                current2 = cv2.imread(self.diff1_p + "/" + image)
                current2 = cv2.dilate(current2,
                                      kernel,
                                      iterations=self.dilate_it1)
                current2 = cv2.erode(current2,
                                     kernel,
                                     iterations=self.erode_it1)
                temp = cv2.bitwise_and(current1, current2)
                temp = cv2.dilate(temp, kernel, iterations=self.dilate_it2)
                temp = cv2.erode(temp, kernel, iterations=self.erode_it2)
                temp = cv2.threshold(cv2.cvtColor(temp, cv2.COLOR_BGR2GRAY),
                                     100, 255, cv2.THRESH_BINARY)[1]

                # collect and process contours
                contours = cv2.findContours(temp, cv2.RETR_EXTERNAL,
                                            cv2.CHAIN_APPROX_SIMPLE)[0]
                contours = self.selectContours(contours)
                contour_dict[image] = contours

                # save image with contours
                temp = cv2.imread(self.folder + "/" + image)
                cv2.drawContours(temp, contours, -1, (0, 255, 0), 3)
                cv2.imwrite(self.diff2_p + "/" + image, temp)
                indx += 1
                self.prog.increase()

            f = open(self.contour, "w")
            json.dump(contour_dict,
                      f,
                      default=lambda obj: obj.tolist()
                      if isinstance(obj, np.ndarray) else obj)
            f.close()
            self.setParams()
            self.done = True
            self.prog.increase(num=10)
            self.window.destroy()

    def setParams(self):
        self.params_json["canny_lower"] = self.canny_lower
        self.params_json["canny_upper"] = self.canny_upper
        self.params_json["kernel"] = self.kernel
        self.params_json["dilate_it1"] = self.dilate_it1
        self.params_json["erode_it1"] = self.erode_it1
        self.params_json["dilate_it2"] = self.dilate_it2
        self.params_json["erode_it2"] = self.erode_it2
        self.params_json["area_lower"] = self.area_lower
        self.params_json["area_upper"] = self.area_upper
        f = open(self.params, "w")
        json.dump(self.params_json, f)
        f.close()

    def getDirs(self):
        """Return directories of difference pictures"""
        return ([self.diff1_p, self.diff2_p], self.params,
                self.contour) if self.done else (["", ""], "", "")