Improved image control (very epic)
This commit is contained in:
parent
6bd918b3ba
commit
468f62633c
@ -169,9 +169,9 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="prev" named="True">
|
<object class="ttk.Button" id="prev_img" named="True">
|
||||||
|
<property name="command" type="command" cbtype="with_wid">imgCtl</property>
|
||||||
<property name="text" translatable="yes">< Prev img</property>
|
<property name="text" translatable="yes">< Prev img</property>
|
||||||
<bind sequence="<ButtonPress>" handler="img_prev" add="" />
|
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">2</property>
|
<property name="column">2</property>
|
||||||
<property name="ipadx">10</property>
|
<property name="ipadx">10</property>
|
||||||
@ -181,9 +181,9 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="next" named="True">
|
<object class="ttk.Button" id="next_img" named="True">
|
||||||
|
<property name="command" type="command" cbtype="with_wid">imgCtl</property>
|
||||||
<property name="text" translatable="yes">Next img ></property>
|
<property name="text" translatable="yes">Next img ></property>
|
||||||
<bind sequence="<ButtonPress>" handler="img_next" add="" />
|
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">3</property>
|
<property name="column">3</property>
|
||||||
<property name="ipadx">10</property>
|
<property name="ipadx">10</property>
|
||||||
@ -197,10 +197,10 @@
|
|||||||
<property name="text" translatable="yes">Export PNG</property>
|
<property name="text" translatable="yes">Export PNG</property>
|
||||||
<bind sequence="<ButtonPress>" handler="apply" add="" />
|
<bind sequence="<ButtonPress>" handler="apply" add="" />
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">3</property>
|
<property name="column">1</property>
|
||||||
<property name="columnspan">1</property>
|
<property name="columnspan">1</property>
|
||||||
<property name="ipadx">10</property>
|
<property name="ipadx">30</property>
|
||||||
<property name="row">6</property>
|
<property name="row">8</property>
|
||||||
<property name="rowspan">1</property>
|
<property name="rowspan">1</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
@ -208,10 +208,10 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="ttk.Entry" id="entry1">
|
<object class="ttk.Entry" id="entry1">
|
||||||
<property name="textvariable">int:export_id</property>
|
<property name="textvariable">int:export_id</property>
|
||||||
<property name="width">15</property>
|
<property name="width">10</property>
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">2</property>
|
<property name="column">0</property>
|
||||||
<property name="row">6</property>
|
<property name="row">8</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -219,9 +219,9 @@
|
|||||||
<object class="ttk.Label" id="label1">
|
<object class="ttk.Label" id="label1">
|
||||||
<property name="text" translatable="yes">Export Image by ID</property>
|
<property name="text" translatable="yes">Export Image by ID</property>
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">2</property>
|
<property name="column">0</property>
|
||||||
<property name="columnspan">2</property>
|
<property name="columnspan">2</property>
|
||||||
<property name="row">5</property>
|
<property name="row">7</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -280,9 +280,9 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="export_all" named="True">
|
<object class="ttk.Button" id="export" named="True">
|
||||||
|
<property name="command" type="command" cbtype="with_wid">applyAll</property>
|
||||||
<property name="text" translatable="yes">Export ID for entire dataset</property>
|
<property name="text" translatable="yes">Export ID for entire dataset</property>
|
||||||
<bind sequence="<ButtonRelease>" handler="apply_all" add="" />
|
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">2</property>
|
<property name="column">2</property>
|
||||||
<property name="columnspan">2</property>
|
<property name="columnspan">2</property>
|
||||||
@ -303,17 +303,42 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="button1">
|
<object class="ttk.Button" id="analyse" named="True">
|
||||||
|
<property name="command" type="command" cbtype="with_wid">applyAll</property>
|
||||||
<property name="text" translatable="yes">Run analysis for entire dataset (!)</property>
|
<property name="text" translatable="yes">Run analysis for entire dataset (!)</property>
|
||||||
<bind sequence="<ButtonPress>" handler="analyse_all" add="" />
|
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">2</property>
|
<property name="column">2</property>
|
||||||
<property name="columnspan">2</property>
|
<property name="columnspan">2</property>
|
||||||
<property name="ipadx">15</property>
|
<property name="ipadx">15</property>
|
||||||
|
<property name="pady">5</property>
|
||||||
<property name="row">8</property>
|
<property name="row">8</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="ttk.Button" id="prev_tree" named="True">
|
||||||
|
<property name="command" type="command" cbtype="with_wid">imgCtl</property>
|
||||||
|
<property name="text" translatable="yes">< Prev tree</property>
|
||||||
|
<layout manager="grid">
|
||||||
|
<property name="column">2</property>
|
||||||
|
<property name="ipadx">10</property>
|
||||||
|
<property name="pady">5</property>
|
||||||
|
<property name="row">5</property>
|
||||||
|
</layout>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="ttk.Button" id="next_tree" named="True">
|
||||||
|
<property name="command" type="command" cbtype="with_wid">imgCtl</property>
|
||||||
|
<property name="text" translatable="yes">Next tree ></property>
|
||||||
|
<layout manager="grid">
|
||||||
|
<property name="column">3</property>
|
||||||
|
<property name="ipadx">10</property>
|
||||||
|
<property name="pady">5</property>
|
||||||
|
<property name="row">5</property>
|
||||||
|
</layout>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
165
src/suite.py
165
src/suite.py
@ -27,6 +27,7 @@ config_json = json.load(config_file)
|
|||||||
|
|
||||||
log = Logger(config_json["out"])
|
log = Logger(config_json["out"])
|
||||||
|
|
||||||
|
|
||||||
## UI class setup
|
## UI class setup
|
||||||
class MainApp:
|
class MainApp:
|
||||||
def __init__(self, master=None):
|
def __init__(self, master=None):
|
||||||
@ -101,25 +102,47 @@ class MainApp:
|
|||||||
"""
|
"""
|
||||||
self.mainwindow.mainloop()
|
self.mainwindow.mainloop()
|
||||||
|
|
||||||
def img_prev(self, event=None):
|
def imgCtl(self, widget_id):
|
||||||
"""
|
"""
|
||||||
Open previous image from path
|
Bunch a functions to switch between images in the given dataset
|
||||||
"""
|
"""
|
||||||
if self.img_current == 0:
|
cmd = widget_id.split("_")
|
||||||
self.img_current = self.img_max - 1
|
|
||||||
else:
|
# Determine detection based on widget id
|
||||||
self.img_current = self.img_current - 1
|
if cmd[0] == "next":
|
||||||
|
dir = 1
|
||||||
|
elif cmd[0] == "prev":
|
||||||
|
dir = -1
|
||||||
|
|
||||||
|
# Get name of current img
|
||||||
|
start = copy.deepcopy(
|
||||||
|
self.img_name.split("_")[0]
|
||||||
|
) # deepcopy cus snaky boi language likes to create pointers
|
||||||
|
next = start
|
||||||
|
|
||||||
|
while start == next:
|
||||||
|
# Check for limits
|
||||||
|
self.img_current += dir
|
||||||
|
if self.img_current == self.img_max:
|
||||||
|
self.img_current = 0
|
||||||
|
elif self.img_current == -1:
|
||||||
|
self.img_current = self.img_max - 1
|
||||||
|
|
||||||
|
if cmd[1] == "img":
|
||||||
|
break # Stop if only one image should be skipped
|
||||||
|
elif cmd[1] == "tree":
|
||||||
|
self.updatePath()
|
||||||
|
next = copy.deepcopy(self.img_name.split("_")[0])
|
||||||
|
|
||||||
|
# Update UI
|
||||||
self.update(self)
|
self.update(self)
|
||||||
|
|
||||||
def img_next(self, event=None):
|
"""
|
||||||
"""
|
TODO:
|
||||||
Open next image from path
|
- Primitive surface area
|
||||||
"""
|
- Derivative max x and y pos
|
||||||
if self.img_current == (self.img_max - 1):
|
- Function min, max + y position
|
||||||
self.img_current = 0
|
"""
|
||||||
else:
|
|
||||||
self.img_current = self.img_current + 1
|
|
||||||
self.update(self)
|
|
||||||
|
|
||||||
def apply(self, event=None, path=None):
|
def apply(self, event=None, path=None):
|
||||||
"""
|
"""
|
||||||
@ -150,14 +173,19 @@ class MainApp:
|
|||||||
else:
|
else:
|
||||||
print("Nothing to export!")
|
print("Nothing to export!")
|
||||||
|
|
||||||
def apply_all(self, event=None, export = True):
|
def applyAll(self, widget_id):
|
||||||
"""
|
"""
|
||||||
Export given preprocess id for every image in the dataset folder
|
Export given preprocess id for every image in the dataset folder
|
||||||
"""
|
"""
|
||||||
|
if widget_id == "export":
|
||||||
|
export = True
|
||||||
|
elif widget_id == "analyse":
|
||||||
|
export = False
|
||||||
|
|
||||||
img_id = self.export_id.get()
|
img_id = self.export_id.get()
|
||||||
img_current = copy.deepcopy(self.img_current)
|
img_current = copy.deepcopy(self.img_current)
|
||||||
|
|
||||||
if (export):
|
if export:
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
path = pathlib.Path(
|
path = pathlib.Path(
|
||||||
config_json["out"],
|
config_json["out"],
|
||||||
@ -166,28 +194,24 @@ class MainApp:
|
|||||||
os.mkdir(path)
|
os.mkdir(path)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
self.img_next()
|
if export:
|
||||||
|
|
||||||
if (export):
|
|
||||||
self.apply(path=path)
|
self.apply(path=path)
|
||||||
|
|
||||||
|
self.imgCtl("next_img")
|
||||||
if self.img_current == img_current:
|
if self.img_current == img_current:
|
||||||
break
|
break
|
||||||
|
|
||||||
## Ensure display is always correct with image
|
## Ensure display is always correct with image
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def analyse_all(self, event=None):
|
def addOutput(self, data, name: str):
|
||||||
self.apply_all(export=False)
|
|
||||||
|
|
||||||
def add_output(self, data, name: str):
|
|
||||||
"""
|
"""
|
||||||
Add CV2 image to canvas output
|
Add CV2 image to canvas output
|
||||||
"""
|
"""
|
||||||
self.output[0].append(data)
|
self.output[0].append(data)
|
||||||
self.output[1].append(name)
|
self.output[1].append(name)
|
||||||
|
|
||||||
def draw_output(self, size):
|
def drawOutput(self, size):
|
||||||
# Check if size of canvas has updated
|
# Check if size of canvas has updated
|
||||||
drawW = self.canvas.winfo_width()
|
drawW = self.canvas.winfo_width()
|
||||||
|
|
||||||
@ -253,14 +277,17 @@ class MainApp:
|
|||||||
|
|
||||||
for th1 in range(0, canny_max, canny_step):
|
for th1 in range(0, canny_max, canny_step):
|
||||||
for th2 in range(0, canny_max, canny_step):
|
for th2 in range(0, canny_max, canny_step):
|
||||||
# Canny Edge Detection
|
|
||||||
edges = cv2.Canny(image=img, threshold1=th1, threshold2=th2)
|
|
||||||
|
|
||||||
w_res = cv2.countNonZero(edges)
|
|
||||||
y_ind = (int)(th1 / canny_step)
|
y_ind = (int)(th1 / canny_step)
|
||||||
x_ind = (int)(th2 / canny_step)
|
x_ind = (int)(th2 / canny_step)
|
||||||
|
|
||||||
results[y_ind].append(w_res)
|
# Ignore all edges where th1 > th2
|
||||||
|
if th1 <= th2:
|
||||||
|
# Canny Edge Detection
|
||||||
|
edges = cv2.Canny(image=img, threshold1=th1, threshold2=th2)
|
||||||
|
w_res = cv2.countNonZero(edges)
|
||||||
|
results[y_ind].append(w_res)
|
||||||
|
else:
|
||||||
|
results[y_ind].append(0)
|
||||||
|
|
||||||
# print(f"Result at thres {th1}, {th2}; \tIndex {y_ind}, {x_ind} \t= {w_res}")
|
# print(f"Result at thres {th1}, {th2}; \tIndex {y_ind}, {x_ind} \t= {w_res}")
|
||||||
# print(results[y_ind])
|
# print(results[y_ind])
|
||||||
@ -313,23 +340,12 @@ class MainApp:
|
|||||||
log.add(f"Mean {label}", mean[idx])
|
log.add(f"Mean {label}", mean[idx])
|
||||||
log.add(f"Std {label}", std[idx])
|
log.add(f"Std {label}", std[idx])
|
||||||
|
|
||||||
def update(self, event=None, part_update=None):
|
def updatePath(self):
|
||||||
|
"""
|
||||||
|
Only update image name and path
|
||||||
|
"""
|
||||||
path = self.img_path.get()
|
path = self.img_path.get()
|
||||||
|
|
||||||
## Check if hist and canny hm have to be rerendered
|
|
||||||
if part_update == None: ## 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:
|
|
||||||
if part_update == True:
|
|
||||||
print("Partial update forced!")
|
|
||||||
else:
|
|
||||||
print("Full update forced!")
|
|
||||||
|
|
||||||
if path != None and path != "":
|
if path != None and path != "":
|
||||||
# Get all images at current path
|
# Get all images at current path
|
||||||
images = []
|
images = []
|
||||||
@ -341,6 +357,28 @@ class MainApp:
|
|||||||
|
|
||||||
self.img_max = len(images)
|
self.img_max = len(images)
|
||||||
self.img_name = os.path.split(images[self.img_current])[1]
|
self.img_name = os.path.split(images[self.img_current])[1]
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update(self, event=None, part_update=None):
|
||||||
|
## Check if hist and canny hm have to be rerendered
|
||||||
|
if (
|
||||||
|
part_update == None
|
||||||
|
): ## 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:
|
||||||
|
if part_update == True:
|
||||||
|
print("Partial update forced!")
|
||||||
|
else:
|
||||||
|
print("Full update forced!")
|
||||||
|
|
||||||
|
if self.updatePath():
|
||||||
log.add("Img", self.img_name)
|
log.add("Img", self.img_name)
|
||||||
|
|
||||||
# Get all user vars
|
# Get all user vars
|
||||||
@ -355,26 +393,27 @@ class MainApp:
|
|||||||
self.output = [[] for x in range(2)]
|
self.output = [[] for x in range(2)]
|
||||||
|
|
||||||
# Import and resize image
|
# Import and resize image
|
||||||
img = cv2.imread(images[self.img_current])
|
# img = cv2.imread(images[self.img_current])
|
||||||
|
img = cv2.imread(os.path.join(self.img_path.get(), self.img_name))
|
||||||
img = cv2.resize(img, (size, size), interpolation=cv2.INTER_AREA)
|
img = cv2.resize(img, (size, size), interpolation=cv2.INTER_AREA)
|
||||||
self.add_output(img, "Original")
|
self.addOutput(img, "Original")
|
||||||
|
|
||||||
# Set grayscale
|
# Set grayscale
|
||||||
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||||
self.add_output(img_gray, "Grayscale")
|
self.addOutput(img_gray, "Grayscale")
|
||||||
|
|
||||||
# Contrast / brightness boost
|
# Contrast / brightness boost
|
||||||
contrast_val = contrast / 100
|
contrast_val = contrast / 100
|
||||||
bright_val = bright / 100
|
bright_val = bright / 100
|
||||||
img_contrast = np.clip(
|
img_contrast = np.clip(
|
||||||
contrast_val * (img_gray + bright_val), 0, 255
|
contrast_val * (img_gray + bright_val), 0, 255
|
||||||
).astype(np.uint8)
|
).astype(np.uint8)
|
||||||
# self.add_output(img_contrast, F"Contrast / Brightness\n c+{contrast_val} b+{bright_val}")
|
# self.addOutput(img_contrast, F"Contrast / Brightness\n c+{contrast_val} b+{bright_val}")
|
||||||
self.add_output(img_contrast, f"BCG")
|
self.addOutput(img_contrast, f"BCG")
|
||||||
|
|
||||||
# Blurred edition
|
# Blurred edition
|
||||||
img_blur = cv2.GaussianBlur(img_gray, (3, 3), 0)
|
img_blur = cv2.GaussianBlur(img_gray, (3, 3), 0)
|
||||||
self.add_output(img_blur, "Blurred_k3")
|
self.addOutput(img_blur, "Blurred_k3")
|
||||||
|
|
||||||
# Sobel edge
|
# Sobel edge
|
||||||
if sxy in ["x", "y", "both"]:
|
if sxy in ["x", "y", "both"]:
|
||||||
@ -394,17 +433,17 @@ class MainApp:
|
|||||||
else:
|
else:
|
||||||
img_sobel = img_gray
|
img_sobel = img_gray
|
||||||
|
|
||||||
self.add_output(img_sobel, "Sobel_edge")
|
self.addOutput(img_sobel, "Sobel_edge")
|
||||||
log.add("Sobel nonzero", cv2.countNonZero(img_sobel))
|
log.add("Sobel nonzero", cv2.countNonZero(img_sobel))
|
||||||
|
|
||||||
# Canny edge
|
# Canny edge
|
||||||
img_canny = cv2.Canny(image=img_blur, threshold1=ct1, threshold2=ct2)
|
img_canny = cv2.Canny(image=img_blur, threshold1=ct1, threshold2=ct2)
|
||||||
self.add_output(img_canny, "Canny_edge")
|
self.addOutput(img_canny, "Canny_edge")
|
||||||
|
|
||||||
# BGR
|
# BGR
|
||||||
self.add_output(img[:, :, 0], "BGR_B")
|
self.addOutput(img[:, :, 0], "BGR_B")
|
||||||
self.add_output(img[:, :, 1], "BGR_G")
|
self.addOutput(img[:, :, 1], "BGR_G")
|
||||||
self.add_output(img[:, :, 2], "BGR_R")
|
self.addOutput(img[:, :, 2], "BGR_R")
|
||||||
|
|
||||||
if img is not None:
|
if img is not None:
|
||||||
self.drawHist(img, ("B", "G", "R"), 0, 0)
|
self.drawHist(img, ("B", "G", "R"), 0, 0)
|
||||||
@ -412,10 +451,10 @@ class MainApp:
|
|||||||
|
|
||||||
# HSV
|
# HSV
|
||||||
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
||||||
self.add_output(img_hsv, "HSV")
|
self.addOutput(img_hsv, "HSV")
|
||||||
self.add_output(img_hsv[:, :, 0], "HSV_H") # H
|
self.addOutput(img_hsv[:, :, 0], "HSV_H") # H
|
||||||
self.add_output(img_hsv[:, :, 1], "HSV_S") # S
|
self.addOutput(img_hsv[:, :, 1], "HSV_S") # S
|
||||||
self.add_output(img_hsv[:, :, 2], "HSV_V") # V
|
self.addOutput(img_hsv[:, :, 2], "HSV_V") # V
|
||||||
|
|
||||||
if not part_update:
|
if not part_update:
|
||||||
if img_hsv is not None:
|
if img_hsv is not None:
|
||||||
@ -430,11 +469,11 @@ class MainApp:
|
|||||||
if not part_update:
|
if not part_update:
|
||||||
log.update()
|
log.update()
|
||||||
else:
|
else:
|
||||||
log.clear() # Prevent partial updates from breaking log
|
log.clear() # Prevent partial updates from breaking log
|
||||||
|
|
||||||
# Show all data
|
# Show all data
|
||||||
plt.show(block=False) ## Graphs
|
plt.show(block=False) ## Graphs
|
||||||
self.draw_output(size) ## Images
|
self.drawOutput(size) ## Images
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Loading…
Reference in New Issue
Block a user