ColonyCounter/window.py

226 lines
8.1 KiB
Python

from copy import deepcopy
import tkinter as tk
from os.path import join
from PIL import Image, ImageTk
from cv2 import convertScaleAbs, cvtColor, COLOR_BGR2RGB
from json import load, dump
from fileutils import dslice, summarize, DIROUT
IMAGES = []
CURIMINDEX = 0
CURQUEUE = {}
WIDTH = 1028
HEIGHT = 1080
def init(imgList):
global CURQUEUE
IMAGES.extend(imgList)
CURQUEUE = deepcopy(IMAGES[CURIMINDEX].opqueue)
loadCMPImage()
loadImage()
def convertImage(img):
# Currently only expect grayscale and RGB images
if img.dtype == "uint16":
img = convertScaleAbs(img, alpha=255.0/65535.0)
if len(img.shape) > 2:
img = cvtColor(img, COLOR_BGR2RGB)
img = Image.fromarray(img)
return ImageTk.PhotoImage(img)
def loadImage(event=None):
IMAGES[CURIMINDEX].apply(CURQUEUE)
label = IMAGES[CURIMINDEX].__repr__()
img = convertImage(IMAGES[CURIMINDEX].data)
IMLABEL.configure(text=label)
IMLABEL.text = label
CURIMAGE.configure(image=img)
CURIMAGE.image = img
def loadCMPImage(event=None):
IMAGES[CURIMINDEX].apply(dslice(CURQUEUE, 0, int(S_CMPIMAGE.get())))
img = convertImage(IMAGES[CURIMINDEX].data)
CMPIMAGE.configure(image=img)
CMPIMAGE.image = img
def loadImageNext(event=None):
global CURIMINDEX
CURIMINDEX = (CURIMINDEX+1) % (len(IMAGES))
reset()
loadCMPImage()
def loadImagePrevious(event=None):
global CURIMINDEX
CURIMINDEX = (CURIMINDEX-1) % (len(IMAGES))
reset()
loadCMPImage()
def reset(event=None):
global CURQUEUE
CURQUEUE = deepcopy(IMAGES[CURIMINDEX].opqueue)
if "wshed" in CURQUEUE:
S_BLURSIZE.set(CURQUEUE["wshed"][2]["blur"])
S_THRESH.set(CURQUEUE["wshed"][2]["min_thresh"])
S_SIZE.set(CURQUEUE["wshed"][2]["min_size"])
S_DISTANCE.set(CURQUEUE["wshed"][2]["dist"])
S_MBRIGHT.set(CURQUEUE["wshed"][2]["min_mean_brightness"])
S_ROUND.set(CURQUEUE["wshed"][2]["min_roundness"])
loadImage()
def apply(event=None):
IMAGES[CURIMINDEX].opqueue = deepcopy(CURQUEUE)
def set_blursize(ksize):
if "wshed" in CURQUEUE:
CURQUEUE["wshed"][2]["blur"] = int(ksize)-1
loadImage()
def set_min_thresh(min_thresh):
if "wshed" in CURQUEUE:
CURQUEUE["wshed"][2]["min_thresh"] = int(min_thresh)
loadImage()
def set_min_size(min_size):
if "wshed" in CURQUEUE:
CURQUEUE["wshed"][2]["min_size"] = int(min_size)
loadImage()
def set_distance(dist):
if "wshed" in CURQUEUE:
CURQUEUE["wshed"][2]["dist"] = int(dist)
loadImage()
def set_min_mean_brightness(min_mean_brightness):
if "wshed" in CURQUEUE:
CURQUEUE["wshed"][2]["min_mean_brightness"] = int(min_mean_brightness)
loadImage()
def set_min_roundness(min_roundness):
if "wshed" in CURQUEUE:
CURQUEUE["wshed"][2]["min_roundness"] = float(min_roundness)
loadImage()
def set_comp(qslice):
loadCMPImage()
def add_pos(event):
if "wshed" in CURQUEUE:
CURQUEUE["wshed"][2]["ignore"].append((event.x, event.y))
loadImage()
def remove_pos(event):
if "wshed" in CURQUEUE:
# print(f"box_x: {event.x-5}, {event.x+5}")
# print(f"box_y: {event.y-5}, {event.y+5}")
CURQUEUE["wshed"][2]["ignore"] = [
pos for pos in
CURQUEUE["wshed"][2]["ignore"]
if (((event.x-15) < pos[0] < (event.x+15)) is False)
and (((event.y-15) < pos[1] < (event.y+15)) is False)
]
loadImage()
def clear_pos(event):
if "wshed" in CURQUEUE:
CURQUEUE["wshed"][2]["ignore"] = []
loadImage()
def export(event=None):
[(f.apply(f.opqueue), f.save(True, f.opqueue)) for f in IMAGES]
summary = summarize([f.meta for f in IMAGES])
summary.to_csv(join(DIROUT, "summary.csv"), index=False)
summary.groupby(["Hour", "Culture"]).mean().round(4).to_csv(join(DIROUT, "summary_mean.csv"))
summary.groupby(["Hour", "Culture"]).std().round(4).to_csv(join(DIROUT, "summary_std.csv"))
def settings_load(event=None):
with open("imageConfig.json", "r") as f:
settings = load(f)
for (n, i) in enumerate(IMAGES):
if repr(i) in settings:
[
IMAGES[n].opqueue.__setitem__(k,
[i.opqueue[k][0]] +
settings[repr(i)][k]
)
for k in i.opqueue
if k in settings[repr(i)]
]
reset()
def settings_save(event=None):
with open("imageConfig.json", "w") as f:
dump(dict([[repr(i), dict([[o, i.opqueue[o][1:]] for o in i.opqueue])] for i in IMAGES]), f)
def run():
ROOT.mainloop()
ROOT = tk.Tk()
ROOT.wm_title("FP Analysis")
ROOT.geometry(f"{WIDTH}x{HEIGHT}")
ROOT.rowconfigure(0, weight=3)
ROOT.rowconfigure(1, weight=10)
ROOT.rowconfigure(2, weight=1)
ROOT.bind("<Left>", loadImagePrevious)
ROOT.bind("<Right>", loadImageNext)
ROOT.bind("q", loadImagePrevious)
ROOT.bind("w", loadImageNext)
ROOT.bind("a", apply)
ROOT.bind("r", reset)
ROOT.bind("s", settings_save)
ROOT.bind("l", settings_load)
ROOT.bind("e", export)
ROOT.bind("c", clear_pos)
F_SLIDERS = tk.Frame(ROOT, width=WIDTH, height=50)
F_SLIDERS.grid(row=0)
F_IMAGE = tk.Canvas(ROOT, width=WIDTH, height=500)
F_IMAGE.grid(row=1)
F_IMAGE.rowconfigure(0, weight=0)
F_IMAGE.rowconfigure(1, weight=10)
F_BUTTONS = tk.Frame(ROOT, width=WIDTH, height=50)
F_BUTTONS.grid(row=2)
S_BLURSIZE = tk.Scale(F_SLIDERS, label='Blur Size', from_=0, to=255, orient=tk.HORIZONTAL, length=WIDTH-10, showvalue=True, tickinterval=25, resolution=2, command=set_blursize)
S_BLURSIZE.grid(row=0, column=0, sticky="N")
S_THRESH = tk.Scale(F_SLIDERS, label='Min Threshold', from_=0, to=255, orient=tk.HORIZONTAL, length=WIDTH-10, showvalue=True, tickinterval=25, resolution=1, command=set_min_thresh)
S_THRESH.grid(row=1, column=0, sticky="N")
S_DISTANCE = tk.Scale(F_SLIDERS, label='Distance Transform', from_=0, to=50, orient=tk.HORIZONTAL, length=WIDTH-10, showvalue=True, tickinterval=10, resolution=1, command=set_distance)
S_DISTANCE.grid(row=2, column=0, sticky="N")
S_SIZE = tk.Scale(F_SLIDERS, label='Filter: Min Size', from_=0, to=500, orient=tk.HORIZONTAL, length=WIDTH-10, showvalue=True, tickinterval=25, resolution=1, command=set_min_size)
S_SIZE.grid(row=3, column=0, sticky="N")
S_MBRIGHT = tk.Scale(F_SLIDERS, label='Filter: Min Mean Brightness', from_=0, to=255, orient=tk.HORIZONTAL, length=WIDTH-10, showvalue=True, tickinterval=25, resolution=1, command=set_min_mean_brightness)
S_MBRIGHT.grid(row=4, column=0, sticky="N")
S_ROUND = tk.Scale(F_SLIDERS, label='Filter: Min Roundness', from_=0, to=1, orient=tk.HORIZONTAL, length=WIDTH-10, showvalue=True, tickinterval=0.1, resolution=0.05, command=set_min_roundness)
S_ROUND.grid(row=5, column=0, sticky="N")
S_CMPIMAGE = tk.Scale(F_IMAGE, label=None, from_=0, to=4, orient=tk.HORIZONTAL, length=WIDTH/2-10, showvalue=False, tickinterval=1, resolution=1, command=set_comp)
S_CMPIMAGE.grid(row=0, column=0, sticky="N")
IMLABEL = tk.Label(F_IMAGE, text="", height=1)
IMLABEL.grid(row=0, column=1)
CURIMAGE = tk.Label(F_IMAGE, image=None)
CURIMAGE.grid(row=1, column=1)
CMPIMAGE = tk.Label(F_IMAGE, image=None)
CMPIMAGE.grid(row=1, column=0)
CURIMAGE.bind("<Button 1>", add_pos)
CURIMAGE.bind("<Button 3>", remove_pos)
CURIMAGE.bind("<Button-1>", add_pos)
CURIMAGE.bind("<Button-3>", remove_pos)
B_RESET = tk.Button(F_BUTTONS, text="Reset", command=reset)
B_RESET.grid(row=0, column=0, sticky="W")
B_APPLY = tk.Button(F_BUTTONS, text="Apply", command=apply)
B_APPLY.grid(row=0, column=1, sticky="E")
B_PREVIOUS = tk.Button(F_BUTTONS, text="Previous", command=loadImagePrevious)
B_PREVIOUS.grid(row=0, column=2, sticky="E")
B_NEXT = tk.Button(F_BUTTONS, text="Next", command=loadImageNext)
B_NEXT.grid(row=0, column=3, sticky="E")
B_SLOAD = tk.Button(F_BUTTONS, text="Load Settings", command=settings_load)
B_SLOAD.grid(row=0, column=4, sticky="E")
B_SSAVE = tk.Button(F_BUTTONS, text="Save Settings", command=settings_save)
B_SSAVE.grid(row=0, column=5, sticky="E")
B_EXPORT = tk.Button(F_BUTTONS, text="Export Images", command=export)
B_EXPORT.grid(row=0, column=6, sticky="E")