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
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 (["", ""], "", "")