From cae0aed939f7b56f14abca9f998dddda6fdeca58 Mon Sep 17 00:00:00 2001 From: Arne van Iterson Date: Mon, 25 Sep 2023 22:08:01 +0200 Subject: [PATCH 1/3] Added contrast and brightness adjust --- src/helpers/gui/main.ui | 255 ++++++++++++++++++++++------------------ src/suite.py | 30 +++-- 2 files changed, 158 insertions(+), 127 deletions(-) diff --git a/src/helpers/gui/main.ui b/src/helpers/gui/main.ui index 05cdc74..6f882ea 100644 --- a/src/helpers/gui/main.ui +++ b/src/helpers/gui/main.ui @@ -28,50 +28,49 @@ - top + bottom 0 1000 int:canny_thr1 - 0 - 60 + 1 0 - 0 + 1 - Canny Edge threshold 1 - - 0 - 1 - - - - - - 0 - 1000 - int:canny_thr2 - + Canny thresh. 1 1 - 2 - 60 0 - - Canny Edge threshold 2 + + bottom + 0 + 1000 + int:canny_thr2 + - 1 - 2 - 5 + 2 + 1 1 + + + + + + Canny thresh. 2 + + 2 + 1 + 5 + 0 1 @@ -80,73 +79,65 @@ true string:img_path + Dataset directory 0 1 30 - 4 + 7 1 - - Dataset path - - 0 - 1 - 5 - - - - - + + bottom 0 - 30 - int:blur_rate + 500 + int:contrast 0 60 0 - 2 - - - - - - Blur kernel size - - 0 3 + + + Contrast adjust + + 0 + 2 + + + - top + bottom 10 1000 int:img_size - 0 - 1 + 1 + 2 60 - 6 + 3 1 - Size + Display size adjust - 0 - 1 - 7 + 1 + 2 + 2 1 @@ -157,66 +148,58 @@ x y both - 1 - 2 - 40 - 2 - - - - - - Sobel edge - - 1 - 2 - 5 - 3 - - - - - - Prev img - - - 1 - 10 - 4 - - - - - - Next img - - - 2 - 10 - 4 - - - - - - Switch image - - 1 - 2 - 5 + 0 + 1 + 42 5 + + + center + Sobel edge + + 0 + 1 + 4 + + + + + + < Prev img + + + 1 + 10 + 5 + 1 + + + + + + Next img > + + + 2 + 10 + 5 + 1 + + + - Export dataset + Useless button 2 1 0 - 6 + 7 + 1 @@ -224,25 +207,27 @@ int:export_id 15 - - 1 - 6 - - - - - - Img to export 1 7 + + + Useless imput + + 1 + 2 + 6 + + + - 10 + 15 0 + 4 disabled Image metadata should appear here 25 @@ -255,6 +240,48 @@ + + + bottom + -500 + 500 + int:brightness + + + 0 + 60 + 1 + + + + + + Brightness adjust + + 0 + 0 + + + + + + Dataset path + + 0 + 6 + + + + + + Image control + + 1 + 2 + 4 + + + diff --git a/src/suite.py b/src/suite.py index c04a365..f357275 100644 --- a/src/suite.py +++ b/src/suite.py @@ -47,11 +47,13 @@ class MainApp: self.canny_thr1 = None self.canny_thr2 = None self.img_path = None - self.blur_rate = None + self.contrast = None self.img_size = None self.sobel_select = None self.export_id = None - builder.import_variables(self, ['canny_thr1','canny_thr2','img_path','blur_rate','img_size', 'sobel_select', 'export_id']) + self.brightness = None + builder.import_variables(self,['canny_thr1','canny_thr2','img_path','contrast','img_size','sobel_select','export_id','brightness']) + builder.connect_callbacks(self) # Load values from config after UI has been initialised @@ -189,7 +191,8 @@ class MainApp: # print(f"Result at thres {th1}, {th2}; \tIndex {y_ind}, {x_ind} \t= {w_res}") # print(results[y_ind]) - + + self.axs[column, row].title.set_text(F"Mean: {np.matrix(results).mean()}") self.axs[column, row].imshow(results) self.axs[column, row].xaxis.set_major_formatter(lambda x, pos: str(x*canny_step)) self.axs[column, row].yaxis.set_major_formatter(lambda x, pos: str(x*canny_step)) @@ -215,9 +218,10 @@ class MainApp: # Get all user vars ct1 = self.canny_thr1.get() ct2 = self.canny_thr2.get() - br = self.blur_rate.get() sxy = self.sobel_select.get() size = self.img_size.get() + contrast = self.contrast.get() + bright = self.brightness.get() # Import and resize image img = cv2.imread(images[self.img_current]) @@ -228,15 +232,15 @@ class MainApp: img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) self.add_output(img_gray, "Grayscale") + # Contrast / brightness boost + contrast_val = contrast / 100 + bright_val = bright / 100 + img_contrast = np.clip(contrast_val * (img_gray + bright_val), 0, 255).astype(np.uint8) + self.add_output(img_contrast, F"Contrast / Brightness\n c+{contrast_val} b+{bright_val}") + # Blurred edition - if (br == 0): - img_blur = img_gray - elif (br % 2 == 1): - img_blur = cv2.GaussianBlur(img_gray, (br, br), 0) - else: - print(f"Blur rate changed to {br - 1}") - img_blur = cv2.GaussianBlur(img_gray, (br-1, br-1), 0) - self.add_output(img_blur, "Blurred") + img_blur = cv2.GaussianBlur(img_gray, (3, 3), 0) + self.add_output(img_blur, "Blurred (3)") # Sobel edge if sxy in ['x', 'y', 'both']: @@ -253,7 +257,7 @@ class MainApp: img_sobel = cv2.Sobel(src=img_blur, ddepth=cv2.CV_8U, dx=dx, dy=dy, ksize=5) else: img_sobel = img_gray - self.add_output(img_sobel, "Sobel Edge") + self.add_output(img_sobel, F"Sobel Edge\n nz={cv2.countNonZero(img_sobel)}") # Canny edge img_canny = cv2.Canny(image=img_blur,threshold1=ct1,threshold2=ct2) From dcd4411fbdade4cd1a54596508341680c76b760c Mon Sep 17 00:00:00 2001 From: Arne van Iterson Date: Wed, 27 Sep 2023 14:20:20 +0200 Subject: [PATCH 2/3] Added Export function Removed useless input :( --- src/config/config.template.json | 1 + src/helpers/gui/main.ui | 8 +++++--- src/suite.py | 21 +++++++++++++++++++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/config/config.template.json b/src/config/config.template.json index 7594d9d..a17404f 100644 --- a/src/config/config.template.json +++ b/src/config/config.template.json @@ -1,4 +1,5 @@ { "path": "", + "out": "", "size": 750 } \ No newline at end of file diff --git a/src/helpers/gui/main.ui b/src/helpers/gui/main.ui index 6f882ea..6936046 100644 --- a/src/helpers/gui/main.ui +++ b/src/helpers/gui/main.ui @@ -192,7 +192,7 @@ - Useless button + Export PNG 2 @@ -215,7 +215,7 @@ - Useless imput + Export Image by ID 1 2 @@ -228,8 +228,10 @@ 15 0 4 + false disabled - Image metadata should appear here + true + Image IDs should appear here 25 word diff --git a/src/suite.py b/src/suite.py index 16988e4..2367bef 100644 --- a/src/suite.py +++ b/src/suite.py @@ -10,6 +10,8 @@ import time import matplotlib.pyplot as plt import json from helpers.statistics import imgStats +import datetime +import os ## UI config load PROJECT_PATH = pathlib.Path(__file__).parent @@ -97,11 +99,25 @@ class MainApp: ''' Export current dataset ''' + # Get export settings img_arr = self.tk_imgs img_id = self.export_id.get() + path = config_json["out"] - if (img_id >= 0 and img_id < len(img_arr)): - print("export") + if (img_id >= 0 and img_id < len(img_arr)): + # Create file + now = datetime.datetime.now() + new_file_name = F"{now.strftime('%Y-%m-%dT%H.%M.%S')}-id{img_id}.png" + + # Put data + file_path = pathlib.Path(path, new_file_name) + # print(file_path) + + imgpil = ImageTk.getimage(self.tk_imgs[img_id]) + imgpil.save(file_path, "PNG" ) + imgpil.close() + + print(f"Exported Image ID {img_id} to {os.path.join(path, new_file_name)}") else: print("Nothing to export!") @@ -206,6 +222,7 @@ class MainApp: def update(self, event=None): path = self.img_path.get() + # print(path) if path != None and path != "": # Get all images at current path From 8e7f1b57f157c738cc90e1e38453c76de043e0d0 Mon Sep 17 00:00:00 2001 From: Arne van Iterson Date: Wed, 27 Sep 2023 16:38:22 +0200 Subject: [PATCH 3/3] Added export all and partial update --- src/helpers/gui/main.ui | 24 +++++++++++- src/suite.py | 84 ++++++++++++++++++++++++++++++----------- 2 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/helpers/gui/main.ui b/src/helpers/gui/main.ui index 6936046..27b05d0 100644 --- a/src/helpers/gui/main.ui +++ b/src/helpers/gui/main.ui @@ -197,7 +197,7 @@ 2 1 - 0 + 10 7 1 @@ -284,6 +284,28 @@ + + + Export ID for entire dataset + + + 1 + 2 + 30 + 8 + + + + + + TkIconFont + Powered by ARNweb.nl & TomSelier.com + + 0 + 8 + + + diff --git a/src/suite.py b/src/suite.py index 2367bef..cc27040 100644 --- a/src/suite.py +++ b/src/suite.py @@ -12,6 +12,7 @@ import json from helpers.statistics import imgStats import datetime import os +import copy ## UI config load PROJECT_PATH = pathlib.Path(__file__).parent @@ -40,6 +41,7 @@ class MainApp: # Keep track of images in dataset self.img_current = 0 + self.img_old = -1 self.img_max = 0 # Plots @@ -50,12 +52,14 @@ class MainApp: self.canny_thr2 = None self.img_path = None self.contrast = None + self.img_size = None + self.img_size_old = 0 ## Check if the rendering size has changed, if it has the analysis has to be run + self.sobel_select = None self.export_id = None self.brightness = None builder.import_variables(self,['canny_thr1','canny_thr2','img_path','contrast','img_size','sobel_select','export_id','brightness']) - builder.connect_callbacks(self) # Load values from config after UI has been initialised @@ -95,19 +99,22 @@ class MainApp: self.img_current = self.img_current + 1 self.update(self) - def apply(self, event=None): + def apply(self, event=None, path=None): ''' Export current dataset ''' # Get export settings img_arr = self.tk_imgs img_id = self.export_id.get() - path = config_json["out"] + if path == None: + path = config_json["out"] + else: + print(F"Using path: {path}") if (img_id >= 0 and img_id < len(img_arr)): # Create file now = datetime.datetime.now() - new_file_name = F"{now.strftime('%Y-%m-%dT%H.%M.%S')}-id{img_id}.png" + new_file_name = F"{self.img_current}-{self.output[1][img_id]}-{now.strftime('%Y-%m-%dT%H.%M.%S')}.png" # Put data file_path = pathlib.Path(path, new_file_name) @@ -120,6 +127,25 @@ class MainApp: print(f"Exported Image ID {img_id} to {os.path.join(path, new_file_name)}") else: print("Nothing to export!") + + def apply_all(self, event=None): + img_id = self.export_id.get() + img_current = copy.deepcopy(self.img_current) + + now = datetime.datetime.now() + path = pathlib.Path(config_json["out"], F"{self.output[1][img_id]}-all-{now.strftime('%Y-%m-%dT%H.%M.%S')}/") + os.mkdir(path) + + while True: + self.img_next() + self.update(part_update=True) # Enforce partial update since we don't need the histograms etc. + self.apply(path=path) + + if (self.img_current == img_current): + break + + ## Ensure display is always correct with image + self.update() def add_output(self, data, name: str): ''' @@ -162,7 +188,6 @@ class MainApp: self.meta.insert(END, F"{idx}: {self.output[1][idx]}\n") # Clear output - self.output = [[] for x in range(2)] self.meta.config(state=DISABLED) # Draw canvas @@ -220,9 +245,19 @@ class MainApp: %(labels[0], mean[0], labels[1], mean[1], labels[2], mean[2], labels[0], std[0], labels[1], std[1], labels[2], std[2])) - def update(self, event=None): + def update(self, event=None, part_update=False): path = self.img_path.get() - # print(path) + + ## Check if hist and canny hm have to be rerendered + if not part_update: ## If partial update has not been forced, check if full update is required + if (self.img_current != self.img_old or self.img_size != self.img_size_old): + part_update = False + self.img_old = self.img_current + self.img_size_old = self.img_size + else: + part_update = True + else: + print("Partial update forced!") if path != None and path != "": # Get all images at current path @@ -240,6 +275,9 @@ class MainApp: contrast = self.contrast.get() bright = self.brightness.get() + # Clear output + self.output = [[] for x in range(2)] + # Import and resize image img = cv2.imread(images[self.img_current]) img = cv2.resize(img, (size, size), interpolation = cv2.INTER_AREA) @@ -253,11 +291,12 @@ class MainApp: contrast_val = contrast / 100 bright_val = bright / 100 img_contrast = np.clip(contrast_val * (img_gray + bright_val), 0, 255).astype(np.uint8) - self.add_output(img_contrast, F"Contrast / Brightness\n c+{contrast_val} b+{bright_val}") + # self.add_output(img_contrast, F"Contrast / Brightness\n c+{contrast_val} b+{bright_val}") + self.add_output(img_contrast, F"BCG") # Blurred edition img_blur = cv2.GaussianBlur(img_gray, (3, 3), 0) - self.add_output(img_blur, "Blurred (3)") + self.add_output(img_blur, "Blurred_k3") # Sobel edge if sxy in ['x', 'y', 'both']: @@ -274,17 +313,18 @@ class MainApp: img_sobel = cv2.Sobel(src=img_blur, ddepth=cv2.CV_8U, dx=dx, dy=dy, ksize=5) else: img_sobel = img_gray - self.add_output(img_sobel, F"Sobel Edge\n nz={cv2.countNonZero(img_sobel)}") + self.add_output(img_sobel, "Sobel_edge") + # self.add_output(img_sobel, F"Sobel Edge\n nz={cv2.countNonZero(img_sobel)}") # Canny edge img_canny = cv2.Canny(image=img_blur,threshold1=ct1,threshold2=ct2) - self.add_output(img_canny, "Canny Edge") + self.add_output(img_canny, "Canny_edge") self.writeStats(img, ('B', 'G', 'R'), 0, 0) # BGR - self.add_output(img[:, :, 0], "BGR B") - self.add_output(img[:, :, 1], "BGR G") - self.add_output(img[:, :, 2], "BGR R") + self.add_output(img[:, :, 0], "BGR_B") + self.add_output(img[:, :, 1], "BGR_G") + self.add_output(img[:, :, 2], "BGR_R") if img is not None: self.drawHist(img, ('B', 'G', 'R'), 0, 0) @@ -292,16 +332,18 @@ class MainApp: # HSV img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) self.add_output(img_hsv, "HSV") - self.add_output(img_hsv[:, :, 0], "HSV H") # H - self.add_output(img_hsv[:, :, 1], "HSV S") # S - self.add_output(img_hsv[:, :, 2], "HSV V") # V + self.add_output(img_hsv[:, :, 0], "HSV_H") # H + self.add_output(img_hsv[:, :, 1], "HSV_S") # S + self.add_output(img_hsv[:, :, 2], "HSV_V") # V - if img_hsv is not None: - self.drawHist(img_hsv, ('H', 'S', 'V'), 0, 1) - self.writeStats(img_hsv, ('H', 'S', 'V'), 0, 1) + if not part_update: + if img_hsv is not None: + self.drawHist(img_hsv, ('H', 'S', 'V'), 0, 1) + self.writeStats(img_hsv, ('H', 'S', 'V'), 0, 1) # Canny Heatmap - self.drawCannyHM(img, 1, 1) + if not part_update: + self.drawCannyHM(img, 1, 1) # Show all data plt.show(block=False) ## Graphs