Merge branch 'main' of https://arnweb.nl/gitea/arne/EV5_Beeldherk_Bomen
59
.gitignore
vendored
@ -1 +1,58 @@
|
|||||||
out
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
bin/
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
out/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
|
||||||
|
# Mr Developer
|
||||||
|
.mr.developer.cfg
|
||||||
|
.project
|
||||||
|
.pydevproject
|
||||||
|
|
||||||
|
# Rope
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# Temp
|
||||||
|
.venv/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Config file
|
||||||
|
src/config/config.json
|
18
md/nuts.md
@ -1,18 +0,0 @@
|
|||||||
### Jullie gaan noten herkennen
|
|
||||||
|
|
||||||
Hoe de neuk? Nut.
|
|
||||||
1. Hoe gaan we de noten herkennen (beeld, smaak, geluid, text)?
|
|
||||||
- Beeldherkenning lol
|
|
||||||
2. Wat zijn de visuele kenmerken van de noten?
|
|
||||||
- Vorm, kleur, textuur, grootte
|
|
||||||
3. Hoeveel noten willen we herkennen
|
|
||||||
- Qua type, hoeveel verschillende noten
|
|
||||||
- Qua tijd, hoeveel noten tegelijkertijd
|
|
||||||
4. Wat is de achtergrond
|
|
||||||
5. Met wat voor camera worden de noten bekeken
|
|
||||||
6. Is de noot verwerkt
|
|
||||||
|
|
||||||
De deelvragen moeten sequentieel zijn
|
|
||||||
|
|
||||||
|
|
||||||
Wij moeten presenteren volgende week
|
|
4
src/config/config.template.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"path": "",
|
||||||
|
"size": 750
|
||||||
|
}
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 2.7 MiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
@ -5,6 +5,7 @@
|
|||||||
<property name="height">800</property>
|
<property name="height">800</property>
|
||||||
<property name="title" translatable="yes">Tree Recogniser 7000</property>
|
<property name="title" translatable="yes">Tree Recogniser 7000</property>
|
||||||
<property name="width">200</property>
|
<property name="width">200</property>
|
||||||
|
<bind sequence="<Destroy>" handler="on_quit" add="" />
|
||||||
<containerlayout manager="pack">
|
<containerlayout manager="pack">
|
||||||
<property name="propagate">false</property>
|
<property name="propagate">false</property>
|
||||||
</containerlayout>
|
</containerlayout>
|
||||||
@ -175,9 +176,9 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="button1">
|
<object class="ttk.Button" id="prev" named="True">
|
||||||
<property name="text" translatable="yes">Next img</property>
|
<property name="text" translatable="yes">Prev img</property>
|
||||||
<bind sequence="<ButtonPress>" handler="img_next" add="" />
|
<bind sequence="<ButtonPress>" handler="img_prev" add="" />
|
||||||
<layout manager="grid">
|
<layout manager="grid">
|
||||||
<property name="column">1</property>
|
<property name="column">1</property>
|
||||||
<property name="ipadx">10</property>
|
<property name="ipadx">10</property>
|
||||||
@ -186,9 +187,9 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="ttk.Button" id="button2">
|
<object class="ttk.Button" id="next" named="True">
|
||||||
<property name="text" translatable="yes">Prev img</property>
|
<property name="text" translatable="yes">Next img</property>
|
||||||
<bind sequence="<ButtonPress>" handler="img_prev" add="" />
|
<bind sequence="<ButtonPress>" handler="img_next" 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>
|
||||||
@ -213,7 +214,7 @@
|
|||||||
<bind sequence="<ButtonPress>" handler="apply" add="" />
|
<bind sequence="<ButtonPress>" handler="apply" 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">1</property>
|
||||||
<property name="ipadx">0</property>
|
<property name="ipadx">0</property>
|
||||||
<property name="row">6</property>
|
<property name="row">6</property>
|
||||||
</layout>
|
</layout>
|
||||||
@ -238,6 +239,22 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="tk.Text" id="dataset" named="True">
|
||||||
|
<property name="height">10</property>
|
||||||
|
<property name="pady">0</property>
|
||||||
|
<property name="state">disabled</property>
|
||||||
|
<property name="text" translatable="yes">Image metadata should appear here</property>
|
||||||
|
<property name="width">25</property>
|
||||||
|
<property name="wrap">word</property>
|
||||||
|
<layout manager="grid">
|
||||||
|
<property name="column">3</property>
|
||||||
|
<property name="columnspan">1</property>
|
||||||
|
<property name="row">0</property>
|
||||||
|
<property name="rowspan">8</property>
|
||||||
|
</layout>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
165
src/suite.py
@ -8,10 +8,18 @@ import numpy as np
|
|||||||
import cv2
|
import cv2
|
||||||
import time
|
import time
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
import json
|
||||||
|
|
||||||
|
## UI config load
|
||||||
PROJECT_PATH = pathlib.Path(__file__).parent
|
PROJECT_PATH = pathlib.Path(__file__).parent
|
||||||
PROJECT_UI = "./src/gui/main.ui"
|
PROJECT_UI = "./src/helpers/gui/main.ui"
|
||||||
|
|
||||||
|
## Config file load
|
||||||
|
CONFIG_PATH = "./src/config/config.json"
|
||||||
|
config_file = open(CONFIG_PATH)
|
||||||
|
config_json = json.load(config_file)
|
||||||
|
|
||||||
|
## UI class setup
|
||||||
class MainApp:
|
class MainApp:
|
||||||
def __init__(self, master=None):
|
def __init__(self, master=None):
|
||||||
self.builder = builder = pygubu.Builder()
|
self.builder = builder = pygubu.Builder()
|
||||||
@ -21,38 +29,51 @@ class MainApp:
|
|||||||
# Main widget
|
# Main widget
|
||||||
self.mainwindow = builder.get_object("main", master)
|
self.mainwindow = builder.get_object("main", master)
|
||||||
|
|
||||||
|
# Canvas for output images
|
||||||
self.canvas = builder.get_object("output_canvas")
|
self.canvas = builder.get_object("output_canvas")
|
||||||
self.tk_imgs = []
|
self.tk_imgs = [] # Required or python will forget
|
||||||
|
self.output = [[] for x in range(2)]
|
||||||
|
self.meta = builder.get_object("dataset")
|
||||||
|
|
||||||
self.canny_thr1 = None
|
# Keep track of images in dataset
|
||||||
self.canny_thr2 = None
|
|
||||||
|
|
||||||
self.img_path = None
|
|
||||||
self.img_current = 0
|
self.img_current = 0
|
||||||
self.img_max = 0
|
self.img_max = 0
|
||||||
|
|
||||||
# Plots
|
# Plots
|
||||||
self.axs = self.createPlot(2, 2)
|
self.axs = self.createPlot(2, 2)
|
||||||
|
|
||||||
# UI
|
# UI Variables
|
||||||
|
self.canny_thr1 = None
|
||||||
|
self.canny_thr2 = None
|
||||||
|
self.img_path = None
|
||||||
self.blur_rate = None
|
self.blur_rate = None
|
||||||
self.img_size = None
|
self.img_size = None
|
||||||
self.sobel_select = None
|
self.sobel_select = None
|
||||||
self.export_id = None
|
self.export_id = None
|
||||||
builder.import_variables(self,
|
builder.import_variables(self, ['canny_thr1','canny_thr2','img_path','blur_rate','img_size', 'sobel_select', 'export_id'])
|
||||||
['canny_thr1',
|
|
||||||
'canny_thr2',
|
|
||||||
'img_path',
|
|
||||||
'blur_rate',
|
|
||||||
'img_size',
|
|
||||||
'sobel_select',
|
|
||||||
'export_id'])
|
|
||||||
builder.connect_callbacks(self)
|
builder.connect_callbacks(self)
|
||||||
|
|
||||||
|
# Load values from config after UI has been initialised
|
||||||
|
self.img_path.set(config_json["path"])
|
||||||
|
self.img_size.set(config_json["size"])
|
||||||
|
|
||||||
|
def on_quit(self, event=None):
|
||||||
|
'''
|
||||||
|
Close PLT windows on main app quit
|
||||||
|
'''
|
||||||
|
plt.close()
|
||||||
|
self.mainwindow.quit();
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
'''
|
||||||
|
Run loop
|
||||||
|
'''
|
||||||
self.mainwindow.mainloop()
|
self.mainwindow.mainloop()
|
||||||
|
|
||||||
def img_prev(self, event=None):
|
def img_prev(self, event=None):
|
||||||
|
'''
|
||||||
|
Open previous image from path
|
||||||
|
'''
|
||||||
if self.img_current == 0:
|
if self.img_current == 0:
|
||||||
self.img_current = self.img_max - 1
|
self.img_current = self.img_max - 1
|
||||||
else:
|
else:
|
||||||
@ -60,6 +81,9 @@ class MainApp:
|
|||||||
self.update(self)
|
self.update(self)
|
||||||
|
|
||||||
def img_next(self, event=None):
|
def img_next(self, event=None):
|
||||||
|
'''
|
||||||
|
Open next image from path
|
||||||
|
'''
|
||||||
if self.img_current == (self.img_max - 1):
|
if self.img_current == (self.img_max - 1):
|
||||||
self.img_current = 0
|
self.img_current = 0
|
||||||
else:
|
else:
|
||||||
@ -67,6 +91,9 @@ class MainApp:
|
|||||||
self.update(self)
|
self.update(self)
|
||||||
|
|
||||||
def apply(self, event=None):
|
def apply(self, event=None):
|
||||||
|
'''
|
||||||
|
Export current dataset
|
||||||
|
'''
|
||||||
img_arr = self.tk_imgs
|
img_arr = self.tk_imgs
|
||||||
img_id = self.export_id.get()
|
img_id = self.export_id.get()
|
||||||
|
|
||||||
@ -75,6 +102,54 @@ class MainApp:
|
|||||||
else:
|
else:
|
||||||
print("Nothing to export!")
|
print("Nothing to export!")
|
||||||
|
|
||||||
|
def add_output(self, data, name: str):
|
||||||
|
'''
|
||||||
|
Add CV2 image to canvas output
|
||||||
|
'''
|
||||||
|
self.output[0].append(data)
|
||||||
|
self.output[1].append(name)
|
||||||
|
|
||||||
|
def draw_output(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=NORMAL)
|
||||||
|
self.meta.delete(1.0, END)
|
||||||
|
|
||||||
|
# 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=NW,image=self.tk_imgs[idx],tags="og")
|
||||||
|
drawX += size
|
||||||
|
|
||||||
|
# Add name to text box
|
||||||
|
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
|
||||||
|
# TODO IDK volgens mij moet je deze wel callen maar het programma doet het nog (geen vragen stellen)
|
||||||
|
# self.canvas.draw()
|
||||||
|
|
||||||
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
|
||||||
@ -120,11 +195,6 @@ class MainApp:
|
|||||||
|
|
||||||
def update(self, event=None):
|
def update(self, event=None):
|
||||||
path = self.img_path.get()
|
path = self.img_path.get()
|
||||||
print(path)
|
|
||||||
|
|
||||||
drawW = self.canvas.winfo_width()
|
|
||||||
drawX = 0
|
|
||||||
drawY = 0
|
|
||||||
|
|
||||||
if path != None and path != "":
|
if path != None and path != "":
|
||||||
# Get all images at current path
|
# Get all images at current path
|
||||||
@ -141,31 +211,24 @@ class MainApp:
|
|||||||
sxy = self.sobel_select.get()
|
sxy = self.sobel_select.get()
|
||||||
size = self.img_size.get()
|
size = self.img_size.get()
|
||||||
|
|
||||||
# Keep array of cv2 images to output
|
|
||||||
output = []
|
|
||||||
|
|
||||||
# Clear previously printed images
|
|
||||||
self.tk_imgs = []
|
|
||||||
|
|
||||||
# Import and resize image
|
# Import and resize image
|
||||||
img = cv2.imread(images[self.img_current])
|
img = cv2.imread(images[self.img_current])
|
||||||
img = cv2.resize(img, (size, size), interpolation = cv2.INTER_AREA)
|
img = cv2.resize(img, (size, size), interpolation = cv2.INTER_AREA)
|
||||||
output.append(img) # First output image is original
|
self.add_output(img, "Original")
|
||||||
|
|
||||||
# Set grayscale
|
# Set grayscale
|
||||||
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||||
output.append(img_gray) # Second output is grayscale
|
self.add_output(img_gray, "Grayscale")
|
||||||
|
|
||||||
# Blurred edition
|
# Blurred edition
|
||||||
if (br == 0):
|
if (br == 0):
|
||||||
# Disable
|
|
||||||
img_blur = img_gray
|
img_blur = img_gray
|
||||||
elif (br % 2 == 1):
|
elif (br % 2 == 1):
|
||||||
img_blur = cv2.GaussianBlur(img_gray, (br, br), 0)
|
img_blur = cv2.GaussianBlur(img_gray, (br, br), 0)
|
||||||
else:
|
else:
|
||||||
print(f"Blur rate changed to {br - 1}")
|
print(f"Blur rate changed to {br - 1}")
|
||||||
img_blur = cv2.GaussianBlur(img_gray, (br-1, br-1), 0)
|
img_blur = cv2.GaussianBlur(img_gray, (br-1, br-1), 0)
|
||||||
output.append(img_blur)
|
self.add_output(img_blur, "Blurred")
|
||||||
|
|
||||||
# Sobel edge
|
# Sobel edge
|
||||||
if sxy in ['x', 'y', 'both']:
|
if sxy in ['x', 'y', 'both']:
|
||||||
@ -182,48 +245,36 @@ class MainApp:
|
|||||||
img_sobel = cv2.Sobel(src=img_blur, ddepth=cv2.CV_8U, dx=dx, dy=dy, ksize=5)
|
img_sobel = cv2.Sobel(src=img_blur, ddepth=cv2.CV_8U, dx=dx, dy=dy, ksize=5)
|
||||||
else:
|
else:
|
||||||
img_sobel = img_gray
|
img_sobel = img_gray
|
||||||
output.append(img_sobel)
|
self.add_output(img_sobel, "Sobel Edge")
|
||||||
|
|
||||||
# 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)
|
||||||
output.append(img_canny)
|
self.add_output(img_canny, "Canny Edge")
|
||||||
|
|
||||||
# BGR
|
# BGR
|
||||||
output.append(img[:, :, 0]) # B
|
self.add_output(img[:, :, 0], "BGR B")
|
||||||
output.append(img[:, :, 1]) # G
|
self.add_output(img[:, :, 1], "BGR G")
|
||||||
output.append(img[:, :, 2]) # R
|
self.add_output(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)
|
||||||
|
|
||||||
# HSV
|
# HSV
|
||||||
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
||||||
output.append(img_hsv)
|
self.add_output(img_hsv, "HSV")
|
||||||
output.append(img_hsv[:, :, 0]) # H
|
self.add_output(img_hsv[:, :, 0], "HSV H") # H
|
||||||
output.append(img_hsv[:, :, 1]) # S
|
self.add_output(img_hsv[:, :, 1], "HSV S") # S
|
||||||
output.append(img_hsv[:, :, 2]) # V
|
self.add_output(img_hsv[:, :, 2], "HSV V") # V
|
||||||
|
|
||||||
if img_hsv is not None:
|
if img_hsv is not None:
|
||||||
self.drawHist(img_hsv, ('H', 'S', 'V'), 0, 1)
|
self.drawHist(img_hsv, ('H', 'S', 'V'), 0, 1)
|
||||||
|
|
||||||
|
# Canny Heatmap
|
||||||
self.drawCannyHM(img, 1, 1)
|
self.drawCannyHM(img, 1, 1)
|
||||||
|
|
||||||
plt.show(block=False)
|
# Show all data
|
||||||
|
plt.show(block=False) ## Graphs
|
||||||
# Draw all output images
|
self.draw_output(size) ## Images
|
||||||
for idx, data in enumerate(output):
|
|
||||||
# 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=NW,image=self.tk_imgs[idx],tags="og")
|
|
||||||
drawX += size
|
|
||||||
self.canvas.draw()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = MainApp()
|
app = MainApp()
|
||||||
|