From 689cf5c0cf11736201ecf324e004b288fb138b74 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sun, 29 Jan 2023 13:03:45 -0800 Subject: [PATCH 01/35] control for 1 frame --- algorithms/CenterRowAlgorithm.py | 2 +- test_pf.py | 66 ++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index fd29486..8dedd05 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -82,7 +82,7 @@ def process_frame(self, frame, show): angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) - return black_frame, angle + return black_frame, mask, contours, angle def create_binary_mask(self, frame): """ diff --git a/test_pf.py b/test_pf.py index 9a59f42..b748c31 100644 --- a/test_pf.py +++ b/test_pf.py @@ -1,9 +1,10 @@ -import time import argparse import os.path as path import sys +import time import cv2 as cv +import numpy as np from omegaconf import OmegaConf from algorithms.CenterRowAlgorithm import CenterRowAlgorithm @@ -13,7 +14,6 @@ from algorithms.MiniContoursDownwards import MiniContoursDownwards from algorithms.ScanningAlgorithm import ScanningAlgorithm - # parser for command line arguments parser = argparse.ArgumentParser() parser.add_argument('-a', '--alg', required=True) @@ -34,7 +34,8 @@ ScanningAlgorithm), ('check_row_end', CheckRowEnd)] - +# +frames_for_GUI = "" # ability to pass in args for reusability def main(args): @@ -96,6 +97,7 @@ def main(args): ) + # copied from test_algorithms.py # runs the algorithm on each frame of video, count the vanishing point uptime def run_algorithm(alg, vid_file): @@ -106,7 +108,12 @@ def run_algorithm(alg, vid_file): total_run = 0 uptime = 0 - all_frame = [] + all_frame_time = [] + # + global frames_for_GUI + frames_for_GUI = {} + i = 0 + # while vid.isOpened(): ret, frame = vid.read() if not ret: @@ -114,11 +121,11 @@ def run_algorithm(alg, vid_file): break start_time_frame = time.time() - processed_image, angle = alg.process_frame(frame, show=args.show) + binary, mask, ctrs, angle = alg.process_frame(frame, show=args.show) end_time_frame = time.time() - all_frame.append(end_time_frame - start_time_frame) + all_frame_time.append(end_time_frame - start_time_frame) - print(angle) + # print(angle) # counters if angle is not None: @@ -127,19 +134,54 @@ def run_algorithm(alg, vid_file): if args.show: cv.imshow( - f'{args.alg}s algorithm on {args.vid}s video', processed_image) + f'{args.alg}s algorithm on {args.vid}s video', binary) + frames_for_GUI.update({"binary"+str(i): binary}) + frames_for_GUI.update({"mask"+str(i): mask}) + frames_for_GUI.update({"ctrs"+str(i): ctrs}) + i += 1 - key = cv.waitKey(25) - - # Exit if Esc key is pressed + key = cv.waitKey(1) if key == 27: break vid.release() cv.destroyAllWindows() - return uptime, total_run, all_frame + return uptime, total_run, all_frame_time + +# display binary_mask, black, contours ... +# TODO: add controller for index of frame +# TODO: add pause/resume functionality +# TODO: add controller after pause + +def update_view(val): + global frames_for_GUI + + if val == 0: + img = frames_for_GUI['binary10'] + elif val == 1: + img = frames_for_GUI['mask10'] + elif val == 2: + img = frames_for_GUI['ctrs10'] + + cv.imshow('Window', img) + + +def init_gui(): + cv.namedWindow('Window') + cv.createTrackbar('Toggle View', 'Window', 0, 2, update_view) + + while True: + key = cv.waitKey(1) + if key == 27: + break + + cv.destroyAllWindows() + + if __name__ == '__main__': args = parser.parse_args() main(args) + if args.show: + init_gui() From dd8f3714a76ce0d87b092f39d339397569cd6b1d Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 4 Feb 2023 11:04:45 -0800 Subject: [PATCH 02/35] controller for all frames --- algorithms/CenterRowAlgorithm.py | 2 +- test_pf.py | 66 +++++++++++++++++++------------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index 8dedd05..26e96e3 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -82,7 +82,7 @@ def process_frame(self, frame, show): angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) - return black_frame, mask, contours, angle + return black_frame, mask, contour_frame, angle def create_binary_mask(self, frame): """ diff --git a/test_pf.py b/test_pf.py index b748c31..cf13914 100644 --- a/test_pf.py +++ b/test_pf.py @@ -36,8 +36,16 @@ CheckRowEnd)] # frames_for_GUI = "" +number_of_frames = 0 +current_frame_number = 0 +# 0 = binary +# 1 = mask +# 2 = contour +current_frame_type = 0 # ability to pass in args for reusability + + def main(args): # verify that video exists in ./videos if not path.isfile(f'videos/{args.vid}.mp4'): @@ -97,7 +105,6 @@ def main(args): ) - # copied from test_algorithms.py # runs the algorithm on each frame of video, count the vanishing point uptime def run_algorithm(alg, vid_file): @@ -110,9 +117,9 @@ def run_algorithm(alg, vid_file): uptime = 0 all_frame_time = [] # - global frames_for_GUI + global frames_for_GUI, number_of_frames frames_for_GUI = {} - i = 0 + number_of_frames = 0 # while vid.isOpened(): ret, frame = vid.read() @@ -133,14 +140,13 @@ def run_algorithm(alg, vid_file): total_run += 1 if args.show: - cv.imshow( - f'{args.alg}s algorithm on {args.vid}s video', binary) - frames_for_GUI.update({"binary"+str(i): binary}) - frames_for_GUI.update({"mask"+str(i): mask}) - frames_for_GUI.update({"ctrs"+str(i): ctrs}) - i += 1 - - key = cv.waitKey(1) + cv.imshow(f'{args.alg}s algorithm on {args.vid}s video', binary) + frames_for_GUI.update({"0"+str(number_of_frames): binary}) + frames_for_GUI.update({"1"+str(number_of_frames): mask}) + frames_for_GUI.update({"2"+str(number_of_frames): ctrs}) + number_of_frames += 1 + + key = cv.waitKey(1) if key == 27: break @@ -149,35 +155,43 @@ def run_algorithm(alg, vid_file): return uptime, total_run, all_frame_time # display binary_mask, black, contours ... -# TODO: add controller for index of frame # TODO: add pause/resume functionality # TODO: add controller after pause -def update_view(val): - global frames_for_GUI - - if val == 0: - img = frames_for_GUI['binary10'] - elif val == 1: - img = frames_for_GUI['mask10'] - elif val == 2: - img = frames_for_GUI['ctrs10'] - + +def render_view(): + global frames_for_GUI, current_frame_type, current_frame_number + img = frames_for_GUI[str(current_frame_type) + str(current_frame_number)] cv.imshow('Window', img) - + + +def update_view(val): + global current_frame_type + current_frame_type = val + render_view() + + +def update_frame(val): + global current_frame_number + current_frame_number = val + render_view() + def init_gui(): + global number_of_frames, current_frame_number, current_frame_type + current_frame_type = 0 + current_frame_number = 0 cv.namedWindow('Window') cv.createTrackbar('Toggle View', 'Window', 0, 2, update_view) + cv.createTrackbar('Toggle Frame', 'Window', 0, + number_of_frames, update_frame) while True: key = cv.waitKey(1) if key == 27: break - - cv.destroyAllWindows() - + cv.destroyAllWindows() if __name__ == '__main__': From bd90af0ad85228d39d0b83f68cc0d2e6fe517ac2 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 4 Feb 2023 12:17:11 -0800 Subject: [PATCH 03/35] real time toggle --- algorithms/CenterRowAlgorithm.py | 2 +- test_pf.py | 29 ++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index 26e96e3..21f419d 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -82,7 +82,7 @@ def process_frame(self, frame, show): angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) - return black_frame, mask, contour_frame, angle + return frame, black_frame, mask, contour_frame, angle def create_binary_mask(self, frame): """ diff --git a/test_pf.py b/test_pf.py index cf13914..9a66fae 100644 --- a/test_pf.py +++ b/test_pf.py @@ -104,6 +104,10 @@ def main(args): "\n" ) +rt_frame_type = 0 +def update_rt_view(val): + global rt_frame_type + rt_frame_type = val # copied from test_algorithms.py # runs the algorithm on each frame of video, count the vanishing point uptime @@ -117,10 +121,15 @@ def run_algorithm(alg, vid_file): uptime = 0 all_frame_time = [] # - global frames_for_GUI, number_of_frames + global frames_for_GUI, number_of_frames, rt_frame_type frames_for_GUI = {} number_of_frames = 0 # + window_name = f'{args.alg}s algorithm on {args.vid}s video' + if args.show: + cv.namedWindow(window_name) + cv.createTrackbar('Toggle View', window_name, 0, 3, update_rt_view) + while vid.isOpened(): ret, frame = vid.read() if not ret: @@ -128,7 +137,7 @@ def run_algorithm(alg, vid_file): break start_time_frame = time.time() - binary, mask, ctrs, angle = alg.process_frame(frame, show=args.show) + standard, binary, mask, ctrs, angle = alg.process_frame(frame, show=args.show) end_time_frame = time.time() all_frame_time.append(end_time_frame - start_time_frame) @@ -140,7 +149,18 @@ def run_algorithm(alg, vid_file): total_run += 1 if args.show: - cv.imshow(f'{args.alg}s algorithm on {args.vid}s video', binary) + # key = cv.waitKey(1) + # if key == 80: + # pause_process() + if (rt_frame_type == 0): + cv.imshow(window_name, binary) + elif (rt_frame_type == 1): + cv.imshow(window_name, mask) + elif (rt_frame_type == 2): + cv.imshow(window_name, ctrs) + elif (rt_frame_type == 3): + cv.imshow(window_name, standard) + frames_for_GUI.update({"0"+str(number_of_frames): binary}) frames_for_GUI.update({"1"+str(number_of_frames): mask}) frames_for_GUI.update({"2"+str(number_of_frames): ctrs}) @@ -158,6 +178,9 @@ def run_algorithm(alg, vid_file): # TODO: add pause/resume functionality # TODO: add controller after pause +def pause_process(): + + time.sleep() def render_view(): global frames_for_GUI, current_frame_type, current_frame_number From 65f6e99d81ab9f41324d749fe46ebe643e3030f4 Mon Sep 17 00:00:00 2001 From: Abhigyan Dabla Date: Sun, 5 Feb 2023 14:37:07 -0800 Subject: [PATCH 04/35] forcing me to save --- pre_process.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++ test_pf.py | 37 ++++++++++++++++--- 2 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 pre_process.py diff --git a/pre_process.py b/pre_process.py new file mode 100644 index 0000000..7b29a56 --- /dev/null +++ b/pre_process.py @@ -0,0 +1,97 @@ +import cv2 +import math +import numpy as np + +BRIGHTNESS_BASELINE = 110 +SATURATION_BASELINE = 105 +ACCEPTABLE_DIFFERENCE = 25 + + +def alter_brightness(img, amount): + if amount > 0: + return increase_brightness(img, amount) + elif amount < 0: + return decrease_brightness(img, -1 * amount) + else: + return img + + +def alter_saturation(img, amount): + if amount > 0: + return increase_saturation(img, amount) + elif amount < 0: + return decrease_saturation(img, -1 * amount) + else: + return img + + +def decrease_brightness(img, value): + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + h, s, v = cv2.split(hsv) + + v[v < value] = 0 + v[v >= value] -= value + + final_hsv = cv2.merge((h, s, v)) + return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR) + + +def increase_brightness(img, value): + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + h, s, v = cv2.split(hsv) + + lim = 255 - value + v[v > lim] = 255 + v[v <= lim] += value + + final_hsv = cv2.merge((h, s, v)) + return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR) + + +def decrease_saturation(img, value): + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + h, s, v = cv2.split(hsv) + + s[s < value] = 0 + s[s >= value] -= value + + final_hsv = cv2.merge((h, s, v)) + return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR) + + +def increase_saturation(img, value): + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + h, s, v = cv2.split(hsv) + + lim = 255 - value + s[s > lim] = 255 + s[s <= lim] += value + + final_hsv = cv2.merge((h, s, v)) + return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR) + + +def standardize_frame(img): + try: + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + except: + img_float32 = np.float32(img) + hsv = cv2.cvtColor(img_float32, cv2.COLOR_BGR2HSV) + + mean_brightness = hsv[..., 2].mean() + mean_saturation = hsv[..., 1].mean() + + if mean_brightness < BRIGHTNESS_BASELINE: + if mean_brightness + ACCEPTABLE_DIFFERENCE < BRIGHTNESS_BASELINE: + img = alter_brightness(img, math.floor(BRIGHTNESS_BASELINE - (mean_brightness + ACCEPTABLE_DIFFERENCE))) + else: + if mean_brightness - ACCEPTABLE_DIFFERENCE > BRIGHTNESS_BASELINE: + img = alter_brightness(img, math.floor(BRIGHTNESS_BASELINE - (mean_brightness - ACCEPTABLE_DIFFERENCE))) + if mean_saturation < SATURATION_BASELINE: + if mean_saturation + ACCEPTABLE_DIFFERENCE < SATURATION_BASELINE: + img = alter_saturation(img, math.floor(SATURATION_BASELINE - (mean_saturation + ACCEPTABLE_DIFFERENCE))) + else: + if mean_saturation - ACCEPTABLE_DIFFERENCE > SATURATION_BASELINE: + img = alter_saturation(img, math.floor(SATURATION_BASELINE - (mean_saturation - ACCEPTABLE_DIFFERENCE))) + + return img \ No newline at end of file diff --git a/test_pf.py b/test_pf.py index cf13914..90d4803 100644 --- a/test_pf.py +++ b/test_pf.py @@ -2,7 +2,7 @@ import os.path as path import sys import time - +import pre_process import cv2 as cv import numpy as np from omegaconf import OmegaConf @@ -42,6 +42,8 @@ # 1 = mask # 2 = contour current_frame_type = 0 +current_avg_brightness = 110 +current_avg_saturation = 105 # ability to pass in args for reusability @@ -161,7 +163,16 @@ def run_algorithm(alg, vid_file): def render_view(): global frames_for_GUI, current_frame_type, current_frame_number + global current_avg_brightness, current_avg_saturation + img = frames_for_GUI[str(current_frame_type) + str(current_frame_number)] + + pre_process.ACCEPTABLE_DIFFERENCE = 0 + pre_process.BRIGHTNESS_BASELINE = current_avg_brightness + pre_process.SATURATION_BASELINE = current_avg_saturation + + img = pre_process.standardize_frame(img) + cv.imshow('Window', img) @@ -177,14 +188,32 @@ def update_frame(val): render_view() +def update_brightness(val): + global current_avg_brightness + current_avg_brightness = val + render_view() + + +def update_saturation(val): + global current_avg_saturation + current_avg_saturation = val + render_view() + + def init_gui(): global number_of_frames, current_frame_number, current_frame_type + global current_avg_brightness, current_avg_saturation + current_frame_type = 0 current_frame_number = 0 + current_avg_brightness = 110 + current_avg_saturation = 105 + cv.namedWindow('Window') - cv.createTrackbar('Toggle View', 'Window', 0, 2, update_view) - cv.createTrackbar('Toggle Frame', 'Window', 0, - number_of_frames, update_frame) + cv.createTrackbar('Toggle View', 'Window', current_frame_type, 2, update_view) + cv.createTrackbar('Toggle Frame', 'Window', current_frame_number, number_of_frames, update_frame) + cv.createTrackbar('Brightness', 'Window', current_avg_brightness, 255, update_brightness) + cv.createTrackbar('Saturation', 'Window', current_avg_saturation, 255, update_saturation) while True: key = cv.waitKey(1) From 555d0f0ab34216fc8ad73e65a09b50af45ed055d Mon Sep 17 00:00:00 2001 From: Abhigyan Dabla Date: Sun, 5 Feb 2023 14:43:13 -0800 Subject: [PATCH 05/35] Added brightness and saturation toggles to window --- test_pf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test_pf.py b/test_pf.py index a0c0259..f7f0408 100644 --- a/test_pf.py +++ b/test_pf.py @@ -251,3 +251,4 @@ def init_gui(): main(args) if args.show: init_gui() + From 8cc1051aae4df4720c9d8af8acbc4113207ffd4b Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 11 Feb 2023 13:51:08 -0800 Subject: [PATCH 06/35] real time toggle for brightness and saturation --- gui.py | 66 ++++++++++++++++++ node_modules/.yarn-integrity | 10 +++ test_pf.py | 132 +++++++++++------------------------ yarn.lock | 4 ++ 4 files changed, 119 insertions(+), 93 deletions(-) create mode 100644 gui.py create mode 100644 node_modules/.yarn-integrity create mode 100644 yarn.lock diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..1daa506 --- /dev/null +++ b/gui.py @@ -0,0 +1,66 @@ +from tkinter import (HORIZONTAL, Button, Checkbutton, Frame, IntVar, Label, + Radiobutton, Tk, ttk, PhotoImage, Canvas, Image) + +import cv2 +import numpy as np + +class SimpleGUI(Tk): + + def __init__(self, frame): + super().__init__() + self.frame = frame + self.initializeUI() + + def initializeUI(self): + self.title("Live View") + self.minsize(300, 200) # width, height + self.geometry("400x350+50+50") + self.setupWindow() + + def setupWindow(self): + """ Set up the widgets.""" + title = Label(self, text="Live Toggles", + font=('Helvetica', 20), bd=10) + title.pack() + + line = ttk.Separator(self, orient=HORIZONTAL) + line.pack(fill='x') + + payment_label = Label(self, text="Select View", bd=10) + payment_label.pack(anchor='w') + + # Create integer variable + self.var = IntVar() + self.var.set(0) # Use set() initialize the variable + self.current_view = ["No Filter", "Binary", "Mask"] + + for i, method in enumerate(self.current_view): + self.curr_v = Radiobutton( + self, text=method, variable=self.var, value=i) + self.curr_v.pack(anchor='w') + + # Use ttk to add styling to button + style = ttk.Style() + style.configure('TButton', bg='skyblue', fg='white') + + # Create button that will call the method to display text and + # close the program + next_button = ttk.Button(self, text="Apply", command=self.printResults) + next_button.pack() + + # Add canvas for displaying frame + self.canvas = Canvas(self, width=400, height=300) + self.canvas.pack() + + # Convert frame to PhotoImage object for display in canvas + frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGBA) + self.frame = cv2.resize(frame, (400, 300)) + self.photo = PhotoImage(image=Image.fromarray(self.frame)) + self.canvas.create_image(0, 0, image=self.photo, anchor='nw') + + def printResults(self): + """Print the results of the checkboxes and radio buttons.""" + + index = self.var.get() + print("View: {}".format(self.current_view[index])) + self.quit() diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 0000000..20d6d48 --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "darwin-arm64-108", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/test_pf.py b/test_pf.py index f7f0408..d376ec4 100644 --- a/test_pf.py +++ b/test_pf.py @@ -2,11 +2,12 @@ import os.path as path import sys import time -import pre_process + import cv2 as cv import numpy as np from omegaconf import OmegaConf +import pre_process from algorithms.CenterRowAlgorithm import CenterRowAlgorithm from algorithms.CheckRowEnd import CheckRowEnd from algorithms.HoughAlgorithm import HoughAlgorithm @@ -38,16 +39,13 @@ frames_for_GUI = "" number_of_frames = 0 current_frame_number = 0 -# 0 = binary -# 1 = mask -# 2 = contour current_frame_type = 0 current_avg_brightness = 110 current_avg_saturation = 105 - -# ability to pass in args for reusability +rt_frame_type = 0 +# ability to pass in args for reusability def main(args): # verify that video exists in ./videos if not path.isfile(f'videos/{args.vid}.mp4'): @@ -106,11 +104,22 @@ def main(args): "\n" ) -rt_frame_type = 0 + def update_rt_view(val): global rt_frame_type rt_frame_type = val + +def apply_rt_brightness(val): + global current_avg_brightness + current_avg_brightness = val + + +def apply_rt_saturation(val): + global current_avg_saturation + current_avg_saturation = val + + # copied from test_algorithms.py # runs the algorithm on each frame of video, count the vanishing point uptime def run_algorithm(alg, vid_file): @@ -123,7 +132,9 @@ def run_algorithm(alg, vid_file): uptime = 0 all_frame_time = [] # - global frames_for_GUI, number_of_frames, rt_frame_type + global frames_for_GUI, number_of_frames, rt_frame_type, current_frame_number + global current_avg_brightness, current_avg_saturation + frames_for_GUI = {} number_of_frames = 0 # @@ -131,7 +142,13 @@ def run_algorithm(alg, vid_file): if args.show: cv.namedWindow(window_name) cv.createTrackbar('Toggle View', window_name, 0, 3, update_rt_view) - + # cv.createTrackbar('Toggle View', 'Window', current_frame_type, 2, update_view) + # cv.createTrackbar('Toggle Frame', window_name, current_frame_number, number_of_frames, update_frame) + cv.createTrackbar('Brightness', window_name, + current_avg_brightness, 255, apply_rt_brightness) + cv.createTrackbar('Saturation', window_name, + current_avg_saturation, 255, apply_rt_saturation) + while vid.isOpened(): ret, frame = vid.read() if not ret: @@ -139,7 +156,8 @@ def run_algorithm(alg, vid_file): break start_time_frame = time.time() - standard, binary, mask, ctrs, angle = alg.process_frame(frame, show=args.show) + standard, binary, mask, ctrs, angle = alg.process_frame( + frame, show=args.show) end_time_frame = time.time() all_frame_time.append(end_time_frame - start_time_frame) @@ -151,104 +169,32 @@ def run_algorithm(alg, vid_file): total_run += 1 if args.show: - # key = cv.waitKey(1) - # if key == 80: - # pause_process() - if (rt_frame_type == 0): - cv.imshow(window_name, binary) - elif (rt_frame_type == 1): - cv.imshow(window_name, mask) - elif (rt_frame_type == 2): - cv.imshow(window_name, ctrs) - elif (rt_frame_type == 3): - cv.imshow(window_name, standard) - + frames_for_GUI.update({"0"+str(number_of_frames): binary}) frames_for_GUI.update({"1"+str(number_of_frames): mask}) frames_for_GUI.update({"2"+str(number_of_frames): ctrs}) - number_of_frames += 1 - - key = cv.waitKey(1) - if key == 27: - break - - vid.release() - cv.destroyAllWindows() - return uptime, total_run, all_frame_time - -# display binary_mask, black, contours ... -# TODO: add pause/resume functionality -# TODO: add controller after pause - -def pause_process(): - - time.sleep() - -def render_view(): - global frames_for_GUI, current_frame_type, current_frame_number - global current_avg_brightness, current_avg_saturation - - img = frames_for_GUI[str(current_frame_type) + str(current_frame_number)] - - pre_process.ACCEPTABLE_DIFFERENCE = 0 - pre_process.BRIGHTNESS_BASELINE = current_avg_brightness - pre_process.SATURATION_BASELINE = current_avg_saturation - - img = pre_process.standardize_frame(img) - - cv.imshow('Window', img) - - -def update_view(val): - global current_frame_type - current_frame_type = val - render_view() - - -def update_frame(val): - global current_frame_number - current_frame_number = val - render_view() - - -def update_brightness(val): - global current_avg_brightness - current_avg_brightness = val - render_view() - - -def update_saturation(val): - global current_avg_saturation - current_avg_saturation = val - render_view() + frames_for_GUI.update({"3"+str(number_of_frames): standard}) + img = frames_for_GUI[str(rt_frame_type) + str(number_of_frames)] + number_of_frames += 1 -def init_gui(): - global number_of_frames, current_frame_number, current_frame_type - global current_avg_brightness, current_avg_saturation + pre_process.ACCEPTABLE_DIFFERENCE = 0 + pre_process.BRIGHTNESS_BASELINE = current_avg_brightness + pre_process.SATURATION_BASELINE = current_avg_saturation - current_frame_type = 0 - current_frame_number = 0 - current_avg_brightness = 110 - current_avg_saturation = 105 + img = pre_process.standardize_frame(img) - cv.namedWindow('Window') - cv.createTrackbar('Toggle View', 'Window', current_frame_type, 2, update_view) - cv.createTrackbar('Toggle Frame', 'Window', current_frame_number, number_of_frames, update_frame) - cv.createTrackbar('Brightness', 'Window', current_avg_brightness, 255, update_brightness) - cv.createTrackbar('Saturation', 'Window', current_avg_saturation, 255, update_saturation) + cv.imshow(window_name, img) - while True: key = cv.waitKey(1) if key == 27: break + vid.release() cv.destroyAllWindows() + return uptime, total_run, all_frame_time if __name__ == '__main__': args = parser.parse_args() main(args) - if args.show: - init_gui() - diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fb57ccd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + From 8b7377abc95f45efd9fb126654cac24ff1c32d74 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 11 Feb 2023 13:52:10 -0800 Subject: [PATCH 07/35] real time toggle for brightness and saturation --- node_modules/.yarn-integrity | 10 ---------- yarn.lock | 4 ---- 2 files changed, 14 deletions(-) delete mode 100644 node_modules/.yarn-integrity delete mode 100644 yarn.lock diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity deleted file mode 100644 index 20d6d48..0000000 --- a/node_modules/.yarn-integrity +++ /dev/null @@ -1,10 +0,0 @@ -{ - "systemParams": "darwin-arm64-108", - "modulesFolders": [], - "flags": [], - "linkedModules": [], - "topLevelPatterns": [], - "lockfileEntries": {}, - "files": [], - "artifacts": {} -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index fb57ccd..0000000 --- a/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - From fc550cd05ca74e61c45057ef29148e69b992e3fd Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 11 Feb 2023 18:00:08 -0800 Subject: [PATCH 08/35] created better GUI with tkinter --- gui.py | 175 ++++++++++++++++++++++++++++++++++------------------- test_pf.py | 62 +++---------------- 2 files changed, 121 insertions(+), 116 deletions(-) diff --git a/gui.py b/gui.py index 1daa506..3e2bd52 100644 --- a/gui.py +++ b/gui.py @@ -1,66 +1,115 @@ -from tkinter import (HORIZONTAL, Button, Checkbutton, Frame, IntVar, Label, - Radiobutton, Tk, ttk, PhotoImage, Canvas, Image) +import tkinter as tk -import cv2 +import cv2 as cv import numpy as np +from PIL import Image, ImageTk -class SimpleGUI(Tk): - - def __init__(self, frame): - super().__init__() - self.frame = frame - self.initializeUI() - - def initializeUI(self): - self.title("Live View") - self.minsize(300, 200) # width, height - self.geometry("400x350+50+50") - self.setupWindow() - - def setupWindow(self): - """ Set up the widgets.""" - title = Label(self, text="Live Toggles", - font=('Helvetica', 20), bd=10) - title.pack() - - line = ttk.Separator(self, orient=HORIZONTAL) - line.pack(fill='x') - - payment_label = Label(self, text="Select View", bd=10) - payment_label.pack(anchor='w') - - # Create integer variable - self.var = IntVar() - self.var.set(0) # Use set() initialize the variable - self.current_view = ["No Filter", "Binary", "Mask"] - - for i, method in enumerate(self.current_view): - self.curr_v = Radiobutton( - self, text=method, variable=self.var, value=i) - self.curr_v.pack(anchor='w') - - # Use ttk to add styling to button - style = ttk.Style() - style.configure('TButton', bg='skyblue', fg='white') - - # Create button that will call the method to display text and - # close the program - next_button = ttk.Button(self, text="Apply", command=self.printResults) - next_button.pack() - - # Add canvas for displaying frame - self.canvas = Canvas(self, width=400, height=300) - self.canvas.pack() - - # Convert frame to PhotoImage object for display in canvas - frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGBA) - self.frame = cv2.resize(frame, (400, 300)) - self.photo = PhotoImage(image=Image.fromarray(self.frame)) - self.canvas.create_image(0, 0, image=self.photo, anchor='nw') - - def printResults(self): - """Print the results of the checkboxes and radio buttons.""" - - index = self.var.get() - print("View: {}".format(self.current_view[index])) - self.quit() +import pre_process + +img_dict = {} + + +class GUI: + """Creates GUI with tkinter: initializes a master window, creates the same number of radiobuttons as the length of img_dict, + creates 2 sliders for brightness and saturation, then render images accordingly\n + :param `root` = tk.Tk() + :param `img_dict` = { "frameType1": numpy.ndarray } + :param `window_name` = String + """ + + def __init__(self, master, img_dict, window_name): + self.master = master + self.master.title(window_name) + + self.current_avg_brightness = 110 + self.current_avg_saturation = 105 + + self.var = tk.IntVar() + self.curr_selected = 1 + + self.frame = tk.Frame(self.master, bg="white", height=600, width=600) + self.frame.pack(fill="both", expand="yes") + + self.label = tk.Label(self.frame, bg="white", width=450, height=450) + self.label.pack(fill="both", expand="yes") + + for i, (img_name, img_array) in enumerate(img_dict.items(), 1): + radiobutton = tk.Radiobutton( + self.master, text=img_name, variable=self.var, value=i, command=lambda i=i: self.update_frameType(i)) + radiobutton.pack(pady=10, side="bottom") + + self.brightness_scale = tk.Scale(self.master, from_=0, to=255, orient="horizontal", + label="Brightness", command=lambda x: self.update_brightness(x)) + self.brightness_scale.set(self.current_avg_brightness) + self.brightness_scale.pack(pady=10, side="bottom") + + self.saturation_scale = tk.Scale(self.master, from_=0, to=255, orient="horizontal", + label="Saturation", command=lambda x: self.update_saturation(x)) + self.saturation_scale.set(self.current_avg_saturation) + self.saturation_scale.pack(pady=10, side="bottom") + + self.render_image() + + def update_frameType(self, next): + self.curr_selected = next + + def update_brightness(self, value): + self.current_avg_brightness = int(value) + + def update_saturation(self, value): + self.current_avg_saturation = int(value) + + def apply_filter(self, img): + pre_process.ACCEPTABLE_DIFFERENCE = 0 + pre_process.BRIGHTNESS_BASELINE = self.current_avg_brightness + pre_process.SATURATION_BASELINE = self.current_avg_saturation + return pre_process.standardize_frame(img) + + def render_image(self): + global img_dict + curr_img = list(img_dict.values())[self.curr_selected - 1] + if np.any(curr_img != np.zeros((100, 100))): + curr_img = self.apply_filter(curr_img) + + curr_img = np.array(curr_img).astype(np.uint8) + curr_img = cv.cvtColor(curr_img, cv.COLOR_BGR2RGB) + + curr_img = Image.fromarray(curr_img) + curr_img = ImageTk.PhotoImage(curr_img) + self.label.config(image=curr_img) + self.label.image = curr_img + self.master.update() + + def update_dict(self, key_value): + global img_dict + img_dict.update(key_value) + + +def startGUI(window_name, **kwargs): + """Constructs GUI: pass in a `window name` and at least 1 name of frame: `name1="my_chosen_name_1", name2="my_chosen_name_2", ...`\n + use `update_dict({"my_chosen_name_1": numpy.ndarray})` to update image\n + call `render_image()` after updating dict to render\n + :param `window_name` of type String + :(param >= 1) `name1="some string"`, `name2="some string"`, `name3="some string"`, ... + """ + global img_dict + # for i in range(numberOfViews): + # frame_type = "frameType" + str(i + 1) + # img_dict[frame_type] = np.zeros((100, 100)) + curr_name = "name" + curr_index = 1 + if kwargs == {}: + raise KeyError("kwargs is empty in startGUI(), start with name1= ... ") + + for key, value in kwargs.items(): + if key == curr_name+str(curr_index): + frame_type = value + img_dict[frame_type] = np.zeros((100, 100)) + curr_index += 1 + else: + raise KeyError("argument " + curr_name + + str(curr_index) + " is missing in startGUI()") + + root = tk.Tk() + app = GUI(root, img_dict, window_name) + return app diff --git a/test_pf.py b/test_pf.py index d376ec4..c45cbf1 100644 --- a/test_pf.py +++ b/test_pf.py @@ -2,18 +2,19 @@ import os.path as path import sys import time +import tkinter as tk import cv2 as cv import numpy as np from omegaconf import OmegaConf -import pre_process from algorithms.CenterRowAlgorithm import CenterRowAlgorithm from algorithms.CheckRowEnd import CheckRowEnd from algorithms.HoughAlgorithm import HoughAlgorithm from algorithms.MiniContoursAlgorithm import MiniContoursAlgorithm from algorithms.MiniContoursDownwards import MiniContoursDownwards from algorithms.ScanningAlgorithm import ScanningAlgorithm +from gui import startGUI # parser for command line arguments parser = argparse.ArgumentParser() @@ -36,13 +37,7 @@ ('check_row_end', CheckRowEnd)] # -frames_for_GUI = "" number_of_frames = 0 -current_frame_number = 0 -current_frame_type = 0 -current_avg_brightness = 110 -current_avg_saturation = 105 -rt_frame_type = 0 # ability to pass in args for reusability @@ -105,21 +100,6 @@ def main(args): ) -def update_rt_view(val): - global rt_frame_type - rt_frame_type = val - - -def apply_rt_brightness(val): - global current_avg_brightness - current_avg_brightness = val - - -def apply_rt_saturation(val): - global current_avg_saturation - current_avg_saturation = val - - # copied from test_algorithms.py # runs the algorithm on each frame of video, count the vanishing point uptime def run_algorithm(alg, vid_file): @@ -132,22 +112,10 @@ def run_algorithm(alg, vid_file): uptime = 0 all_frame_time = [] # - global frames_for_GUI, number_of_frames, rt_frame_type, current_frame_number - global current_avg_brightness, current_avg_saturation - - frames_for_GUI = {} - number_of_frames = 0 - # - window_name = f'{args.alg}s algorithm on {args.vid}s video' if args.show: - cv.namedWindow(window_name) - cv.createTrackbar('Toggle View', window_name, 0, 3, update_rt_view) - # cv.createTrackbar('Toggle View', 'Window', current_frame_type, 2, update_view) - # cv.createTrackbar('Toggle Frame', window_name, current_frame_number, number_of_frames, update_frame) - cv.createTrackbar('Brightness', window_name, - current_avg_brightness, 255, apply_rt_brightness) - cv.createTrackbar('Saturation', window_name, - current_avg_saturation, 255, apply_rt_saturation) + window_name = f'{args.alg}s algorithm on {args.vid}s video' + app = startGUI(window_name, name1="standard", + name2="binary", name3="mask") while vid.isOpened(): ret, frame = vid.read() @@ -169,22 +137,10 @@ def run_algorithm(alg, vid_file): total_run += 1 if args.show: - - frames_for_GUI.update({"0"+str(number_of_frames): binary}) - frames_for_GUI.update({"1"+str(number_of_frames): mask}) - frames_for_GUI.update({"2"+str(number_of_frames): ctrs}) - frames_for_GUI.update({"3"+str(number_of_frames): standard}) - - img = frames_for_GUI[str(rt_frame_type) + str(number_of_frames)] - number_of_frames += 1 - - pre_process.ACCEPTABLE_DIFFERENCE = 0 - pre_process.BRIGHTNESS_BASELINE = current_avg_brightness - pre_process.SATURATION_BASELINE = current_avg_saturation - - img = pre_process.standardize_frame(img) - - cv.imshow(window_name, img) + app.update_dict({'standard': standard}) + app.update_dict({'binary': binary}) + app.update_dict({'mask': mask}) + app.render_image() key = cv.waitKey(1) if key == 27: From 54e4e36c915934459389e10e61a3ad03a462640e Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Mon, 13 Feb 2023 03:22:53 -0800 Subject: [PATCH 09/35] configure to support all algorithm --- README.md | 6 +++--- algorithms/CenterRowAlgorithm.py | 8 ++++++-- algorithms/CheckRowEnd.py | 4 ++++ algorithms/HoughAlgorithm.py | 4 ++++ algorithms/MiniContoursAlgorithm.py | 16 +++++++++++----- algorithms/MiniContoursDownwards.py | 4 ++++ algorithms/ScanningAlgorithm.py | 4 ++++ algorithms/SeesawAlgorithm.py | 4 ++++ gui.py | 3 ++- test_pf.py | 6 +++--- 10 files changed, 45 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index f361aea..8b88848 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ `test_pf.py:` - - runs performance tests on any of the algorithms against any video + - runs performance tests on any of the algorithms against any video with an optional GUI - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - arguments : - `-a/--alg` : name of algorithm @@ -46,8 +46,8 @@ - `-v/--vid` : name of video - video has to be stored in `./videos/` - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show` : process drawings and show frame - - example : `python test_pf.py -a center_row -v farm4` + - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views + - example : `python test_pf.py -a center_row -v farm4 -s` ### Commands to Start World in Gazebo diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index 21f419d..97c7b6b 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -2,8 +2,8 @@ import cv2 as cv import numpy as np -from algorithms.Algorithm import Algorithm +from algorithms.Algorithm import Algorithm from algorithms.utils import Lines @@ -44,6 +44,10 @@ def __init__(self, config): self.center = None self.center_angle = 0 + def get_extra_content(self, frame, show): + item1, item2 = self.process_frame(frame, show) + return frame, item1, item2 + def process_frame(self, frame, show): """Uses contouring to create contours around each crop row and uses these contours to find centroid lines, row vanishing point, a center contour and the angle between the center contour and vanishing point\n @@ -82,7 +86,7 @@ def process_frame(self, frame, show): angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) - return frame, black_frame, mask, contour_frame, angle + return black_frame, angle def create_binary_mask(self, frame): """ diff --git a/algorithms/CheckRowEnd.py b/algorithms/CheckRowEnd.py index d13a9ad..8cf78c1 100644 --- a/algorithms/CheckRowEnd.py +++ b/algorithms/CheckRowEnd.py @@ -17,6 +17,10 @@ def __init__(self, config): # Percentage of empty rows required to register as row end self.percentage_of_empty_rows = config.percentage_of_empty_rows + def get_extra_content(self, frame, show): + item1, item2 = self.process_frame(frame, show) + return frame, item1, item2 + def process_frame(self, frame, show): """Averages values in each row in a mask of the frame. If the number of rows with an average value of zero is greater than req_rows_empty, then frame is row end\n diff --git a/algorithms/HoughAlgorithm.py b/algorithms/HoughAlgorithm.py index 6239505..69c4836 100644 --- a/algorithms/HoughAlgorithm.py +++ b/algorithms/HoughAlgorithm.py @@ -32,6 +32,10 @@ def __init__(self, config): # resize factor self.RESIZE_FACTOR = config.resize_factor + def get_extra_content(self, frame, show): + item1, item2 = self.process_frame(frame, show) + return frame, item1, item2 + # processFrame function that is called to process a frame of a video # takes in frame mat object obtained from cv2 video.read() def process_frame(self, frame, show=True): diff --git a/algorithms/MiniContoursAlgorithm.py b/algorithms/MiniContoursAlgorithm.py index 77712e1..9e1d3e9 100644 --- a/algorithms/MiniContoursAlgorithm.py +++ b/algorithms/MiniContoursAlgorithm.py @@ -1,11 +1,13 @@ -import cv2 -import numpy as np -import time +import math import operator import sys -import math -from algorithms.utils import Lines +import time + +import cv2 +import numpy as np + from algorithms.Algorithm import Algorithm +from algorithms.utils import Lines class MiniContoursAlgorithm(Algorithm): @@ -199,6 +201,10 @@ def get_center_hough_lines( return frame, lines, point_lines + def get_extra_content(self, frame, show): + item1, item2 = self.process_frame(frame, show) + return frame, item1, item2 + def process_frame(self, original_frame, num_strips=60, show=False): # original_frame: BGR frame diff --git a/algorithms/MiniContoursDownwards.py b/algorithms/MiniContoursDownwards.py index 80569f5..51472db 100644 --- a/algorithms/MiniContoursDownwards.py +++ b/algorithms/MiniContoursDownwards.py @@ -152,6 +152,10 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p return frame, line + def get_extra_content(self, frame, show): + item1, item2 = self.process_frame(frame, show) + return frame, item1, item2 + def process_frame(self, original_frame, num_strips=60, show=False): """"" parameters: diff --git a/algorithms/ScanningAlgorithm.py b/algorithms/ScanningAlgorithm.py index 5522fb5..36c8981 100644 --- a/algorithms/ScanningAlgorithm.py +++ b/algorithms/ScanningAlgorithm.py @@ -74,6 +74,10 @@ def create_line(self, start_x, start_y, end_x, end_y): return line + def get_extra_content(self, frame, show): + item1, item2 = self.process_frame(frame, show) + return frame, item1, item2 + def process_frame(self, frame, show): hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) diff --git a/algorithms/SeesawAlgorithm.py b/algorithms/SeesawAlgorithm.py index 5bb3e98..8048fe5 100644 --- a/algorithms/SeesawAlgorithm.py +++ b/algorithms/SeesawAlgorithm.py @@ -23,6 +23,10 @@ def __init__(self, config): self.HEIGHT = int(config.frame_length) self.WIDTH = int(config.frame_width) + def get_extra_content(self, frame, show): + item1, item2 = self.process_frame(frame, show) + return frame, item1, item2 + def process_frame(self, frame, show): black_frame, points, both_points = self.plot_points(frame) diff --git a/gui.py b/gui.py index 3e2bd52..5aa2ee3 100644 --- a/gui.py +++ b/gui.py @@ -7,7 +7,8 @@ import pre_process img_dict = {} - +# TODO: check_row_end img +# TODO: pause and continue class GUI: """Creates GUI with tkinter: initializes a master window, creates the same number of radiobuttons as the length of img_dict, diff --git a/test_pf.py b/test_pf.py index c45cbf1..ca924d4 100644 --- a/test_pf.py +++ b/test_pf.py @@ -115,7 +115,7 @@ def run_algorithm(alg, vid_file): if args.show: window_name = f'{args.alg}s algorithm on {args.vid}s video' app = startGUI(window_name, name1="standard", - name2="binary", name3="mask") + name2="binary") while vid.isOpened(): ret, frame = vid.read() @@ -124,7 +124,7 @@ def run_algorithm(alg, vid_file): break start_time_frame = time.time() - standard, binary, mask, ctrs, angle = alg.process_frame( + standard, binary, angle = alg.get_extra_content( frame, show=args.show) end_time_frame = time.time() all_frame_time.append(end_time_frame - start_time_frame) @@ -139,7 +139,7 @@ def run_algorithm(alg, vid_file): if args.show: app.update_dict({'standard': standard}) app.update_dict({'binary': binary}) - app.update_dict({'mask': mask}) + # app.update_dict({'mask': ctrs}) app.render_image() key = cv.waitKey(1) From 45bdc627fc2accdc9af9ee1c20b29ad75c3e8bd6 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Mon, 13 Feb 2023 13:47:25 -0800 Subject: [PATCH 10/35] update doc, minor change to gui and pre_process --- README.md | 2 +- algorithms/CenterRowAlgorithm.py | 2 +- algorithms/CheckRowEnd.py | 2 +- algorithms/HoughAlgorithm.py | 2 +- algorithms/MiniContoursAlgorithm.py | 2 +- algorithms/MiniContoursDownwards.py | 2 +- algorithms/ScanningAlgorithm.py | 2 +- algorithms/SeesawAlgorithm.py | 2 +- gui.py | 5 +++-- pre_process.py | 20 ++++++++++++++------ test_pf.py | 10 +++++----- 11 files changed, 30 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 8b88848..ea47957 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ - video has to be stored in `./videos/` - video configuration has to be stored as a `yaml` at `./config/video/` - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views - - example : `python test_pf.py -a center_row -v farm4 -s` + - example : `python test_pf.py -a center_row -v crop -s` ### Commands to Start World in Gazebo diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index 97c7b6b..af91fc1 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -46,7 +46,7 @@ def __init__(self, config): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) - return frame, item1, item2 + return item1, item2 def process_frame(self, frame, show): """Uses contouring to create contours around each crop row and uses these contours to find centroid lines, diff --git a/algorithms/CheckRowEnd.py b/algorithms/CheckRowEnd.py index 8cf78c1..9586bc4 100644 --- a/algorithms/CheckRowEnd.py +++ b/algorithms/CheckRowEnd.py @@ -19,7 +19,7 @@ def __init__(self, config): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) - return frame, item1, item2 + return item1, item2 def process_frame(self, frame, show): """Averages values in each row in a mask of the frame. If the number of rows with an average value diff --git a/algorithms/HoughAlgorithm.py b/algorithms/HoughAlgorithm.py index 69c4836..860a589 100644 --- a/algorithms/HoughAlgorithm.py +++ b/algorithms/HoughAlgorithm.py @@ -34,7 +34,7 @@ def __init__(self, config): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) - return frame, item1, item2 + return item1, item2 # processFrame function that is called to process a frame of a video # takes in frame mat object obtained from cv2 video.read() diff --git a/algorithms/MiniContoursAlgorithm.py b/algorithms/MiniContoursAlgorithm.py index 9e1d3e9..12d2e24 100644 --- a/algorithms/MiniContoursAlgorithm.py +++ b/algorithms/MiniContoursAlgorithm.py @@ -203,7 +203,7 @@ def get_center_hough_lines( def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) - return frame, item1, item2 + return item1, item2 def process_frame(self, original_frame, num_strips=60, show=False): diff --git a/algorithms/MiniContoursDownwards.py b/algorithms/MiniContoursDownwards.py index 51472db..873b92c 100644 --- a/algorithms/MiniContoursDownwards.py +++ b/algorithms/MiniContoursDownwards.py @@ -154,7 +154,7 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) - return frame, item1, item2 + return item1, item2 def process_frame(self, original_frame, num_strips=60, show=False): """"" diff --git a/algorithms/ScanningAlgorithm.py b/algorithms/ScanningAlgorithm.py index 36c8981..f812718 100644 --- a/algorithms/ScanningAlgorithm.py +++ b/algorithms/ScanningAlgorithm.py @@ -76,7 +76,7 @@ def create_line(self, start_x, start_y, end_x, end_y): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) - return frame, item1, item2 + return item1, item2 def process_frame(self, frame, show): diff --git a/algorithms/SeesawAlgorithm.py b/algorithms/SeesawAlgorithm.py index 8048fe5..b3214a9 100644 --- a/algorithms/SeesawAlgorithm.py +++ b/algorithms/SeesawAlgorithm.py @@ -25,7 +25,7 @@ def __init__(self, config): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) - return frame, item1, item2 + return item1, item2 def process_frame(self, frame, show): diff --git a/gui.py b/gui.py index 5aa2ee3..73da31d 100644 --- a/gui.py +++ b/gui.py @@ -7,13 +7,14 @@ import pre_process img_dict = {} -# TODO: check_row_end img + # TODO: pause and continue +# TODO: return without error onClose class GUI: """Creates GUI with tkinter: initializes a master window, creates the same number of radiobuttons as the length of img_dict, creates 2 sliders for brightness and saturation, then render images accordingly\n - :param `root` = tk.Tk() + :param `master` = tk.Tk() :param `img_dict` = { "frameType1": numpy.ndarray } :param `window_name` = String """ diff --git a/pre_process.py b/pre_process.py index 7b29a56..7c4994c 100644 --- a/pre_process.py +++ b/pre_process.py @@ -1,5 +1,6 @@ -import cv2 import math + +import cv2 import numpy as np BRIGHTNESS_BASELINE = 110 @@ -72,6 +73,9 @@ def increase_saturation(img, value): def standardize_frame(img): + if len(img.shape) < 3: + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + try: hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) except: @@ -83,15 +87,19 @@ def standardize_frame(img): if mean_brightness < BRIGHTNESS_BASELINE: if mean_brightness + ACCEPTABLE_DIFFERENCE < BRIGHTNESS_BASELINE: - img = alter_brightness(img, math.floor(BRIGHTNESS_BASELINE - (mean_brightness + ACCEPTABLE_DIFFERENCE))) + img = alter_brightness(img, math.floor( + BRIGHTNESS_BASELINE - (mean_brightness + ACCEPTABLE_DIFFERENCE))) else: if mean_brightness - ACCEPTABLE_DIFFERENCE > BRIGHTNESS_BASELINE: - img = alter_brightness(img, math.floor(BRIGHTNESS_BASELINE - (mean_brightness - ACCEPTABLE_DIFFERENCE))) + img = alter_brightness(img, math.floor( + BRIGHTNESS_BASELINE - (mean_brightness - ACCEPTABLE_DIFFERENCE))) if mean_saturation < SATURATION_BASELINE: if mean_saturation + ACCEPTABLE_DIFFERENCE < SATURATION_BASELINE: - img = alter_saturation(img, math.floor(SATURATION_BASELINE - (mean_saturation + ACCEPTABLE_DIFFERENCE))) + img = alter_saturation(img, math.floor( + SATURATION_BASELINE - (mean_saturation + ACCEPTABLE_DIFFERENCE))) else: if mean_saturation - ACCEPTABLE_DIFFERENCE > SATURATION_BASELINE: - img = alter_saturation(img, math.floor(SATURATION_BASELINE - (mean_saturation - ACCEPTABLE_DIFFERENCE))) + img = alter_saturation(img, math.floor( + SATURATION_BASELINE - (mean_saturation - ACCEPTABLE_DIFFERENCE))) - return img \ No newline at end of file + return img diff --git a/test_pf.py b/test_pf.py index ca924d4..223b12f 100644 --- a/test_pf.py +++ b/test_pf.py @@ -39,6 +39,7 @@ # number_of_frames = 0 +# TODO: log performance live # ability to pass in args for reusability def main(args): @@ -115,7 +116,7 @@ def run_algorithm(alg, vid_file): if args.show: window_name = f'{args.alg}s algorithm on {args.vid}s video' app = startGUI(window_name, name1="standard", - name2="binary") + name2="processed") while vid.isOpened(): ret, frame = vid.read() @@ -124,7 +125,7 @@ def run_algorithm(alg, vid_file): break start_time_frame = time.time() - standard, binary, angle = alg.get_extra_content( + processed, angle = alg.get_extra_content( frame, show=args.show) end_time_frame = time.time() all_frame_time.append(end_time_frame - start_time_frame) @@ -137,9 +138,8 @@ def run_algorithm(alg, vid_file): total_run += 1 if args.show: - app.update_dict({'standard': standard}) - app.update_dict({'binary': binary}) - # app.update_dict({'mask': ctrs}) + app.update_dict({'standard': frame}) + app.update_dict({'processed': processed}) app.render_image() key = cv.waitKey(1) From 42c493d0c82eb2e6781e71f5e4b2873b3bdc4b2c Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 18 Feb 2023 12:41:26 -0800 Subject: [PATCH 11/35] pass in filtered frame to algorithm with gui --- gui.py | 15 +++++++++++++++ test_pf.py | 35 +++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/gui.py b/gui.py index 73da31d..0fb126d 100644 --- a/gui.py +++ b/gui.py @@ -10,6 +10,8 @@ # TODO: pause and continue # TODO: return without error onClose +# TODO: pass the process into algorithm + class GUI: """Creates GUI with tkinter: initializes a master window, creates the same number of radiobuttons as the length of img_dict, @@ -25,6 +27,7 @@ def __init__(self, master, img_dict, window_name): self.current_avg_brightness = 110 self.current_avg_saturation = 105 + self.fps = 0 self.var = tk.IntVar() self.curr_selected = 1 @@ -35,6 +38,9 @@ def __init__(self, master, img_dict, window_name): self.label = tk.Label(self.frame, bg="white", width=450, height=450) self.label.pack(fill="both", expand="yes") + self.fps_label = tk.Label(self.master, text="Frames Per Second: " + str(self.fps)) + self.fps_label.pack(pady=10, side="bottom") + for i, (img_name, img_array) in enumerate(img_dict.items(), 1): radiobutton = tk.Radiobutton( self.master, text=img_name, variable=self.var, value=i, command=lambda i=i: self.update_frameType(i)) @@ -50,8 +56,15 @@ def __init__(self, master, img_dict, window_name): self.saturation_scale.set(self.current_avg_saturation) self.saturation_scale.pack(pady=10, side="bottom") + self.render_image() + def onClose(self): + self.master.destroy() + + def update_fps(self, next): + self.fps = next + def update_frameType(self, next): self.curr_selected = next @@ -80,8 +93,10 @@ def render_image(self): curr_img = ImageTk.PhotoImage(curr_img) self.label.config(image=curr_img) self.label.image = curr_img + self.fps_label.config(text="Frames Per Second: " + str(self.fps)) self.master.update() + def update_dict(self, key_value): global img_dict img_dict.update(key_value) diff --git a/test_pf.py b/test_pf.py index 223b12f..6506b73 100644 --- a/test_pf.py +++ b/test_pf.py @@ -112,6 +112,8 @@ def run_algorithm(alg, vid_file): total_run = 0 uptime = 0 all_frame_time = [] + time_till_second = 0 + frame_within_second = 0 # if args.show: window_name = f'{args.alg}s algorithm on {args.vid}s video' @@ -123,29 +125,42 @@ def run_algorithm(alg, vid_file): if not ret: print('No More Frames Remaining\n') break - + # + if args.show: + app.update_dict({'standard': frame}) + frame = app.apply_filter(frame) + # start_time_frame = time.time() + # processed, angle = alg.get_extra_content( frame, show=args.show) + # end_time_frame = time.time() + # all_frame_time.append(end_time_frame - start_time_frame) - + # # print(angle) - + time_till_second += end_time_frame - start_time_frame + frame_within_second += 1 + # + if args.show: + app.update_dict({'processed': processed}) + x = time_till_second # replace with the integer you want to check + tolerance = 0.1 + if abs(x - 1) <= tolerance: + app.update_fps(frame_within_second) + frame_within_second = 0 + time_till_second = 0 + app.render_image() # counters if angle is not None: uptime += 1 total_run += 1 - - if args.show: - app.update_dict({'standard': frame}) - app.update_dict({'processed': processed}) - app.render_image() - + # key = cv.waitKey(1) if key == 27: break - + # vid.release() cv.destroyAllWindows() return uptime, total_run, all_frame_time From 3eebc25a63aab38383e49b7f54fc5fa2e5625655 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 18 Feb 2023 13:44:58 -0800 Subject: [PATCH 12/35] minor changes --- gui.py | 47 ++++++++++++++++++++++++++--------------------- test_pf.py | 4 ++-- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/gui.py b/gui.py index 0fb126d..8f73eda 100644 --- a/gui.py +++ b/gui.py @@ -7,10 +7,10 @@ import pre_process img_dict = {} +isActive = False # TODO: pause and continue # TODO: return without error onClose -# TODO: pass the process into algorithm class GUI: @@ -38,7 +38,8 @@ def __init__(self, master, img_dict, window_name): self.label = tk.Label(self.frame, bg="white", width=450, height=450) self.label.pack(fill="both", expand="yes") - self.fps_label = tk.Label(self.master, text="Frames Per Second: " + str(self.fps)) + self.fps_label = tk.Label( + self.master, text="Frames Per Second: " + str(self.fps)) self.fps_label.pack(pady=10, side="bottom") for i, (img_name, img_array) in enumerate(img_dict.items(), 1): @@ -56,10 +57,11 @@ def __init__(self, master, img_dict, window_name): self.saturation_scale.set(self.current_avg_saturation) self.saturation_scale.pack(pady=10, side="bottom") - self.render_image() def onClose(self): + global isActive + isActive = False self.master.destroy() def update_fps(self, next): @@ -81,20 +83,25 @@ def apply_filter(self, img): return pre_process.standardize_frame(img) def render_image(self): - global img_dict - curr_img = list(img_dict.values())[self.curr_selected - 1] - if np.any(curr_img != np.zeros((100, 100))): - curr_img = self.apply_filter(curr_img) - - curr_img = np.array(curr_img).astype(np.uint8) - curr_img = cv.cvtColor(curr_img, cv.COLOR_BGR2RGB) - - curr_img = Image.fromarray(curr_img) - curr_img = ImageTk.PhotoImage(curr_img) - self.label.config(image=curr_img) - self.label.image = curr_img - self.fps_label.config(text="Frames Per Second: " + str(self.fps)) - self.master.update() + global isActive + try: + if isActive: + global img_dict + curr_img = list(img_dict.values())[self.curr_selected - 1] + if np.any(curr_img != np.zeros((100, 100))): + curr_img = self.apply_filter(curr_img) + + curr_img = np.array(curr_img).astype(np.uint8) + curr_img = cv.cvtColor(curr_img, cv.COLOR_BGR2RGB) + + curr_img = Image.fromarray(curr_img) + curr_img = ImageTk.PhotoImage(curr_img) + self.label.config(image=curr_img) + self.label.image = curr_img + self.fps_label.config(text="Frames Per Second: ~" + str(self.fps)) + self.master.update() + except KeyError: + print() def update_dict(self, key_value): @@ -109,10 +116,7 @@ def startGUI(window_name, **kwargs): :param `window_name` of type String :(param >= 1) `name1="some string"`, `name2="some string"`, `name3="some string"`, ... """ - global img_dict - # for i in range(numberOfViews): - # frame_type = "frameType" + str(i + 1) - # img_dict[frame_type] = np.zeros((100, 100)) + global img_dict, isActive curr_name = "name" curr_index = 1 if kwargs == {}: @@ -129,4 +133,5 @@ def startGUI(window_name, **kwargs): root = tk.Tk() app = GUI(root, img_dict, window_name) + isActive = True return app diff --git a/test_pf.py b/test_pf.py index 6506b73..d62d809 100644 --- a/test_pf.py +++ b/test_pf.py @@ -39,7 +39,7 @@ # number_of_frames = 0 -# TODO: log performance live + # ability to pass in args for reusability def main(args): @@ -146,7 +146,7 @@ def run_algorithm(alg, vid_file): if args.show: app.update_dict({'processed': processed}) x = time_till_second # replace with the integer you want to check - tolerance = 0.1 + tolerance = 0.05 if abs(x - 1) <= tolerance: app.update_fps(frame_within_second) frame_within_second = 0 From f8d4529a6ff324e93b58b0cdf524d717f7f43bd1 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Thu, 2 Mar 2023 01:47:42 -0800 Subject: [PATCH 13/35] connect hsv from gui to algorithms, and added upper and lower hsv to gui --- README.md | 2 +- algorithms/CenterRowAlgorithm.py | 7 ++ algorithms/CheckRowEnd.py | 6 ++ algorithms/HoughAlgorithm.py | 6 ++ algorithms/MiniContoursAlgorithm.py | 6 ++ algorithms/MiniContoursDownwards.py | 6 ++ algorithms/ScanningAlgorithm.py | 6 ++ algorithms/SeesawAlgorithm.py | 6 ++ gui.py | 149 ++++++++++++++++++++++------ test_pf.py | 13 ++- 10 files changed, 169 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index ea47957..3240167 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - arguments : - `-a/--alg` : name of algorithm - - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, or `check_row_end` + - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` - `-v/--vid` : name of video - video has to be stored in `./videos/` - video configuration has to be stored as a `yaml` at `./config/video/` diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index af91fc1..fe50ec6 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -18,6 +18,7 @@ def __init__(self, config): # masking range for green self.LOW_GREEN = np.array(config.lower_hsv_threshold) self.HIGH_GREEN = np.array(config.upper_hsv_threshold) + # print(self.LOW_GREEN, self.HIGH_GREEN) # filtering parameters self.averaging_kernel_size = config.averaging_kernel_size @@ -47,6 +48,12 @@ def __init__(self, config): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) return item1, item2 + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) def process_frame(self, frame, show): """Uses contouring to create contours around each crop row and uses these contours to find centroid lines, diff --git a/algorithms/CheckRowEnd.py b/algorithms/CheckRowEnd.py index 9586bc4..58b24c8 100644 --- a/algorithms/CheckRowEnd.py +++ b/algorithms/CheckRowEnd.py @@ -20,6 +20,12 @@ def __init__(self, config): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) return item1, item2 + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) def process_frame(self, frame, show): """Averages values in each row in a mask of the frame. If the number of rows with an average value diff --git a/algorithms/HoughAlgorithm.py b/algorithms/HoughAlgorithm.py index 860a589..ae2723d 100644 --- a/algorithms/HoughAlgorithm.py +++ b/algorithms/HoughAlgorithm.py @@ -35,6 +35,12 @@ def __init__(self, config): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) return item1, item2 + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) # processFrame function that is called to process a frame of a video # takes in frame mat object obtained from cv2 video.read() diff --git a/algorithms/MiniContoursAlgorithm.py b/algorithms/MiniContoursAlgorithm.py index 12d2e24..ed7d5c4 100644 --- a/algorithms/MiniContoursAlgorithm.py +++ b/algorithms/MiniContoursAlgorithm.py @@ -204,6 +204,12 @@ def get_center_hough_lines( def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) return item1, item2 + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) def process_frame(self, original_frame, num_strips=60, show=False): diff --git a/algorithms/MiniContoursDownwards.py b/algorithms/MiniContoursDownwards.py index 873b92c..a16981f 100644 --- a/algorithms/MiniContoursDownwards.py +++ b/algorithms/MiniContoursDownwards.py @@ -155,6 +155,12 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) return item1, item2 + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) def process_frame(self, original_frame, num_strips=60, show=False): """"" diff --git a/algorithms/ScanningAlgorithm.py b/algorithms/ScanningAlgorithm.py index f812718..1a02dbf 100644 --- a/algorithms/ScanningAlgorithm.py +++ b/algorithms/ScanningAlgorithm.py @@ -77,6 +77,12 @@ def create_line(self, start_x, start_y, end_x, end_y): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) return item1, item2 + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) def process_frame(self, frame, show): diff --git a/algorithms/SeesawAlgorithm.py b/algorithms/SeesawAlgorithm.py index b3214a9..13f5872 100644 --- a/algorithms/SeesawAlgorithm.py +++ b/algorithms/SeesawAlgorithm.py @@ -26,6 +26,12 @@ def __init__(self, config): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) return item1, item2 + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) def process_frame(self, frame, show): diff --git a/gui.py b/gui.py index 8f73eda..164d3f1 100644 --- a/gui.py +++ b/gui.py @@ -9,9 +9,6 @@ img_dict = {} isActive = False -# TODO: pause and continue -# TODO: return without error onClose - class GUI: """Creates GUI with tkinter: initializes a master window, creates the same number of radiobuttons as the length of img_dict, @@ -32,10 +29,10 @@ def __init__(self, master, img_dict, window_name): self.var = tk.IntVar() self.curr_selected = 1 - self.frame = tk.Frame(self.master, bg="white", height=600, width=600) - self.frame.pack(fill="both", expand="yes") + self.frame = tk.Frame(self.master, height=600, width=600) + self.frame.pack(fill="both", expand=True, padx=20, pady=20) - self.label = tk.Label(self.frame, bg="white", width=450, height=450) + self.label = tk.Label(self.frame, width=450, height=450) self.label.pack(fill="both", expand="yes") self.fps_label = tk.Label( @@ -56,13 +53,90 @@ def __init__(self, master, img_dict, window_name): label="Saturation", command=lambda x: self.update_saturation(x)) self.saturation_scale.set(self.current_avg_saturation) self.saturation_scale.pack(pady=10, side="bottom") - + # + # + self.lower_frame = tk.Frame(self.frame) + self.lower_frame.pack(in_=self.frame, anchor="c", side="bottom") + self.upper_frame = tk.Frame(self.frame) + self.upper_frame.pack(in_=self.frame, anchor="c", side="bottom") + # + # + self.LOW_GREEN = [35, 80, 80] + self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") + self.low_h_entry_label.pack(pady=10, side="left") + self.low_h_entry = tk.Entry( + self.lower_frame, width=10, justify="center", font="Courier 12") + self.low_h_entry.insert(0, str(self.LOW_GREEN[0])) + self.low_h_entry.pack(pady=10, side="left") + # + self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") + self.low_s_entry_label.pack(pady=10, side="left") + self.low_s_entry = tk.Entry( + self.lower_frame, width=10, justify="center", font="Courier 12") + self.low_s_entry.insert(0, str(self.LOW_GREEN[1])) + self.low_s_entry.pack(pady=10, side="left") + # + self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") + self.low_v_entry_label.pack(pady=10, side="left") + self.low_v_entry = tk.Entry( + self.lower_frame, width=10, justify="center", font="Courier 12") + self.low_v_entry.insert(0, str(self.LOW_GREEN[2])) + self.low_v_entry.pack(pady=10, side="left") + # + self.update_btn_low = tk.Button( + self.lower_frame, text="Update", command=self.update_lower_hsv) + self.update_btn_low.pack(pady=0, side="left") + # + # + self.UPPER_GREEN = [80, 255, 255] + self.up_h_entry_label = tk.Label(self.upper_frame, text="UPPER-H:") + self.up_h_entry_label.pack(pady=10, side="left") + self.up_h_entry = tk.Entry( + self.upper_frame, width=10, justify="center", font="Courier 12") + self.up_h_entry.insert(0, str(self.UPPER_GREEN[0])) + self.up_h_entry.pack(pady=10, side="left") + # + self.up_s_entry_label = tk.Label(self.upper_frame, text="S:") + self.up_s_entry_label.pack(pady=10, side="left") + self.up_s_entry = tk.Entry( + self.upper_frame, width=10, justify="center", font="Courier 12") + self.up_s_entry.insert(0, str(self.UPPER_GREEN[1])) + self.up_s_entry.pack(pady=10, side="left") + # + self.up_v_entry_label = tk.Label(self.upper_frame, text="V:") + self.up_v_entry_label.pack(pady=10, side="left") + self.up_v_entry = tk.Entry( + self.upper_frame, width=10, justify="center", font="Courier 12") + self.up_v_entry.insert(0, str(self.UPPER_GREEN[2])) + self.up_v_entry.pack(pady=10, side="left") + # + self.update_btn_high = tk.Button( + self.upper_frame, text="Update", command=self.update_upper_hsv) + self.update_btn_high.pack(pady=0, side="left") + # + # + # self.render_image() - def onClose(self): - global isActive - isActive = False - self.master.destroy() + def update_upper_hsv(self): + upper_h = int(self.up_h_entry.get()) + upper_s = int(self.up_s_entry.get()) + upper_v = int(self.up_v_entry.get()) + self.UPPER_GREEN = (upper_h, upper_s, upper_v) + print(self.UPPER_GREEN) + + def update_lower_hsv(self): + lower_h = int(self.low_h_entry.get()) + lower_s = int(self.low_s_entry.get()) + lower_v = int(self.low_v_entry.get()) + self.LOW_GREEN = (lower_h, lower_s, lower_v) + print(self.LOW_GREEN) + + def getLowerHSV(self): + return self.LOW_GREEN + + def getUpperHSV(self): + return self.UPPER_GREEN def update_fps(self, next): self.fps = next @@ -76,6 +150,10 @@ def update_brightness(self, value): def update_saturation(self, value): self.current_avg_saturation = int(value) + def update_dict(self, key_value): + global img_dict + img_dict.update(key_value) + def apply_filter(self, img): pre_process.ACCEPTABLE_DIFFERENCE = 0 pre_process.BRIGHTNESS_BASELINE = self.current_avg_brightness @@ -83,30 +161,34 @@ def apply_filter(self, img): return pre_process.standardize_frame(img) def render_image(self): + global img_dict + curr_img = list(img_dict.values())[self.curr_selected - 1] + if np.any(curr_img != np.zeros((100, 100))): + curr_img = self.apply_filter(curr_img) + + curr_img = np.array(curr_img).astype(np.uint8) + curr_img = cv.cvtColor(curr_img, cv.COLOR_BGR2RGB) + + curr_img = Image.fromarray(curr_img) + curr_img = ImageTk.PhotoImage(curr_img) + self.label.config(image=curr_img) + self.label.image = curr_img + self.fps_label.config( + text="Frames Per Second: ~" + str(self.fps)) + self.master.update() + + def isActive(self): global isActive - try: - if isActive: - global img_dict - curr_img = list(img_dict.values())[self.curr_selected - 1] - if np.any(curr_img != np.zeros((100, 100))): - curr_img = self.apply_filter(curr_img) - - curr_img = np.array(curr_img).astype(np.uint8) - curr_img = cv.cvtColor(curr_img, cv.COLOR_BGR2RGB) - - curr_img = Image.fromarray(curr_img) - curr_img = ImageTk.PhotoImage(curr_img) - self.label.config(image=curr_img) - self.label.image = curr_img - self.fps_label.config(text="Frames Per Second: ~" + str(self.fps)) - self.master.update() - except KeyError: - print() + return isActive - def update_dict(self, key_value): - global img_dict - img_dict.update(key_value) +global root + + +def onClose(): + global isActive + isActive = False + root.destroy() def startGUI(window_name, **kwargs): @@ -130,8 +212,9 @@ def startGUI(window_name, **kwargs): else: raise KeyError("argument " + curr_name + str(curr_index) + " is missing in startGUI()") - + global root root = tk.Tk() + root.protocol("WM_DELETE_WINDOW", onClose) app = GUI(root, img_dict, window_name) isActive = True return app diff --git a/test_pf.py b/test_pf.py index d62d809..eed512e 100644 --- a/test_pf.py +++ b/test_pf.py @@ -14,6 +14,7 @@ from algorithms.MiniContoursAlgorithm import MiniContoursAlgorithm from algorithms.MiniContoursDownwards import MiniContoursDownwards from algorithms.ScanningAlgorithm import ScanningAlgorithm +from algorithms.SeesawAlgorithm import SeesawAlgorithm from gui import startGUI # parser for command line arguments @@ -35,7 +36,9 @@ ('scanning', ScanningAlgorithm), ('check_row_end', - CheckRowEnd)] + CheckRowEnd), + ('seesaw', + SeesawAlgorithm)] # number_of_frames = 0 @@ -126,8 +129,10 @@ def run_algorithm(alg, vid_file): print('No More Frames Remaining\n') break # - if args.show: + if args.show and app.isActive(): app.update_dict({'standard': frame}) + alg.update_lower_hsv(app.getLowerHSV()) + alg.update_upper_hsv(app.getUpperHSV()) frame = app.apply_filter(frame) # start_time_frame = time.time() @@ -139,11 +144,11 @@ def run_algorithm(alg, vid_file): # all_frame_time.append(end_time_frame - start_time_frame) # - # print(angle) + print(angle) time_till_second += end_time_frame - start_time_frame frame_within_second += 1 # - if args.show: + if args.show and app.isActive(): app.update_dict({'processed': processed}) x = time_till_second # replace with the integer you want to check tolerance = 0.05 From 7283f9449363aa414916f1b9a8bb646b7e816b40 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Thu, 2 Mar 2023 02:32:10 -0800 Subject: [PATCH 14/35] alert when UPPER hsv < LOWER hsv and vice versa --- gui.py | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/gui.py b/gui.py index 164d3f1..e5369ba 100644 --- a/gui.py +++ b/gui.py @@ -86,6 +86,10 @@ def __init__(self, master, img_dict, window_name): self.update_btn_low = tk.Button( self.lower_frame, text="Update", command=self.update_lower_hsv) self.update_btn_low.pack(pady=0, side="left") + + self.alert_for_hsv = tk.Label( + self.upper_frame, text="", fg="red") + self.alert_for_hsv.pack(pady=10, side="top") # # self.UPPER_GREEN = [80, 255, 255] @@ -122,14 +126,44 @@ def update_upper_hsv(self): upper_h = int(self.up_h_entry.get()) upper_s = int(self.up_s_entry.get()) upper_v = int(self.up_v_entry.get()) - self.UPPER_GREEN = (upper_h, upper_s, upper_v) + if upper_h > self.LOW_GREEN[0] and upper_s > self.LOW_GREEN[1] and upper_v > self.LOW_GREEN[2]: + self.UPPER_GREEN = (upper_h, upper_s, upper_v) + self.alert_for_hsv.config( + text="") + else: + self.alert_for_hsv.config( + text="Invalid UPPER HSV values") + self.up_h_entry.delete(0, tk.END) + self.up_h_entry.insert( + 0, str(self.UPPER_GREEN[0])) + self.up_v_entry.delete(0, tk.END) + self.up_v_entry.insert( + 0, str(self.UPPER_GREEN[1])) + self.up_s_entry.delete(0, tk.END) + self.up_s_entry.insert( + 0, str(self.UPPER_GREEN[2])) print(self.UPPER_GREEN) def update_lower_hsv(self): lower_h = int(self.low_h_entry.get()) lower_s = int(self.low_s_entry.get()) lower_v = int(self.low_v_entry.get()) - self.LOW_GREEN = (lower_h, lower_s, lower_v) + if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2]: + self.LOWER_GREEN = (lower_h, lower_s, lower_v) + self.alert_for_hsv.config( + text="") + else: + self.alert_for_hsv.config( + text="Invalid LOWER HSV values") + self.low_h_entry.delete(0, tk.END) + self.low_h_entry.insert( + 0, str(self.LOW_GREEN[0])) + self.low_v_entry.delete(0, tk.END) + self.low_v_entry.insert( + 0, str(self.LOW_GREEN[1])) + self.low_s_entry.delete(0, tk.END) + self.low_s_entry.insert( + 0, str(self.LOW_GREEN[2])) print(self.LOW_GREEN) def getLowerHSV(self): From f586e9d5f7820e059d2f1665a9c596c1ff7fd2b0 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Thu, 2 Mar 2023 16:05:18 -0800 Subject: [PATCH 15/35] small addition to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3240167..217646f 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ - `-v/--vid` : name of video - video has to be stored in `./videos/` - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views + - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV - example : `python test_pf.py -a center_row -v crop -s` ### Commands to Start World in Gazebo From a4a6809a2bb81d87023f288d17d1ca6a70907dee Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Thu, 2 Mar 2023 17:48:50 -0800 Subject: [PATCH 16/35] autopep8 --- README.md | 82 +++++++++++++++++++++++++++--------------------------- test_pf.py | 3 +- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 5191d89..1006a53 100644 --- a/README.md +++ b/README.md @@ -2,54 +2,54 @@ > For AgroBot one of the main concern is the robot's navigation. The robot must autonomously navigate the crop rows and for this task we are taking a computer vision approach. The goal of this script is to efficiently detect crop rows on a video. -## Getting started +# Getting started -### How to install +# How to install -- install [python 3.9](https://www.python.org/downloads/release/python-390/) +- install[python 3.9](https: // www.python.org/downloads/release/python-390/) - `git clone` this repository - run `pip install pipenv` - `cd` into project root - run `pipenv install` to install project dependencies - run `pipenv shell` to launch virtual environment -### Scripts +# Scripts -`test_algorithms.py:` +`test_algorithms.py: ` - runs any of the 4 algorithms against any video -- arguments : - - `-a/--alg` : name of algorithm - - `hough`, `center_row`, `mini_contour` or `scanning` - - `-v/--vid` : name of video - - video has to be stored in `./videos/` - - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show` : process drawings and show frame -- example : `python test_algorithms.py -a center_row -v crop` +- arguments: + - `-a/--alg`: name of algorithm + - `hough`, `center_row`, `mini_contour` or `scanning` + - `-v/--vid`: name of video + - video has to be stored in `./videos/` + - video configuration has to be stored as a `yaml` at `./config/video/` + - `-s/--show`: process drawings and show frame +- example: `python test_algorithms.py - a center_row - v crop` -`create_dataset.py:` +`create_dataset.py: ` - aids in data extraction from any video -- arguments : - - `-v/--vid` : name of video - - video has to be stored in `./videos/` - - `-i/--interval (optional, default = 30)`: interval between keyframes - - `-o/--overwrite (optional, default = False)` : flag to indicate that you wish to overwrite files at `./extract/vid` -- example : `python create_dataset.py -v sim` or `python create_dataset.py -v crop -i 60 -o` - -`test_pf.py:` - - runs performance tests on any of the algorithms against any video with an optional GUI +- arguments: + - `-v/--vid`: name of video + - video has to be stored in `./videos/` + - `-i/--interval(optional, default=30)`: interval between keyframes + - `-o/--overwrite(optional, default=False)`: flag to indicate that you wish to overwrite files at `./extract/vid` +- example: `python create_dataset.py - v sim` or `python create_dataset.py - v crop - i 60 - o` + +`test_pf.py: ` + - runs performance tests on any of the algorithms against any video with an optional GUI - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - - arguments : - - `-a/--alg` : name of algorithm - - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` - - `-v/--vid` : name of video - - video has to be stored in `./videos/` - - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV - - example : `python test_pf.py -a center_row -v crop -s` - -### Commands to Start World in Gazebo + - arguments: + - `-a/--alg`: name of algorithm + - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` + - `-v/--vid`: name of video + - video has to be stored in `./videos/` + - video configuration has to be stored as a `yaml` at `./config/video/` + - `-s/--show`: creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV + - example : `python test_pf.py - a center_row - v crop - s` + +# Commands to Start World in Gazebo Run the following Commands the first time: @@ -59,7 +59,7 @@ Run the following Commands the first time: Run the following commands to open the world: - Go to agrobot_ws/src folder -- Run bash script (start_corn_fields.sh or start_wheat_fields.sh) +- Run bash script(start_corn_fields.sh or start_wheat_fields.sh) To run PID, open a new terminal: @@ -67,13 +67,13 @@ To run PID, open a new terminal: To reset the position of the robot: -- Edit > Reset model poses (to reset the position of your robot to the start position) +- Edit > Reset model poses(to reset the position of your robot to the start position) -## Demo +# Demo ![](/readme_files/demo_vid.mp4) -### Colour Filtering +# Colour Filtering To accomplish this task, we need to first denoise the image. In particular, since we know we are looking for crops which are green in colour, we can filter based on colour. We convert our image to HSV format and define upper and lower bound @@ -89,9 +89,9 @@ By using bitwise and operator we see that this mask does correspond to the green ![crop bitwise](/readme_files/greenregions.png) -### Denoising and Smoothing +# Denoising and Smoothing -Next, we need to denoise the resulting mask (which is a binary image containing the crop rows). To do this, we perform +Next, we need to denoise the resulting mask(which is a binary image containing the crop rows). To do this, we perform gaussian blurring following by multiple iterations of dilation. Here is the resulting mask after dilation: ![denoising](/readme_files/denoising.png) @@ -101,7 +101,7 @@ and also we want rows that are far from the camera to be merged into one. This i our navigation system and by merging them, we can avoid detecting them in our edge and line detection in subsequent steps. -### Line Detection +# Line Detection Although, the mask image is a binary image and it can be used directly with Hough transform, the large number of points in the image lead to noisy and slow computation. Hence, Canny edge detection was used to first decrease the number of @@ -115,7 +115,7 @@ image shows the resulting lines we detected. ![Houghlines](/readme_files/Houghlines.png) -### How does this fit in with the rest of the system? +# How does this fit in with the rest of the system? We are currently using these lines and calculating their intersection which occurs at vanishing point. Then we are using a PID controller to minimimize the distance of this vanishing point from the center of our frame. Using this method, we diff --git a/test_pf.py b/test_pf.py index eed512e..54a3120 100644 --- a/test_pf.py +++ b/test_pf.py @@ -43,7 +43,6 @@ number_of_frames = 0 - # ability to pass in args for reusability def main(args): # verify that video exists in ./videos @@ -150,7 +149,7 @@ def run_algorithm(alg, vid_file): # if args.show and app.isActive(): app.update_dict({'processed': processed}) - x = time_till_second # replace with the integer you want to check + x = time_till_second # replace with the integer you want to check tolerance = 0.05 if abs(x - 1) <= tolerance: app.update_fps(frame_within_second) From 3385cddce11e1bc1b1fcac06a96bb5b29a62e95b Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Thu, 2 Mar 2023 18:59:01 -0800 Subject: [PATCH 17/35] added Pillow dependency --- Pipfile | 1 + Pipfile.lock | 207 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 134 insertions(+), 74 deletions(-) diff --git a/Pipfile b/Pipfile index 50c6ea5..cad4778 100644 --- a/Pipfile +++ b/Pipfile @@ -9,6 +9,7 @@ omegaconf = "*" numpy = "*" tk = "*" python-rospkg = "*" +Pillow = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index a32af54..b94ca4a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "de0fdc3b461c618fe29b332e6a8a8bc79f8fdddd34a8794fbb05693489399e01" + "sha256": "7f8d6aec1f0e34cf3689d4c0572c47a2dbbed8521d01a028a25baea455be60ed" }, "pipfile-spec": 6, "requires": { @@ -18,98 +18,151 @@ "default": { "antlr4-python3-runtime": { "hashes": [ - "sha256:15793f5d0512a372b4e7d2284058ad32ce7dd27126b105fb0b2245130445db33" + "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b" ], - "version": "==4.8" - }, - "dataclasses": { - "hashes": [ - "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf", - "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97" - ], - "markers": "python_version == '3.6'", - "version": "==0.8" + "version": "==4.9.3" }, "numpy": { "hashes": [ - "sha256:012426a41bc9ab63bb158635aecccc7610e3eff5d31d1eb43bc099debc979d94", - "sha256:06fab248a088e439402141ea04f0fffb203723148f6ee791e9c75b3e9e82f080", - "sha256:0eef32ca3132a48e43f6a0f5a82cb508f22ce5a3d6f67a8329c81c8e226d3f6e", - "sha256:1ded4fce9cfaaf24e7a0ab51b7a87be9038ea1ace7f34b841fe3b6894c721d1c", - "sha256:2e55195bc1c6b705bfd8ad6f288b38b11b1af32f3c8289d6c50d47f950c12e76", - "sha256:2ea52bd92ab9f768cc64a4c3ef8f4b2580a17af0a5436f6126b08efbd1838371", - "sha256:36674959eed6957e61f11c912f71e78857a8d0604171dfd9ce9ad5cbf41c511c", - "sha256:384ec0463d1c2671170901994aeb6dce126de0a95ccc3976c43b0038a37329c2", - "sha256:39b70c19ec771805081578cc936bbe95336798b7edf4732ed102e7a43ec5c07a", - "sha256:400580cbd3cff6ffa6293df2278c75aef2d58d8d93d3c5614cd67981dae68ceb", - "sha256:43d4c81d5ffdff6bae58d66a3cd7f54a7acd9a0e7b18d97abb255defc09e3140", - "sha256:50a4a0ad0111cc1b71fa32dedd05fa239f7fb5a43a40663269bb5dc7877cfd28", - "sha256:603aa0706be710eea8884af807b1b3bc9fb2e49b9f4da439e76000f3b3c6ff0f", - "sha256:6149a185cece5ee78d1d196938b2a8f9d09f5a5ebfbba66969302a778d5ddd1d", - "sha256:759e4095edc3c1b3ac031f34d9459fa781777a93ccc633a472a5468587a190ff", - "sha256:7fb43004bce0ca31d8f13a6eb5e943fa73371381e53f7074ed21a4cb786c32f8", - "sha256:811daee36a58dc79cf3d8bdd4a490e4277d0e4b7d103a001a4e73ddb48e7e6aa", - "sha256:8b5e972b43c8fc27d56550b4120fe6257fdc15f9301914380b27f74856299fea", - "sha256:99abf4f353c3d1a0c7a5f27699482c987cf663b1eac20db59b8c7b061eabd7fc", - "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73", - "sha256:a12ff4c8ddfee61f90a1633a4c4afd3f7bcb32b11c52026c92a12e1325922d0d", - "sha256:a4646724fba402aa7504cd48b4b50e783296b5e10a524c7a6da62e4a8ac9698d", - "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4", - "sha256:a9d17f2be3b427fbb2bce61e596cf555d6f8a56c222bd2ca148baeeb5e5c783c", - "sha256:ab83f24d5c52d60dbc8cd0528759532736b56db58adaa7b5f1f76ad551416a1e", - "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea", - "sha256:c843b3f50d1ab7361ca4f0b3639bf691569493a56808a0b0c54a051d260b7dbd", - "sha256:cae865b1cae1ec2663d8ea56ef6ff185bad091a5e33ebbadd98de2cfa3fa668f", - "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff", - "sha256:cf2402002d3d9f91c8b01e66fbb436a4ed01c6498fffed0e4c7566da1d40ee1e", - "sha256:d051ec1c64b85ecc69531e1137bb9751c6830772ee5c1c426dbcfe98ef5788d7", - "sha256:d6631f2e867676b13026e2846180e2c13c1e11289d67da08d71cacb2cd93d4aa", - "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827", - "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60" + "sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22", + "sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f", + "sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9", + "sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96", + "sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0", + "sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a", + "sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281", + "sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04", + "sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468", + "sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253", + "sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756", + "sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a", + "sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb", + "sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d", + "sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0", + "sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910", + "sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978", + "sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5", + "sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f", + "sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a", + "sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5", + "sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2", + "sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d", + "sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95", + "sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5", + "sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d", + "sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780", + "sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa" ], "index": "pypi", - "version": "==1.19.5" + "version": "==1.24.2" }, "omegaconf": { "hashes": [ - "sha256:be93d73eaa2564fbe52d88ee13e3b79f4c6e04876b2f326551a21391f7dc6367", - "sha256:c65e05530369484e074a24038fe31812c73561aa9d916abfd1209e4073136ae5" + "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b", + "sha256:d5d4b6d29955cc50ad50c46dc269bcd92c6e00f5f90d23ab5fee7bfca4ba4cc7" ], "index": "pypi", - "version": "==2.1.1" + "version": "==2.3.0" }, "opencv-python": { "hashes": [ - "sha256:05c5139d620e8d02f7ce0921796d55736fa19fa15e2ec00a388db2eb1ae1e9a1", - "sha256:085232718f28bddd265da480874c37db5c7354cb08f23f4a68a8639b16276a89", - "sha256:18a4a14015eee30d9cd514db8cdefbf594b1d5c234762d27abe512d62a333bc3", - "sha256:205a73adb29c37e42475645519e612e843a985475da993d10b4d5daa6afec36a", - "sha256:3c001d3feec7f3140f1fb78dfc52ca28122db8240826882d175a208a89d2731b", - "sha256:437f30e300725e1d1b3744dbfbc66a523a4744792b58f3dbe1e9140c8f4dfba5", - "sha256:5366fcd6eae4243add3c8c92142045850f1db8e464bcf0b75313e1596b2e3671", - "sha256:54c64e86a087841869901fd34462bb6bec01cd4652800fdf5d92fe7b0596c82f", - "sha256:6763729fcfee2a08e069aa1982c9a8c1abf55b9cdf2fb9640eda1d85bdece19a", - "sha256:68813b720b88e4951e84399b9a8a7b532d45a07a96ea8f539636242f862e32e0", - "sha256:7f41b97d84ac66bdf13cb4d9f4dad3e159525ba1e3f421e670c787ce536eb70a", - "sha256:831b92fe63ce18dd628f71104da7e60596658b75e2fa16b83aefa3eb10c115e2", - "sha256:881f3d85269500e0c7d72b140a6ebb5c14a089f8140fb9da7ce01f12a245858e", - "sha256:8852be06c0749fef0d9c58f532bbcb0570968c59e41cf56b90f5c92593c6e108", - "sha256:8b5bc61be7fc8565140b746288b370a4bfdb4edb9d680b66bb914e7690485db1", - "sha256:8d3282138f3a8646941089aae142684910ebe40776266448eab5f4bb609fc63f", - "sha256:9a78558b5ae848386edbb843c761e5fed5a8480be9af16274a5a78838529edeb", - "sha256:b42bbba9f5421865377c7960bd4f3dd881003b322a6bf46ed2302b89224d102b", - "sha256:c360cb76ad1ddbd5d2d3e730b42f2ff6e4be08ea6f4a6eefacca175d27467e8f", - "sha256:cdc3363c2911d7cfc6c9f55308c51c2841a7aecbf0bf5e791499d220ce89d880", - "sha256:e1f54736272830a1e895cedf7a4ee67737e31e966d380c82a81ef22515d043a3", - "sha256:e42c644a70d5c54f53a4b114dbd88b4eb83f42a9ca998f07bd5682f3f404efcc", - "sha256:f1bda4d144f5204e077ca4571453ebb2015e5748d5e0043386c92c2bbf7f52eb", - "sha256:f3ac2355217114a683f3f72a9c40a5890914a59c4a2df62e4083c66ff65c9cf9" + "sha256:3424794a711f33284581f3c1e4b071cfc827d02b99d6fd9a35391f517c453306", + "sha256:7a297e7651e22eb17c265ddbbc80e2ba2a8ff4f4a1696a67c45e5f5798245842", + "sha256:812af57553ec1c6709060c63f6b7e9ad07ddc0f592f3ccc6d00c71e0fe0e6376", + "sha256:cd08343654c6b88c5a8c25bf425f8025aed2e3189b4d7306b5861d32affaf737", + "sha256:d4f8880440c433a0025d78804dda6901d1e8e541a561dda66892d90290aef881", + "sha256:ebfc0a3a2f57716e709028b992e4de7fd8752105d7a768531c4f434043c6f9ff", + "sha256:eda115797b114fc16ca6f182b91c5d984f0015c19bec3145e55d33d708e9bae1" + ], + "index": "pypi", + "version": "==4.7.0.72" + }, + "pillow": { + "hashes": [ + "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33", + "sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b", + "sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e", + "sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35", + "sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153", + "sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9", + "sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569", + "sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57", + "sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8", + "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1", + "sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264", + "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157", + "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9", + "sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133", + "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9", + "sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab", + "sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6", + "sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5", + "sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df", + "sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503", + "sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b", + "sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa", + "sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327", + "sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493", + "sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d", + "sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4", + "sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4", + "sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35", + "sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2", + "sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c", + "sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011", + "sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a", + "sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e", + "sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f", + "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848", + "sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57", + "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f", + "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c", + "sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9", + "sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5", + "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9", + "sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d", + "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0", + "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1", + "sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e", + "sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815", + "sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0", + "sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b", + "sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd", + "sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c", + "sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3", + "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab", + "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858", + "sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5", + "sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee", + "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343", + "sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb", + "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47", + "sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed", + "sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837", + "sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286", + "sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28", + "sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628", + "sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df", + "sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d", + "sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d", + "sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a", + "sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6", + "sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336", + "sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132", + "sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070", + "sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe", + "sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a", + "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd", + "sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391", + "sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a", + "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12" ], "index": "pypi", - "version": "==4.5.3.56" + "version": "==9.4.0" }, "pyyaml": { "hashes": [ + "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", @@ -121,26 +174,32 @@ "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" ], From b43fbfa1872101c87605bf2f2fb73d59d528f434 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Thu, 2 Mar 2023 19:24:41 -0800 Subject: [PATCH 18/35] show the whole frame --- gui.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/gui.py b/gui.py index e5369ba..4653a9f 100644 --- a/gui.py +++ b/gui.py @@ -36,29 +36,32 @@ def __init__(self, master, img_dict, window_name): self.label.pack(fill="both", expand="yes") self.fps_label = tk.Label( - self.master, text="Frames Per Second: " + str(self.fps)) + self.frame, text="Frames Per Second: " + str(self.fps)) self.fps_label.pack(pady=10, side="bottom") for i, (img_name, img_array) in enumerate(img_dict.items(), 1): radiobutton = tk.Radiobutton( - self.master, text=img_name, variable=self.var, value=i, command=lambda i=i: self.update_frameType(i)) + self.frame, text=img_name, variable=self.var, value=i, command=lambda i=i: self.update_frameType(i)) radiobutton.pack(pady=10, side="bottom") - self.brightness_scale = tk.Scale(self.master, from_=0, to=255, orient="horizontal", + self.brightness_scale = tk.Scale(self.frame, from_=0, to=255, orient="horizontal", label="Brightness", command=lambda x: self.update_brightness(x)) self.brightness_scale.set(self.current_avg_brightness) self.brightness_scale.pack(pady=10, side="bottom") - self.saturation_scale = tk.Scale(self.master, from_=0, to=255, orient="horizontal", + self.saturation_scale = tk.Scale(self.frame, from_=0, to=255, orient="horizontal", label="Saturation", command=lambda x: self.update_saturation(x)) self.saturation_scale.set(self.current_avg_saturation) self.saturation_scale.pack(pady=10, side="bottom") # # - self.lower_frame = tk.Frame(self.frame) - self.lower_frame.pack(in_=self.frame, anchor="c", side="bottom") self.upper_frame = tk.Frame(self.frame) self.upper_frame.pack(in_=self.frame, anchor="c", side="bottom") + self.lower_frame = tk.Frame(self.frame) + self.lower_frame.pack(in_=self.frame, anchor="c", side="bottom") + self.alert_for_hsv = tk.Label( + self.lower_frame, text="", fg="red") + self.alert_for_hsv.pack(pady=10, side="top") # # self.LOW_GREEN = [35, 80, 80] @@ -86,10 +89,6 @@ def __init__(self, master, img_dict, window_name): self.update_btn_low = tk.Button( self.lower_frame, text="Update", command=self.update_lower_hsv) self.update_btn_low.pack(pady=0, side="left") - - self.alert_for_hsv = tk.Label( - self.upper_frame, text="", fg="red") - self.alert_for_hsv.pack(pady=10, side="top") # # self.UPPER_GREEN = [80, 255, 255] @@ -204,6 +203,22 @@ def render_image(self): curr_img = cv.cvtColor(curr_img, cv.COLOR_BGR2RGB) curr_img = Image.fromarray(curr_img) + + # Get the dimensions of the available space + max_width = self.label.winfo_width() + max_height = self.label.winfo_height() + + # Get the dimensions of the image + img_width, img_height = curr_img.size + + # Calculate the scale factor to fit the image within the available space + scale_factor = min(max_width / img_width, max_height / img_height) + + # Resize the image to fit within the available space, while maintaining aspect ratio + new_width = int(img_width * scale_factor) + new_height = int(img_height * scale_factor) + curr_img = curr_img.resize((new_width, new_height), Image.ANTIALIAS) + curr_img = ImageTk.PhotoImage(curr_img) self.label.config(image=curr_img) self.label.image = curr_img From 5c90f93afb76f9bce1e894e9641f5d9eb52c6c71 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Thu, 2 Mar 2023 21:45:23 -0800 Subject: [PATCH 19/35] added scrollbar --- gui.py | 91 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/gui.py b/gui.py index 4653a9f..ada87ee 100644 --- a/gui.py +++ b/gui.py @@ -8,6 +8,8 @@ img_dict = {} isActive = False +mw = 600 +mh = 600 class GUI: @@ -21,6 +23,7 @@ class GUI: def __init__(self, master, img_dict, window_name): self.master = master self.master.title(window_name) + self.master.geometry("550x800") self.current_avg_brightness = 110 self.current_avg_saturation = 105 @@ -29,62 +32,77 @@ def __init__(self, master, img_dict, window_name): self.var = tk.IntVar() self.curr_selected = 1 - self.frame = tk.Frame(self.master, height=600, width=600) - self.frame.pack(fill="both", expand=True, padx=20, pady=20) + self.container = tk.Frame(self.master) + self.container.pack(side='top', anchor='nw', fill="both", expand=True, padx=5, pady=5) - self.label = tk.Label(self.frame, width=450, height=450) - self.label.pack(fill="both", expand="yes") + self.img_container = tk.Label(self.container) + # self.img_container.place(x=0, y=0, relwidth=1, relheight=1, anchor='nw') + self.img_container.pack(side='top', anchor='nw', fill="none", expand=False, padx=2, pady=2) + self.img_container.config(width=600, height=400) + + self.canvas = tk.Canvas(self.container) + self.scrollbar = tk.Scrollbar(self.container, orient="vertical", command=self.canvas.yview) + self.scrollable_frame = tk.Frame(self.canvas) + + self.scrollable_frame.bind("", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) + + self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor='c') + self.canvas.configure(yscrollcommand=self.scrollbar.set) + + self.canvas.pack(side="left", fill="both", expand=True) + self.canvas.config(height=400) + self.scrollbar.pack(side="right", fill="y") self.fps_label = tk.Label( - self.frame, text="Frames Per Second: " + str(self.fps)) - self.fps_label.pack(pady=10, side="bottom") + self.scrollable_frame, text="Frames Per Second: " + str(self.fps)) + self.fps_label.pack(pady=5, side="bottom") for i, (img_name, img_array) in enumerate(img_dict.items(), 1): radiobutton = tk.Radiobutton( - self.frame, text=img_name, variable=self.var, value=i, command=lambda i=i: self.update_frameType(i)) - radiobutton.pack(pady=10, side="bottom") + self.scrollable_frame, text=img_name, variable=self.var, value=i, command=lambda i=i: self.update_frameType(i)) + radiobutton.pack(pady=5, side="bottom") - self.brightness_scale = tk.Scale(self.frame, from_=0, to=255, orient="horizontal", + self.brightness_scale = tk.Scale(self.scrollable_frame, from_=0, to=255, orient="horizontal", label="Brightness", command=lambda x: self.update_brightness(x)) self.brightness_scale.set(self.current_avg_brightness) - self.brightness_scale.pack(pady=10, side="bottom") + self.brightness_scale.pack(pady=5, side="bottom") - self.saturation_scale = tk.Scale(self.frame, from_=0, to=255, orient="horizontal", + self.saturation_scale = tk.Scale(self.scrollable_frame, from_=0, to=255, orient="horizontal", label="Saturation", command=lambda x: self.update_saturation(x)) self.saturation_scale.set(self.current_avg_saturation) - self.saturation_scale.pack(pady=10, side="bottom") + self.saturation_scale.pack(pady=5, side="bottom") # # - self.upper_frame = tk.Frame(self.frame) - self.upper_frame.pack(in_=self.frame, anchor="c", side="bottom") - self.lower_frame = tk.Frame(self.frame) - self.lower_frame.pack(in_=self.frame, anchor="c", side="bottom") + self.upper_frame = tk.Frame(self.scrollable_frame) + self.upper_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") + self.lower_frame = tk.Frame(self.scrollable_frame) + self.lower_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") self.alert_for_hsv = tk.Label( self.lower_frame, text="", fg="red") - self.alert_for_hsv.pack(pady=10, side="top") + self.alert_for_hsv.pack(pady=5, side="top") # # self.LOW_GREEN = [35, 80, 80] self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") - self.low_h_entry_label.pack(pady=10, side="left") + self.low_h_entry_label.pack(pady=5, side="left") self.low_h_entry = tk.Entry( self.lower_frame, width=10, justify="center", font="Courier 12") self.low_h_entry.insert(0, str(self.LOW_GREEN[0])) - self.low_h_entry.pack(pady=10, side="left") + self.low_h_entry.pack(pady=5, side="left") # self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") - self.low_s_entry_label.pack(pady=10, side="left") + self.low_s_entry_label.pack(pady=5, side="left") self.low_s_entry = tk.Entry( self.lower_frame, width=10, justify="center", font="Courier 12") self.low_s_entry.insert(0, str(self.LOW_GREEN[1])) - self.low_s_entry.pack(pady=10, side="left") + self.low_s_entry.pack(pady=5, side="left") # self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") - self.low_v_entry_label.pack(pady=10, side="left") + self.low_v_entry_label.pack(pady=5, side="left") self.low_v_entry = tk.Entry( self.lower_frame, width=10, justify="center", font="Courier 12") self.low_v_entry.insert(0, str(self.LOW_GREEN[2])) - self.low_v_entry.pack(pady=10, side="left") + self.low_v_entry.pack(pady=5, side="left") # self.update_btn_low = tk.Button( self.lower_frame, text="Update", command=self.update_lower_hsv) @@ -93,25 +111,25 @@ def __init__(self, master, img_dict, window_name): # self.UPPER_GREEN = [80, 255, 255] self.up_h_entry_label = tk.Label(self.upper_frame, text="UPPER-H:") - self.up_h_entry_label.pack(pady=10, side="left") + self.up_h_entry_label.pack(pady=5, side="left") self.up_h_entry = tk.Entry( self.upper_frame, width=10, justify="center", font="Courier 12") self.up_h_entry.insert(0, str(self.UPPER_GREEN[0])) - self.up_h_entry.pack(pady=10, side="left") + self.up_h_entry.pack(pady=5, side="left") # self.up_s_entry_label = tk.Label(self.upper_frame, text="S:") - self.up_s_entry_label.pack(pady=10, side="left") + self.up_s_entry_label.pack(pady=5, side="left") self.up_s_entry = tk.Entry( self.upper_frame, width=10, justify="center", font="Courier 12") self.up_s_entry.insert(0, str(self.UPPER_GREEN[1])) - self.up_s_entry.pack(pady=10, side="left") + self.up_s_entry.pack(pady=5, side="left") # self.up_v_entry_label = tk.Label(self.upper_frame, text="V:") - self.up_v_entry_label.pack(pady=10, side="left") + self.up_v_entry_label.pack(pady=5, side="left") self.up_v_entry = tk.Entry( self.upper_frame, width=10, justify="center", font="Courier 12") self.up_v_entry.insert(0, str(self.UPPER_GREEN[2])) - self.up_v_entry.pack(pady=10, side="left") + self.up_v_entry.pack(pady=5, side="left") # self.update_btn_high = tk.Button( self.upper_frame, text="Update", command=self.update_upper_hsv) @@ -205,8 +223,8 @@ def render_image(self): curr_img = Image.fromarray(curr_img) # Get the dimensions of the available space - max_width = self.label.winfo_width() - max_height = self.label.winfo_height() + max_width = self.img_container.winfo_width() + max_height = self.img_container.winfo_height() # Get the dimensions of the image img_width, img_height = curr_img.size @@ -220,16 +238,23 @@ def render_image(self): curr_img = curr_img.resize((new_width, new_height), Image.ANTIALIAS) curr_img = ImageTk.PhotoImage(curr_img) - self.label.config(image=curr_img) - self.label.image = curr_img + self.img_container.config(image=curr_img) + self.img_container.image = curr_img self.fps_label.config( text="Frames Per Second: ~" + str(self.fps)) self.master.update() + def isActive(self): global isActive return isActive +def on_master_configure(event): + # Get the updated height and width of the master widget + global mw, mh + mw = event.width + mh = event.height + # print("New window size: {}x{}".format(mw, mh)) global root From b7e12ef42f61a8c4a65bd07d339f94182a26d399 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Fri, 3 Mar 2023 15:55:10 -0800 Subject: [PATCH 20/35] fixed dimension problem, expand smoother --- README.md | 84 +++++++++++++++++++++++++++---------------------------- gui.py | 40 ++++++++++---------------- 2 files changed, 57 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 1006a53..4d6389b 100644 --- a/README.md +++ b/README.md @@ -2,54 +2,54 @@ > For AgroBot one of the main concern is the robot's navigation. The robot must autonomously navigate the crop rows and for this task we are taking a computer vision approach. The goal of this script is to efficiently detect crop rows on a video. -# Getting started +## Getting started -# How to install +### How to install -- install[python 3.9](https: // www.python.org/downloads/release/python-390/) +- install [python 3.9](https://www.python.org/downloads/release/python-390/) - `git clone` this repository - run `pip install pipenv` - `cd` into project root - run `pipenv install` to install project dependencies - run `pipenv shell` to launch virtual environment -# Scripts +### Scripts -`test_algorithms.py: ` +`test_algorithms.py:` - runs any of the 4 algorithms against any video -- arguments: - - `-a/--alg`: name of algorithm - - `hough`, `center_row`, `mini_contour` or `scanning` - - `-v/--vid`: name of video - - video has to be stored in `./videos/` - - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show`: process drawings and show frame -- example: `python test_algorithms.py - a center_row - v crop` +- arguments : + - `-a/--alg` : name of algorithm + - `hough`, `center_row`, `mini_contour` or `scanning` + - `-v/--vid` : name of video + - video has to be stored in `./videos/` + - video configuration has to be stored as a `yaml` at `./config/video/` + - `-s/--show` : process drawings and show frame +- example : `python test_algorithms.py -a center_row -v crop` -`create_dataset.py: ` +`create_dataset.py:` - aids in data extraction from any video -- arguments: - - `-v/--vid`: name of video - - video has to be stored in `./videos/` - - `-i/--interval(optional, default=30)`: interval between keyframes - - `-o/--overwrite(optional, default=False)`: flag to indicate that you wish to overwrite files at `./extract/vid` -- example: `python create_dataset.py - v sim` or `python create_dataset.py - v crop - i 60 - o` - -`test_pf.py: ` - - runs performance tests on any of the algorithms against any video with an optional GUI +- arguments : + - `-v/--vid` : name of video + - video has to be stored in `./videos/` + - `-i/--interval (optional, default = 30)`: interval between keyframes + - `-o/--overwrite (optional, default = False)` : flag to indicate that you wish to overwrite files at `./extract/vid` +- example : `python create_dataset.py -v sim` or `python create_dataset.py -v crop -i 60 -o` + +`test_pf.py:` + - runs performance tests on any of the algorithms against any video with an optional GUI - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - - arguments: - - `-a/--alg`: name of algorithm - - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` - - `-v/--vid`: name of video - - video has to be stored in `./videos/` - - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show`: creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV - - example : `python test_pf.py - a center_row - v crop - s` - -# Commands to Start World in Gazebo + - arguments : + - `-a/--alg` : name of algorithm + - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` + - `-v/--vid` : name of video + - video has to be stored in `./videos/` + - video configuration has to be stored as a `yaml` at `./config/video/` + - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV + - example : `python test_pf.py -a center_row -v crop -s` + +### Commands to Start World in Gazebo Run the following Commands the first time: @@ -59,7 +59,7 @@ Run the following Commands the first time: Run the following commands to open the world: - Go to agrobot_ws/src folder -- Run bash script(start_corn_fields.sh or start_wheat_fields.sh) +- Run bash script (start_corn_fields.sh or start_wheat_fields.sh) To run PID, open a new terminal: @@ -67,13 +67,13 @@ To run PID, open a new terminal: To reset the position of the robot: -- Edit > Reset model poses(to reset the position of your robot to the start position) +- Edit > Reset model poses (to reset the position of your robot to the start position) -# Demo +## Demo ![](/readme_files/demo_vid.mp4) -# Colour Filtering +### Colour Filtering To accomplish this task, we need to first denoise the image. In particular, since we know we are looking for crops which are green in colour, we can filter based on colour. We convert our image to HSV format and define upper and lower bound @@ -89,9 +89,9 @@ By using bitwise and operator we see that this mask does correspond to the green ![crop bitwise](/readme_files/greenregions.png) -# Denoising and Smoothing +### Denoising and Smoothing -Next, we need to denoise the resulting mask(which is a binary image containing the crop rows). To do this, we perform +Next, we need to denoise the resulting mask (which is a binary image containing the crop rows). To do this, we perform gaussian blurring following by multiple iterations of dilation. Here is the resulting mask after dilation: ![denoising](/readme_files/denoising.png) @@ -101,7 +101,7 @@ and also we want rows that are far from the camera to be merged into one. This i our navigation system and by merging them, we can avoid detecting them in our edge and line detection in subsequent steps. -# Line Detection +### Line Detection Although, the mask image is a binary image and it can be used directly with Hough transform, the large number of points in the image lead to noisy and slow computation. Hence, Canny edge detection was used to first decrease the number of @@ -115,8 +115,8 @@ image shows the resulting lines we detected. ![Houghlines](/readme_files/Houghlines.png) -# How does this fit in with the rest of the system? +### How does this fit in with the rest of the system? We are currently using these lines and calculating their intersection which occurs at vanishing point. Then we are using a PID controller to minimimize the distance of this vanishing point from the center of our frame. Using this method, we -are able to centre our chassis over the crop rows we are traversing. +are able to centre our chassis over the crop rows we are traversing. \ No newline at end of file diff --git a/gui.py b/gui.py index ada87ee..36cf6b2 100644 --- a/gui.py +++ b/gui.py @@ -8,8 +8,6 @@ img_dict = {} isActive = False -mw = 600 -mh = 600 class GUI: @@ -23,7 +21,7 @@ class GUI: def __init__(self, master, img_dict, window_name): self.master = master self.master.title(window_name) - self.master.geometry("550x800") + self.master.geometry("600x700") self.current_avg_brightness = 110 self.current_avg_saturation = 105 @@ -33,24 +31,22 @@ def __init__(self, master, img_dict, window_name): self.curr_selected = 1 self.container = tk.Frame(self.master) - self.container.pack(side='top', anchor='nw', fill="both", expand=True, padx=5, pady=5) - - self.img_container = tk.Label(self.container) - # self.img_container.place(x=0, y=0, relwidth=1, relheight=1, anchor='nw') - self.img_container.pack(side='top', anchor='nw', fill="none", expand=False, padx=2, pady=2) - self.img_container.config(width=600, height=400) + self.container.pack(side='top', anchor='nw', fill="both", expand=True) self.canvas = tk.Canvas(self.container) self.scrollbar = tk.Scrollbar(self.container, orient="vertical", command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) - self.scrollable_frame.bind("", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) + self.img_container = tk.Label(self.scrollable_frame) + self.img_container.pack(side='top', fill="both", expand=True) + # self.img_container.config(width=600, height=400) + self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor='c') self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) - self.canvas.config(height=400) + # self.canvas.config(width=600, height=300) self.scrollbar.pack(side="right", fill="y") self.fps_label = tk.Label( @@ -86,21 +82,21 @@ def __init__(self, master, img_dict, window_name): self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") self.low_h_entry_label.pack(pady=5, side="left") self.low_h_entry = tk.Entry( - self.lower_frame, width=10, justify="center", font="Courier 12") + self.lower_frame, width=5, justify="center", font="Courier 12") self.low_h_entry.insert(0, str(self.LOW_GREEN[0])) self.low_h_entry.pack(pady=5, side="left") # self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") self.low_s_entry_label.pack(pady=5, side="left") self.low_s_entry = tk.Entry( - self.lower_frame, width=10, justify="center", font="Courier 12") + self.lower_frame, width=5, justify="center", font="Courier 12") self.low_s_entry.insert(0, str(self.LOW_GREEN[1])) self.low_s_entry.pack(pady=5, side="left") # self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") self.low_v_entry_label.pack(pady=5, side="left") self.low_v_entry = tk.Entry( - self.lower_frame, width=10, justify="center", font="Courier 12") + self.lower_frame, width=5, justify="center", font="Courier 12") self.low_v_entry.insert(0, str(self.LOW_GREEN[2])) self.low_v_entry.pack(pady=5, side="left") # @@ -113,21 +109,21 @@ def __init__(self, master, img_dict, window_name): self.up_h_entry_label = tk.Label(self.upper_frame, text="UPPER-H:") self.up_h_entry_label.pack(pady=5, side="left") self.up_h_entry = tk.Entry( - self.upper_frame, width=10, justify="center", font="Courier 12") + self.upper_frame, width=5, justify="center", font="Courier 12") self.up_h_entry.insert(0, str(self.UPPER_GREEN[0])) self.up_h_entry.pack(pady=5, side="left") # self.up_s_entry_label = tk.Label(self.upper_frame, text="S:") self.up_s_entry_label.pack(pady=5, side="left") self.up_s_entry = tk.Entry( - self.upper_frame, width=10, justify="center", font="Courier 12") + self.upper_frame, width=5, justify="center", font="Courier 12") self.up_s_entry.insert(0, str(self.UPPER_GREEN[1])) self.up_s_entry.pack(pady=5, side="left") # self.up_v_entry_label = tk.Label(self.upper_frame, text="V:") self.up_v_entry_label.pack(pady=5, side="left") self.up_v_entry = tk.Entry( - self.upper_frame, width=10, justify="center", font="Courier 12") + self.upper_frame, width=5, justify="center", font="Courier 12") self.up_v_entry.insert(0, str(self.UPPER_GREEN[2])) self.up_v_entry.pack(pady=5, side="left") # @@ -223,8 +219,8 @@ def render_image(self): curr_img = Image.fromarray(curr_img) # Get the dimensions of the available space - max_width = self.img_container.winfo_width() - max_height = self.img_container.winfo_height() + max_width = self.canvas.winfo_width() + max_height = self.canvas.winfo_height() # Get the dimensions of the image img_width, img_height = curr_img.size @@ -249,12 +245,6 @@ def isActive(self): global isActive return isActive -def on_master_configure(event): - # Get the updated height and width of the master widget - global mw, mh - mw = event.width - mh = event.height - # print("New window size: {}x{}".format(mw, mh)) global root From 47b84ef6b3cfb5998a163ac0090e3ed5ddaffdd8 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Fri, 3 Mar 2023 16:43:53 -0800 Subject: [PATCH 21/35] fix bug with lowerhsv and apply precommit --- algorithms/CenterDownwards.py | 18 +++-- algorithms/CenterRowAlgorithm.py | 20 +++-- algorithms/CheckRowEnd.py | 2 +- algorithms/HoughAlgorithm.py | 11 ++- algorithms/MiniContoursAlgorithm.py | 29 +++++--- algorithms/MiniContoursDownwards.py | 26 ++++--- algorithms/ScanningAlgorithm.py | 35 ++++++--- algorithms/SeesawAlgorithm.py | 20 +++-- algorithms/utils/Lines.py | 6 +- algorithms/utils/delete_small_contours.py | 6 +- create_dataset.py | 3 +- gui.py | 89 +++++++++++++++-------- helper_scripts/DataExtractor.py | 9 ++- test_algorithms.py | 12 ++- 14 files changed, 188 insertions(+), 98 deletions(-) diff --git a/algorithms/CenterDownwards.py b/algorithms/CenterDownwards.py index cd0daa5..916914b 100644 --- a/algorithms/CenterDownwards.py +++ b/algorithms/CenterDownwards.py @@ -21,7 +21,8 @@ def __init__(self, config): # filtering parameters self.averaging_kernel_size = config.averaging_kernel_size - self.gauss_kernel_size = list(map(int, config.gauss_kernel_size.split(','))) + self.gauss_kernel_size = list( + map(int, config.gauss_kernel_size.split(','))) self.dilate_kernel_size = config.dilate_kernel_size self.sigma_x = config.sigma_x @@ -71,7 +72,8 @@ def process_frame(self, frame, show): contours, contour_frame = self.get_contours(mask) cv.drawContours(black_frame, contours, -1, self.contour_color, 3) cv.fillPoly(black_frame, pts=contours, color=self.contour_color) - lines, slopes, ellipse_frame = self.ellipse_slopes(contours, black_frame) + lines, slopes, ellipse_frame = self.ellipse_slopes( + contours, black_frame) if show: Lines.draw_lines_on_frame(lines, black_frame) @@ -117,7 +119,8 @@ def get_contours(self, binary_mask): """ frame = np.zeros((self.HEIGHT, self.WIDTH, 3)) ret, thresh = cv.threshold(binary_mask, 0, 254, 0) - contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) + contours, hierarchy = cv.findContours( + thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) return contours, frame @@ -144,7 +147,8 @@ def ellipse_slopes(self, contours, black_frame): rect = cv.minAreaRect(con) box = cv.boxPoints(rect) box = np.int0(box) - black_frame = cv.drawContours(black_frame, [box], 0, (255, 255, 255), 2) + black_frame = cv.drawContours( + black_frame, [box], 0, (255, 255, 255), 2) ellipse = cv.fitEllipse(con) self.contours.append(ellipse) cv.ellipse(black_frame, ellipse, (255, 255, 255), 2) @@ -178,11 +182,13 @@ def ellipse_slopes(self, contours, black_frame): slopes.append(slope) lefty = int((-x * vy / vx) + y) righty = int(((cols - x) * vy / vx) + y) - black_frame = cv.line(black_frame, (cols - 1, righty), (0, lefty), (255, 255, 0), 9) + black_frame = cv.line( + black_frame, (cols - 1, righty), (0, lefty), (255, 255, 0), 9) lines.append([cols - 1, righty, 0, lefty]) else: - black_frame = cv.line(black_frame, (int(x), 0), (int(x), rows - 1), (255, 255, 0), 9) + black_frame = cv.line(black_frame, (int(x), 0), + (int(x), rows - 1), (255, 255, 0), 9) lines.append([int(x), 0, int(x), rows - 1]) return lines, slopes, black_frame diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index fe50ec6..1dcc675 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -22,7 +22,8 @@ def __init__(self, config): # filtering parameters self.averaging_kernel_size = config.averaging_kernel_size - self.gauss_kernel_size = list(map(int, config.gauss_kernel_size.split(','))) + self.gauss_kernel_size = list( + map(int, config.gauss_kernel_size.split(','))) self.dilate_kernel_size = config.dilate_kernel_size self.sigma_x = config.sigma_x @@ -54,7 +55,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, frame, show): """Uses contouring to create contours around each crop row and uses these contours to find centroid lines, row vanishing point, a center contour and the angle between the center contour and vanishing point\n @@ -76,7 +77,8 @@ def process_frame(self, frame, show): cv.drawContours(black_frame, contours, -1, self.contour_color, 3) # fillPoly fills in the polygons in the frame cv.fillPoly(black_frame, pts=contours, color=self.contour_color) - lines, slopes, ellipse_frame = self.ellipse_slopes(contours, black_frame) + lines, slopes, ellipse_frame = self.ellipse_slopes( + contours, black_frame) if show: Lines.draw_lines_on_frame(lines, black_frame) @@ -84,14 +86,16 @@ def process_frame(self, frame, show): intersections = Lines.get_intersections(lines) x_points = [point[0] for point in intersections] y_points = [point[1] for point in intersections] - vanishing_point = Lines.draw_vanishing_point(ellipse_frame, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point( + ellipse_frame, x_points, y_points, show) if vanishing_point: center_contour, angle = self.find_center_contour(vanishing_point) if show: cv.ellipse(black_frame, center_contour, (0, 255, 0), 2) - angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) + angle = Lines.calculate_angle_from_v_point( + vanishing_point, self.WIDTH, self.HEIGHT) return black_frame, angle @@ -121,7 +125,8 @@ def get_contours(self, binary_mask): """ frame = np.zeros((self.HEIGHT, self.WIDTH, 3)) ret, thresh = cv.threshold(binary_mask, 0, 254, 0) - contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) + contours, hierarchy = cv.findContours( + thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) cv.drawContours(frame, contours, -1, (0, 255, 0), 2) cv.fillPoly(frame, pts=contours, color=(0, 255, 0)) @@ -143,7 +148,8 @@ def ellipse_slopes(self, contours, black_frame): rect = cv.minAreaRect(cnt) box = cv.boxPoints(rect) box = np.int0(box) - black_frame = cv.drawContours(black_frame, [box], 0, (255, 255, 255), 2) + black_frame = cv.drawContours( + black_frame, [box], 0, (255, 255, 255), 2) ellipse = cv.fitEllipse(cnt) self.contours.append(ellipse) cv.ellipse(black_frame, ellipse, (255, 255, 255), 2) diff --git a/algorithms/CheckRowEnd.py b/algorithms/CheckRowEnd.py index 58b24c8..4572752 100644 --- a/algorithms/CheckRowEnd.py +++ b/algorithms/CheckRowEnd.py @@ -26,7 +26,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, frame, show): """Averages values in each row in a mask of the frame. If the number of rows with an average value of zero is greater than req_rows_empty, then frame is row end\n diff --git a/algorithms/HoughAlgorithm.py b/algorithms/HoughAlgorithm.py index ae2723d..868cb6c 100644 --- a/algorithms/HoughAlgorithm.py +++ b/algorithms/HoughAlgorithm.py @@ -41,7 +41,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + # processFrame function that is called to process a frame of a video # takes in frame mat object obtained from cv2 video.read() def process_frame(self, frame, show=True): @@ -92,12 +92,15 @@ def process_frame(self, frame, show=True): y_points = [point[1] for point in intersections] if show: - vanishing_point = Lines.draw_vanishing_point(line_img, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point( + line_img, x_points, y_points, show) else: - vanishing_point = Lines.draw_vanishing_point(frame, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point( + frame, x_points, y_points, show) # Calculating angle from vanishing point to (self.WIDTH // 2, 0) - angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) + angle = Lines.calculate_angle_from_v_point( + vanishing_point, self.WIDTH, self.HEIGHT) if show: return line_img, angle diff --git a/algorithms/MiniContoursAlgorithm.py b/algorithms/MiniContoursAlgorithm.py index ed7d5c4..dc3e796 100644 --- a/algorithms/MiniContoursAlgorithm.py +++ b/algorithms/MiniContoursAlgorithm.py @@ -72,7 +72,8 @@ def get_centroids(self, mask, num_strips): centroids = [] for i, strip in enumerate(strips): - contours, hierarchy = cv2.findContours(strip, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + contours, hierarchy = cv2.findContours( + strip, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) strip_centroids = [] for contour in contours: M = cv2.moments(contour) @@ -106,7 +107,8 @@ def get_center_hough_lines( # lines: list of [[votes, rho, theta]] of all lines # point_lines: list of [votes, pt1, pt2] of all lines - mask = cv2.inRange(cv2.cvtColor(frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) + mask = cv2.inRange(cv2.cvtColor( + frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) mask = cv2.medianBlur(mask, 9) # mask = cv2.GaussianBlur(mask, (9,9), 10) # mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, self.morphology_kernel) @@ -134,7 +136,8 @@ def get_center_hough_lines( if show: cv2.line(frame, (0, cut_off_height), (width // 2, 0), self.color_2) - cv2.line(frame, (width, cut_off_height), (width // 2, 0), self.color_2) + cv2.line(frame, (width, cut_off_height), + (width // 2, 0), self.color_2) for i, strip_centroid in enumerate(centroids): if i > int(0.3 * len(centroids)): @@ -149,12 +152,16 @@ def get_center_hough_lines( segmented_points[idx].append([int(x), int(y)]) if show: - cv2.circle(frame, (int(centroid[0]), int(centroid[1])), 3, self.color_1, -1) - cv2.circle(mask, (int(centroid[0]), int(centroid[1])), 3, self.color_1, -1) - points_vector.append([int(centroid[0]), int(centroid[1])]) + cv2.circle(frame, (int(centroid[0]), int( + centroid[1])), 3, self.color_1, -1) + cv2.circle(mask, (int(centroid[0]), int( + centroid[1])), 3, self.color_1, -1) + points_vector.append( + [int(centroid[0]), int(centroid[1])]) else: if show: - cv2.circle(frame, (int(centroid[0]), int(centroid[1])), 3, self.color_3, -1) + cv2.circle(frame, (int(centroid[0]), int( + centroid[1])), 3, self.color_3, -1) c_mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) @@ -210,7 +217,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, original_frame, num_strips=60, show=False): # original_frame: BGR frame @@ -236,9 +243,11 @@ def process_frame(self, original_frame, num_strips=60, show=False): x_points = [point[0] for point in intersections] y_points = [point[1] for point in intersections] - vanishing_point = Lines.draw_vanishing_point(frame, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point( + frame, x_points, y_points, show) # Calculating angle from vanishing point to (self.WIDTH // 2, 0) - angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) + angle = Lines.calculate_angle_from_v_point( + vanishing_point, self.WIDTH, self.HEIGHT) return frame, angle diff --git a/algorithms/MiniContoursDownwards.py b/algorithms/MiniContoursDownwards.py index a16981f..5271d8c 100644 --- a/algorithms/MiniContoursDownwards.py +++ b/algorithms/MiniContoursDownwards.py @@ -24,11 +24,14 @@ def __init__(self, config): self.high_green = np.array(config.high_green) # random colors for drawing lines - self.color_1 = (255, 255, 0) # teal (for contour points that are within bounds) + # teal (for contour points that are within bounds) + self.color_1 = (255, 255, 0) # pink (for the line of best fit through all the contour points that are within bounds) self.color_2 = (200, 0, 255) - self.color_3 = (0, 0, 255) # red (for contour points that are out of bounds) - self.midline = (0, 129, 255) # orange (for a reference line vertically down center of frame) + # red (for contour points that are out of bounds) + self.color_3 = (0, 0, 255) + # orange (for a reference line vertically down center of frame) + self.midline = (0, 129, 255) # parameters for calculating centroids and drawing the best fit line among them self.num_strips = self.config.num_strips @@ -65,7 +68,8 @@ def get_centroids(self, mask, num_strips, show): centroids = [] for i, strip in enumerate(strips): - contours, hierarchy = cv2.findContours(strip, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + contours, hierarchy = cv2.findContours( + strip, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) strip_centroids = [] for contour in contours: M = cv2.moments(contour) @@ -89,7 +93,8 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p -line: A vector [vx, vy, x, y] representing the line of best fit through all the centroids. [vx, vy] is a vector that describes the direction of the line, where (x, y) when taken together is a point on the line """"" - mask = cv2.inRange(cv2.cvtColor(frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) + mask = cv2.inRange(cv2.cvtColor( + frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) mask = cv2.medianBlur(mask, 9) # mask = cv2.GaussianBlur(mask, (9,9), 10) # mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, self.morphology_kernel) @@ -111,8 +116,10 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p idx = int(x / width * split_factor) segmented_points[idx].append([int(x), int(y)]) if show: - cv2.circle(frame, (int(centroid[0]), int(centroid[1])), 3, self.color_1, -1) - cv2.circle(mask, (int(centroid[0]), int(centroid[1])), 3, self.color_1, -1) + cv2.circle(frame, (int(centroid[0]), int( + centroid[1])), 3, self.color_1, -1) + cv2.circle(mask, (int(centroid[0]), int( + centroid[1])), 3, self.color_1, -1) points_vector.append([int(centroid[0]), int(centroid[1])]) c_mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) @@ -161,7 +168,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, original_frame, num_strips=60, show=False): """"" parameters: @@ -184,7 +191,8 @@ def process_frame(self, original_frame, num_strips=60, show=False): if line[0] is not None: angle = round(math.degrees(math.atan(-line[0] / line[1])), 2) - cv2.line(frame, (int(self.WIDTH / 2), 0), (int(self.WIDTH / 2), int(self.HEIGHT)), self.midline, 2) + cv2.line(frame, (int(self.WIDTH / 2), 0), + (int(self.WIDTH / 2), int(self.HEIGHT)), self.midline, 2) print(angle) return frame, angle else: diff --git a/algorithms/ScanningAlgorithm.py b/algorithms/ScanningAlgorithm.py index 1a02dbf..d10e567 100644 --- a/algorithms/ScanningAlgorithm.py +++ b/algorithms/ScanningAlgorithm.py @@ -26,7 +26,8 @@ def __init__(self, config): self.right_x_bound = int(self.WIDTH * (1 - self.config.bounding_box_x)) self.upper_y_bound = int(self.HEIGHT * self.config.bounding_box_y) self.lower_y_bound = self.HEIGHT - 1 - self.mid_y = self.lower_y_bound - (self.lower_y_bound - self.upper_y_bound) // 2 + self.mid_y = self.lower_y_bound - \ + (self.lower_y_bound - self.upper_y_bound) // 2 self.kernel = np.ones((5, 5), np.uint8) @@ -37,14 +38,16 @@ def __init__(self, config): # creates lines from the top of the horizon to the bottom for top_x in range(0, self.WIDTH, self.pixel_gap): for bottom_x in range(0, self.WIDTH, self.pixel_gap): - line = self.create_line(top_x, self.upper_y_bound, bottom_x, self.lower_y_bound) + line = self.create_line( + top_x, self.upper_y_bound, bottom_x, self.lower_y_bound) self.lines.append(line) # creates lines from horizon to the right side for top_x in range(0, self.WIDTH, self.pixel_gap): for right_y in range(self.mid_y, self.lower_y_bound, self.pixel_gap): - line1 = self.create_line(top_x, self.upper_y_bound, self.WIDTH - 1, right_y) + line1 = self.create_line( + top_x, self.upper_y_bound, self.WIDTH - 1, right_y) self.lines.append(line1) # creates lines from horizon to the left side @@ -83,7 +86,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, frame, show): hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) @@ -98,7 +101,8 @@ def process_frame(self, frame, show): return mask, None self.upper_y_bound = white_pixels[0][0] - self.mid_y = self.lower_y_bound - (self.lower_y_bound - self.upper_y_bound) // 2 + self.mid_y = self.lower_y_bound - \ + (self.lower_y_bound - self.upper_y_bound) // 2 # use this to invert the mask # mask = ~mask @@ -131,7 +135,8 @@ def process_frame(self, frame, show): most_prominent_pos_lines = pos_array[:, 1][largest_pos_indices] most_prominent_neg_lines = neg_array[:, 1][largest_neg_indices] - most_prominent_lines = numpy.concatenate((most_prominent_pos_lines, most_prominent_neg_lines), axis=None) + most_prominent_lines = numpy.concatenate( + (most_prominent_pos_lines, most_prominent_neg_lines), axis=None) # convert to lines as defined in Lines.py converted_lines = [] @@ -146,27 +151,33 @@ def process_frame(self, frame, show): x_points = [point[0] for point in intersections] y_points = [point[1] for point in intersections] - vanishing_point = Lines.draw_vanishing_point(frame, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point( + frame, x_points, y_points, show) if show: for line in converted_lines: - frame = cv2.line(frame, (line[0], line[1]), (line[2], line[3]), (255, 255, 255), 1) + frame = cv2.line( + frame, (line[0], line[1]), (line[2], line[3]), (255, 255, 255), 1) # line for the middle of frame - frame = cv2.line(frame, (self.WIDTH // 2, self.HEIGHT), (self.WIDTH // 2, 0), (0, 0, 255), 1) + frame = cv2.line(frame, (self.WIDTH // 2, self.HEIGHT), + (self.WIDTH // 2, 0), (0, 0, 255), 1) # point with x coordinate of the vanishing point and y coordinate of the end of the crop row - frame = cv2.circle(frame, (vanishing_point[0], self.upper_y_bound), 5, (0, 255, 0), -1) + frame = cv2.circle( + frame, (vanishing_point[0], self.upper_y_bound), 5, (0, 255, 0), -1) # point in the middle of frame at midpoint between the horizon and bottom of the screen - frame = cv2.circle(frame, (self.WIDTH // 2, self.mid_y), 5, (0, 255, 0), -1) + frame = cv2.circle( + frame, (self.WIDTH // 2, self.mid_y), 5, (0, 255, 0), -1) # line between the two points above frame = cv2.line(frame, (self.WIDTH // 2, self.mid_y), (vanishing_point[0], self.upper_y_bound), (0, 255, 0), 2) # Calculating angle from vanishing point to (self.WIDTH // 2, 0) - angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) + angle = Lines.calculate_angle_from_v_point( + vanishing_point, self.WIDTH, self.HEIGHT) return frame, angle diff --git a/algorithms/SeesawAlgorithm.py b/algorithms/SeesawAlgorithm.py index f729645..c48e23d 100644 --- a/algorithms/SeesawAlgorithm.py +++ b/algorithms/SeesawAlgorithm.py @@ -15,7 +15,8 @@ def __init__(self, config): # filtering parameters self.averaging_kernel_size = config.averaging_kernel_size - self.gauss_kernel_size = list(map(int, config.gauss_kernel_size.split(','))) + self.gauss_kernel_size = list( + map(int, config.gauss_kernel_size.split(','))) self.dilate_kernel_size = config.dilate_kernel_size self.sigma_x = config.sigma_x @@ -32,7 +33,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, frame, show): black_frame, points, both_points = self.plot_points(frame) @@ -45,11 +46,13 @@ def process_frame(self, frame, show): x2 = int(x + vx * self.WIDTH) y1 = int(y - vy * self.HEIGHT) y2 = int(y + vy * self.HEIGHT) - black_frame = cv.line(black_frame, (x1, y1), (x2, y2), (0, 255, 255), 9) + black_frame = cv.line(black_frame, (x1, y1), + (x2, y2), (0, 255, 255), 9) """calculate angle""" if y1 - y2 != 0: - angle = round(math.degrees(math.atan(int(x2 - x1) / int(y1 - y2))), 2) + angle = round(math.degrees( + math.atan(int(x2 - x1) / int(y1 - y2))), 2) else: angle = None @@ -79,7 +82,8 @@ def plot_points(self, frame): while square_low < self.HEIGHT: normalized = False seg_left = left[int(square_low) + 1:int(square_high), 0:half_width] - seg_right = right[int(square_low) + 1:int(square_high), 0:half_width] + seg_right = right[int(square_low) + + 1:int(square_high), 0:half_width] left_x = int(np.sum(seg_left == 255) / bar_height) right_x = int(np.sum(seg_right == 255) / bar_height) @@ -97,10 +101,12 @@ def plot_points(self, frame): black_frame = cv.rectangle(black_frame, (half_width, square_low), ( x2, int(square_high)), (255, 255, 0), 3) - both_point = [int((x1 + x2) / 2), int((square_high + square_low) / 2)] + both_point = [int((x1 + x2) / 2), + int((square_high + square_low) / 2)] both_points.append(both_point) - black_frame = cv.circle(black_frame, both_point, radius=0, color=(0, 0, 255), thickness=15) + black_frame = cv.circle( + black_frame, both_point, radius=0, color=(0, 0, 255), thickness=15) square_high += bar_height square_low += bar_height diff --git a/algorithms/utils/Lines.py b/algorithms/utils/Lines.py index 048d897..e6df41e 100644 --- a/algorithms/utils/Lines.py +++ b/algorithms/utils/Lines.py @@ -89,7 +89,8 @@ def get_intersections(lines, min_slope=1): x1R, y1R, x2R, y2R = assign_coordinate_values(lineR) # calls the getIntersection helper function - intersect = get_intersection(((x1L, y1L), (x2L, y2L)), ((x1R, y1R), (x2R, y2R))) + intersect = get_intersection( + ((x1L, y1L), (x2L, y2L)), ((x1R, y1R), (x2R, y2R))) if isinstance(intersect, bool): continue @@ -228,7 +229,8 @@ def draw_vanishing_point(frame, x_points, y_points, show, use_median=True): intersecting_x = np.mean(filtered_x) intersecting_y = np.mean(filtered_y) if show: - cv.circle(frame, (int(intersecting_x), int(intersecting_y)), 8, (255, 0, 0), -1) + cv.circle(frame, (int(intersecting_x), int( + intersecting_y)), 8, (255, 0, 0), -1) return (int(intersecting_x), int(intersecting_y)) else: return None diff --git a/algorithms/utils/delete_small_contours.py b/algorithms/utils/delete_small_contours.py index ffd6f93..cdcd10b 100644 --- a/algorithms/utils/delete_small_contours.py +++ b/algorithms/utils/delete_small_contours.py @@ -36,9 +36,11 @@ def morph_op(mask, kernel_size, op, iterations): if op == 2: mask = cv.dilate(mask, kernel, iterations=iterations) if op == 3: - mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, kernel, iterations=iterations) + mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, + kernel, iterations=iterations) if op == 4: - mask = cv.morphologyEx(mask, cv.MORPH_OPEN, kernel, iterations=iterations) + mask = cv.morphologyEx(mask, cv.MORPH_OPEN, + kernel, iterations=iterations) else: mask = mask diff --git a/create_dataset.py b/create_dataset.py index 0cb2b91..5f2a4ae 100644 --- a/create_dataset.py +++ b/create_dataset.py @@ -23,7 +23,8 @@ def main(): # verify that video exists in ./videos if not path.isfile(f'videos/{video_name}.mp4'): - print('--vid', video_name, "is an invalid video name, make sure video exists in ./videos") + print('--vid', video_name, + "is an invalid video name, make sure video exists in ./videos") sys.exit() # make sure any important data does not get overwritten diff --git a/gui.py b/gui.py index 36cf6b2..d326dca 100644 --- a/gui.py +++ b/gui.py @@ -34,15 +34,18 @@ def __init__(self, master, img_dict, window_name): self.container.pack(side='top', anchor='nw', fill="both", expand=True) self.canvas = tk.Canvas(self.container) - self.scrollbar = tk.Scrollbar(self.container, orient="vertical", command=self.canvas.yview) + self.scrollbar = tk.Scrollbar( + self.container, orient="vertical", command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) - self.scrollable_frame.bind("", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) + self.scrollable_frame.bind("", lambda e: self.canvas.configure( + scrollregion=self.canvas.bbox("all"))) - self.img_container = tk.Label(self.scrollable_frame) + self.img_container = tk.Label(self.scrollable_frame, padx=10, pady=10) self.img_container.pack(side='top', fill="both", expand=True) # self.img_container.config(width=600, height=400) - self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor='c') + self.canvas.create_window( + (0, 0), window=self.scrollable_frame, anchor='c') self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) @@ -70,34 +73,36 @@ def __init__(self, master, img_dict, window_name): # # self.upper_frame = tk.Frame(self.scrollable_frame) - self.upper_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") + self.upper_frame.pack(in_=self.scrollable_frame, + anchor="c", side="bottom") self.lower_frame = tk.Frame(self.scrollable_frame) - self.lower_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") + self.lower_frame.pack(in_=self.scrollable_frame, + anchor="c", side="bottom") self.alert_for_hsv = tk.Label( self.lower_frame, text="", fg="red") self.alert_for_hsv.pack(pady=5, side="top") # # - self.LOW_GREEN = [35, 80, 80] + self.LOWER_GREEN = [35, 80, 80] self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") self.low_h_entry_label.pack(pady=5, side="left") self.low_h_entry = tk.Entry( self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_h_entry.insert(0, str(self.LOW_GREEN[0])) + self.low_h_entry.insert(0, str(self.LOWER_GREEN[0])) self.low_h_entry.pack(pady=5, side="left") # self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") self.low_s_entry_label.pack(pady=5, side="left") self.low_s_entry = tk.Entry( self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_s_entry.insert(0, str(self.LOW_GREEN[1])) + self.low_s_entry.insert(0, str(self.LOWER_GREEN[1])) self.low_s_entry.pack(pady=5, side="left") # self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") self.low_v_entry_label.pack(pady=5, side="left") self.low_v_entry = tk.Entry( self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_v_entry.insert(0, str(self.LOW_GREEN[2])) + self.low_v_entry.insert(0, str(self.LOWER_GREEN[2])) self.low_v_entry.pack(pady=5, side="left") # self.update_btn_low = tk.Button( @@ -139,48 +144,70 @@ def update_upper_hsv(self): upper_h = int(self.up_h_entry.get()) upper_s = int(self.up_s_entry.get()) upper_v = int(self.up_v_entry.get()) - if upper_h > self.LOW_GREEN[0] and upper_s > self.LOW_GREEN[1] and upper_v > self.LOW_GREEN[2]: + if upper_h > self.LOWER_GREEN[0] and upper_s > self.LOWER_GREEN[1] and upper_v > self.LOWER_GREEN[2] and upper_h <= 255 and upper_s <= 255 and upper_v <= 255: self.UPPER_GREEN = (upper_h, upper_s, upper_v) self.alert_for_hsv.config( - text="") + text="new UPPER-HSV: " + str(self.UPPER_GREEN)) else: + warning = "INVALID: " + if upper_h <= self.LOWER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif upper_s <= self.LOWER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif upper_v <= self.LOWER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "UPPER_V > 255\t" + self.alert_for_hsv.config( - text="Invalid UPPER HSV values") + text=warning) + self.up_h_entry.delete(0, tk.END) self.up_h_entry.insert( 0, str(self.UPPER_GREEN[0])) - self.up_v_entry.delete(0, tk.END) - self.up_v_entry.insert( - 0, str(self.UPPER_GREEN[1])) self.up_s_entry.delete(0, tk.END) self.up_s_entry.insert( + 0, str(self.UPPER_GREEN[1])) + self.up_v_entry.delete(0, tk.END) + self.up_v_entry.insert( 0, str(self.UPPER_GREEN[2])) - print(self.UPPER_GREEN) + # print(self.UPPER_GREEN) def update_lower_hsv(self): lower_h = int(self.low_h_entry.get()) lower_s = int(self.low_s_entry.get()) lower_v = int(self.low_v_entry.get()) - if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2]: + if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2] and lower_h >= 0 and lower_s >= 0 and lower_v >= 0: self.LOWER_GREEN = (lower_h, lower_s, lower_v) self.alert_for_hsv.config( - text="") + text="new LOWER-HSV: " + str(self.LOWER_GREEN)) else: + warning = "INVALID: " + if lower_h >= self.UPPER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif lower_s >= self.UPPER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif lower_v >= self.UPPER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "LOWER_V < 0\t" + self.alert_for_hsv.config( - text="Invalid LOWER HSV values") + text=warning) + self.low_h_entry.delete(0, tk.END) self.low_h_entry.insert( - 0, str(self.LOW_GREEN[0])) - self.low_v_entry.delete(0, tk.END) - self.low_v_entry.insert( - 0, str(self.LOW_GREEN[1])) + 0, str(self.LOWER_GREEN[0])) self.low_s_entry.delete(0, tk.END) self.low_s_entry.insert( - 0, str(self.LOW_GREEN[2])) - print(self.LOW_GREEN) + 0, str(self.LOWER_GREEN[1])) + self.low_v_entry.delete(0, tk.END) + self.low_v_entry.insert( + 0, str(self.LOWER_GREEN[2])) + # print(self.LOWER_GREEN) def getLowerHSV(self): - return self.LOW_GREEN + return self.LOWER_GREEN def getUpperHSV(self): return self.UPPER_GREEN @@ -231,15 +258,17 @@ def render_image(self): # Resize the image to fit within the available space, while maintaining aspect ratio new_width = int(img_width * scale_factor) new_height = int(img_height * scale_factor) + if new_height > 10 and new_width > 10: + new_width -= 10 + new_height -= 10 curr_img = curr_img.resize((new_width, new_height), Image.ANTIALIAS) curr_img = ImageTk.PhotoImage(curr_img) - self.img_container.config(image=curr_img) + self.img_container.config(image=curr_img, padx=10, pady=10) self.img_container.image = curr_img self.fps_label.config( - text="Frames Per Second: ~" + str(self.fps)) + text="Frames Per Second: ~" + str(self.fps), padx=10, pady=10) self.master.update() - def isActive(self): global isActive diff --git a/helper_scripts/DataExtractor.py b/helper_scripts/DataExtractor.py index 5b06752..e650d5a 100644 --- a/helper_scripts/DataExtractor.py +++ b/helper_scripts/DataExtractor.py @@ -46,17 +46,20 @@ def extract(self): success, frame = stream.read() h, w, d = frame.shape - out = cv.VideoWriter(f'{self.video_path}/{self.name}_key.mp4', cv.VideoWriter_fourcc(*'mp4v'), 1, (w, h)) + out = cv.VideoWriter(f'{self.video_path}/{self.name}_key.mp4', + cv.VideoWriter_fourcc(*'mp4v'), 1, (w, h)) count = 0 while success: # if frame is a keyframe create a red, vertical line in the middle of the frame and save it if count % self.interval == 0: - frame = cv.line(frame, (w // 2, 0), (w // 2, h), (0, 0, 255), thickness=2) + frame = cv.line(frame, (w // 2, 0), (w // 2, h), + (0, 0, 255), thickness=2) self.frames.append(frame) self.frames_copy.append(frame.copy()) out.write(frame) - cv.imwrite(f'{self.keyframes_path}/{self.name}_{count}.jpg', frame) + cv.imwrite( + f'{self.keyframes_path}/{self.name}_{count}.jpg', frame) print('frame ', count) success, frame = stream.read() count += 1 diff --git a/test_algorithms.py b/test_algorithms.py index 1ad1564..debc10f 100644 --- a/test_algorithms.py +++ b/test_algorithms.py @@ -30,7 +30,8 @@ def main(): # verify that video exists in ./videos if not path.isfile(f'videos/{args.vid}.mp4'): - print('--vid', args.vid, "is an invalid video name, make sure it video exists in ./videos") + print('--vid', args.vid, + "is an invalid video name, make sure it video exists in ./videos") sys.exit() # verify that config file for video exists @@ -40,7 +41,8 @@ def main(): # verify that config file for algorithm exists if not path.isfile(f'config/algorithm/{args.alg}.yaml'): - print(f"--alg config for {args.alg} is not defined in ./config/algorithm/") + print( + f"--alg config for {args.alg} is not defined in ./config/algorithm/") sys.exit() # set video config @@ -62,7 +64,8 @@ def main(): exists = True if not exists: - print(f"{args.alg} is an invalid algorithm, list of valid argument values: {algo_list}") + print( + f"{args.alg} is an invalid algorithm, list of valid argument values: {algo_list}") sys.exit() @@ -85,7 +88,8 @@ def run_algorithm(alg, vid_file): print(angle) if args.show: - cv.imshow(f'{args.alg}s algorithm on {args.vid}s video', processed_image) + cv.imshow( + f'{args.alg}s algorithm on {args.vid}s video', processed_image) key = cv.waitKey(25) From c3968ce7f62b0c7db6ebe080e1ea7c25f7eda08f Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Fri, 3 Mar 2023 16:57:14 -0800 Subject: [PATCH 22/35] revert --- README.md | 84 ++++++++++++++++++------------------ gui.py | 127 +++++++++++++++++++++++------------------------------- 2 files changed, 96 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index 4d6389b..1006a53 100644 --- a/README.md +++ b/README.md @@ -2,54 +2,54 @@ > For AgroBot one of the main concern is the robot's navigation. The robot must autonomously navigate the crop rows and for this task we are taking a computer vision approach. The goal of this script is to efficiently detect crop rows on a video. -## Getting started +# Getting started -### How to install +# How to install -- install [python 3.9](https://www.python.org/downloads/release/python-390/) +- install[python 3.9](https: // www.python.org/downloads/release/python-390/) - `git clone` this repository - run `pip install pipenv` - `cd` into project root - run `pipenv install` to install project dependencies - run `pipenv shell` to launch virtual environment -### Scripts +# Scripts -`test_algorithms.py:` +`test_algorithms.py: ` - runs any of the 4 algorithms against any video -- arguments : - - `-a/--alg` : name of algorithm - - `hough`, `center_row`, `mini_contour` or `scanning` - - `-v/--vid` : name of video - - video has to be stored in `./videos/` - - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show` : process drawings and show frame -- example : `python test_algorithms.py -a center_row -v crop` +- arguments: + - `-a/--alg`: name of algorithm + - `hough`, `center_row`, `mini_contour` or `scanning` + - `-v/--vid`: name of video + - video has to be stored in `./videos/` + - video configuration has to be stored as a `yaml` at `./config/video/` + - `-s/--show`: process drawings and show frame +- example: `python test_algorithms.py - a center_row - v crop` -`create_dataset.py:` +`create_dataset.py: ` - aids in data extraction from any video -- arguments : - - `-v/--vid` : name of video - - video has to be stored in `./videos/` - - `-i/--interval (optional, default = 30)`: interval between keyframes - - `-o/--overwrite (optional, default = False)` : flag to indicate that you wish to overwrite files at `./extract/vid` -- example : `python create_dataset.py -v sim` or `python create_dataset.py -v crop -i 60 -o` - -`test_pf.py:` - - runs performance tests on any of the algorithms against any video with an optional GUI +- arguments: + - `-v/--vid`: name of video + - video has to be stored in `./videos/` + - `-i/--interval(optional, default=30)`: interval between keyframes + - `-o/--overwrite(optional, default=False)`: flag to indicate that you wish to overwrite files at `./extract/vid` +- example: `python create_dataset.py - v sim` or `python create_dataset.py - v crop - i 60 - o` + +`test_pf.py: ` + - runs performance tests on any of the algorithms against any video with an optional GUI - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - - arguments : - - `-a/--alg` : name of algorithm - - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` - - `-v/--vid` : name of video - - video has to be stored in `./videos/` - - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV - - example : `python test_pf.py -a center_row -v crop -s` - -### Commands to Start World in Gazebo + - arguments: + - `-a/--alg`: name of algorithm + - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` + - `-v/--vid`: name of video + - video has to be stored in `./videos/` + - video configuration has to be stored as a `yaml` at `./config/video/` + - `-s/--show`: creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV + - example : `python test_pf.py - a center_row - v crop - s` + +# Commands to Start World in Gazebo Run the following Commands the first time: @@ -59,7 +59,7 @@ Run the following Commands the first time: Run the following commands to open the world: - Go to agrobot_ws/src folder -- Run bash script (start_corn_fields.sh or start_wheat_fields.sh) +- Run bash script(start_corn_fields.sh or start_wheat_fields.sh) To run PID, open a new terminal: @@ -67,13 +67,13 @@ To run PID, open a new terminal: To reset the position of the robot: -- Edit > Reset model poses (to reset the position of your robot to the start position) +- Edit > Reset model poses(to reset the position of your robot to the start position) -## Demo +# Demo ![](/readme_files/demo_vid.mp4) -### Colour Filtering +# Colour Filtering To accomplish this task, we need to first denoise the image. In particular, since we know we are looking for crops which are green in colour, we can filter based on colour. We convert our image to HSV format and define upper and lower bound @@ -89,9 +89,9 @@ By using bitwise and operator we see that this mask does correspond to the green ![crop bitwise](/readme_files/greenregions.png) -### Denoising and Smoothing +# Denoising and Smoothing -Next, we need to denoise the resulting mask (which is a binary image containing the crop rows). To do this, we perform +Next, we need to denoise the resulting mask(which is a binary image containing the crop rows). To do this, we perform gaussian blurring following by multiple iterations of dilation. Here is the resulting mask after dilation: ![denoising](/readme_files/denoising.png) @@ -101,7 +101,7 @@ and also we want rows that are far from the camera to be merged into one. This i our navigation system and by merging them, we can avoid detecting them in our edge and line detection in subsequent steps. -### Line Detection +# Line Detection Although, the mask image is a binary image and it can be used directly with Hough transform, the large number of points in the image lead to noisy and slow computation. Hence, Canny edge detection was used to first decrease the number of @@ -115,8 +115,8 @@ image shows the resulting lines we detected. ![Houghlines](/readme_files/Houghlines.png) -### How does this fit in with the rest of the system? +# How does this fit in with the rest of the system? We are currently using these lines and calculating their intersection which occurs at vanishing point. Then we are using a PID controller to minimimize the distance of this vanishing point from the center of our frame. Using this method, we -are able to centre our chassis over the crop rows we are traversing. \ No newline at end of file +are able to centre our chassis over the crop rows we are traversing. diff --git a/gui.py b/gui.py index d326dca..78cffcb 100644 --- a/gui.py +++ b/gui.py @@ -8,6 +8,8 @@ img_dict = {} isActive = False +mw = 600 +mh = 600 class GUI: @@ -21,7 +23,7 @@ class GUI: def __init__(self, master, img_dict, window_name): self.master = master self.master.title(window_name) - self.master.geometry("600x700") + self.master.geometry("550x800") self.current_avg_brightness = 110 self.current_avg_saturation = 105 @@ -31,25 +33,24 @@ def __init__(self, master, img_dict, window_name): self.curr_selected = 1 self.container = tk.Frame(self.master) - self.container.pack(side='top', anchor='nw', fill="both", expand=True) + self.container.pack(side='top', anchor='nw', fill="both", expand=True, padx=5, pady=5) + + self.img_container = tk.Label(self.container) + # self.img_container.place(x=0, y=0, relwidth=1, relheight=1, anchor='nw') + self.img_container.pack(side='top', anchor='nw', fill="none", expand=False, padx=2, pady=2) + self.img_container.config(width=600, height=400) self.canvas = tk.Canvas(self.container) - self.scrollbar = tk.Scrollbar( - self.container, orient="vertical", command=self.canvas.yview) + self.scrollbar = tk.Scrollbar(self.container, orient="vertical", command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) - self.scrollable_frame.bind("", lambda e: self.canvas.configure( - scrollregion=self.canvas.bbox("all"))) - self.img_container = tk.Label(self.scrollable_frame, padx=10, pady=10) - self.img_container.pack(side='top', fill="both", expand=True) - # self.img_container.config(width=600, height=400) + self.scrollable_frame.bind("", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) - self.canvas.create_window( - (0, 0), window=self.scrollable_frame, anchor='c') + self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor='c') self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) - # self.canvas.config(width=600, height=300) + self.canvas.config(height=400) self.scrollbar.pack(side="right", fill="y") self.fps_label = tk.Label( @@ -73,36 +74,34 @@ def __init__(self, master, img_dict, window_name): # # self.upper_frame = tk.Frame(self.scrollable_frame) - self.upper_frame.pack(in_=self.scrollable_frame, - anchor="c", side="bottom") + self.upper_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") self.lower_frame = tk.Frame(self.scrollable_frame) - self.lower_frame.pack(in_=self.scrollable_frame, - anchor="c", side="bottom") + self.lower_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") self.alert_for_hsv = tk.Label( self.lower_frame, text="", fg="red") self.alert_for_hsv.pack(pady=5, side="top") # # - self.LOWER_GREEN = [35, 80, 80] + self.LOW_GREEN = [35, 80, 80] self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") self.low_h_entry_label.pack(pady=5, side="left") self.low_h_entry = tk.Entry( - self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_h_entry.insert(0, str(self.LOWER_GREEN[0])) + self.lower_frame, width=10, justify="center", font="Courier 12") + self.low_h_entry.insert(0, str(self.LOW_GREEN[0])) self.low_h_entry.pack(pady=5, side="left") # self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") self.low_s_entry_label.pack(pady=5, side="left") self.low_s_entry = tk.Entry( - self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_s_entry.insert(0, str(self.LOWER_GREEN[1])) + self.lower_frame, width=10, justify="center", font="Courier 12") + self.low_s_entry.insert(0, str(self.LOW_GREEN[1])) self.low_s_entry.pack(pady=5, side="left") # self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") self.low_v_entry_label.pack(pady=5, side="left") self.low_v_entry = tk.Entry( - self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_v_entry.insert(0, str(self.LOWER_GREEN[2])) + self.lower_frame, width=10, justify="center", font="Courier 12") + self.low_v_entry.insert(0, str(self.LOW_GREEN[2])) self.low_v_entry.pack(pady=5, side="left") # self.update_btn_low = tk.Button( @@ -114,21 +113,21 @@ def __init__(self, master, img_dict, window_name): self.up_h_entry_label = tk.Label(self.upper_frame, text="UPPER-H:") self.up_h_entry_label.pack(pady=5, side="left") self.up_h_entry = tk.Entry( - self.upper_frame, width=5, justify="center", font="Courier 12") + self.upper_frame, width=10, justify="center", font="Courier 12") self.up_h_entry.insert(0, str(self.UPPER_GREEN[0])) self.up_h_entry.pack(pady=5, side="left") # self.up_s_entry_label = tk.Label(self.upper_frame, text="S:") self.up_s_entry_label.pack(pady=5, side="left") self.up_s_entry = tk.Entry( - self.upper_frame, width=5, justify="center", font="Courier 12") + self.upper_frame, width=10, justify="center", font="Courier 12") self.up_s_entry.insert(0, str(self.UPPER_GREEN[1])) self.up_s_entry.pack(pady=5, side="left") # self.up_v_entry_label = tk.Label(self.upper_frame, text="V:") self.up_v_entry_label.pack(pady=5, side="left") self.up_v_entry = tk.Entry( - self.upper_frame, width=5, justify="center", font="Courier 12") + self.upper_frame, width=10, justify="center", font="Courier 12") self.up_v_entry.insert(0, str(self.UPPER_GREEN[2])) self.up_v_entry.pack(pady=5, side="left") # @@ -144,70 +143,48 @@ def update_upper_hsv(self): upper_h = int(self.up_h_entry.get()) upper_s = int(self.up_s_entry.get()) upper_v = int(self.up_v_entry.get()) - if upper_h > self.LOWER_GREEN[0] and upper_s > self.LOWER_GREEN[1] and upper_v > self.LOWER_GREEN[2] and upper_h <= 255 and upper_s <= 255 and upper_v <= 255: + if upper_h > self.LOW_GREEN[0] and upper_s > self.LOW_GREEN[1] and upper_v > self.LOW_GREEN[2]: self.UPPER_GREEN = (upper_h, upper_s, upper_v) self.alert_for_hsv.config( - text="new UPPER-HSV: " + str(self.UPPER_GREEN)) + text="") else: - warning = "INVALID: " - if upper_h <= self.LOWER_GREEN[0]: - warning += "UPPER_H <= LOWER_H\t" - elif upper_s <= self.LOWER_GREEN[1]: - warning += "UPPER_S <= LOWER_S\t" - elif upper_v <= self.LOWER_GREEN[2]: - warning += "UPPER_V <= LOWER_V\t" - else: - warning += "UPPER_V > 255\t" - self.alert_for_hsv.config( - text=warning) - + text="Invalid UPPER HSV values") self.up_h_entry.delete(0, tk.END) self.up_h_entry.insert( 0, str(self.UPPER_GREEN[0])) - self.up_s_entry.delete(0, tk.END) - self.up_s_entry.insert( - 0, str(self.UPPER_GREEN[1])) self.up_v_entry.delete(0, tk.END) self.up_v_entry.insert( + 0, str(self.UPPER_GREEN[1])) + self.up_s_entry.delete(0, tk.END) + self.up_s_entry.insert( 0, str(self.UPPER_GREEN[2])) - # print(self.UPPER_GREEN) + print(self.UPPER_GREEN) def update_lower_hsv(self): lower_h = int(self.low_h_entry.get()) lower_s = int(self.low_s_entry.get()) lower_v = int(self.low_v_entry.get()) - if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2] and lower_h >= 0 and lower_s >= 0 and lower_v >= 0: + if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2]: self.LOWER_GREEN = (lower_h, lower_s, lower_v) self.alert_for_hsv.config( - text="new LOWER-HSV: " + str(self.LOWER_GREEN)) + text="") else: - warning = "INVALID: " - if lower_h >= self.UPPER_GREEN[0]: - warning += "UPPER_H <= LOWER_H\t" - elif lower_s >= self.UPPER_GREEN[1]: - warning += "UPPER_S <= LOWER_S\t" - elif lower_v >= self.UPPER_GREEN[2]: - warning += "UPPER_V <= LOWER_V\t" - else: - warning += "LOWER_V < 0\t" - self.alert_for_hsv.config( - text=warning) - + text="Invalid LOWER HSV values") self.low_h_entry.delete(0, tk.END) self.low_h_entry.insert( - 0, str(self.LOWER_GREEN[0])) - self.low_s_entry.delete(0, tk.END) - self.low_s_entry.insert( - 0, str(self.LOWER_GREEN[1])) + 0, str(self.LOW_GREEN[0])) self.low_v_entry.delete(0, tk.END) self.low_v_entry.insert( - 0, str(self.LOWER_GREEN[2])) - # print(self.LOWER_GREEN) + 0, str(self.LOW_GREEN[1])) + self.low_s_entry.delete(0, tk.END) + self.low_s_entry.insert( + 0, str(self.LOW_GREEN[2])) + print(self.LOW_GREEN) def getLowerHSV(self): - return self.LOWER_GREEN + return self.LOW_GREEN def getUpperHSV(self): return self.UPPER_GREEN @@ -246,8 +223,8 @@ def render_image(self): curr_img = Image.fromarray(curr_img) # Get the dimensions of the available space - max_width = self.canvas.winfo_width() - max_height = self.canvas.winfo_height() + max_width = self.img_container.winfo_width() + max_height = self.img_container.winfo_height() # Get the dimensions of the image img_width, img_height = curr_img.size @@ -258,22 +235,26 @@ def render_image(self): # Resize the image to fit within the available space, while maintaining aspect ratio new_width = int(img_width * scale_factor) new_height = int(img_height * scale_factor) - if new_height > 10 and new_width > 10: - new_width -= 10 - new_height -= 10 curr_img = curr_img.resize((new_width, new_height), Image.ANTIALIAS) curr_img = ImageTk.PhotoImage(curr_img) - self.img_container.config(image=curr_img, padx=10, pady=10) + self.img_container.config(image=curr_img) self.img_container.image = curr_img self.fps_label.config( - text="Frames Per Second: ~" + str(self.fps), padx=10, pady=10) + text="Frames Per Second: ~" + str(self.fps)) self.master.update() + def isActive(self): global isActive return isActive +def on_master_configure(event): + # Get the updated height and width of the master widget + global mw, mh + mw = event.width + mh = event.height + # print("New window size: {}x{}".format(mw, mh)) global root @@ -310,4 +291,4 @@ def startGUI(window_name, **kwargs): root.protocol("WM_DELETE_WINDOW", onClose) app = GUI(root, img_dict, window_name) isActive = True - return app + return app \ No newline at end of file From 0c64b53190df715702d0b3c42443152ce5253db5 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Fri, 3 Mar 2023 17:23:20 -0800 Subject: [PATCH 23/35] reverted some commits --- README.md | 84 ++++++++++++++++++------------------ gui.py | 125 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 114 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 1006a53..4d6389b 100644 --- a/README.md +++ b/README.md @@ -2,54 +2,54 @@ > For AgroBot one of the main concern is the robot's navigation. The robot must autonomously navigate the crop rows and for this task we are taking a computer vision approach. The goal of this script is to efficiently detect crop rows on a video. -# Getting started +## Getting started -# How to install +### How to install -- install[python 3.9](https: // www.python.org/downloads/release/python-390/) +- install [python 3.9](https://www.python.org/downloads/release/python-390/) - `git clone` this repository - run `pip install pipenv` - `cd` into project root - run `pipenv install` to install project dependencies - run `pipenv shell` to launch virtual environment -# Scripts +### Scripts -`test_algorithms.py: ` +`test_algorithms.py:` - runs any of the 4 algorithms against any video -- arguments: - - `-a/--alg`: name of algorithm - - `hough`, `center_row`, `mini_contour` or `scanning` - - `-v/--vid`: name of video - - video has to be stored in `./videos/` - - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show`: process drawings and show frame -- example: `python test_algorithms.py - a center_row - v crop` +- arguments : + - `-a/--alg` : name of algorithm + - `hough`, `center_row`, `mini_contour` or `scanning` + - `-v/--vid` : name of video + - video has to be stored in `./videos/` + - video configuration has to be stored as a `yaml` at `./config/video/` + - `-s/--show` : process drawings and show frame +- example : `python test_algorithms.py -a center_row -v crop` -`create_dataset.py: ` +`create_dataset.py:` - aids in data extraction from any video -- arguments: - - `-v/--vid`: name of video - - video has to be stored in `./videos/` - - `-i/--interval(optional, default=30)`: interval between keyframes - - `-o/--overwrite(optional, default=False)`: flag to indicate that you wish to overwrite files at `./extract/vid` -- example: `python create_dataset.py - v sim` or `python create_dataset.py - v crop - i 60 - o` - -`test_pf.py: ` - - runs performance tests on any of the algorithms against any video with an optional GUI +- arguments : + - `-v/--vid` : name of video + - video has to be stored in `./videos/` + - `-i/--interval (optional, default = 30)`: interval between keyframes + - `-o/--overwrite (optional, default = False)` : flag to indicate that you wish to overwrite files at `./extract/vid` +- example : `python create_dataset.py -v sim` or `python create_dataset.py -v crop -i 60 -o` + +`test_pf.py:` + - runs performance tests on any of the algorithms against any video with an optional GUI - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - - arguments: - - `-a/--alg`: name of algorithm - - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` - - `-v/--vid`: name of video - - video has to be stored in `./videos/` - - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show`: creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV - - example : `python test_pf.py - a center_row - v crop - s` - -# Commands to Start World in Gazebo + - arguments : + - `-a/--alg` : name of algorithm + - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` + - `-v/--vid` : name of video + - video has to be stored in `./videos/` + - video configuration has to be stored as a `yaml` at `./config/video/` + - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV + - example : `python test_pf.py -a center_row -v crop -s` + +### Commands to Start World in Gazebo Run the following Commands the first time: @@ -59,7 +59,7 @@ Run the following Commands the first time: Run the following commands to open the world: - Go to agrobot_ws/src folder -- Run bash script(start_corn_fields.sh or start_wheat_fields.sh) +- Run bash script (start_corn_fields.sh or start_wheat_fields.sh) To run PID, open a new terminal: @@ -67,13 +67,13 @@ To run PID, open a new terminal: To reset the position of the robot: -- Edit > Reset model poses(to reset the position of your robot to the start position) +- Edit > Reset model poses (to reset the position of your robot to the start position) -# Demo +## Demo ![](/readme_files/demo_vid.mp4) -# Colour Filtering +### Colour Filtering To accomplish this task, we need to first denoise the image. In particular, since we know we are looking for crops which are green in colour, we can filter based on colour. We convert our image to HSV format and define upper and lower bound @@ -89,9 +89,9 @@ By using bitwise and operator we see that this mask does correspond to the green ![crop bitwise](/readme_files/greenregions.png) -# Denoising and Smoothing +### Denoising and Smoothing -Next, we need to denoise the resulting mask(which is a binary image containing the crop rows). To do this, we perform +Next, we need to denoise the resulting mask (which is a binary image containing the crop rows). To do this, we perform gaussian blurring following by multiple iterations of dilation. Here is the resulting mask after dilation: ![denoising](/readme_files/denoising.png) @@ -101,7 +101,7 @@ and also we want rows that are far from the camera to be merged into one. This i our navigation system and by merging them, we can avoid detecting them in our edge and line detection in subsequent steps. -# Line Detection +### Line Detection Although, the mask image is a binary image and it can be used directly with Hough transform, the large number of points in the image lead to noisy and slow computation. Hence, Canny edge detection was used to first decrease the number of @@ -115,8 +115,8 @@ image shows the resulting lines we detected. ![Houghlines](/readme_files/Houghlines.png) -# How does this fit in with the rest of the system? +### How does this fit in with the rest of the system? We are currently using these lines and calculating their intersection which occurs at vanishing point. Then we are using a PID controller to minimimize the distance of this vanishing point from the center of our frame. Using this method, we -are able to centre our chassis over the crop rows we are traversing. +are able to centre our chassis over the crop rows we are traversing. \ No newline at end of file diff --git a/gui.py b/gui.py index 78cffcb..9eb7417 100644 --- a/gui.py +++ b/gui.py @@ -8,8 +8,6 @@ img_dict = {} isActive = False -mw = 600 -mh = 600 class GUI: @@ -23,7 +21,7 @@ class GUI: def __init__(self, master, img_dict, window_name): self.master = master self.master.title(window_name) - self.master.geometry("550x800") + self.master.geometry("600x700") self.current_avg_brightness = 110 self.current_avg_saturation = 105 @@ -33,24 +31,25 @@ def __init__(self, master, img_dict, window_name): self.curr_selected = 1 self.container = tk.Frame(self.master) - self.container.pack(side='top', anchor='nw', fill="both", expand=True, padx=5, pady=5) - - self.img_container = tk.Label(self.container) - # self.img_container.place(x=0, y=0, relwidth=1, relheight=1, anchor='nw') - self.img_container.pack(side='top', anchor='nw', fill="none", expand=False, padx=2, pady=2) - self.img_container.config(width=600, height=400) + self.container.pack(side='top', anchor='nw', fill="both", expand=True) self.canvas = tk.Canvas(self.container) - self.scrollbar = tk.Scrollbar(self.container, orient="vertical", command=self.canvas.yview) + self.scrollbar = tk.Scrollbar( + self.container, orient="vertical", command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) + self.scrollable_frame.bind("", lambda e: self.canvas.configure( + scrollregion=self.canvas.bbox("all"))) - self.scrollable_frame.bind("", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) + self.img_container = tk.Label(self.scrollable_frame, padx=10, pady=10) + self.img_container.pack(side='top', fill="both", expand=True) + # self.img_container.config(width=600, height=400) - self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor='c') + self.canvas.create_window( + (0, 0), window=self.scrollable_frame, anchor='c') self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) - self.canvas.config(height=400) + # self.canvas.config(width=600, height=300) self.scrollbar.pack(side="right", fill="y") self.fps_label = tk.Label( @@ -74,34 +73,36 @@ def __init__(self, master, img_dict, window_name): # # self.upper_frame = tk.Frame(self.scrollable_frame) - self.upper_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") + self.upper_frame.pack(in_=self.scrollable_frame, + anchor="c", side="bottom") self.lower_frame = tk.Frame(self.scrollable_frame) - self.lower_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") + self.lower_frame.pack(in_=self.scrollable_frame, + anchor="c", side="bottom") self.alert_for_hsv = tk.Label( self.lower_frame, text="", fg="red") self.alert_for_hsv.pack(pady=5, side="top") # # - self.LOW_GREEN = [35, 80, 80] + self.LOWER_GREEN = [35, 80, 80] self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") self.low_h_entry_label.pack(pady=5, side="left") self.low_h_entry = tk.Entry( - self.lower_frame, width=10, justify="center", font="Courier 12") - self.low_h_entry.insert(0, str(self.LOW_GREEN[0])) + self.lower_frame, width=5, justify="center", font="Courier 12") + self.low_h_entry.insert(0, str(self.LOWER_GREEN[0])) self.low_h_entry.pack(pady=5, side="left") # self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") self.low_s_entry_label.pack(pady=5, side="left") self.low_s_entry = tk.Entry( - self.lower_frame, width=10, justify="center", font="Courier 12") - self.low_s_entry.insert(0, str(self.LOW_GREEN[1])) + self.lower_frame, width=5, justify="center", font="Courier 12") + self.low_s_entry.insert(0, str(self.LOWER_GREEN[1])) self.low_s_entry.pack(pady=5, side="left") # self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") self.low_v_entry_label.pack(pady=5, side="left") self.low_v_entry = tk.Entry( - self.lower_frame, width=10, justify="center", font="Courier 12") - self.low_v_entry.insert(0, str(self.LOW_GREEN[2])) + self.lower_frame, width=5, justify="center", font="Courier 12") + self.low_v_entry.insert(0, str(self.LOWER_GREEN[2])) self.low_v_entry.pack(pady=5, side="left") # self.update_btn_low = tk.Button( @@ -113,21 +114,21 @@ def __init__(self, master, img_dict, window_name): self.up_h_entry_label = tk.Label(self.upper_frame, text="UPPER-H:") self.up_h_entry_label.pack(pady=5, side="left") self.up_h_entry = tk.Entry( - self.upper_frame, width=10, justify="center", font="Courier 12") + self.upper_frame, width=5, justify="center", font="Courier 12") self.up_h_entry.insert(0, str(self.UPPER_GREEN[0])) self.up_h_entry.pack(pady=5, side="left") # self.up_s_entry_label = tk.Label(self.upper_frame, text="S:") self.up_s_entry_label.pack(pady=5, side="left") self.up_s_entry = tk.Entry( - self.upper_frame, width=10, justify="center", font="Courier 12") + self.upper_frame, width=5, justify="center", font="Courier 12") self.up_s_entry.insert(0, str(self.UPPER_GREEN[1])) self.up_s_entry.pack(pady=5, side="left") # self.up_v_entry_label = tk.Label(self.upper_frame, text="V:") self.up_v_entry_label.pack(pady=5, side="left") self.up_v_entry = tk.Entry( - self.upper_frame, width=10, justify="center", font="Courier 12") + self.upper_frame, width=5, justify="center", font="Courier 12") self.up_v_entry.insert(0, str(self.UPPER_GREEN[2])) self.up_v_entry.pack(pady=5, side="left") # @@ -143,48 +144,70 @@ def update_upper_hsv(self): upper_h = int(self.up_h_entry.get()) upper_s = int(self.up_s_entry.get()) upper_v = int(self.up_v_entry.get()) - if upper_h > self.LOW_GREEN[0] and upper_s > self.LOW_GREEN[1] and upper_v > self.LOW_GREEN[2]: + if upper_h > self.LOWER_GREEN[0] and upper_s > self.LOWER_GREEN[1] and upper_v > self.LOWER_GREEN[2] and upper_h <= 255 and upper_s <= 255 and upper_v <= 255: self.UPPER_GREEN = (upper_h, upper_s, upper_v) self.alert_for_hsv.config( - text="") + text="new UPPER-HSV: " + str(self.UPPER_GREEN)) else: + warning = "INVALID: " + if upper_h <= self.LOWER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif upper_s <= self.LOWER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif upper_v <= self.LOWER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "UPPER_V > 255\t" + self.alert_for_hsv.config( - text="Invalid UPPER HSV values") + text=warning) + self.up_h_entry.delete(0, tk.END) self.up_h_entry.insert( 0, str(self.UPPER_GREEN[0])) - self.up_v_entry.delete(0, tk.END) - self.up_v_entry.insert( - 0, str(self.UPPER_GREEN[1])) self.up_s_entry.delete(0, tk.END) self.up_s_entry.insert( + 0, str(self.UPPER_GREEN[1])) + self.up_v_entry.delete(0, tk.END) + self.up_v_entry.insert( 0, str(self.UPPER_GREEN[2])) - print(self.UPPER_GREEN) + # print(self.UPPER_GREEN) def update_lower_hsv(self): lower_h = int(self.low_h_entry.get()) lower_s = int(self.low_s_entry.get()) lower_v = int(self.low_v_entry.get()) - if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2]: + if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2] and lower_h >= 0 and lower_s >= 0 and lower_v >= 0: self.LOWER_GREEN = (lower_h, lower_s, lower_v) self.alert_for_hsv.config( - text="") + text="new LOWER-HSV: " + str(self.LOWER_GREEN)) else: + warning = "INVALID: " + if lower_h >= self.UPPER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif lower_s >= self.UPPER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif lower_v >= self.UPPER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "LOWER_V < 0\t" + self.alert_for_hsv.config( - text="Invalid LOWER HSV values") + text=warning) + self.low_h_entry.delete(0, tk.END) self.low_h_entry.insert( - 0, str(self.LOW_GREEN[0])) - self.low_v_entry.delete(0, tk.END) - self.low_v_entry.insert( - 0, str(self.LOW_GREEN[1])) + 0, str(self.LOWER_GREEN[0])) self.low_s_entry.delete(0, tk.END) self.low_s_entry.insert( - 0, str(self.LOW_GREEN[2])) - print(self.LOW_GREEN) + 0, str(self.LOWER_GREEN[1])) + self.low_v_entry.delete(0, tk.END) + self.low_v_entry.insert( + 0, str(self.LOWER_GREEN[2])) + # print(self.LOWER_GREEN) def getLowerHSV(self): - return self.LOW_GREEN + return self.LOWER_GREEN def getUpperHSV(self): return self.UPPER_GREEN @@ -223,8 +246,8 @@ def render_image(self): curr_img = Image.fromarray(curr_img) # Get the dimensions of the available space - max_width = self.img_container.winfo_width() - max_height = self.img_container.winfo_height() + max_width = self.canvas.winfo_width() + max_height = self.canvas.winfo_height() # Get the dimensions of the image img_width, img_height = curr_img.size @@ -235,26 +258,22 @@ def render_image(self): # Resize the image to fit within the available space, while maintaining aspect ratio new_width = int(img_width * scale_factor) new_height = int(img_height * scale_factor) + if new_height > 10 and new_width > 10: + new_width -= 10 + new_height -= 10 curr_img = curr_img.resize((new_width, new_height), Image.ANTIALIAS) curr_img = ImageTk.PhotoImage(curr_img) - self.img_container.config(image=curr_img) + self.img_container.config(image=curr_img, padx=10, pady=10) self.img_container.image = curr_img self.fps_label.config( - text="Frames Per Second: ~" + str(self.fps)) + text="Frames Per Second: ~" + str(self.fps), padx=10, pady=10) self.master.update() - def isActive(self): global isActive return isActive -def on_master_configure(event): - # Get the updated height and width of the master widget - global mw, mh - mw = event.width - mh = event.height - # print("New window size: {}x{}".format(mw, mh)) global root From bc467bb801377c58e5c34867879fb9cf0888cbc0 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Fri, 3 Mar 2023 17:28:29 -0800 Subject: [PATCH 24/35] Revert "fix bug with lowerhsv and apply precommit" This reverts commit 47b84ef6b3cfb5998a163ac0090e3ed5ddaffdd8. --- algorithms/CenterDownwards.py | 15 ++-- algorithms/CenterRowAlgorithm.py | 20 ++--- algorithms/CheckRowEnd.py | 2 +- algorithms/HoughAlgorithm.py | 11 +-- algorithms/MiniContoursAlgorithm.py | 29 +++----- algorithms/MiniContoursDownwards.py | 26 +++---- algorithms/ScanningAlgorithm.py | 35 +++------ algorithms/SeesawAlgorithm.py | 20 ++--- algorithms/utils/Lines.py | 6 +- algorithms/utils/delete_small_contours.py | 6 +- create_dataset.py | 3 +- gui.py | 89 ++++++++--------------- helper_scripts/DataExtractor.py | 9 +-- test_algorithms.py | 12 +-- 14 files changed, 97 insertions(+), 186 deletions(-) diff --git a/algorithms/CenterDownwards.py b/algorithms/CenterDownwards.py index 654dcd7..664b959 100644 --- a/algorithms/CenterDownwards.py +++ b/algorithms/CenterDownwards.py @@ -21,8 +21,7 @@ def __init__(self, config): # filtering parameters self.averaging_kernel_size = config.averaging_kernel_size - self.gauss_kernel_size = list( - map(int, config.gauss_kernel_size.split(','))) + self.gauss_kernel_size = list(map(int, config.gauss_kernel_size.split(','))) self.dilate_kernel_size = config.dilate_kernel_size self.sigma_x = config.sigma_x @@ -122,8 +121,7 @@ def get_contours(self, binary_mask): """ frame = np.zeros((self.HEIGHT, self.WIDTH, 3)) ret, thresh = cv.threshold(binary_mask, 0, 254, 0) - contours, hierarchy = cv.findContours( - thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) + contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) return contours, frame @@ -151,8 +149,7 @@ def ellipse_slopes(self, contours, black_frame): rect = cv.minAreaRect(con) box = cv.boxPoints(rect) box = np.int0(box) - black_frame = cv.drawContours( - black_frame, [box], 0, (255, 255, 255), 2) + black_frame = cv.drawContours(black_frame, [box], 0, (255, 255, 255), 2) ellipse = cv.fitEllipse(con) self.contours.append(ellipse) cv.ellipse(black_frame, ellipse, (255, 255, 255), 2) @@ -186,13 +183,11 @@ def ellipse_slopes(self, contours, black_frame): slopes.append(slope) lefty = int((-x * vy / vx) + y) righty = int(((cols - x) * vy / vx) + y) - black_frame = cv.line( - black_frame, (cols - 1, righty), (0, lefty), (255, 255, 0), 9) + black_frame = cv.line(black_frame, (cols - 1, righty), (0, lefty), (255, 255, 0), 9) lines.append([cols - 1, righty, 0, lefty]) else: - black_frame = cv.line(black_frame, (int(x), 0), - (int(x), rows - 1), (255, 255, 0), 9) + black_frame = cv.line(black_frame, (int(x), 0), (int(x), rows - 1), (255, 255, 0), 9) lines.append([int(x), 0, int(x), rows - 1]) return lines, slopes, black_frame diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index 1dcc675..fe50ec6 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -22,8 +22,7 @@ def __init__(self, config): # filtering parameters self.averaging_kernel_size = config.averaging_kernel_size - self.gauss_kernel_size = list( - map(int, config.gauss_kernel_size.split(','))) + self.gauss_kernel_size = list(map(int, config.gauss_kernel_size.split(','))) self.dilate_kernel_size = config.dilate_kernel_size self.sigma_x = config.sigma_x @@ -55,7 +54,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, frame, show): """Uses contouring to create contours around each crop row and uses these contours to find centroid lines, row vanishing point, a center contour and the angle between the center contour and vanishing point\n @@ -77,8 +76,7 @@ def process_frame(self, frame, show): cv.drawContours(black_frame, contours, -1, self.contour_color, 3) # fillPoly fills in the polygons in the frame cv.fillPoly(black_frame, pts=contours, color=self.contour_color) - lines, slopes, ellipse_frame = self.ellipse_slopes( - contours, black_frame) + lines, slopes, ellipse_frame = self.ellipse_slopes(contours, black_frame) if show: Lines.draw_lines_on_frame(lines, black_frame) @@ -86,16 +84,14 @@ def process_frame(self, frame, show): intersections = Lines.get_intersections(lines) x_points = [point[0] for point in intersections] y_points = [point[1] for point in intersections] - vanishing_point = Lines.draw_vanishing_point( - ellipse_frame, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point(ellipse_frame, x_points, y_points, show) if vanishing_point: center_contour, angle = self.find_center_contour(vanishing_point) if show: cv.ellipse(black_frame, center_contour, (0, 255, 0), 2) - angle = Lines.calculate_angle_from_v_point( - vanishing_point, self.WIDTH, self.HEIGHT) + angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) return black_frame, angle @@ -125,8 +121,7 @@ def get_contours(self, binary_mask): """ frame = np.zeros((self.HEIGHT, self.WIDTH, 3)) ret, thresh = cv.threshold(binary_mask, 0, 254, 0) - contours, hierarchy = cv.findContours( - thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) + contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) cv.drawContours(frame, contours, -1, (0, 255, 0), 2) cv.fillPoly(frame, pts=contours, color=(0, 255, 0)) @@ -148,8 +143,7 @@ def ellipse_slopes(self, contours, black_frame): rect = cv.minAreaRect(cnt) box = cv.boxPoints(rect) box = np.int0(box) - black_frame = cv.drawContours( - black_frame, [box], 0, (255, 255, 255), 2) + black_frame = cv.drawContours(black_frame, [box], 0, (255, 255, 255), 2) ellipse = cv.fitEllipse(cnt) self.contours.append(ellipse) cv.ellipse(black_frame, ellipse, (255, 255, 255), 2) diff --git a/algorithms/CheckRowEnd.py b/algorithms/CheckRowEnd.py index 4572752..58b24c8 100644 --- a/algorithms/CheckRowEnd.py +++ b/algorithms/CheckRowEnd.py @@ -26,7 +26,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, frame, show): """Averages values in each row in a mask of the frame. If the number of rows with an average value of zero is greater than req_rows_empty, then frame is row end\n diff --git a/algorithms/HoughAlgorithm.py b/algorithms/HoughAlgorithm.py index 868cb6c..ae2723d 100644 --- a/algorithms/HoughAlgorithm.py +++ b/algorithms/HoughAlgorithm.py @@ -41,7 +41,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + # processFrame function that is called to process a frame of a video # takes in frame mat object obtained from cv2 video.read() def process_frame(self, frame, show=True): @@ -92,15 +92,12 @@ def process_frame(self, frame, show=True): y_points = [point[1] for point in intersections] if show: - vanishing_point = Lines.draw_vanishing_point( - line_img, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point(line_img, x_points, y_points, show) else: - vanishing_point = Lines.draw_vanishing_point( - frame, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point(frame, x_points, y_points, show) # Calculating angle from vanishing point to (self.WIDTH // 2, 0) - angle = Lines.calculate_angle_from_v_point( - vanishing_point, self.WIDTH, self.HEIGHT) + angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) if show: return line_img, angle diff --git a/algorithms/MiniContoursAlgorithm.py b/algorithms/MiniContoursAlgorithm.py index dc3e796..ed7d5c4 100644 --- a/algorithms/MiniContoursAlgorithm.py +++ b/algorithms/MiniContoursAlgorithm.py @@ -72,8 +72,7 @@ def get_centroids(self, mask, num_strips): centroids = [] for i, strip in enumerate(strips): - contours, hierarchy = cv2.findContours( - strip, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + contours, hierarchy = cv2.findContours(strip, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) strip_centroids = [] for contour in contours: M = cv2.moments(contour) @@ -107,8 +106,7 @@ def get_center_hough_lines( # lines: list of [[votes, rho, theta]] of all lines # point_lines: list of [votes, pt1, pt2] of all lines - mask = cv2.inRange(cv2.cvtColor( - frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) + mask = cv2.inRange(cv2.cvtColor(frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) mask = cv2.medianBlur(mask, 9) # mask = cv2.GaussianBlur(mask, (9,9), 10) # mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, self.morphology_kernel) @@ -136,8 +134,7 @@ def get_center_hough_lines( if show: cv2.line(frame, (0, cut_off_height), (width // 2, 0), self.color_2) - cv2.line(frame, (width, cut_off_height), - (width // 2, 0), self.color_2) + cv2.line(frame, (width, cut_off_height), (width // 2, 0), self.color_2) for i, strip_centroid in enumerate(centroids): if i > int(0.3 * len(centroids)): @@ -152,16 +149,12 @@ def get_center_hough_lines( segmented_points[idx].append([int(x), int(y)]) if show: - cv2.circle(frame, (int(centroid[0]), int( - centroid[1])), 3, self.color_1, -1) - cv2.circle(mask, (int(centroid[0]), int( - centroid[1])), 3, self.color_1, -1) - points_vector.append( - [int(centroid[0]), int(centroid[1])]) + cv2.circle(frame, (int(centroid[0]), int(centroid[1])), 3, self.color_1, -1) + cv2.circle(mask, (int(centroid[0]), int(centroid[1])), 3, self.color_1, -1) + points_vector.append([int(centroid[0]), int(centroid[1])]) else: if show: - cv2.circle(frame, (int(centroid[0]), int( - centroid[1])), 3, self.color_3, -1) + cv2.circle(frame, (int(centroid[0]), int(centroid[1])), 3, self.color_3, -1) c_mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) @@ -217,7 +210,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, original_frame, num_strips=60, show=False): # original_frame: BGR frame @@ -243,11 +236,9 @@ def process_frame(self, original_frame, num_strips=60, show=False): x_points = [point[0] for point in intersections] y_points = [point[1] for point in intersections] - vanishing_point = Lines.draw_vanishing_point( - frame, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point(frame, x_points, y_points, show) # Calculating angle from vanishing point to (self.WIDTH // 2, 0) - angle = Lines.calculate_angle_from_v_point( - vanishing_point, self.WIDTH, self.HEIGHT) + angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) return frame, angle diff --git a/algorithms/MiniContoursDownwards.py b/algorithms/MiniContoursDownwards.py index 5bccaa4..c67cf59 100644 --- a/algorithms/MiniContoursDownwards.py +++ b/algorithms/MiniContoursDownwards.py @@ -24,14 +24,11 @@ def __init__(self, config): self.high_green = np.array(config.high_green) # random colors for drawing lines - # teal (for contour points that are within bounds) - self.color_1 = (255, 255, 0) + self.color_1 = (255, 255, 0) # teal (for contour points that are within bounds) # pink (for the line of best fit through all the contour points that are within bounds) self.color_2 = (200, 0, 255) - # red (for contour points that are out of bounds) - self.color_3 = (0, 0, 255) - # orange (for a reference line vertically down center of frame) - self.midline = (0, 129, 255) + self.color_3 = (0, 0, 255) # red (for contour points that are out of bounds) + self.midline = (0, 129, 255) # orange (for a reference line vertically down center of frame) # parameters for calculating centroids and drawing the best fit line among them self.num_strips = self.config.num_strips @@ -68,8 +65,7 @@ def get_centroids(self, mask, num_strips, show): centroids = [] for i, strip in enumerate(strips): - contours, hierarchy = cv2.findContours( - strip, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + contours, hierarchy = cv2.findContours(strip, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) strip_centroids = [] for contour in contours: M = cv2.moments(contour) @@ -93,8 +89,7 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p -line: A vector [vx, vy, x, y] representing the line of best fit through all the centroids. [vx, vy] is a vector that describes the direction of the line, where (x, y) when taken together is a point on the line """"" - mask = cv2.inRange(cv2.cvtColor( - frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) + mask = cv2.inRange(cv2.cvtColor(frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) mask = cv2.medianBlur(mask, 9) # mask = cv2.GaussianBlur(mask, (9,9), 10) # mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, self.morphology_kernel) @@ -116,10 +111,8 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p idx = int(x / width * split_factor) segmented_points[idx].append([int(x), int(y)]) if show: - cv2.circle(frame, (int(centroid[0]), int( - centroid[1])), 3, self.color_1, -1) - cv2.circle(mask, (int(centroid[0]), int( - centroid[1])), 3, self.color_1, -1) + cv2.circle(frame, (int(centroid[0]), int(centroid[1])), 3, self.color_1, -1) + cv2.circle(mask, (int(centroid[0]), int(centroid[1])), 3, self.color_1, -1) points_vector.append([int(centroid[0]), int(centroid[1])]) c_mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) @@ -168,7 +161,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, original_frame, num_strips=60, show=False): """"" parameters: @@ -191,8 +184,7 @@ def process_frame(self, original_frame, num_strips=60, show=False): if line is not None and line[0] is not None: angle = round(math.degrees(math.atan(-line[0] / line[1])), 2) - cv2.line(frame, (int(self.WIDTH / 2), 0), - (int(self.WIDTH / 2), int(self.HEIGHT)), self.midline, 2) + cv2.line(frame, (int(self.WIDTH / 2), 0), (int(self.WIDTH / 2), int(self.HEIGHT)), self.midline, 2) print(angle) return frame, angle else: diff --git a/algorithms/ScanningAlgorithm.py b/algorithms/ScanningAlgorithm.py index d10e567..1a02dbf 100644 --- a/algorithms/ScanningAlgorithm.py +++ b/algorithms/ScanningAlgorithm.py @@ -26,8 +26,7 @@ def __init__(self, config): self.right_x_bound = int(self.WIDTH * (1 - self.config.bounding_box_x)) self.upper_y_bound = int(self.HEIGHT * self.config.bounding_box_y) self.lower_y_bound = self.HEIGHT - 1 - self.mid_y = self.lower_y_bound - \ - (self.lower_y_bound - self.upper_y_bound) // 2 + self.mid_y = self.lower_y_bound - (self.lower_y_bound - self.upper_y_bound) // 2 self.kernel = np.ones((5, 5), np.uint8) @@ -38,16 +37,14 @@ def __init__(self, config): # creates lines from the top of the horizon to the bottom for top_x in range(0, self.WIDTH, self.pixel_gap): for bottom_x in range(0, self.WIDTH, self.pixel_gap): - line = self.create_line( - top_x, self.upper_y_bound, bottom_x, self.lower_y_bound) + line = self.create_line(top_x, self.upper_y_bound, bottom_x, self.lower_y_bound) self.lines.append(line) # creates lines from horizon to the right side for top_x in range(0, self.WIDTH, self.pixel_gap): for right_y in range(self.mid_y, self.lower_y_bound, self.pixel_gap): - line1 = self.create_line( - top_x, self.upper_y_bound, self.WIDTH - 1, right_y) + line1 = self.create_line(top_x, self.upper_y_bound, self.WIDTH - 1, right_y) self.lines.append(line1) # creates lines from horizon to the left side @@ -86,7 +83,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, frame, show): hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) @@ -101,8 +98,7 @@ def process_frame(self, frame, show): return mask, None self.upper_y_bound = white_pixels[0][0] - self.mid_y = self.lower_y_bound - \ - (self.lower_y_bound - self.upper_y_bound) // 2 + self.mid_y = self.lower_y_bound - (self.lower_y_bound - self.upper_y_bound) // 2 # use this to invert the mask # mask = ~mask @@ -135,8 +131,7 @@ def process_frame(self, frame, show): most_prominent_pos_lines = pos_array[:, 1][largest_pos_indices] most_prominent_neg_lines = neg_array[:, 1][largest_neg_indices] - most_prominent_lines = numpy.concatenate( - (most_prominent_pos_lines, most_prominent_neg_lines), axis=None) + most_prominent_lines = numpy.concatenate((most_prominent_pos_lines, most_prominent_neg_lines), axis=None) # convert to lines as defined in Lines.py converted_lines = [] @@ -151,33 +146,27 @@ def process_frame(self, frame, show): x_points = [point[0] for point in intersections] y_points = [point[1] for point in intersections] - vanishing_point = Lines.draw_vanishing_point( - frame, x_points, y_points, show) + vanishing_point = Lines.draw_vanishing_point(frame, x_points, y_points, show) if show: for line in converted_lines: - frame = cv2.line( - frame, (line[0], line[1]), (line[2], line[3]), (255, 255, 255), 1) + frame = cv2.line(frame, (line[0], line[1]), (line[2], line[3]), (255, 255, 255), 1) # line for the middle of frame - frame = cv2.line(frame, (self.WIDTH // 2, self.HEIGHT), - (self.WIDTH // 2, 0), (0, 0, 255), 1) + frame = cv2.line(frame, (self.WIDTH // 2, self.HEIGHT), (self.WIDTH // 2, 0), (0, 0, 255), 1) # point with x coordinate of the vanishing point and y coordinate of the end of the crop row - frame = cv2.circle( - frame, (vanishing_point[0], self.upper_y_bound), 5, (0, 255, 0), -1) + frame = cv2.circle(frame, (vanishing_point[0], self.upper_y_bound), 5, (0, 255, 0), -1) # point in the middle of frame at midpoint between the horizon and bottom of the screen - frame = cv2.circle( - frame, (self.WIDTH // 2, self.mid_y), 5, (0, 255, 0), -1) + frame = cv2.circle(frame, (self.WIDTH // 2, self.mid_y), 5, (0, 255, 0), -1) # line between the two points above frame = cv2.line(frame, (self.WIDTH // 2, self.mid_y), (vanishing_point[0], self.upper_y_bound), (0, 255, 0), 2) # Calculating angle from vanishing point to (self.WIDTH // 2, 0) - angle = Lines.calculate_angle_from_v_point( - vanishing_point, self.WIDTH, self.HEIGHT) + angle = Lines.calculate_angle_from_v_point(vanishing_point, self.WIDTH, self.HEIGHT) return frame, angle diff --git a/algorithms/SeesawAlgorithm.py b/algorithms/SeesawAlgorithm.py index c48e23d..f729645 100644 --- a/algorithms/SeesawAlgorithm.py +++ b/algorithms/SeesawAlgorithm.py @@ -15,8 +15,7 @@ def __init__(self, config): # filtering parameters self.averaging_kernel_size = config.averaging_kernel_size - self.gauss_kernel_size = list( - map(int, config.gauss_kernel_size.split(','))) + self.gauss_kernel_size = list(map(int, config.gauss_kernel_size.split(','))) self.dilate_kernel_size = config.dilate_kernel_size self.sigma_x = config.sigma_x @@ -33,7 +32,7 @@ def update_lower_hsv(self, next): def update_upper_hsv(self, next): self.HIGH_GREEN = np.array(next) - + def process_frame(self, frame, show): black_frame, points, both_points = self.plot_points(frame) @@ -46,13 +45,11 @@ def process_frame(self, frame, show): x2 = int(x + vx * self.WIDTH) y1 = int(y - vy * self.HEIGHT) y2 = int(y + vy * self.HEIGHT) - black_frame = cv.line(black_frame, (x1, y1), - (x2, y2), (0, 255, 255), 9) + black_frame = cv.line(black_frame, (x1, y1), (x2, y2), (0, 255, 255), 9) """calculate angle""" if y1 - y2 != 0: - angle = round(math.degrees( - math.atan(int(x2 - x1) / int(y1 - y2))), 2) + angle = round(math.degrees(math.atan(int(x2 - x1) / int(y1 - y2))), 2) else: angle = None @@ -82,8 +79,7 @@ def plot_points(self, frame): while square_low < self.HEIGHT: normalized = False seg_left = left[int(square_low) + 1:int(square_high), 0:half_width] - seg_right = right[int(square_low) + - 1:int(square_high), 0:half_width] + seg_right = right[int(square_low) + 1:int(square_high), 0:half_width] left_x = int(np.sum(seg_left == 255) / bar_height) right_x = int(np.sum(seg_right == 255) / bar_height) @@ -101,12 +97,10 @@ def plot_points(self, frame): black_frame = cv.rectangle(black_frame, (half_width, square_low), ( x2, int(square_high)), (255, 255, 0), 3) - both_point = [int((x1 + x2) / 2), - int((square_high + square_low) / 2)] + both_point = [int((x1 + x2) / 2), int((square_high + square_low) / 2)] both_points.append(both_point) - black_frame = cv.circle( - black_frame, both_point, radius=0, color=(0, 0, 255), thickness=15) + black_frame = cv.circle(black_frame, both_point, radius=0, color=(0, 0, 255), thickness=15) square_high += bar_height square_low += bar_height diff --git a/algorithms/utils/Lines.py b/algorithms/utils/Lines.py index e6df41e..048d897 100644 --- a/algorithms/utils/Lines.py +++ b/algorithms/utils/Lines.py @@ -89,8 +89,7 @@ def get_intersections(lines, min_slope=1): x1R, y1R, x2R, y2R = assign_coordinate_values(lineR) # calls the getIntersection helper function - intersect = get_intersection( - ((x1L, y1L), (x2L, y2L)), ((x1R, y1R), (x2R, y2R))) + intersect = get_intersection(((x1L, y1L), (x2L, y2L)), ((x1R, y1R), (x2R, y2R))) if isinstance(intersect, bool): continue @@ -229,8 +228,7 @@ def draw_vanishing_point(frame, x_points, y_points, show, use_median=True): intersecting_x = np.mean(filtered_x) intersecting_y = np.mean(filtered_y) if show: - cv.circle(frame, (int(intersecting_x), int( - intersecting_y)), 8, (255, 0, 0), -1) + cv.circle(frame, (int(intersecting_x), int(intersecting_y)), 8, (255, 0, 0), -1) return (int(intersecting_x), int(intersecting_y)) else: return None diff --git a/algorithms/utils/delete_small_contours.py b/algorithms/utils/delete_small_contours.py index cdcd10b..ffd6f93 100644 --- a/algorithms/utils/delete_small_contours.py +++ b/algorithms/utils/delete_small_contours.py @@ -36,11 +36,9 @@ def morph_op(mask, kernel_size, op, iterations): if op == 2: mask = cv.dilate(mask, kernel, iterations=iterations) if op == 3: - mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, - kernel, iterations=iterations) + mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, kernel, iterations=iterations) if op == 4: - mask = cv.morphologyEx(mask, cv.MORPH_OPEN, - kernel, iterations=iterations) + mask = cv.morphologyEx(mask, cv.MORPH_OPEN, kernel, iterations=iterations) else: mask = mask diff --git a/create_dataset.py b/create_dataset.py index 5f2a4ae..0cb2b91 100644 --- a/create_dataset.py +++ b/create_dataset.py @@ -23,8 +23,7 @@ def main(): # verify that video exists in ./videos if not path.isfile(f'videos/{video_name}.mp4'): - print('--vid', video_name, - "is an invalid video name, make sure video exists in ./videos") + print('--vid', video_name, "is an invalid video name, make sure video exists in ./videos") sys.exit() # make sure any important data does not get overwritten diff --git a/gui.py b/gui.py index 9eb7417..74112bb 100644 --- a/gui.py +++ b/gui.py @@ -34,18 +34,15 @@ def __init__(self, master, img_dict, window_name): self.container.pack(side='top', anchor='nw', fill="both", expand=True) self.canvas = tk.Canvas(self.container) - self.scrollbar = tk.Scrollbar( - self.container, orient="vertical", command=self.canvas.yview) + self.scrollbar = tk.Scrollbar(self.container, orient="vertical", command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) - self.scrollable_frame.bind("", lambda e: self.canvas.configure( - scrollregion=self.canvas.bbox("all"))) + self.scrollable_frame.bind("", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) - self.img_container = tk.Label(self.scrollable_frame, padx=10, pady=10) + self.img_container = tk.Label(self.scrollable_frame) self.img_container.pack(side='top', fill="both", expand=True) # self.img_container.config(width=600, height=400) - self.canvas.create_window( - (0, 0), window=self.scrollable_frame, anchor='c') + self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor='c') self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) @@ -73,36 +70,34 @@ def __init__(self, master, img_dict, window_name): # # self.upper_frame = tk.Frame(self.scrollable_frame) - self.upper_frame.pack(in_=self.scrollable_frame, - anchor="c", side="bottom") + self.upper_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") self.lower_frame = tk.Frame(self.scrollable_frame) - self.lower_frame.pack(in_=self.scrollable_frame, - anchor="c", side="bottom") + self.lower_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") self.alert_for_hsv = tk.Label( self.lower_frame, text="", fg="red") self.alert_for_hsv.pack(pady=5, side="top") # # - self.LOWER_GREEN = [35, 80, 80] + self.LOW_GREEN = [35, 80, 80] self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") self.low_h_entry_label.pack(pady=5, side="left") self.low_h_entry = tk.Entry( self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_h_entry.insert(0, str(self.LOWER_GREEN[0])) + self.low_h_entry.insert(0, str(self.LOW_GREEN[0])) self.low_h_entry.pack(pady=5, side="left") # self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") self.low_s_entry_label.pack(pady=5, side="left") self.low_s_entry = tk.Entry( self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_s_entry.insert(0, str(self.LOWER_GREEN[1])) + self.low_s_entry.insert(0, str(self.LOW_GREEN[1])) self.low_s_entry.pack(pady=5, side="left") # self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") self.low_v_entry_label.pack(pady=5, side="left") self.low_v_entry = tk.Entry( self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_v_entry.insert(0, str(self.LOWER_GREEN[2])) + self.low_v_entry.insert(0, str(self.LOW_GREEN[2])) self.low_v_entry.pack(pady=5, side="left") # self.update_btn_low = tk.Button( @@ -144,70 +139,48 @@ def update_upper_hsv(self): upper_h = int(self.up_h_entry.get()) upper_s = int(self.up_s_entry.get()) upper_v = int(self.up_v_entry.get()) - if upper_h > self.LOWER_GREEN[0] and upper_s > self.LOWER_GREEN[1] and upper_v > self.LOWER_GREEN[2] and upper_h <= 255 and upper_s <= 255 and upper_v <= 255: + if upper_h > self.LOW_GREEN[0] and upper_s > self.LOW_GREEN[1] and upper_v > self.LOW_GREEN[2]: self.UPPER_GREEN = (upper_h, upper_s, upper_v) self.alert_for_hsv.config( - text="new UPPER-HSV: " + str(self.UPPER_GREEN)) + text="") else: - warning = "INVALID: " - if upper_h <= self.LOWER_GREEN[0]: - warning += "UPPER_H <= LOWER_H\t" - elif upper_s <= self.LOWER_GREEN[1]: - warning += "UPPER_S <= LOWER_S\t" - elif upper_v <= self.LOWER_GREEN[2]: - warning += "UPPER_V <= LOWER_V\t" - else: - warning += "UPPER_V > 255\t" - self.alert_for_hsv.config( - text=warning) - + text="Invalid UPPER HSV values") self.up_h_entry.delete(0, tk.END) self.up_h_entry.insert( 0, str(self.UPPER_GREEN[0])) - self.up_s_entry.delete(0, tk.END) - self.up_s_entry.insert( - 0, str(self.UPPER_GREEN[1])) self.up_v_entry.delete(0, tk.END) self.up_v_entry.insert( + 0, str(self.UPPER_GREEN[1])) + self.up_s_entry.delete(0, tk.END) + self.up_s_entry.insert( 0, str(self.UPPER_GREEN[2])) - # print(self.UPPER_GREEN) + print(self.UPPER_GREEN) def update_lower_hsv(self): lower_h = int(self.low_h_entry.get()) lower_s = int(self.low_s_entry.get()) lower_v = int(self.low_v_entry.get()) - if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2] and lower_h >= 0 and lower_s >= 0 and lower_v >= 0: + if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2]: self.LOWER_GREEN = (lower_h, lower_s, lower_v) self.alert_for_hsv.config( - text="new LOWER-HSV: " + str(self.LOWER_GREEN)) + text="") else: - warning = "INVALID: " - if lower_h >= self.UPPER_GREEN[0]: - warning += "UPPER_H <= LOWER_H\t" - elif lower_s >= self.UPPER_GREEN[1]: - warning += "UPPER_S <= LOWER_S\t" - elif lower_v >= self.UPPER_GREEN[2]: - warning += "UPPER_V <= LOWER_V\t" - else: - warning += "LOWER_V < 0\t" - self.alert_for_hsv.config( - text=warning) - + text="Invalid LOWER HSV values") self.low_h_entry.delete(0, tk.END) self.low_h_entry.insert( - 0, str(self.LOWER_GREEN[0])) - self.low_s_entry.delete(0, tk.END) - self.low_s_entry.insert( - 0, str(self.LOWER_GREEN[1])) + 0, str(self.LOW_GREEN[0])) self.low_v_entry.delete(0, tk.END) self.low_v_entry.insert( - 0, str(self.LOWER_GREEN[2])) - # print(self.LOWER_GREEN) + 0, str(self.LOW_GREEN[1])) + self.low_s_entry.delete(0, tk.END) + self.low_s_entry.insert( + 0, str(self.LOW_GREEN[2])) + print(self.LOW_GREEN) def getLowerHSV(self): - return self.LOWER_GREEN + return self.LOW_GREEN def getUpperHSV(self): return self.UPPER_GREEN @@ -258,17 +231,15 @@ def render_image(self): # Resize the image to fit within the available space, while maintaining aspect ratio new_width = int(img_width * scale_factor) new_height = int(img_height * scale_factor) - if new_height > 10 and new_width > 10: - new_width -= 10 - new_height -= 10 curr_img = curr_img.resize((new_width, new_height), Image.ANTIALIAS) curr_img = ImageTk.PhotoImage(curr_img) - self.img_container.config(image=curr_img, padx=10, pady=10) + self.img_container.config(image=curr_img) self.img_container.image = curr_img self.fps_label.config( - text="Frames Per Second: ~" + str(self.fps), padx=10, pady=10) + text="Frames Per Second: ~" + str(self.fps)) self.master.update() + def isActive(self): global isActive diff --git a/helper_scripts/DataExtractor.py b/helper_scripts/DataExtractor.py index e650d5a..5b06752 100644 --- a/helper_scripts/DataExtractor.py +++ b/helper_scripts/DataExtractor.py @@ -46,20 +46,17 @@ def extract(self): success, frame = stream.read() h, w, d = frame.shape - out = cv.VideoWriter(f'{self.video_path}/{self.name}_key.mp4', - cv.VideoWriter_fourcc(*'mp4v'), 1, (w, h)) + out = cv.VideoWriter(f'{self.video_path}/{self.name}_key.mp4', cv.VideoWriter_fourcc(*'mp4v'), 1, (w, h)) count = 0 while success: # if frame is a keyframe create a red, vertical line in the middle of the frame and save it if count % self.interval == 0: - frame = cv.line(frame, (w // 2, 0), (w // 2, h), - (0, 0, 255), thickness=2) + frame = cv.line(frame, (w // 2, 0), (w // 2, h), (0, 0, 255), thickness=2) self.frames.append(frame) self.frames_copy.append(frame.copy()) out.write(frame) - cv.imwrite( - f'{self.keyframes_path}/{self.name}_{count}.jpg', frame) + cv.imwrite(f'{self.keyframes_path}/{self.name}_{count}.jpg', frame) print('frame ', count) success, frame = stream.read() count += 1 diff --git a/test_algorithms.py b/test_algorithms.py index debc10f..1ad1564 100644 --- a/test_algorithms.py +++ b/test_algorithms.py @@ -30,8 +30,7 @@ def main(): # verify that video exists in ./videos if not path.isfile(f'videos/{args.vid}.mp4'): - print('--vid', args.vid, - "is an invalid video name, make sure it video exists in ./videos") + print('--vid', args.vid, "is an invalid video name, make sure it video exists in ./videos") sys.exit() # verify that config file for video exists @@ -41,8 +40,7 @@ def main(): # verify that config file for algorithm exists if not path.isfile(f'config/algorithm/{args.alg}.yaml'): - print( - f"--alg config for {args.alg} is not defined in ./config/algorithm/") + print(f"--alg config for {args.alg} is not defined in ./config/algorithm/") sys.exit() # set video config @@ -64,8 +62,7 @@ def main(): exists = True if not exists: - print( - f"{args.alg} is an invalid algorithm, list of valid argument values: {algo_list}") + print(f"{args.alg} is an invalid algorithm, list of valid argument values: {algo_list}") sys.exit() @@ -88,8 +85,7 @@ def run_algorithm(alg, vid_file): print(angle) if args.show: - cv.imshow( - f'{args.alg}s algorithm on {args.vid}s video', processed_image) + cv.imshow(f'{args.alg}s algorithm on {args.vid}s video', processed_image) key = cv.waitKey(25) From d1b497015a69d0fd53e7a8c68952cceebef43fcd Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Fri, 3 Mar 2023 17:31:40 -0800 Subject: [PATCH 25/35] recommit 'fixed dimension problem, expand smoother, fix bug with lowerhsv' --- gui.py | 89 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/gui.py b/gui.py index 74112bb..9eb7417 100644 --- a/gui.py +++ b/gui.py @@ -34,15 +34,18 @@ def __init__(self, master, img_dict, window_name): self.container.pack(side='top', anchor='nw', fill="both", expand=True) self.canvas = tk.Canvas(self.container) - self.scrollbar = tk.Scrollbar(self.container, orient="vertical", command=self.canvas.yview) + self.scrollbar = tk.Scrollbar( + self.container, orient="vertical", command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) - self.scrollable_frame.bind("", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) + self.scrollable_frame.bind("", lambda e: self.canvas.configure( + scrollregion=self.canvas.bbox("all"))) - self.img_container = tk.Label(self.scrollable_frame) + self.img_container = tk.Label(self.scrollable_frame, padx=10, pady=10) self.img_container.pack(side='top', fill="both", expand=True) # self.img_container.config(width=600, height=400) - self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor='c') + self.canvas.create_window( + (0, 0), window=self.scrollable_frame, anchor='c') self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True) @@ -70,34 +73,36 @@ def __init__(self, master, img_dict, window_name): # # self.upper_frame = tk.Frame(self.scrollable_frame) - self.upper_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") + self.upper_frame.pack(in_=self.scrollable_frame, + anchor="c", side="bottom") self.lower_frame = tk.Frame(self.scrollable_frame) - self.lower_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") + self.lower_frame.pack(in_=self.scrollable_frame, + anchor="c", side="bottom") self.alert_for_hsv = tk.Label( self.lower_frame, text="", fg="red") self.alert_for_hsv.pack(pady=5, side="top") # # - self.LOW_GREEN = [35, 80, 80] + self.LOWER_GREEN = [35, 80, 80] self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") self.low_h_entry_label.pack(pady=5, side="left") self.low_h_entry = tk.Entry( self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_h_entry.insert(0, str(self.LOW_GREEN[0])) + self.low_h_entry.insert(0, str(self.LOWER_GREEN[0])) self.low_h_entry.pack(pady=5, side="left") # self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") self.low_s_entry_label.pack(pady=5, side="left") self.low_s_entry = tk.Entry( self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_s_entry.insert(0, str(self.LOW_GREEN[1])) + self.low_s_entry.insert(0, str(self.LOWER_GREEN[1])) self.low_s_entry.pack(pady=5, side="left") # self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") self.low_v_entry_label.pack(pady=5, side="left") self.low_v_entry = tk.Entry( self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_v_entry.insert(0, str(self.LOW_GREEN[2])) + self.low_v_entry.insert(0, str(self.LOWER_GREEN[2])) self.low_v_entry.pack(pady=5, side="left") # self.update_btn_low = tk.Button( @@ -139,48 +144,70 @@ def update_upper_hsv(self): upper_h = int(self.up_h_entry.get()) upper_s = int(self.up_s_entry.get()) upper_v = int(self.up_v_entry.get()) - if upper_h > self.LOW_GREEN[0] and upper_s > self.LOW_GREEN[1] and upper_v > self.LOW_GREEN[2]: + if upper_h > self.LOWER_GREEN[0] and upper_s > self.LOWER_GREEN[1] and upper_v > self.LOWER_GREEN[2] and upper_h <= 255 and upper_s <= 255 and upper_v <= 255: self.UPPER_GREEN = (upper_h, upper_s, upper_v) self.alert_for_hsv.config( - text="") + text="new UPPER-HSV: " + str(self.UPPER_GREEN)) else: + warning = "INVALID: " + if upper_h <= self.LOWER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif upper_s <= self.LOWER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif upper_v <= self.LOWER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "UPPER_V > 255\t" + self.alert_for_hsv.config( - text="Invalid UPPER HSV values") + text=warning) + self.up_h_entry.delete(0, tk.END) self.up_h_entry.insert( 0, str(self.UPPER_GREEN[0])) - self.up_v_entry.delete(0, tk.END) - self.up_v_entry.insert( - 0, str(self.UPPER_GREEN[1])) self.up_s_entry.delete(0, tk.END) self.up_s_entry.insert( + 0, str(self.UPPER_GREEN[1])) + self.up_v_entry.delete(0, tk.END) + self.up_v_entry.insert( 0, str(self.UPPER_GREEN[2])) - print(self.UPPER_GREEN) + # print(self.UPPER_GREEN) def update_lower_hsv(self): lower_h = int(self.low_h_entry.get()) lower_s = int(self.low_s_entry.get()) lower_v = int(self.low_v_entry.get()) - if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2]: + if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2] and lower_h >= 0 and lower_s >= 0 and lower_v >= 0: self.LOWER_GREEN = (lower_h, lower_s, lower_v) self.alert_for_hsv.config( - text="") + text="new LOWER-HSV: " + str(self.LOWER_GREEN)) else: + warning = "INVALID: " + if lower_h >= self.UPPER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif lower_s >= self.UPPER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif lower_v >= self.UPPER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "LOWER_V < 0\t" + self.alert_for_hsv.config( - text="Invalid LOWER HSV values") + text=warning) + self.low_h_entry.delete(0, tk.END) self.low_h_entry.insert( - 0, str(self.LOW_GREEN[0])) - self.low_v_entry.delete(0, tk.END) - self.low_v_entry.insert( - 0, str(self.LOW_GREEN[1])) + 0, str(self.LOWER_GREEN[0])) self.low_s_entry.delete(0, tk.END) self.low_s_entry.insert( - 0, str(self.LOW_GREEN[2])) - print(self.LOW_GREEN) + 0, str(self.LOWER_GREEN[1])) + self.low_v_entry.delete(0, tk.END) + self.low_v_entry.insert( + 0, str(self.LOWER_GREEN[2])) + # print(self.LOWER_GREEN) def getLowerHSV(self): - return self.LOW_GREEN + return self.LOWER_GREEN def getUpperHSV(self): return self.UPPER_GREEN @@ -231,15 +258,17 @@ def render_image(self): # Resize the image to fit within the available space, while maintaining aspect ratio new_width = int(img_width * scale_factor) new_height = int(img_height * scale_factor) + if new_height > 10 and new_width > 10: + new_width -= 10 + new_height -= 10 curr_img = curr_img.resize((new_width, new_height), Image.ANTIALIAS) curr_img = ImageTk.PhotoImage(curr_img) - self.img_container.config(image=curr_img) + self.img_container.config(image=curr_img, padx=10, pady=10) self.img_container.image = curr_img self.fps_label.config( - text="Frames Per Second: ~" + str(self.fps)) + text="Frames Per Second: ~" + str(self.fps), padx=10, pady=10) self.master.update() - def isActive(self): global isActive From be8fd0d75b4fc55040e40c10efc5f813563e9e49 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 4 Mar 2023 13:33:01 -0800 Subject: [PATCH 26/35] add masked --- README.md | 4 ++-- algorithms/SeesawAlgorithm.py | 3 ++- gui.py | 3 ++- test_pf.py | 5 +++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4d6389b..14717e1 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ - example : `python create_dataset.py -v sim` or `python create_dataset.py -v crop -i 60 -o` `test_pf.py:` - - runs performance tests on any of the algorithms against any video with an optional GUI + - runs performance tests on seesaw ~~(any of the algorithms)~~ against any video with an optional GUI - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - arguments : - `-a/--alg` : name of algorithm @@ -47,7 +47,7 @@ - video has to be stored in `./videos/` - video configuration has to be stored as a `yaml` at `./config/video/` - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV - - example : `python test_pf.py -a center_row -v crop -s` + - example : `python test_pf.py -a seesaw -v crop -s` ### Commands to Start World in Gazebo diff --git a/algorithms/SeesawAlgorithm.py b/algorithms/SeesawAlgorithm.py index f729645..0ff6b94 100644 --- a/algorithms/SeesawAlgorithm.py +++ b/algorithms/SeesawAlgorithm.py @@ -24,8 +24,9 @@ def __init__(self, config): self.WIDTH = int(config.frame_width) def get_extra_content(self, frame, show): + maskf = self.create_binary_mask(frame) item1, item2 = self.process_frame(frame, show) - return item1, item2 + return item1, item2, maskf def update_lower_hsv(self, next): self.LOW_GREEN = np.array(next) diff --git a/gui.py b/gui.py index 9eb7417..54f35b0 100644 --- a/gui.py +++ b/gui.py @@ -268,6 +268,7 @@ def render_image(self): self.img_container.image = curr_img self.fps_label.config( text="Frames Per Second: ~" + str(self.fps), padx=10, pady=10) + self.master.update() def isActive(self): @@ -307,7 +308,7 @@ def startGUI(window_name, **kwargs): str(curr_index) + " is missing in startGUI()") global root root = tk.Tk() + isActive = True root.protocol("WM_DELETE_WINDOW", onClose) app = GUI(root, img_dict, window_name) - isActive = True return app \ No newline at end of file diff --git a/test_pf.py b/test_pf.py index 54a3120..82a1e8c 100644 --- a/test_pf.py +++ b/test_pf.py @@ -120,7 +120,7 @@ def run_algorithm(alg, vid_file): if args.show: window_name = f'{args.alg}s algorithm on {args.vid}s video' app = startGUI(window_name, name1="standard", - name2="processed") + name2="processed", name3='masked') while vid.isOpened(): ret, frame = vid.read() @@ -136,7 +136,7 @@ def run_algorithm(alg, vid_file): # start_time_frame = time.time() # - processed, angle = alg.get_extra_content( + processed, angle, maskf = alg.get_extra_content( frame, show=args.show) # end_time_frame = time.time() @@ -149,6 +149,7 @@ def run_algorithm(alg, vid_file): # if args.show and app.isActive(): app.update_dict({'processed': processed}) + app.update_dict({'masked': maskf}) x = time_till_second # replace with the integer you want to check tolerance = 0.05 if abs(x - 1) <= tolerance: From be7c5d512fa1b08b60eac17cf57cba1ed544f43d Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 11 Mar 2023 13:53:07 -0800 Subject: [PATCH 27/35] retrieve mask from every algorithm and display --- algorithms/CenterRowAlgorithm.py | 3 ++- algorithms/CheckRowEnd.py | 3 ++- algorithms/HoughAlgorithm.py | 3 ++- algorithms/MiniContoursAlgorithm.py | 7 ++++++- algorithms/MiniContoursDownwards.py | 7 ++++++- algorithms/ScanningAlgorithm.py | 3 ++- gui.py | 3 ++- test_pf.py | 3 ++- 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index fe50ec6..3e6759f 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -46,8 +46,9 @@ def __init__(self, config): self.center_angle = 0 def get_extra_content(self, frame, show): + maskf = self.create_binary_mask(frame) item1, item2 = self.process_frame(frame, show) - return item1, item2 + return item1, item2, maskf def update_lower_hsv(self, next): self.LOW_GREEN = np.array(next) diff --git a/algorithms/CheckRowEnd.py b/algorithms/CheckRowEnd.py index 58b24c8..3f1588a 100644 --- a/algorithms/CheckRowEnd.py +++ b/algorithms/CheckRowEnd.py @@ -18,8 +18,9 @@ def __init__(self, config): self.percentage_of_empty_rows = config.percentage_of_empty_rows def get_extra_content(self, frame, show): + maskf = self.create_binary_mask(frame) item1, item2 = self.process_frame(frame, show) - return item1, item2 + return item1, item2, maskf def update_lower_hsv(self, next): self.LOW_GREEN = np.array(next) diff --git a/algorithms/HoughAlgorithm.py b/algorithms/HoughAlgorithm.py index ae2723d..18d4223 100644 --- a/algorithms/HoughAlgorithm.py +++ b/algorithms/HoughAlgorithm.py @@ -33,8 +33,9 @@ def __init__(self, config): self.RESIZE_FACTOR = config.resize_factor def get_extra_content(self, frame, show): + maskf = self.create_mask(frame) item1, item2 = self.process_frame(frame, show) - return item1, item2 + return item1, item2, maskf def update_lower_hsv(self, next): self.LOW_GREEN = np.array(next) diff --git a/algorithms/MiniContoursAlgorithm.py b/algorithms/MiniContoursAlgorithm.py index ed7d5c4..ebd021f 100644 --- a/algorithms/MiniContoursAlgorithm.py +++ b/algorithms/MiniContoursAlgorithm.py @@ -10,6 +10,7 @@ from algorithms.utils import Lines +cmask = "" class MiniContoursAlgorithm(Algorithm): # applies hsv binarization to the image # slices the image into horizontal strips and finds all the contours in each strip @@ -108,6 +109,8 @@ def get_center_hough_lines( mask = cv2.inRange(cv2.cvtColor(frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) mask = cv2.medianBlur(mask, 9) + global cmask + cmask = mask # mask = cv2.GaussianBlur(mask, (9,9), 10) # mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, self.morphology_kernel) centroids = self.get_centroids(mask, num_strips=num_strips) @@ -202,8 +205,10 @@ def get_center_hough_lines( return frame, lines, point_lines def get_extra_content(self, frame, show): + global cmask + # maskf = self.create_binary_mask(frame) item1, item2 = self.process_frame(frame, show) - return item1, item2 + return item1, item2, cmask def update_lower_hsv(self, next): self.LOW_GREEN = np.array(next) diff --git a/algorithms/MiniContoursDownwards.py b/algorithms/MiniContoursDownwards.py index c67cf59..4ededeb 100644 --- a/algorithms/MiniContoursDownwards.py +++ b/algorithms/MiniContoursDownwards.py @@ -6,6 +6,7 @@ import math from algorithms.utils import Lines +cmask = "" class MiniContoursDownwards(): @@ -144,6 +145,8 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p else: line = None + global cmask + cmask = c_mask if show: cv2.imshow('frame', frame) cv2.imshow('mask', mask) @@ -153,8 +156,10 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p return frame, line def get_extra_content(self, frame, show): + global cmask + # maskf = self.create_binary_mask(frame) item1, item2 = self.process_frame(frame, show) - return item1, item2 + return item1, item2, cmask def update_lower_hsv(self, next): self.LOW_GREEN = np.array(next) diff --git a/algorithms/ScanningAlgorithm.py b/algorithms/ScanningAlgorithm.py index 1a02dbf..637e04a 100644 --- a/algorithms/ScanningAlgorithm.py +++ b/algorithms/ScanningAlgorithm.py @@ -76,7 +76,8 @@ def create_line(self, start_x, start_y, end_x, end_y): def get_extra_content(self, frame, show): item1, item2 = self.process_frame(frame, show) - return item1, item2 + maskf = frame + return item1, item2, maskf def update_lower_hsv(self, next): self.LOW_GREEN = np.array(next) diff --git a/gui.py b/gui.py index 54f35b0..408112a 100644 --- a/gui.py +++ b/gui.py @@ -1,4 +1,4 @@ -import tkinter as tk +import tkinter as tk # change to Tkinter for python2 import cv2 as cv import numpy as np @@ -10,6 +10,7 @@ isActive = False +# scanning not working + no binary_mask class GUI: """Creates GUI with tkinter: initializes a master window, creates the same number of radiobuttons as the length of img_dict, creates 2 sliders for brightness and saturation, then render images accordingly\n diff --git a/test_pf.py b/test_pf.py index 82a1e8c..cd5e804 100644 --- a/test_pf.py +++ b/test_pf.py @@ -132,7 +132,8 @@ def run_algorithm(alg, vid_file): app.update_dict({'standard': frame}) alg.update_lower_hsv(app.getLowerHSV()) alg.update_upper_hsv(app.getUpperHSV()) - frame = app.apply_filter(frame) + # uncomment if want the input to be affected + # frame = app.apply_filter(frame) # start_time_frame = time.time() # From c00d0f43d2facd6b703c7b472eb28c8bea2440a2 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 18 Mar 2023 14:00:45 -0700 Subject: [PATCH 28/35] added sys and path --- gui.py | 22 ++++++++++++++++++++++ test_pf.py | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/gui.py b/gui.py index 408112a..0bedff7 100644 --- a/gui.py +++ b/gui.py @@ -2,6 +2,8 @@ import cv2 as cv import numpy as np +import sys +sys.path.insert(1, '') from PIL import Image, ImageTk import pre_process @@ -10,6 +12,7 @@ isActive = False +# TODO: sliders and show colors # scanning not working + no binary_mask class GUI: """Creates GUI with tkinter: initializes a master window, creates the same number of radiobuttons as the length of img_dict, @@ -136,11 +139,30 @@ def __init__(self, master, img_dict, window_name): self.update_btn_high = tk.Button( self.upper_frame, text="Update", command=self.update_upper_hsv) self.update_btn_high.pack(pady=0, side="left") + + # # Create the three sliders for MAX Hue, Saturation, and Value + # self.max_hue_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Hue", command=self.update_color) + # self.max_hue_slider.pack() + + # self.max_saturation_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Saturation", command=self.update_color) + # self.max_saturation_slider.pack() + + # self.max_value_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Value", command=self.update_color) + # self.max_value_slider.pack() + + # self.color_canvas = tk.Canvas(self.upper_frame, width=50, height=50, bg="#000000") + # self.color_canvas.pack() # # # self.render_image() + def update_color(self, event=None): + r, g, b = self.max_hue_slider.get(), self.max_saturation_slider.get(), self.max_value_slider.get() + color = f"#{r:02x}{g:02x}{b:02x}" + self.color_canvas.config(bg=color) + + def update_upper_hsv(self): upper_h = int(self.up_h_entry.get()) upper_s = int(self.up_s_entry.get()) diff --git a/test_pf.py b/test_pf.py index cd5e804..e8963e5 100644 --- a/test_pf.py +++ b/test_pf.py @@ -133,7 +133,7 @@ def run_algorithm(alg, vid_file): alg.update_lower_hsv(app.getLowerHSV()) alg.update_upper_hsv(app.getUpperHSV()) # uncomment if want the input to be affected - # frame = app.apply_filter(frame) + frame = app.apply_filter(frame) # start_time_frame = time.time() # From 3e926eaef9f24671ea15b5ae388c2b0cfde482e4 Mon Sep 17 00:00:00 2001 From: zoeyzhao57 Date: Sat, 11 Mar 2023 12:29:54 -0800 Subject: [PATCH 29/35] seesaw version 2 --- algorithms/SeesawAlgorithmVersionTwo.py | 120 ++++++++++++++++++++++++ config/algorithm/seesaw_v2.yaml | 18 ++++ helper_scripts/change_res.py | 12 +++ test_algorithms.py | 4 +- 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 algorithms/SeesawAlgorithmVersionTwo.py create mode 100644 config/algorithm/seesaw_v2.yaml create mode 100644 helper_scripts/change_res.py diff --git a/algorithms/SeesawAlgorithmVersionTwo.py b/algorithms/SeesawAlgorithmVersionTwo.py new file mode 100644 index 0000000..c287028 --- /dev/null +++ b/algorithms/SeesawAlgorithmVersionTwo.py @@ -0,0 +1,120 @@ +import math +import cv2 as cv +import numpy +import numpy as np +from algorithms.Algorithm import Algorithm +from helper_scripts.change_res import change_res + + +class SeesawAlgorithmVersionTwo(Algorithm): + + def __init__(self, config): + """ + Sets seesaw algorithm configurations + :param config: config params + """ + # masking range for green + self.LOW_GREEN = np.array(config.lower_hsv_threshold) + self.HIGH_GREEN = np.array(config.upper_hsv_threshold) + + # filtering parameters + self.averaging_kernel_size = config.averaging_kernel_size + self.gauss_kernel_size = list(map(int, config.gauss_kernel_size.split(','))) + self.dilate_kernel_size = config.dilate_kernel_size + self.sigma_x = config.sigma_x + + # dimensions + self.HEIGHT = int(config.frame_length) + self.WIDTH = int(config.frame_width) + + # visual parameters + self.BAR_HEIGHT = config.bar_height + self.RES_FACTOR = config.resolution_factor + + def process_frame(self, frame, show): + """ + Divides screen into horizontal strips and find average location of green for each strip. + Draw the best fit line based the average location, + then calculate the angle between the best fit line and a horizontal line. + :param frame: current frame (mat) + :param show: show/hide frames on screen for debugging + :type show: bool + :return: processed frame (mat), angle [-90, 90] + """ + + frame = change_res(frame, self.RES_FACTOR) + black_frame, average_points = self.plot_points(frame) + average_points = np.array(average_points) + + if average_points.any(): + """get best fit line for centre points""" + [vx, vy, x, y] = cv.fitLine(average_points, cv.DIST_L2, 0, 0.01, 0.01) + x1 = int(x - vx * self.WIDTH) + x2 = int(x + vx * self.WIDTH) + y1 = int(y - vy * self.HEIGHT) + y2 = int(y + vy * self.HEIGHT) + black_frame = cv.line(black_frame, (x1, y1), (x2, y2), (0, 255, 255), 9) + + # calculate angle + if y1 - y2 != 0: + angle = round(math.degrees(math.atan(int(x2 - x1) / int(y1 - y2))), 1) + else: + angle = None + else: + angle = None + + return black_frame, angle + + def plot_points(self, frame): + """ + Divides screen into horizontal strips, and find average location of green for each strip. + :param frame: current frame (mat) + :return: processed frame (mat), list of centre points + """ + + bar_height = int(self.BAR_HEIGHT) + mask = self.create_binary_mask(frame) + + square_low = 0 + square_high = bar_height + + black_frame = frame + + centre_points = [] + + while square_low < self.HEIGHT: + points = [] + + seg = mask[int(square_low) + 1:int(square_high), 0:self.WIDTH] + + condition = (seg == 255) + points = np.where(condition)[1] + + if points.any(): + centre = int(numpy.average(points)) + black_frame = cv.circle(black_frame, [int(centre), int((square_high + square_low) / 2)], + radius=0, color=(0, 0, 255), thickness=15) + centre_points.append([centre, int((square_high + square_low) / 2)]) + + square_high += bar_height + square_low += bar_height + + return frame, centre_points + + def create_binary_mask(self, frame): + """ + :param frame: current frame + :return: binary mask of frame made using self.low_green and self.high_green as the HSV range + """ + + # Run averaging filter to blur the frame + kernel = np.ones((5, 5), np.float32) / 25 + frame = cv.filter2D(frame, -1, kernel) + + # Convert to hsv format to allow for easier colour filtering + hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV) + + # Filter image and allow only shades of green to pass + mask = cv.inRange(hsv, self.LOW_GREEN, self.HIGH_GREEN) + + return mask diff --git a/config/algorithm/seesaw_v2.yaml b/config/algorithm/seesaw_v2.yaml new file mode 100644 index 0000000..405c2d1 --- /dev/null +++ b/config/algorithm/seesaw_v2.yaml @@ -0,0 +1,18 @@ +#filtering parameters +averaging_kernel_size: (5,5) +gauss_kernel_size: 3,3 +dilate_kernel_size: (5,5) +sigma_x: 0 + +contour_color: 0,129,255 +bar_height: 30 +resolution_factor: 100 + +#### size of frame +frame_width: 1280 +frame_length: 720 + +#### hsv thresholds for crop video +lower_hsv_threshold: [35, 80, 80] +upper_hsv_threshold: [80, 255, 255] + diff --git a/helper_scripts/change_res.py b/helper_scripts/change_res.py new file mode 100644 index 0000000..0ca8993 --- /dev/null +++ b/helper_scripts/change_res.py @@ -0,0 +1,12 @@ +import cv2 as cv + + +def change_res(frame, scale_percent): + scale_percent = scale_percent + width = int(frame.shape[1] * scale_percent / 100) + height = int(frame.shape[0] * scale_percent / 100) + dim = (width, height) + + frame = cv.resize(frame, dim, interpolation=cv.INTER_AREA) + + return frame diff --git a/test_algorithms.py b/test_algorithms.py index 1ad1564..7142f9f 100644 --- a/test_algorithms.py +++ b/test_algorithms.py @@ -14,6 +14,7 @@ import pre_process from algorithms.SeesawAlgorithm import SeesawAlgorithm from algorithms.CenterDownwards import CenterDownward +from algorithms.SeesawAlgorithmVersionTwo import SeesawAlgorithmVersionTwo # parser for command line arguments parser = argparse.ArgumentParser() @@ -24,7 +25,8 @@ # list of algorithms algo_list = [('hough', HoughAlgorithm), ('center_row', CenterRowAlgorithm), ('mini_contour', MiniContoursAlgorithm), ('mini_contour_downward', MiniContoursDownwards), - ('scanning', ScanningAlgorithm), ('check_row_end', CheckRowEnd), ('seesaw', SeesawAlgorithm), ("center_down", CenterDownward)] + ('scanning', ScanningAlgorithm), ('check_row_end', CheckRowEnd), ('seesaw', SeesawAlgorithm), + ('seesaw_v2', SeesawAlgorithmVersionTwo), ("center_down", CenterDownward)] def main(): From f167676beee8d65689551ae7a35efad9500f3c45 Mon Sep 17 00:00:00 2001 From: zoeyzhao57 Date: Sat, 11 Mar 2023 13:35:18 -0800 Subject: [PATCH 30/35] seesaw version 2 update --- helper_scripts/change_res.py | 8 ++++++++ test_algorithms.py | 2 ++ 2 files changed, 10 insertions(+) diff --git a/helper_scripts/change_res.py b/helper_scripts/change_res.py index 0ca8993..984dab7 100644 --- a/helper_scripts/change_res.py +++ b/helper_scripts/change_res.py @@ -10,3 +10,11 @@ def change_res(frame, scale_percent): frame = cv.resize(frame, dim, interpolation=cv.INTER_AREA) return frame + + +def change_res_2(frame, resolution): + height, width, channels = frame.shape + new_width = int(width/height*resolution) + frame = cv.resize(frame, (new_width, resolution), interpolation=cv.INTER_AREA) + + return frame \ No newline at end of file diff --git a/test_algorithms.py b/test_algorithms.py index 7142f9f..381d797 100644 --- a/test_algorithms.py +++ b/test_algorithms.py @@ -15,6 +15,7 @@ from algorithms.SeesawAlgorithm import SeesawAlgorithm from algorithms.CenterDownwards import CenterDownward from algorithms.SeesawAlgorithmVersionTwo import SeesawAlgorithmVersionTwo +from helper_scripts.change_res import change_res_2 # parser for command line arguments parser = argparse.ArgumentParser() @@ -76,6 +77,7 @@ def run_algorithm(alg, vid_file): while vid.isOpened(): ret, frame = vid.read() + frame = change_res_2(frame, 720) if not ret: print('No More Frames Remaining') From e7d2c5686a79723d79456ec086e837e0213ef68f Mon Sep 17 00:00:00 2001 From: zoeyzhao57 Date: Sat, 18 Mar 2023 12:58:14 -0700 Subject: [PATCH 31/35] seesaw version 2 update --- algorithms/SeesawAlgorithmVersionTwo.py | 46 +++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/algorithms/SeesawAlgorithmVersionTwo.py b/algorithms/SeesawAlgorithmVersionTwo.py index c287028..98412a4 100644 --- a/algorithms/SeesawAlgorithmVersionTwo.py +++ b/algorithms/SeesawAlgorithmVersionTwo.py @@ -43,7 +43,7 @@ def process_frame(self, frame, show): """ frame = change_res(frame, self.RES_FACTOR) - black_frame, average_points = self.plot_points(frame) + black_frame, average_points, overall_bias = self.plot_points(frame) average_points = np.array(average_points) if average_points.any(): @@ -58,12 +58,20 @@ def process_frame(self, frame, show): # calculate angle if y1 - y2 != 0: angle = round(math.degrees(math.atan(int(x2 - x1) / int(y1 - y2))), 1) + bias = round(numpy.average(overall_bias)/(self.WIDTH/2)*90, 2) + + if abs(angle) > abs(bias): + output = angle + else: + output = bias else: - angle = None + output = None else: - angle = None + output = None + + black_frame = self.print_text(frame, str(output)) - return black_frame, angle + return black_frame, output def plot_points(self, frame): """ @@ -71,7 +79,7 @@ def plot_points(self, frame): :param frame: current frame (mat) :return: processed frame (mat), list of centre points """ - + frame = cv.line(frame, (int(self.WIDTH/2), 0), (int(self.WIDTH/2), int(self.HEIGHT)), (255, 0, 255), 9) bar_height = int(self.BAR_HEIGHT) mask = self.create_binary_mask(frame) @@ -81,6 +89,7 @@ def plot_points(self, frame): black_frame = frame centre_points = [] + overall_bias = [] while square_low < self.HEIGHT: points = [] @@ -92,14 +101,39 @@ def plot_points(self, frame): if points.any(): centre = int(numpy.average(points)) + bias = int(centre - self.WIDTH/2) black_frame = cv.circle(black_frame, [int(centre), int((square_high + square_low) / 2)], radius=0, color=(0, 0, 255), thickness=15) centre_points.append([centre, int((square_high + square_low) / 2)]) + overall_bias.append(bias) square_high += bar_height square_low += bar_height - return frame, centre_points + return frame, centre_points, overall_bias + + + def print_text(self, frame, text): + + # Define the font and text size + font = cv.FONT_HERSHEY_SIMPLEX + font_scale = 1 + + # Define the color and thickness of the text + color = (255, 255, 255) # in BGR format + thickness = 2 + + # Get the size of the text box + text_size, _ = cv.getTextSize(text, font, font_scale, thickness) + + # Calculate the position of the text box + x = int((frame.shape[1] - text_size[0]) / 4) + y = int((frame.shape[0] + text_size[1]) / 4) + + # Draw the text on the image + cv.putText(frame, text, (x, y), font, font_scale, color, thickness) + + return frame def create_binary_mask(self, frame): """ From 0e3c0ddadd44e5c0fa80fa739775c8cfdc287e6e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 Mar 2023 13:41:06 -0800 Subject: [PATCH 32/35] autopep8 action fixes (#72) Co-authored-by: zoeyzhao57 --- helper_scripts/change_res.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helper_scripts/change_res.py b/helper_scripts/change_res.py index 984dab7..afbb319 100644 --- a/helper_scripts/change_res.py +++ b/helper_scripts/change_res.py @@ -14,7 +14,7 @@ def change_res(frame, scale_percent): def change_res_2(frame, resolution): height, width, channels = frame.shape - new_width = int(width/height*resolution) + new_width = int(width / height * resolution) frame = cv.resize(frame, (new_width, resolution), interpolation=cv.INTER_AREA) - return frame \ No newline at end of file + return frame From 38819248d661fb2f99568ddb6c4e57eaa72fe5fc Mon Sep 17 00:00:00 2001 From: zoeyzhao57 Date: Sat, 25 Mar 2023 10:44:26 -0700 Subject: [PATCH 33/35] seesaw version 2 update --- algorithms/SeesawAlgorithmVersionTwo.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/algorithms/SeesawAlgorithmVersionTwo.py b/algorithms/SeesawAlgorithmVersionTwo.py index 98412a4..e41bef9 100644 --- a/algorithms/SeesawAlgorithmVersionTwo.py +++ b/algorithms/SeesawAlgorithmVersionTwo.py @@ -100,7 +100,7 @@ def plot_points(self, frame): points = np.where(condition)[1] if points.any(): - centre = int(numpy.average(points)) + centre = int(numpy.median(points)) bias = int(centre - self.WIDTH/2) black_frame = cv.circle(black_frame, [int(centre), int((square_high + square_low) / 2)], radius=0, color=(0, 0, 255), thickness=15) @@ -112,7 +112,6 @@ def plot_points(self, frame): return frame, centre_points, overall_bias - def print_text(self, frame, text): # Define the font and text size From 65ec752b9508434250d36178372f09451eda5fa2 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 1 Apr 2023 11:55:42 -0700 Subject: [PATCH 34/35] add seesaw2 to gui --- README.md | 4 ++-- algorithms/SeesawAlgorithmVersionTwo.py | 11 +++++++++++ test_pf.py | 6 +++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 14717e1..d6ac79d 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,11 @@ - example : `python create_dataset.py -v sim` or `python create_dataset.py -v crop -i 60 -o` `test_pf.py:` - - runs performance tests on seesaw ~~(any of the algorithms)~~ against any video with an optional GUI + - runs performance tests on algorithms against any video with an optional GUI - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - arguments : - `-a/--alg` : name of algorithm - - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, or `check_row_end` + - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, `seesaw_v2`, or `check_row_end` - `-v/--vid` : name of video - video has to be stored in `./videos/` - video configuration has to be stored as a `yaml` at `./config/video/` diff --git a/algorithms/SeesawAlgorithmVersionTwo.py b/algorithms/SeesawAlgorithmVersionTwo.py index e41bef9..ff6d786 100644 --- a/algorithms/SeesawAlgorithmVersionTwo.py +++ b/algorithms/SeesawAlgorithmVersionTwo.py @@ -31,6 +31,17 @@ def __init__(self, config): self.BAR_HEIGHT = config.bar_height self.RES_FACTOR = config.resolution_factor + def get_extra_content(self, frame, show): + maskf = self.create_binary_mask(frame) + item1, item2 = self.process_frame(frame, show) + return item1, item2, maskf + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) + def process_frame(self, frame, show): """ Divides screen into horizontal strips and find average location of green for each strip. diff --git a/test_pf.py b/test_pf.py index e8963e5..b77c784 100644 --- a/test_pf.py +++ b/test_pf.py @@ -15,6 +15,7 @@ from algorithms.MiniContoursDownwards import MiniContoursDownwards from algorithms.ScanningAlgorithm import ScanningAlgorithm from algorithms.SeesawAlgorithm import SeesawAlgorithm +from algorithms.SeesawAlgorithmVersionTwo import SeesawAlgorithmVersionTwo from gui import startGUI # parser for command line arguments @@ -38,7 +39,10 @@ ('check_row_end', CheckRowEnd), ('seesaw', - SeesawAlgorithm)] + SeesawAlgorithm), + ('seesaw_v2', + SeesawAlgorithmVersionTwo) + ] # number_of_frames = 0 From 7fe691a6d0cf9a5e6d60e635a52e9b47d6e208a1 Mon Sep 17 00:00:00 2001 From: Lymeng Naret Date: Sat, 1 Apr 2023 12:36:46 -0700 Subject: [PATCH 35/35] changed hsv to sliders and add color canvas for each --- README.md | 20 ++--- gui.py | 220 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 157 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index d6ac79d..30ca9f3 100644 --- a/README.md +++ b/README.md @@ -38,16 +38,16 @@ - example : `python create_dataset.py -v sim` or `python create_dataset.py -v crop -i 60 -o` `test_pf.py:` - - runs performance tests on algorithms against any video with an optional GUI - - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - - arguments : - - `-a/--alg` : name of algorithm - - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, `seesaw_v2`, or `check_row_end` - - `-v/--vid` : name of video - - video has to be stored in `./videos/` - - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV - - example : `python test_pf.py -a seesaw -v crop -s` +- runs performance tests on algorithms against any video with an optional GUI +- reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` +- arguments : + - `-a/--alg` : name of algorithm + - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, `seesaw_v2`, or `check_row_end` + - `-v/--vid` : name of video + - video has to be stored in `./videos/` + - video configuration has to be stored as a `yaml` at `./config/video/` + - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV +- example : `python test_pf.py -a seesaw -v crop -s` ### Commands to Start World in Gazebo diff --git a/gui.py b/gui.py index 0bedff7..b7aa6a9 100644 --- a/gui.py +++ b/gui.py @@ -2,8 +2,8 @@ import cv2 as cv import numpy as np -import sys -sys.path.insert(1, '') +# import sys +# sys.path.insert(1, '') from PIL import Image, ImageTk import pre_process @@ -12,7 +12,6 @@ isActive = False -# TODO: sliders and show colors # scanning not working + no binary_mask class GUI: """Creates GUI with tkinter: initializes a master window, creates the same number of radiobuttons as the length of img_dict, @@ -75,92 +74,167 @@ def __init__(self, master, img_dict, window_name): self.saturation_scale.set(self.current_avg_saturation) self.saturation_scale.pack(pady=5, side="bottom") # - # - self.upper_frame = tk.Frame(self.scrollable_frame) - self.upper_frame.pack(in_=self.scrollable_frame, - anchor="c", side="bottom") - self.lower_frame = tk.Frame(self.scrollable_frame) - self.lower_frame.pack(in_=self.scrollable_frame, - anchor="c", side="bottom") + # Create a Frame to hold both the upper and lower frames + self.center_frame = tk.Frame(self.scrollable_frame) + self.center_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") + + self.lower_frame = tk.Frame(self.center_frame) + self.lower_frame.pack(in_=self.center_frame, anchor="c", side="left", ipady=15) + self.upper_frame = tk.Frame(self.center_frame) + self.upper_frame.pack(in_=self.center_frame, anchor="c", side="right", ipadx=15) + self.alert_for_hsv = tk.Label( self.lower_frame, text="", fg="red") - self.alert_for_hsv.pack(pady=5, side="top") + self.alert_for_hsv.pack(side="top", anchor="c") # # self.LOWER_GREEN = [35, 80, 80] - self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") - self.low_h_entry_label.pack(pady=5, side="left") - self.low_h_entry = tk.Entry( - self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_h_entry.insert(0, str(self.LOWER_GREEN[0])) - self.low_h_entry.pack(pady=5, side="left") - # - self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") - self.low_s_entry_label.pack(pady=5, side="left") - self.low_s_entry = tk.Entry( - self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_s_entry.insert(0, str(self.LOWER_GREEN[1])) - self.low_s_entry.pack(pady=5, side="left") - # - self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") - self.low_v_entry_label.pack(pady=5, side="left") - self.low_v_entry = tk.Entry( - self.lower_frame, width=5, justify="center", font="Courier 12") - self.low_v_entry.insert(0, str(self.LOWER_GREEN[2])) - self.low_v_entry.pack(pady=5, side="left") - # - self.update_btn_low = tk.Button( - self.lower_frame, text="Update", command=self.update_lower_hsv) - self.update_btn_low.pack(pady=0, side="left") + # self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") + # self.low_h_entry_label.pack(pady=5, side="left") + # self.low_h_entry = tk.Entry( + # self.lower_frame, width=5, justify="center", font="Courier 12") + # self.low_h_entry.insert(0, str(self.LOWER_GREEN[0])) + # self.low_h_entry.pack(pady=5, side="left") + # # + # self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") + # self.low_s_entry_label.pack(pady=5, side="left") + # self.low_s_entry = tk.Entry( + # self.lower_frame, width=5, justify="center", font="Courier 12") + # self.low_s_entry.insert(0, str(self.LOWER_GREEN[1])) + # self.low_s_entry.pack(pady=5, side="left") + # # + # self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") + # self.low_v_entry_label.pack(pady=5, side="left") + # self.low_v_entry = tk.Entry( + # self.lower_frame, width=5, justify="center", font="Courier 12") + # self.low_v_entry.insert(0, str(self.LOWER_GREEN[2])) + # self.low_v_entry.pack(pady=5, side="left") + # # + # self.update_btn_low = tk.Button( + # self.lower_frame, text="Update", command=self.update_lower_hsv) + # self.update_btn_low.pack(pady=0, side="left") # # self.UPPER_GREEN = [80, 255, 255] - self.up_h_entry_label = tk.Label(self.upper_frame, text="UPPER-H:") - self.up_h_entry_label.pack(pady=5, side="left") - self.up_h_entry = tk.Entry( - self.upper_frame, width=5, justify="center", font="Courier 12") - self.up_h_entry.insert(0, str(self.UPPER_GREEN[0])) - self.up_h_entry.pack(pady=5, side="left") + # self.up_h_entry_label = tk.Label(self.upper_frame, text="UPPER-H:") + # self.up_h_entry_label.pack(pady=5, side="left") + # self.up_h_entry = tk.Entry( + # self.upper_frame, width=5, justify="center", font="Courier 12") + # self.up_h_entry.insert(0, str(self.UPPER_GREEN[0])) + # self.up_h_entry.pack(pady=5, side="left") + # # + # self.up_s_entry_label = tk.Label(self.upper_frame, text="S:") + # self.up_s_entry_label.pack(pady=5, side="left") + # self.up_s_entry = tk.Entry( + # self.upper_frame, width=5, justify="center", font="Courier 12") + # self.up_s_entry.insert(0, str(self.UPPER_GREEN[1])) + # self.up_s_entry.pack(pady=5, side="left") + # # + # self.up_v_entry_label = tk.Label(self.upper_frame, text="V:") + # self.up_v_entry_label.pack(pady=5, side="left") + # self.up_v_entry = tk.Entry( + # self.upper_frame, width=5, justify="center", font="Courier 12") + # self.up_v_entry.insert(0, str(self.UPPER_GREEN[2])) + # self.up_v_entry.pack(pady=5, side="left") + # # + # self.update_btn_high = tk.Button( + # self.upper_frame, text="Update", command=self.update_upper_hsv) + # self.update_btn_high.pack(pady=0, side="left") + + # Create the three sliders for MIN Hue, Saturation, and Value + self.min_hue_slider = tk.Scale(self.lower_frame, from_=0, to=255, orient="horizontal", label="Min Hue", command=self.update_min_color_canvas) + self.min_hue_slider.set(self.LOWER_GREEN[0]) + self.min_hue_slider.pack() + + self.min_saturation_slider = tk.Scale(self.lower_frame, from_=0, to=255, orient="horizontal", label="Min Saturation", command=self.update_min_color_canvas) + self.min_saturation_slider.set(self.LOWER_GREEN[1]) + self.min_saturation_slider.pack() + + self.min_value_slider = tk.Scale(self.lower_frame, from_=0, to=255, orient="horizontal", label="Min Value", command=self.update_min_color_canvas) + self.min_value_slider.set(self.LOWER_GREEN[2]) + self.min_value_slider.pack() + + self.min_color_canvas = tk.Canvas(self.lower_frame, width=20, height=20, bg="#000000") + self.min_color_canvas.pack() + self.min_color_update_button = tk.Button(self.lower_frame, text="Update Min Color", command=self.update_min_color) + self.min_color_update_button.pack() + + # Create the three sliders for MAX Hue, Saturation, and Value + self.max_hue_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Max Hue", command=self.update_max_color_canvas) + self.max_hue_slider.set(self.UPPER_GREEN[0]) + self.max_hue_slider.pack() + + self.max_saturation_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Max Saturation", command=self.update_max_color_canvas) + self.max_saturation_slider.set(self.UPPER_GREEN[1]) + self.max_saturation_slider.pack() + + self.max_value_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Max Value", command=self.update_max_color_canvas) + self.max_value_slider.set(self.UPPER_GREEN[2]) + self.max_value_slider.pack() + + self.max_color_canvas = tk.Canvas(self.upper_frame, width=20, height=20, bg="#000000") + self.max_color_canvas.pack() + self.max_color_update_button = tk.Button(self.upper_frame, text="Update Max Color", command=self.update_max_color) + self.max_color_update_button.pack() # - self.up_s_entry_label = tk.Label(self.upper_frame, text="S:") - self.up_s_entry_label.pack(pady=5, side="left") - self.up_s_entry = tk.Entry( - self.upper_frame, width=5, justify="center", font="Courier 12") - self.up_s_entry.insert(0, str(self.UPPER_GREEN[1])) - self.up_s_entry.pack(pady=5, side="left") # - self.up_v_entry_label = tk.Label(self.upper_frame, text="V:") - self.up_v_entry_label.pack(pady=5, side="left") - self.up_v_entry = tk.Entry( - self.upper_frame, width=5, justify="center", font="Courier 12") - self.up_v_entry.insert(0, str(self.UPPER_GREEN[2])) - self.up_v_entry.pack(pady=5, side="left") # - self.update_btn_high = tk.Button( - self.upper_frame, text="Update", command=self.update_upper_hsv) - self.update_btn_high.pack(pady=0, side="left") + self.render_image() - # # Create the three sliders for MAX Hue, Saturation, and Value - # self.max_hue_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Hue", command=self.update_color) - # self.max_hue_slider.pack() + def update_min_color_canvas(self, event=None): + r, g, b = self.min_hue_slider.get(), self.min_saturation_slider.get(), self.min_value_slider.get() + color = f"#{r:02x}{g:02x}{b:02x}" + self.min_color_canvas.config(bg=color) - # self.max_saturation_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Saturation", command=self.update_color) - # self.max_saturation_slider.pack() + def update_max_color_canvas(self, event=None): + r, g, b = self.max_hue_slider.get(), self.max_saturation_slider.get(), self.max_value_slider.get() + color = f"#{r:02x}{g:02x}{b:02x}" + self.max_color_canvas.config(bg=color) - # self.max_value_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Value", command=self.update_color) - # self.max_value_slider.pack() + def update_min_color(self): + lower_h = self.min_hue_slider.get() + lower_s = self.min_saturation_slider.get() + lower_v = self.min_value_slider.get() + if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2] and lower_h >= 0 and lower_s >= 0 and lower_v >= 0: + self.LOWER_GREEN = [lower_h, lower_s, lower_v] + self.alert_for_hsv.config( + text="new LOWER-HSV: " + str(self.LOWER_GREEN)) + else: + warning = "INVALID: " + if lower_h >= self.UPPER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif lower_s >= self.UPPER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif lower_v >= self.UPPER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "LOWER_V < 0\t" - # self.color_canvas = tk.Canvas(self.upper_frame, width=50, height=50, bg="#000000") - # self.color_canvas.pack() - # - # - # - self.render_image() + self.alert_for_hsv.config( + text=warning) + + def update_max_color(self): + upper_h = self.max_hue_slider.get() + upper_s = self.max_saturation_slider.get() + upper_v = self.max_value_slider.get() + if upper_h > self.LOWER_GREEN[0] and upper_s > self.LOWER_GREEN[1] and upper_v > self.LOWER_GREEN[2] and upper_h <= 255 and upper_s <= 255 and upper_v <= 255: + self.UPPER_GREEN = [upper_h, upper_s, upper_v] + self.alert_for_hsv.config( + text="new UPPER-HSV: " + str(self.UPPER_GREEN)) + else: + warning = "INVALID: " + if upper_h <= self.LOWER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif upper_s <= self.LOWER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif upper_v <= self.LOWER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "UPPER_V > 255\t" + + self.alert_for_hsv.config( + text=warning) - def update_color(self, event=None): - r, g, b = self.max_hue_slider.get(), self.max_saturation_slider.get(), self.max_value_slider.get() - color = f"#{r:02x}{g:02x}{b:02x}" - self.color_canvas.config(bg=color) def update_upper_hsv(self):