Merge branch 'main' of https://arnweb.nl/gitea/arne/EV5_Beeldherk_Bomen
This commit is contained in:
commit
311070d1a6
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"path": "",
|
"path": "",
|
||||||
"out": "",
|
"out": {
|
||||||
"size": 750
|
"img": "",
|
||||||
|
"log": ""
|
||||||
|
},
|
||||||
|
"size": 100
|
||||||
}
|
}
|
103
src/helpers/canvas.py
Normal file
103
src/helpers/canvas.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import cv2
|
||||||
|
import tkinter
|
||||||
|
from PIL import ImageTk, Image
|
||||||
|
import datetime
|
||||||
|
import pathlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
from helpers.logger import C_INFO, C_DONE, C_ERR
|
||||||
|
|
||||||
|
class CVSuiteCanvas:
|
||||||
|
def __init__(self, canvas):
|
||||||
|
self.canvas = canvas
|
||||||
|
|
||||||
|
self.name = "" # Name of current image
|
||||||
|
self.imgs = [] # OpenCV Image data
|
||||||
|
self.tk_imgs = [] # Tkinter Image data
|
||||||
|
self.tags = [] # Tag data
|
||||||
|
|
||||||
|
def add(self, data, name: str):
|
||||||
|
"""
|
||||||
|
Add CV2 image to canvas output
|
||||||
|
"""
|
||||||
|
self.imgs.append(data)
|
||||||
|
self.tags.append(name)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""
|
||||||
|
Clear canvas
|
||||||
|
"""
|
||||||
|
self.imgs = []
|
||||||
|
self.tags = []
|
||||||
|
|
||||||
|
def draw(self, size):
|
||||||
|
"""
|
||||||
|
Update canvas
|
||||||
|
"""
|
||||||
|
# Check if size of canvas has updated
|
||||||
|
drawW = self.canvas.winfo_width()
|
||||||
|
|
||||||
|
# Reset drawing position
|
||||||
|
drawX = 0
|
||||||
|
drawY = 0
|
||||||
|
|
||||||
|
# Clear previously printed images
|
||||||
|
self.tk_imgs = []
|
||||||
|
|
||||||
|
# self.meta.config(state=tkinter.NORMAL)
|
||||||
|
# self.meta.delete(1.0, tkinter.END)
|
||||||
|
# self.meta.insert(tkinter.END, f"{self.img_name}\n")
|
||||||
|
|
||||||
|
# Draw all output images
|
||||||
|
for idx, data in enumerate(self.imgs):
|
||||||
|
# Create ui image
|
||||||
|
tk_img = cv2.cvtColor(data, cv2.COLOR_BGR2RGB)
|
||||||
|
tk_img = ImageTk.PhotoImage(image=Image.fromarray(tk_img))
|
||||||
|
self.tk_imgs.append(tk_img)
|
||||||
|
|
||||||
|
## Check if next item will be out of range
|
||||||
|
if drawX + size >= drawW:
|
||||||
|
drawY += size
|
||||||
|
drawX = 0
|
||||||
|
self.canvas.configure(height=(drawY + size))
|
||||||
|
|
||||||
|
self.canvas.create_image(
|
||||||
|
drawX, drawY, anchor=tkinter.NW, image=self.tk_imgs[idx], tags="og"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add name to text box
|
||||||
|
tag = self.canvas.create_text(drawX+5, drawY+5, anchor=tkinter.NW, text=F"{idx} {self.tags[idx]}", fill="black", font=('Helvetica 12 bold'))
|
||||||
|
tag_bg = self.canvas.create_rectangle(self.canvas.bbox(tag), fill="white")
|
||||||
|
self.canvas.tag_lower(tag_bg,tag)
|
||||||
|
# self.meta.insert(tkinter.END, f"{idx}: {self.output[1][idx]}\n")
|
||||||
|
|
||||||
|
drawX += size
|
||||||
|
|
||||||
|
# Clear output
|
||||||
|
# self.meta.config(state=tkinter.DISABLED)
|
||||||
|
|
||||||
|
def export(self, id, name, path):
|
||||||
|
"""
|
||||||
|
Export id to file
|
||||||
|
"""
|
||||||
|
# Get export settings
|
||||||
|
img_arr = self.tk_imgs
|
||||||
|
|
||||||
|
print(C_INFO, f"Using path: {path}")
|
||||||
|
|
||||||
|
if id >= 0 and id < len(img_arr):
|
||||||
|
# Create file
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
new_file_name = f"{name}-{self.tags[id]}-{now.strftime('%Y-%m-%dT%H.%M.%S')}.png"
|
||||||
|
|
||||||
|
# Put data
|
||||||
|
file_path = pathlib.Path(path, new_file_name)
|
||||||
|
# print(file_path)
|
||||||
|
|
||||||
|
img_pil = ImageTk.getimage(self.tk_imgs[id])
|
||||||
|
img_pil.save(file_path, "PNG")
|
||||||
|
img_pil.close()
|
||||||
|
|
||||||
|
print(C_DONE, f"Exported Image ID {id} ({self.tags[id]}) to {os.path.join(path, new_file_name)}")
|
||||||
|
else:
|
||||||
|
print(C_ERR, "Nothing to export!")
|
@ -194,8 +194,8 @@
|
|||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="button3">
|
<object class="ttk.Button" id="button3">
|
||||||
|
<property name="command" type="command" cbtype="simple">imgExport</property>
|
||||||
<property name="text" translatable="yes">Export PNG</property>
|
<property name="text" translatable="yes">Export PNG</property>
|
||||||
<bind sequence="<ButtonPress>" handler="apply" add="" />
|
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">1</property>
|
<property name="column">1</property>
|
||||||
<property name="columnspan">1</property>
|
<property name="columnspan">1</property>
|
||||||
@ -225,25 +225,6 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="tk.Text" id="dataset" named="True">
|
|
||||||
<property name="height">15</property>
|
|
||||||
<property name="pady">0</property>
|
|
||||||
<property name="selectborderwidth">4</property>
|
|
||||||
<property name="setgrid">false</property>
|
|
||||||
<property name="state">normal</property>
|
|
||||||
<property name="takefocus">true</property>
|
|
||||||
<property name="text" translatable="yes">Image IDs should appear here</property>
|
|
||||||
<property name="width">25</property>
|
|
||||||
<property name="wrap">word</property>
|
|
||||||
<layout manager="grid">
|
|
||||||
<property name="column">4</property>
|
|
||||||
<property name="columnspan">1</property>
|
|
||||||
<property name="row">1</property>
|
|
||||||
<property name="rowspan">7</property>
|
|
||||||
</layout>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.LabeledScale" id="s_bright" named="True">
|
<object class="ttk.LabeledScale" id="s_bright" named="True">
|
||||||
<property name="compound">bottom</property>
|
<property name="compound">bottom</property>
|
||||||
@ -281,7 +262,7 @@
|
|||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="export" named="True">
|
<object class="ttk.Button" id="export" named="True">
|
||||||
<property name="command" type="command" cbtype="with_wid">applyAll</property>
|
<property name="command" type="command" cbtype="with_wid">imgCycle</property>
|
||||||
<property name="text" translatable="yes">Export ID for entire dataset</property>
|
<property name="text" translatable="yes">Export ID for entire dataset</property>
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">2</property>
|
<property name="column">2</property>
|
||||||
@ -297,14 +278,14 @@
|
|||||||
<property name="text" translatable="yes">Powered by ARNweb.nl & TomSelier.com</property>
|
<property name="text" translatable="yes">Powered by ARNweb.nl & TomSelier.com</property>
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">4</property>
|
<property name="column">4</property>
|
||||||
<property name="columnspan">2</property>
|
<property name="columnspan">1</property>
|
||||||
<property name="row">8</property>
|
<property name="row">8</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="analyse" named="True">
|
<object class="ttk.Button" id="analyse" named="True">
|
||||||
<property name="command" type="command" cbtype="with_wid">applyAll</property>
|
<property name="command" type="command" cbtype="with_wid">imgCycle</property>
|
||||||
<property name="text" translatable="yes">Run analysis for entire dataset (!)</property>
|
<property name="text" translatable="yes">Run analysis for entire dataset (!)</property>
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">2</property>
|
<property name="column">2</property>
|
||||||
@ -318,7 +299,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="prev_tree" named="True">
|
<object class="ttk.Button" id="prev_tree" named="True">
|
||||||
<property name="command" type="command" cbtype="with_wid">imgCtl</property>
|
<property name="command" type="command" cbtype="with_wid">imgCtl</property>
|
||||||
<property name="text" translatable="yes">< Prev tree</property>
|
<property name="text" translatable="yes">< Prev tag</property>
|
||||||
<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>
|
||||||
@ -330,7 +311,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="next_tree" named="True">
|
<object class="ttk.Button" id="next_tree" named="True">
|
||||||
<property name="command" type="command" cbtype="with_wid">imgCtl</property>
|
<property name="command" type="command" cbtype="with_wid">imgCtl</property>
|
||||||
<property name="text" translatable="yes">Next tree ></property>
|
<property name="text" translatable="yes">Next tag ></property>
|
||||||
<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>
|
||||||
@ -347,27 +328,18 @@
|
|||||||
<property name="width">25</property>
|
<property name="width">25</property>
|
||||||
<property name="wrap">word</property>
|
<property name="wrap">word</property>
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">5</property>
|
<property name="column">4</property>
|
||||||
<property name="columnspan">1</property>
|
<property name="columnspan">1</property>
|
||||||
<property name="row">1</property>
|
<property name="row">1</property>
|
||||||
<property name="rowspan">7</property>
|
<property name="rowspan">7</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="ttk.Label" id="l_meta" named="True">
|
|
||||||
<property name="text" translatable="yes">Metadata</property>
|
|
||||||
<layout manager="grid">
|
|
||||||
<property name="column">4</property>
|
|
||||||
<property name="row">0</property>
|
|
||||||
</layout>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Label" id="l_tests" named="True">
|
<object class="ttk.Label" id="l_tests" named="True">
|
||||||
<property name="text" translatable="yes">Test results</property>
|
<property name="text" translatable="yes">Test results</property>
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">5</property>
|
<property name="column">4</property>
|
||||||
<property name="row">0</property>
|
<property name="row">0</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
import pathlib
|
import pathlib
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
# Some feedback with colours to make console output easier to read
|
||||||
|
C_INFO = "\u001b[96m*INFO*\u001b[0m\t" # Info - Cyan
|
||||||
|
C_WARN = "\u001b[33m*WARN*\u001b[0m\t" # Warning - Orange
|
||||||
|
C_ERR = "\u001b[31m*ERR*\u001b[0m\t" # Error - Red
|
||||||
|
C_USER = "\u001b[35m*USER*\u001b[0m\t" # User input - Purple
|
||||||
|
C_DONE = "\u001b[32m*DONE*\u001b[0m\t" # Done - Green
|
||||||
|
C_DBUG = "\u001b[94m*DBUG*\u001b[0m\t" # Debug - Blue
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
|
class CVSuiteLogger:
|
||||||
class Logger:
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.fileName = pathlib.Path(
|
self.fileName = pathlib.Path(
|
||||||
path, f"result-{now.strftime('%Y-%m-%dT%H.%M.%S')}.csv"
|
path, f"result-{now.strftime('%Y-%m-%dT%H.%M.%S')}.csv"
|
||||||
)
|
)
|
||||||
self.file = open(self.fileName, "x")
|
self.file = open(self.fileName, encoding="utf-8", mode="x")
|
||||||
|
|
||||||
self.first = True
|
self.first = True
|
||||||
self.index = []
|
self.index = []
|
||||||
@ -19,15 +26,15 @@ class Logger:
|
|||||||
self.index.append(name)
|
self.index.append(name)
|
||||||
self.data.append(value)
|
self.data.append(value)
|
||||||
|
|
||||||
def csv(self, input):
|
def csv(self, data):
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
for idx, item in enumerate(input):
|
for idx, item in enumerate(data):
|
||||||
result += str(item)
|
result += str(item)
|
||||||
if idx != (len(input) - 1):
|
if idx != (len(data) - 1):
|
||||||
result += ", "
|
result += ", "
|
||||||
result += "\n"
|
result += "\n"
|
||||||
print(result)
|
# print(result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
211
src/suite.py
211
src/suite.py
@ -1,48 +1,48 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
import pathlib
|
import pathlib
|
||||||
import pygubu
|
|
||||||
import glob
|
import glob
|
||||||
import tkinter
|
|
||||||
from PIL import ImageTk, Image
|
|
||||||
import numpy as np
|
|
||||||
import cv2
|
|
||||||
import time
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import copy
|
import copy
|
||||||
|
from io import open
|
||||||
|
import numpy as np
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
# GUI
|
||||||
|
import pygubu
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# Helpers
|
||||||
from helpers.statistics import imgStats
|
from helpers.statistics import imgStats
|
||||||
from helpers.logger import Logger
|
from helpers.logger import CVSuiteLogger, C_DBUG
|
||||||
|
from helpers.canvas import CVSuiteCanvas
|
||||||
|
|
||||||
## UI config load
|
## UI config load
|
||||||
PROJECT_PATH = pathlib.Path(__file__).parent
|
PROJECT_PATH = pathlib.Path(__file__).parent
|
||||||
PROJECT_UI = "./src/helpers/gui/main.ui"
|
PROJECT_UI = "./src/helpers/gui/main.ui"
|
||||||
|
TITLE = "Tree Recogniser 7000"
|
||||||
|
|
||||||
## Config file load
|
## Config file load
|
||||||
CONFIG_PATH = "./src/config/config.json"
|
CONFIG_PATH = "./src/config/config.json"
|
||||||
config_file = open(CONFIG_PATH)
|
config_file = open(CONFIG_PATH, encoding="utf-8")
|
||||||
config_json = json.load(config_file)
|
config_json = json.load(config_file)
|
||||||
|
|
||||||
log = Logger(config_json["out"])
|
|
||||||
|
|
||||||
|
|
||||||
## UI class setup
|
## UI class setup
|
||||||
class MainApp:
|
class CVSuite:
|
||||||
def __init__(self, master=None):
|
def __init__(self, master=None):
|
||||||
self.builder = builder = pygubu.Builder()
|
self.builder = builder = pygubu.Builder()
|
||||||
builder.add_resource_path(PROJECT_PATH)
|
builder.add_resource_path(PROJECT_PATH)
|
||||||
builder.add_from_file(PROJECT_UI)
|
builder.add_from_file(PROJECT_UI)
|
||||||
|
|
||||||
# Main widget
|
# Main window
|
||||||
self.mainwindow = builder.get_object("main", master)
|
self.mainwindow = builder.get_object("main", master)
|
||||||
|
|
||||||
# Canvas for output images
|
# Canvas for output images
|
||||||
self.canvas = builder.get_object("output_canvas")
|
self.canvas = CVSuiteCanvas(builder.get_object("output_canvas"))
|
||||||
self.tk_imgs = [] # Required or python will forget
|
|
||||||
self.meta = builder.get_object("dataset")
|
# Log file
|
||||||
self.output = [[] for x in range(2)]
|
self.log = CVSuiteLogger(config_json["out"]["log"])
|
||||||
|
|
||||||
# Keep track of images in dataset
|
# Keep track of images in dataset
|
||||||
self.img_current = 0
|
self.img_current = 0
|
||||||
@ -90,7 +90,7 @@ class MainApp:
|
|||||||
"""
|
"""
|
||||||
if plt is not None:
|
if plt is not None:
|
||||||
plt.close() # Close graph vies
|
plt.close() # Close graph vies
|
||||||
log.file.close() # Close log files
|
self.log.file.close() # Close log files
|
||||||
|
|
||||||
self.mainwindow.quit() # Close main
|
self.mainwindow.quit() # Close main
|
||||||
|
|
||||||
@ -108,19 +108,19 @@ class MainApp:
|
|||||||
|
|
||||||
# Determine detection based on widget id
|
# Determine detection based on widget id
|
||||||
if cmd[0] == "next":
|
if cmd[0] == "next":
|
||||||
dir = 1
|
cdir = 1
|
||||||
elif cmd[0] == "prev":
|
elif cmd[0] == "prev":
|
||||||
dir = -1
|
cdir = -1
|
||||||
|
|
||||||
# Get name of current img
|
# Get name of current img
|
||||||
start = copy.deepcopy(
|
start = copy.deepcopy(
|
||||||
self.img_name.split("_")[0]
|
self.img_name.split("_")[0]
|
||||||
) # deepcopy cus snaky boi language likes to create pointers
|
) # deepcopy cus snaky boi language likes to create pointers
|
||||||
next = start
|
inext = start
|
||||||
|
|
||||||
while start == next:
|
while start == inext:
|
||||||
# Check for limits
|
# Check for limits
|
||||||
self.img_current += dir
|
self.img_current += cdir
|
||||||
if self.img_current == self.img_max:
|
if self.img_current == self.img_max:
|
||||||
self.img_current = 0
|
self.img_current = 0
|
||||||
elif self.img_current == -1:
|
elif self.img_current == -1:
|
||||||
@ -130,43 +130,22 @@ class MainApp:
|
|||||||
break # Stop if only one image should be skipped
|
break # Stop if only one image should be skipped
|
||||||
elif cmd[1] == "tree":
|
elif cmd[1] == "tree":
|
||||||
self.updatePath()
|
self.updatePath()
|
||||||
next = copy.deepcopy(self.img_name.split("_")[0])
|
inext = copy.deepcopy(self.img_name.split("_")[0])
|
||||||
|
|
||||||
# Update UI
|
# Update UI
|
||||||
self.update(self)
|
self.update(self)
|
||||||
|
|
||||||
def apply(self, event=None, path=None):
|
def imgExport(self, event=None, path=config_json["out"]["img"]):
|
||||||
"""
|
"""
|
||||||
Export current dataset
|
Export given preprocess id to file
|
||||||
"""
|
"""
|
||||||
# Get export settings
|
iid = self.export_id.get()
|
||||||
img_arr = self.tk_imgs
|
self.canvas.export(iid, self.img_name.split("_")[0], path)
|
||||||
img_id = self.export_id.get()
|
|
||||||
if path == None:
|
|
||||||
path = config_json["out"]
|
|
||||||
else:
|
|
||||||
print(f"Using path: {path}")
|
|
||||||
|
|
||||||
if img_id >= 0 and img_id < len(img_arr):
|
def imgCycle(self, widget_id):
|
||||||
# Create file
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
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)
|
|
||||||
# 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!")
|
|
||||||
|
|
||||||
def applyAll(self, widget_id):
|
|
||||||
"""
|
"""
|
||||||
Export given preprocess id for every image in the dataset folder
|
GUI Button callback
|
||||||
|
Cycle through all images in the data set
|
||||||
"""
|
"""
|
||||||
if widget_id == "export":
|
if widget_id == "export":
|
||||||
export = True
|
export = True
|
||||||
@ -179,14 +158,14 @@ class MainApp:
|
|||||||
if export:
|
if export:
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
path = pathlib.Path(
|
path = pathlib.Path(
|
||||||
config_json["out"],
|
config_json["out"]["img"],
|
||||||
f"{self.output[1][img_id]}-all-{now.strftime('%Y-%m-%dT%H.%M.%S')}/",
|
f"{self.canvas.tags[img_id]}-all-{now.strftime('%Y-%m-%dT%H.%M.%S')}/",
|
||||||
)
|
)
|
||||||
os.mkdir(path)
|
os.mkdir(path)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if export:
|
if export:
|
||||||
self.apply(path=path)
|
self.imgExport(path=path)
|
||||||
|
|
||||||
self.imgCtl("next_img")
|
self.imgCtl("next_img")
|
||||||
if self.img_current == img_current:
|
if self.img_current == img_current:
|
||||||
@ -195,52 +174,6 @@ class MainApp:
|
|||||||
## Ensure display is always correct with image
|
## Ensure display is always correct with image
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def addOutput(self, data, name: str):
|
|
||||||
"""
|
|
||||||
Add CV2 image to canvas output
|
|
||||||
"""
|
|
||||||
self.output[0].append(data)
|
|
||||||
self.output[1].append(name)
|
|
||||||
|
|
||||||
def drawOutput(self, size):
|
|
||||||
# Check if size of canvas has updated
|
|
||||||
drawW = self.canvas.winfo_width()
|
|
||||||
|
|
||||||
# Reset drawing position
|
|
||||||
drawX = 0
|
|
||||||
drawY = 0
|
|
||||||
|
|
||||||
# Clear previously printed images
|
|
||||||
self.tk_imgs = []
|
|
||||||
|
|
||||||
self.meta.config(state=tkinter.NORMAL)
|
|
||||||
self.meta.delete(1.0, tkinter.END)
|
|
||||||
self.meta.insert(tkinter.END, f"{self.img_name}\n")
|
|
||||||
|
|
||||||
# Draw all output images
|
|
||||||
for idx, data in enumerate(self.output[0]):
|
|
||||||
# Create ui image
|
|
||||||
tk_img = cv2.cvtColor(data, cv2.COLOR_BGR2RGB)
|
|
||||||
tk_img = ImageTk.PhotoImage(image=Image.fromarray(tk_img))
|
|
||||||
self.tk_imgs.append(tk_img)
|
|
||||||
|
|
||||||
## Check if next item will be out of range
|
|
||||||
if drawX + size >= drawW:
|
|
||||||
drawY += size
|
|
||||||
drawX = 0
|
|
||||||
self.canvas.configure(height=(drawY + size))
|
|
||||||
|
|
||||||
self.canvas.create_image(
|
|
||||||
drawX, drawY, anchor=tkinter.NW, image=self.tk_imgs[idx], tags="og"
|
|
||||||
)
|
|
||||||
drawX += size
|
|
||||||
|
|
||||||
# Add name to text box
|
|
||||||
self.meta.insert(tkinter.END, f"{idx}: {self.output[1][idx]}\n")
|
|
||||||
|
|
||||||
# Clear output
|
|
||||||
self.meta.config(state=tkinter.DISABLED)
|
|
||||||
|
|
||||||
def createPlot(self, columns, rows):
|
def createPlot(self, columns, rows):
|
||||||
fig, axs = plt.subplots(columns, rows)
|
fig, axs = plt.subplots(columns, rows)
|
||||||
return axs
|
return axs
|
||||||
@ -284,7 +217,7 @@ class MainApp:
|
|||||||
area = sum(func)
|
area = sum(func)
|
||||||
|
|
||||||
self.axs[column, row - 1].clear()
|
self.axs[column, row - 1].clear()
|
||||||
self.axs[column, row - 1].title.set_text(F"Area: {area}")
|
self.axs[column, row - 1].title.set_text(f"Area: {area}")
|
||||||
self.axs[column, row - 1].plot(func)
|
self.axs[column, row - 1].plot(func)
|
||||||
self.axs[column, row - 1].plot(diff)
|
self.axs[column, row - 1].plot(diff)
|
||||||
|
|
||||||
@ -299,15 +232,15 @@ class MainApp:
|
|||||||
lambda x, pos: str(x * canny_step)
|
lambda x, pos: str(x * canny_step)
|
||||||
)
|
)
|
||||||
|
|
||||||
log.add("Canny Mean", func.mean())
|
self.log.add("Canny Mean", func.mean())
|
||||||
log.add("Canny Std", func.std())
|
self.log.add("Canny Std", func.std())
|
||||||
log.add("Canny Min", func.min())
|
self.log.add("Canny Min", func.min())
|
||||||
log.add("Canny Max x", np.where(func==func.max())[0][0])
|
self.log.add("Canny Max x", np.where(func == func.max())[0][0])
|
||||||
log.add("Canny Max y", func.max())
|
self.log.add("Canny Max y", func.max())
|
||||||
log.add("Canny Diff max y", diff.max())
|
self.log.add("Canny Diff max y", diff.max())
|
||||||
log.add("Canny Diff min x",np.where(diff==diff.min())[0][0])
|
self.log.add("Canny Diff min x", np.where(diff == diff.min())[0][0])
|
||||||
log.add("Canny Diff min y", diff.min())
|
self.log.add("Canny Diff min y", diff.min())
|
||||||
log.add("Canny Area", area)
|
self.log.add("Canny Area", area)
|
||||||
|
|
||||||
def writeStats(self, img, labels, column, row):
|
def writeStats(self, img, labels, column, row):
|
||||||
mean, std = imgStats(img)
|
mean, std = imgStats(img)
|
||||||
@ -330,8 +263,8 @@ class MainApp:
|
|||||||
)
|
)
|
||||||
|
|
||||||
for idx, label in enumerate(labels):
|
for idx, label in enumerate(labels):
|
||||||
log.add(f"Mean {label}", mean[idx])
|
self.log.add(f"Mean {label}", mean[idx])
|
||||||
log.add(f"Std {label}", std[idx])
|
self.log.add(f"Std {label}", std[idx])
|
||||||
|
|
||||||
def updatePath(self):
|
def updatePath(self):
|
||||||
"""
|
"""
|
||||||
@ -357,7 +290,7 @@ class MainApp:
|
|||||||
def update(self, event=None, part_update=None):
|
def update(self, event=None, part_update=None):
|
||||||
## Check if hist and canny hm have to be rerendered
|
## Check if hist and canny hm have to be rerendered
|
||||||
if (
|
if (
|
||||||
part_update == None
|
part_update is None
|
||||||
): ## If partial update has not been forced, check if full update is required
|
): ## 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:
|
if self.img_current != self.img_old or self.img_size != self.img_size_old:
|
||||||
part_update = False
|
part_update = False
|
||||||
@ -372,7 +305,10 @@ class MainApp:
|
|||||||
print("Full update forced!")
|
print("Full update forced!")
|
||||||
|
|
||||||
if self.updatePath():
|
if self.updatePath():
|
||||||
log.add("Tree", self.img_name.split("_")[0])
|
print(C_DBUG, F"Processing {self.img_name}")
|
||||||
|
|
||||||
|
self.mainwindow.title(F"{TITLE} - {self.img_name}")
|
||||||
|
self.log.add("Tree", self.img_name.split("_")[0])
|
||||||
|
|
||||||
# Get all user vars
|
# Get all user vars
|
||||||
ct1 = self.canny_thr1.get()
|
ct1 = self.canny_thr1.get()
|
||||||
@ -383,17 +319,17 @@ class MainApp:
|
|||||||
bright = self.brightness.get()
|
bright = self.brightness.get()
|
||||||
|
|
||||||
# Clear output
|
# Clear output
|
||||||
self.output = [[] for x in range(2)]
|
self.canvas.clear()
|
||||||
|
|
||||||
# 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.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.addOutput(img, "Original")
|
self.canvas.add(img, "Original")
|
||||||
|
|
||||||
# Set grayscale
|
# Set grayscale
|
||||||
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||||
self.addOutput(img_gray, "Grayscale")
|
self.canvas.add(img_gray, "Grayscale")
|
||||||
|
|
||||||
# Contrast / brightness boost
|
# Contrast / brightness boost
|
||||||
contrast_val = contrast / 100
|
contrast_val = contrast / 100
|
||||||
@ -401,12 +337,12 @@ class MainApp:
|
|||||||
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.addOutput(img_contrast, F"Contrast / Brightness\n c+{contrast_val} b+{bright_val}")
|
# self.canvas.add(img_contrast, F"Contrast / Brightness\n c+{contrast_val} b+{bright_val}")
|
||||||
self.addOutput(img_contrast, f"BCG")
|
self.canvas.add(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.addOutput(img_blur, "Blurred_k3")
|
self.canvas.add(img_blur, "Blurred_k3")
|
||||||
|
|
||||||
# Sobel edge
|
# Sobel edge
|
||||||
if sxy in ["x", "y", "both"]:
|
if sxy in ["x", "y", "both"]:
|
||||||
@ -426,17 +362,17 @@ class MainApp:
|
|||||||
else:
|
else:
|
||||||
img_sobel = img_gray
|
img_sobel = img_gray
|
||||||
|
|
||||||
self.addOutput(img_sobel, "Sobel_edge")
|
self.canvas.add(img_sobel, "Sobel_edge")
|
||||||
log.add("Sobel nonzero", cv2.countNonZero(img_sobel))
|
self.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.addOutput(img_canny, "Canny_edge")
|
self.canvas.add(img_canny, "Canny_edge")
|
||||||
|
|
||||||
# BGR
|
# BGR
|
||||||
self.addOutput(img[:, :, 0], "BGR_B")
|
self.canvas.add(img[:, :, 0], "BGR_B")
|
||||||
self.addOutput(img[:, :, 1], "BGR_G")
|
self.canvas.add(img[:, :, 1], "BGR_G")
|
||||||
self.addOutput(img[:, :, 2], "BGR_R")
|
self.canvas.add(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)
|
||||||
@ -444,10 +380,10 @@ class MainApp:
|
|||||||
|
|
||||||
# HSV
|
# HSV
|
||||||
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
||||||
self.addOutput(img_hsv, "HSV")
|
self.canvas.add(img_hsv, "HSV")
|
||||||
self.addOutput(img_hsv[:, :, 0], "HSV_H") # H
|
self.canvas.add(img_hsv[:, :, 0], "HSV_H") # H
|
||||||
self.addOutput(img_hsv[:, :, 1], "HSV_S") # S
|
self.canvas.add(img_hsv[:, :, 1], "HSV_S") # S
|
||||||
self.addOutput(img_hsv[:, :, 2], "HSV_V") # V
|
self.canvas.add(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:
|
||||||
@ -460,14 +396,15 @@ class MainApp:
|
|||||||
|
|
||||||
# Write results to CSV file
|
# Write results to CSV file
|
||||||
if not part_update:
|
if not part_update:
|
||||||
log.update()
|
self.log.update()
|
||||||
else:
|
else:
|
||||||
log.clear() # Prevent partial updates from breaking log
|
self.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.drawOutput(size) ## Images
|
self.canvas.draw(size) ## Images
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = MainApp()
|
app = CVSuite()
|
||||||
app.run()
|
app.run()
|
||||||
|
Loading…
Reference in New Issue
Block a user