Add csv logging component

This commit is contained in:
Arne van Iterson 2023-10-01 18:09:26 +02:00
parent 6162f5a3c1
commit a41d57f451
2 changed files with 237 additions and 135 deletions

47
src/helpers/logger.py Normal file
View File

@ -0,0 +1,47 @@
import pathlib
import datetime
now = datetime.datetime.now()
class Logger:
def __init__(self, path):
self.fileName = pathlib.Path(
path, f"result-{now.strftime('%Y-%m-%dT%H.%M.%S')}.csv"
)
self.file = open(self.fileName, "x")
self.first = True
self.index = []
self.data = []
def add(self, name: str, value):
self.index.append(name)
self.data.append(value)
def csv(self, input):
result = ""
for idx, item in enumerate(input):
result += str(item)
if idx != (len(input) - 1):
result += ", "
result += "\n"
print(result)
return result
def update(self):
if self.first:
self.first = False
self.file.write(self.csv(self.index))
self.file.write(self.csv(self.data))
# Clear data
self.index = []
self.data = []
def __del__(self):
print("Log file closed!")
self.file.close()

View File

@ -9,11 +9,13 @@ import cv2
import time import time
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import json import json
from helpers.statistics import imgStats
import datetime import datetime
import os import os
import copy import copy
from helpers.statistics import imgStats
from helpers.logger import Logger
## 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"
@ -23,6 +25,9 @@ CONFIG_PATH = "./src/config/config.json"
config_file = open(CONFIG_PATH) config_file = open(CONFIG_PATH)
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 MainApp:
def __init__(self, master=None): def __init__(self, master=None):
@ -36,8 +41,8 @@ class MainApp:
# Canvas for output images # Canvas for output images
self.canvas = builder.get_object("output_canvas") self.canvas = builder.get_object("output_canvas")
self.tk_imgs = [] # Required or python will forget self.tk_imgs = [] # Required or python will forget
self.output = [[] for x in range(2)]
self.meta = builder.get_object("dataset") self.meta = builder.get_object("dataset")
self.output = [[] for x in range(2)]
# Keep track of images in dataset # Keep track of images in dataset
self.img_current = 0 self.img_current = 0
@ -60,35 +65,47 @@ class MainApp:
self.sobel_select = None self.sobel_select = None
self.export_id = None self.export_id = None
self.brightness = None self.brightness = None
builder.import_variables(self,['canny_thr1','canny_thr2','img_path','contrast','img_size','sobel_select','export_id','brightness']) builder.import_variables(
self,
[
"canny_thr1",
"canny_thr2",
"img_path",
"contrast",
"img_size",
"sobel_select",
"export_id",
"brightness",
],
)
builder.connect_callbacks(self) builder.connect_callbacks(self)
# Load values from config after UI has been initialised # Load values from config after UI has been initialised
self.img_path.set(config_json["path"]) self.img_path.set(config_json["path"])
self.img_size.set(config_json["size"]) self.img_size.set(config_json["size"])
now = datetime.datetime.now()
self.log = open(pathlib.Path(config_json["out"], F"result-{now.strftime('%Y-%m-%dT%H.%M.%S')}.csv"), "x")
self.log.write("test")
def on_quit(self, event=None): def on_quit(self, event=None):
''' """
Close PLT windows on main app quit Close PLT windows on main app quit
''' """
plt.close() # TODO: This function runs multiple times for some reason
self.log.close()
self.mainwindow.quit(); if plt is not None:
plt.close() # Close graph vies
log.file.close() # Close log files
self.mainwindow.quit() # Close main
def run(self): def run(self):
''' """
Run loop 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 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:
@ -96,9 +113,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 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:
@ -106,28 +123,28 @@ class MainApp:
self.update(self) self.update(self)
def apply(self, event=None, path=None): def apply(self, event=None, path=None):
''' """
Export current dataset Export current dataset
''' """
# Get export settings # Get export settings
img_arr = self.tk_imgs img_arr = self.tk_imgs
img_id = self.export_id.get() img_id = self.export_id.get()
if path == None: if path == None:
path = config_json["out"] path = config_json["out"]
else: else:
print(F"Using path: {path}") print(f"Using path: {path}")
if (img_id >= 0 and img_id < len(img_arr)): if img_id >= 0 and img_id < len(img_arr):
# Create file # Create file
now = datetime.datetime.now() 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" new_file_name = f"{self.img_current}-{self.output[1][img_id]}-{now.strftime('%Y-%m-%dT%H.%M.%S')}.png"
# Put data # Put data
file_path = pathlib.Path(path, new_file_name) file_path = pathlib.Path(path, new_file_name)
# print(file_path) # print(file_path)
imgpil = ImageTk.getimage(self.tk_imgs[img_id]) imgpil = ImageTk.getimage(self.tk_imgs[img_id])
imgpil.save(file_path, "PNG" ) imgpil.save(file_path, "PNG")
imgpil.close() imgpil.close()
print(f"Exported Image ID {img_id} to {os.path.join(path, new_file_name)}") print(f"Exported Image ID {img_id} to {os.path.join(path, new_file_name)}")
@ -135,31 +152,36 @@ class MainApp:
print("Nothing to export!") print("Nothing to export!")
def apply_all(self, event=None): def apply_all(self, event=None):
''' """
Export given preprocess id for every image in the dataset folder Export given preprocess id for every image in the dataset folder
''' """
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)
now = datetime.datetime.now() 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')}/") path = pathlib.Path(
config_json["out"],
f"{self.output[1][img_id]}-all-{now.strftime('%Y-%m-%dT%H.%M.%S')}/",
)
os.mkdir(path) os.mkdir(path)
while True: while True:
self.img_next() self.img_next()
self.update(part_update=True) # Enforce partial update since we don't need the histograms etc. self.update(
part_update=True
) # Enforce partial update since we don't need the histograms etc.
self.apply(path=path) self.apply(path=path)
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 add_output(self, data, name: str): 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)
@ -176,7 +198,7 @@ class MainApp:
self.meta.config(state=NORMAL) self.meta.config(state=NORMAL)
self.meta.delete(1.0, END) self.meta.delete(1.0, END)
self.meta.insert(END, f"{self.img_name[1]}\n") self.meta.insert(END, f"{self.img_name}\n")
# Draw all output images # Draw all output images
for idx, data in enumerate(self.output[0]): for idx, data in enumerate(self.output[0]):
@ -186,31 +208,29 @@ class MainApp:
self.tk_imgs.append(tk_img) self.tk_imgs.append(tk_img)
## Check if next item will be out of range ## Check if next item will be out of range
if (drawX + size >= drawW): if drawX + size >= drawW:
drawY += size drawY += size
drawX = 0 drawX = 0
self.canvas.configure(height=(drawY+size)) self.canvas.configure(height=(drawY + size))
self.canvas.create_image(drawX,drawY,anchor=NW,image=self.tk_imgs[idx],tags="og") self.canvas.create_image(
drawX, drawY, anchor=NW, image=self.tk_imgs[idx], tags="og"
)
drawX += size drawX += size
# Add name to text box # Add name to text box
self.meta.insert(END, F"{idx}: {self.output[1][idx]}\n") self.meta.insert(END, f"{idx}: {self.output[1][idx]}\n")
# Clear output # Clear output
self.meta.config(state=DISABLED) 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
def drawHist(self, image, labels, column, row): def drawHist(self, image, labels, column, row):
self.axs[column, row].clear() self.axs[column, row].clear()
for i,lab in enumerate(labels): for i, lab in enumerate(labels):
hist = cv2.calcHist( hist = cv2.calcHist(
[image], [image],
[i], [i],
@ -243,31 +263,61 @@ class MainApp:
# 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])
func = np.diag(results) func = np.diag(results)
self.axs[column, row-1].clear() self.axs[column, row - 1].clear()
self.axs[column, row-1].title.set_text("Canny F U N C") self.axs[column, row - 1].title.set_text("Canny F U N C")
self.axs[column, row-1].plot(func) self.axs[column, row - 1].plot(func)
self.axs[column, row-1].plot(np.diff(func)) self.axs[column, row - 1].plot(np.diff(func))
self.axs[column, row].title.set_text(F"Mean: {np.matrix(results).mean()}\nStd: {np.matrix(results).std()}") self.axs[column, row].title.set_text(
f"Mean: {np.matrix(results).mean()}\nStd: {np.matrix(results).std()}"
)
self.axs[column, row].imshow(results) 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].xaxis.set_major_formatter(
self.axs[column, row].yaxis.set_major_formatter(lambda x, pos: str(x*canny_step)) lambda x, pos: str(x * canny_step)
)
self.axs[column, row].yaxis.set_major_formatter(
lambda x, pos: str(x * canny_step)
)
log.add("Canny Mean", np.matrix(results).mean())
log.add("Canny Std", np.matrix(results).std())
log.add("Canny Min", np.matrix(results).min())
log.add("Canny Max", np.matrix(results).max())
# log.add("Canny Diff max", np.diff(func))
def writeStats(self, img, labels, column, row): def writeStats(self, img, labels, column, row):
mean, std = imgStats(img) mean, std = imgStats(img)
self.axs[column, row].title.set_text( self.axs[column, row].title.set_text(
"Mean: %c:%d %c:%d %c:%d \nStd: %c:%d %c:%d %c:%d" "Mean: %c:%d %c:%d %c:%d \nStd: %c:%d %c:%d %c:%d"
%(labels[0], mean[0], labels[1], mean[1], labels[2], mean[2], % (
labels[0], std[0], labels[1], std[1], labels[2], std[2])) labels[0],
mean[0],
labels[1],
mean[1],
labels[2],
mean[2],
labels[0],
std[0],
labels[1],
std[1],
labels[2],
std[2],
)
)
for idx, label in enumerate(labels):
log.add(f"Mean {label}", mean[idx])
log.add(f"Std {label}", std[idx])
def update(self, event=None, part_update=False): def update(self, event=None, part_update=False):
path = self.img_path.get() path = self.img_path.get()
## Check if hist and canny hm have to be rerendered ## 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 (
if (self.img_current != self.img_old or self.img_size != self.img_size_old): 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 part_update = False
self.img_old = self.img_current self.img_old = self.img_current
self.img_size_old = self.img_size self.img_size_old = self.img_size
@ -286,7 +336,8 @@ class MainApp:
images.append(file) images.append(file)
self.img_max = len(images) self.img_max = len(images)
self.img_name = os.path.split(images[self.img_current]) self.img_name = os.path.split(images[self.img_current])[1]
log.add("Img", self.img_name)
# Get all user vars # Get all user vars
ct1 = self.canny_thr1.get() ct1 = self.canny_thr1.get()
@ -301,7 +352,7 @@ class MainApp:
# 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)
self.add_output(img, "Original") self.add_output(img, "Original")
# Set grayscale # Set grayscale
@ -311,34 +362,39 @@ class MainApp:
# 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(contrast_val * (img_gray + bright_val), 0, 255).astype(np.uint8) 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") self.add_output(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.add_output(img_blur, "Blurred_k3")
# Sobel edge # Sobel edge
if sxy in ['x', 'y', 'both']: if sxy in ["x", "y", "both"]:
if sxy == 'x': if sxy == "x":
dx = 1 dx = 1
dy = 0 dy = 0
elif sxy == 'y': elif sxy == "y":
dx = 0 dx = 0
dy = 1 dy = 1
elif sxy == 'both': elif sxy == "both":
dx = 1 dx = 1
dy = 1 dy = 1
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
self.add_output(img_sobel, "Sobel_edge") self.add_output(img_sobel, "Sobel_edge")
# self.add_output(img_sobel, F"Sobel Edge\n nz={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.add_output(img_canny, "Canny_edge")
# BGR # BGR
@ -347,8 +403,8 @@ class MainApp:
self.add_output(img[:, :, 2], "BGR_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)
self.writeStats(img, ('B', 'G', 'R'), 0, 0) self.writeStats(img, ("B", "G", "R"), 0, 0)
# HSV # HSV
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
@ -359,8 +415,8 @@ class MainApp:
if not part_update: if not part_update:
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)
self.writeStats(img_hsv, ('H', 'S', 'V'), 0, 1) self.writeStats(img_hsv, ("H", "S", "V"), 0, 1)
# Canny Heatmap # Canny Heatmap
if not part_update: if not part_update:
@ -368,14 +424,13 @@ class MainApp:
# Write results to CSV file # Write results to CSV file
if not part_update: if not part_update:
# if self.log. log.update()
# Only do this if the image has changed
self.log.write(F"{self.img_name[1]}, test\n")
# Show all data # Show all data
plt.show(block=False) ## Graphs plt.show(block=False) ## Graphs
self.draw_output(size) ## Images self.draw_output(size) ## Images
if __name__ == "__main__": if __name__ == "__main__":
app = MainApp() app = MainApp()
app.run() app.run()