|
import os |
|
|
|
import math |
|
import warnings |
|
|
|
from collections import OrderedDict |
|
|
|
|
|
from PIL import Image, ImageTk |
|
|
|
dataset_paths = ['vimeo_test', 'snu_film_easy', 'snu_film_medium', 'snu_film_hard', 'snu_film_extreme', |
|
'xiph_cropped-4k', 'xiph_resized-2k'] |
|
|
|
|
|
class AutoScrollbar(ttk.Scrollbar): |
|
""" A scrollbar that hides itself if it's not needed. Works only for grid geometry manager """ |
|
|
|
def set(self, lo, hi): |
|
if float(lo) <= 0.0 and float(hi) >= 1.0: |
|
self.grid_remove() |
|
else: |
|
self.grid() |
|
ttk.Scrollbar.set(self, lo, hi) |
|
|
|
def pack(self, **kw): |
|
raise tk.TclError('Cannot use pack with the widget ' + self.__class__.__name__) |
|
|
|
def place(self, **kw): |
|
raise tk.TclError('Cannot use place with the widget ' + self.__class__.__name__) |
|
|
|
|
|
class CanvasImage: |
|
""" Display and zoom image """ |
|
|
|
def __init__(self, placeholder, path, row, column, width, height): |
|
""" Initialize the ImageFrame """ |
|
self.imscale = 1.0 |
|
self.__delta = 1.3 |
|
self.__filter = Image.Resampling.BICUBIC |
|
self.__previous_state = 0 |
|
self.path = path |
|
|
|
self.__imframe = placeholder |
|
|
|
hbar = AutoScrollbar(self.__imframe, orient='horizontal') |
|
vbar = AutoScrollbar(self.__imframe, orient='vertical') |
|
hbar.grid(row=row + 1, column=column, columnspan=2, sticky='we') |
|
vbar.grid(row=row, column=column + 2, sticky='ns') |
|
|
|
with warnings.catch_warnings(): |
|
warnings.simplefilter('ignore') |
|
self.__image = Image.open(self.path) |
|
self.imwidth, self.imheight = self.__image.size |
|
self.canvas = tk.Canvas(self.__imframe, highlightthickness=0, |
|
xscrollcommand=hbar.set, yscrollcommand=vbar.set, |
|
width=width, height=height) |
|
self.canvas.grid(row=row, column=column, columnspan=2, sticky='nswe') |
|
self.canvas.update() |
|
hbar.configure(command=self.__scroll_x) |
|
vbar.configure(command=self.__scroll_y) |
|
|
|
self.canvas.bind('<Configure>', lambda event: self.__show_image()) |
|
self.canvas.bind('<ButtonPress-1>', self.__move_from) |
|
self.canvas.bind('<ButtonPress-2>', self.__default) |
|
self.canvas.bind('<B1-Motion>', self.__move_to) |
|
self.canvas.bind('<MouseWheel>', self.__wheel) |
|
self.canvas.bind('<Button-5>', self.__wheel) |
|
self.canvas.bind('<Button-4>', self.__wheel) |
|
|
|
|
|
self.canvas.bind('<Key>', lambda event: self.canvas.after_idle(self.__keystroke, event)) |
|
|
|
self.__huge = False |
|
self.__huge_size = 14000 |
|
self.__band_width = 1024 |
|
Image.MAX_IMAGE_PIXELS = 1000000000 |
|
if self.imwidth * self.imheight > self.__huge_size * self.__huge_size and \ |
|
self.__image.tile[0][0] == 'raw': |
|
self.__huge = True |
|
self.__offset = self.__image.tile[0][2] |
|
self.__tile = [self.__image.tile[0][0], |
|
[0, 0, self.imwidth, 0], |
|
self.__offset, |
|
self.__image.tile[0][3]] |
|
self.__min_side = min(self.imwidth, self.imheight) |
|
|
|
self.__pyramid = [self.smaller()] if self.__huge else [Image.open(self.path)] |
|
|
|
self.__ratio = max(self.imwidth, self.imheight) / self.__huge_size if self.__huge else 1.0 |
|
self.__curr_img = 0 |
|
self.__scale = self.imscale * self.__ratio |
|
self.__reduction = 2 |
|
w, h = self.__pyramid[-1].size |
|
while w > 512 and h > 512: |
|
w /= self.__reduction |
|
h /= self.__reduction |
|
self.__pyramid.append(self.__pyramid[-1].resize((int(w), int(h)), self.__filter)) |
|
|
|
self.container = self.canvas.create_rectangle((0, 0, self.imwidth, self.imheight), width=0) |
|
self.__default() |
|
self.canvas.focus_set() |
|
|
|
def smaller(self): |
|
""" Resize image proportionally and return smaller image """ |
|
w1, h1 = float(self.imwidth), float(self.imheight) |
|
w2, h2 = float(self.__huge_size), float(self.__huge_size) |
|
aspect_ratio1 = w1 / h1 |
|
aspect_ratio2 = w2 / h2 |
|
if aspect_ratio1 == aspect_ratio2: |
|
image = Image.new('RGB', (int(w2), int(h2))) |
|
k = h2 / h1 |
|
w = int(w2) |
|
elif aspect_ratio1 > aspect_ratio2: |
|
image = Image.new('RGB', (int(w2), int(w2 / aspect_ratio1))) |
|
k = h2 / w1 |
|
w = int(w2) |
|
else: |
|
image = Image.new('RGB', (int(h2 * aspect_ratio1), int(h2))) |
|
k = h2 / h1 |
|
w = int(h2 * aspect_ratio1) |
|
i, j, n = 0, 1, round(0.5 + self.imheight / self.__band_width) |
|
while i < self.imheight: |
|
print('\rOpening image: {j} from {n}'.format(j=j, n=n), end='') |
|
band = min(self.__band_width, self.imheight - i) |
|
self.__tile[1][3] = band |
|
self.__tile[2] = self.__offset + self.imwidth * i * 3 |
|
self.__image.close() |
|
self.__image = Image.open(self.path) |
|
self.__image.size = (self.imwidth, band) |
|
self.__image.tile = [self.__tile] |
|
cropped = self.__image.crop((0, 0, self.imwidth, band)) |
|
image.paste(cropped.resize((w, int(band * k) + 1), self.__filter), (0, int(i * k))) |
|
i += band |
|
j += 1 |
|
print('\r' + 30 * ' ' + '\r', end='') |
|
return image |
|
|
|
def redraw_figures(self): |
|
""" Dummy function to redraw figures in the children classes """ |
|
pass |
|
|
|
def __default(self, *args, **kw): |
|
self.imscale = min(self.canvas.winfo_height() / self.imheight, self.canvas.winfo_width() / self.imwidth) |
|
k = self.imscale * self.__ratio |
|
self.__curr_img = min((-1) * int(math.log(k, self.__reduction)), len(self.__pyramid) - 1) |
|
self.__scale = k * math.pow(self.__reduction, max(0, self.__curr_img)) |
|
x, y = 0, (self.canvas.winfo_height() - self.imscale * self.imheight) / 2 |
|
self.canvas.scale('all', x, y, self.imscale, self.imscale) |
|
|
|
self.redraw_figures() |
|
self.__show_image() |
|
|
|
def grid(self, **kw): |
|
""" Put CanvasImage widget on the parent widget """ |
|
self.__imframe.grid(**kw) |
|
self.__imframe.grid(sticky='nswe') |
|
self.__imframe.rowconfigure(0, weight=1) |
|
self.__imframe.columnconfigure(0, weight=1) |
|
|
|
def pack(self, **kw): |
|
""" Exception: cannot use pack with this widget """ |
|
raise Exception('Cannot use pack with the widget ' + self.__class__.__name__) |
|
|
|
def place(self, **kw): |
|
""" Exception: cannot use place with this widget """ |
|
raise Exception('Cannot use place with the widget ' + self.__class__.__name__) |
|
|
|
|
|
def __scroll_x(self, *args, **kwargs): |
|
""" Scroll canvas horizontally and redraw the image """ |
|
self.canvas.xview(*args) |
|
self.__show_image() |
|
|
|
|
|
def __scroll_y(self, *args, **kwargs): |
|
""" Scroll canvas vertically and redraw the image """ |
|
self.canvas.yview(*args) |
|
self.__show_image() |
|
|
|
def __show_image(self): |
|
""" Show image on the Canvas. Implements correct image zoom almost like in Google Maps """ |
|
box_image = self.canvas.coords(self.container) |
|
box_canvas = (self.canvas.canvasx(0), |
|
self.canvas.canvasy(0), |
|
self.canvas.canvasx(self.canvas.winfo_width()), |
|
self.canvas.canvasy(self.canvas.winfo_height())) |
|
box_img_int = tuple(map(int, box_image)) |
|
|
|
box_scroll = [min(box_img_int[0], box_canvas[0]), min(box_img_int[1], box_canvas[1]), |
|
max(box_img_int[2], box_canvas[2]), max(box_img_int[3], box_canvas[3])] |
|
|
|
if box_scroll[0] == box_canvas[0] and box_scroll[2] == box_canvas[2]: |
|
box_scroll[0] = box_img_int[0] |
|
box_scroll[2] = box_img_int[2] |
|
|
|
if box_scroll[1] == box_canvas[1] and box_scroll[3] == box_canvas[3]: |
|
box_scroll[1] = box_img_int[1] |
|
box_scroll[3] = box_img_int[3] |
|
|
|
self.canvas.configure(scrollregion=tuple(map(int, box_scroll))) |
|
x1 = max(box_canvas[0] - box_image[0], 0) |
|
y1 = max(box_canvas[1] - box_image[1], 0) |
|
x2 = min(box_canvas[2], box_image[2]) - box_image[0] |
|
y2 = min(box_canvas[3], box_image[3]) - box_image[1] |
|
if int(x2 - x1) > 0 and int(y2 - y1) > 0: |
|
if self.__huge and self.__curr_img < 0: |
|
h = int((y2 - y1) / self.imscale) |
|
self.__tile[1][3] = h |
|
self.__tile[2] = self.__offset + self.imwidth * int(y1 / self.imscale) * 3 |
|
self.__image.close() |
|
self.__image = Image.open(self.path) |
|
self.__image.size = (self.imwidth, h) |
|
self.__image.tile = [self.__tile] |
|
image = self.__image.crop((int(x1 / self.imscale), 0, int(x2 / self.imscale), h)) |
|
else: |
|
image = self.__pyramid[max(0, self.__curr_img)].crop( |
|
(int(x1 / self.__scale), int(y1 / self.__scale), |
|
int(x2 / self.__scale), int(y2 / self.__scale))) |
|
|
|
imagetk = ImageTk.PhotoImage(image.resize((int(x2 - x1), int(y2 - y1)), self.__filter)) |
|
imageid = self.canvas.create_image(max(box_canvas[0], box_img_int[0]), |
|
max(box_canvas[1], box_img_int[1]), |
|
anchor='nw', image=imagetk) |
|
self.canvas.lower(imageid) |
|
self.canvas.imagetk = imagetk |
|
|
|
def __move_from(self, event): |
|
""" Remember previous coordinates for scrolling with the mouse """ |
|
self.canvas.scan_mark(event.x, event.y) |
|
|
|
def __move_to(self, event): |
|
""" Drag (move) canvas to the new position """ |
|
self.canvas.scan_dragto(event.x, event.y, gain=1) |
|
self.__show_image() |
|
|
|
def outside(self, x, y): |
|
""" Checks if the point (x,y) is outside the image area """ |
|
bbox = self.canvas.coords(self.container) |
|
if bbox[0] < x < bbox[2] and bbox[1] < y < bbox[3]: |
|
return False |
|
else: |
|
return True |
|
|
|
def __wheel(self, event): |
|
""" Zoom with mouse wheel """ |
|
x = self.canvas.canvasx(event.x) |
|
y = self.canvas.canvasy(event.y) |
|
if self.outside(x, y): return |
|
scale = 1.0 |
|
|
|
if event.num == 5 or event.delta == -120: |
|
if round(self.__min_side * self.imscale) < 30: return |
|
self.imscale /= self.__delta |
|
scale /= self.__delta |
|
if event.num == 4 or event.delta == 120: |
|
i = min(self.canvas.winfo_width(), self.canvas.winfo_height()) >> 1 |
|
if i < self.imscale: return |
|
self.imscale *= self.__delta |
|
scale *= self.__delta |
|
|
|
k = self.imscale * self.__ratio |
|
self.__curr_img = min((-1) * int(math.log(k, self.__reduction)), len(self.__pyramid) - 1) |
|
self.__scale = k * math.pow(self.__reduction, max(0, self.__curr_img)) |
|
|
|
self.canvas.scale('all', x, y, scale, scale) |
|
|
|
self.redraw_figures() |
|
self.__show_image() |
|
|
|
def __keystroke(self, event): |
|
""" Scrolling with the keyboard. |
|
Independent from the language of the keyboard, CapsLock, <Ctrl>+<key>, etc. """ |
|
if event.state - self.__previous_state == 4: |
|
pass |
|
else: |
|
self.__previous_state = event.state |
|
|
|
if event.keycode in [68, 39, 102]: |
|
self.__scroll_x('scroll', 1, 'unit', event=event) |
|
elif event.keycode in [65, 37, 100]: |
|
self.__scroll_x('scroll', -1, 'unit', event=event) |
|
elif event.keycode in [87, 38, 104]: |
|
self.__scroll_y('scroll', -1, 'unit', event=event) |
|
elif event.keycode in [83, 40, 98]: |
|
self.__scroll_y('scroll', 1, 'unit', event=event) |
|
|
|
def crop(self, bbox): |
|
""" Crop rectangle from the image and return it """ |
|
if self.__huge: |
|
band = bbox[3] - bbox[1] |
|
self.__tile[1][3] = band |
|
self.__tile[2] = self.__offset + self.imwidth * bbox[1] * 3 |
|
self.__image.close() |
|
self.__image = Image.open(self.path) |
|
self.__image.size = (self.imwidth, band) |
|
self.__image.tile = [self.__tile] |
|
return self.__image.crop((bbox[0], 0, bbox[2], band)) |
|
else: |
|
return self.__pyramid[0].crop(bbox) |
|
|
|
def destroy(self): |
|
""" ImageFrame destructor """ |
|
self.__image.close() |
|
map(lambda i: i.close, self.__pyramid) |
|
del self.__pyramid[:] |
|
del self.__pyramid |
|
self.canvas.destroy() |
|
|
|
|
|
class FirstWindow(ttk.Frame): |
|
def __init__(self, mainframe): |
|
ttk.Frame.__init__(self, master=mainframe) |
|
|
|
|
|
class SampleApp(tk.Tk): |
|
def __init__(self): |
|
tk.Tk.__init__(self) |
|
self.figure_path = '/hdd/spocklabs/save_backup/' |
|
self._exp_frame = None |
|
self._dataset_frame = None |
|
self._figure_frame = None |
|
self.make_exp_frame() |
|
|
|
def make_exp_frame(self): |
|
self.destroy_all() |
|
self._exp_frame = ExpFrame(self) |
|
self._exp_frame.pack() |
|
|
|
def make_dataset_frame(self): |
|
self.destroy_all() |
|
self._dataset_frame = DatsetFrame(self) |
|
self._dataset_frame.pack() |
|
|
|
def make_figure_frame(self): |
|
self.destroy_all() |
|
self._figure_frame = FigureFrame(self) |
|
self._figure_frame.pack() |
|
|
|
def destroy_all(self): |
|
if self._exp_frame is not None: |
|
self._exp_frame.destroy() |
|
if self._dataset_frame is not None: |
|
self._dataset_frame.destroy() |
|
if self._figure_frame is not None: |
|
self._figure_frame.destroy() |
|
|
|
|
|
class ExpFrame(tk.Frame): |
|
def __init__(self, master): |
|
tk.Frame.__init__(self, master) |
|
self.master.title('Select experiments') |
|
self.master.geometry('800x600') |
|
self.experiments = os.listdir(self.master.figure_path) |
|
self.CheckVarietys = [tk.IntVar() for _ in range(len(self.experiments))] |
|
self.checkbuttons = [tk.Checkbutton(self, text=self.experiments[i], variable=self.CheckVarietys[i]) for i in |
|
range(len(self.experiments))] |
|
for checkbutton in self.checkbuttons: |
|
checkbutton.pack() |
|
|
|
start_figure = tk.Button(self, text='Make Figure', command=self.make_figure) |
|
start_figure.pack() |
|
|
|
def make_figure(self): |
|
self.master.figure_experiments = [] |
|
for i in range(len(self.CheckVarietys)): |
|
if self.CheckVarietys[i].get() == 1: |
|
self.master.figure_experiments.append(self.experiments[i]) |
|
self.master.make_dataset_frame() |
|
|
|
|
|
class DatsetFrame(tk.Frame): |
|
def __init__(self, master): |
|
tk.Frame.__init__(self, master) |
|
self.master.title('Select dataset') |
|
button_go_to_dataset = tk.Button(self, text='Go to select dataset', command=self.master.make_exp_frame, width=20) |
|
button_go_to_dataset.grid(row=0, column=0, sticky='w') |
|
self.listbox = tk.Listbox(self, selectmode='extended') |
|
self.listbox.insert(0, 'vimeo test') |
|
self.listbox.insert(1, 'SNU FILM easy') |
|
self.listbox.insert(2, 'SNU FILM medium') |
|
self.listbox.insert(3, 'SNU FILM hard') |
|
self.listbox.insert(4, 'SNU FILM extreme') |
|
self.listbox.insert(5, 'xiph cropped 4k') |
|
self.listbox.insert(6, 'xiph resized 2k') |
|
self.listbox.grid(row=1, column=0, sticky='nsew') |
|
|
|
select_button = tk.Button(self, text='Select dataset', command=self.select_dataset) |
|
select_button.grid(row=2, column=0) |
|
|
|
def select_dataset(self): |
|
self.master.ind = self.listbox.curselection()[0] |
|
self.master.psnr_lines = [] |
|
self.master.dataset_path = dataset_paths[self.master.ind] |
|
for i in range(len(self.master.figure_experiments)): |
|
with open(os.path.join(self.master.figure_path, self.master.figure_experiments[i], 'output/imgs_test', |
|
self.master.dataset_path, |
|
'results.txt')) as f: |
|
psnrs = f.readlines() |
|
file_psnr = {psnrs[j].split(":")[0]: float(psnrs[j].split(":")[1]) for j in range(len(psnrs))} |
|
self.master.psnr_lines.append(file_psnr) |
|
if i == 0: |
|
self.master.file_order_list = [psnrs[j].split(":")[0] for j in range(len(psnrs))] |
|
|
|
self.master.make_figure_frame() |
|
|
|
|
|
class FigureFrame(tk.Frame): |
|
def __init__(self, master): |
|
tk.Frame.__init__(self, master) |
|
self.counter = 0 |
|
self.master.title('Figure') |
|
self.width = min(480, 3840 // (len(self.master.figure_experiments) + 2)) |
|
self.height = self.width * 270 // 480 |
|
self.master.geometry(f'{self.width * (len(self.master.figure_experiments) + 2)+100}x{self.height + 100}+100+100') |
|
|
|
self.make_figure() |
|
|
|
def prev(self): |
|
self.counter -= 1 |
|
if self.counter < 0: |
|
self.counter = len(self.master.file_order_list) - 1 |
|
self.destroy_all() |
|
self.make_figure() |
|
|
|
def next(self): |
|
self.counter += 1 |
|
if self.counter >= len(self.master.file_order_list): |
|
self.counter = 0 |
|
self.destroy_all() |
|
self.make_figure() |
|
|
|
def go_to(self, _): |
|
key = self.entry_goto.get() |
|
self.counter = self.master.file_order_list.index(key) |
|
self.destroy_all() |
|
self.make_figure() |
|
|
|
def open_eog(self): |
|
img_path = self.master.file_order_list[self.counter] |
|
overlayed_path = os.path.join(self.master.figure_path, self.master.figure_experiments[0], 'output/imgs_test', |
|
self.master.dataset_path, img_path, |
|
'overlayedd.png') |
|
os.system(f"eog {overlayed_path}") |
|
for i in range(len(self.master.figure_experiments)): |
|
imgt_pred_path = os.path.join(self.master.figure_path, self.master.figure_experiments[i], 'output/imgs_test', |
|
self.master.dataset_path, img_path, |
|
'imgt_pred.png') |
|
os.system(f"eog {imgt_pred_path}") |
|
imgt_path = os.path.join(self.master.figure_path, self.master.figure_experiments[0], 'output/imgs_test', |
|
self.master.dataset_path, img_path, |
|
'imgt.png') |
|
os.system(f"eog {imgt_path}") |
|
def make_figure(self): |
|
button_go_to_dataset = tk.Button(self, text='go to select dataset', command=self.master.make_exp_frame, width=20) |
|
button_go_to_exp = tk.Button(self, text='go to select exp', command=self.master.make_dataset_frame, width=20) |
|
button_go_to_dataset.grid(row=0, column=0, sticky='nsew') |
|
button_go_to_exp.grid(row=0, column=1, sticky='nsew') |
|
img_path = self.master.file_order_list[self.counter] |
|
|
|
self.overlayed_label = tk.Label(self, text=f'Overlayed {img_path}') |
|
self.overlayed_label.grid(row=1, column=0, columnspan=2) |
|
self.overlayed = CanvasImage(self, os.path.join(self.master.figure_path, self.master.figure_experiments[0], 'output/imgs_test', |
|
self.master.dataset_path, img_path, |
|
'overlayedd.png'), 2, 0, self.width, self.height) |
|
self.imgt_pred_labels = [ |
|
tk.Label(self, text=f'{self.master.figure_experiments[i]} {float(self.master.psnr_lines[i][img_path]):2f}') |
|
for i in range(len(self.master.figure_experiments))] |
|
for i, imgt_pred_label in enumerate(self.imgt_pred_labels): |
|
imgt_pred_label.grid(row=1, column=3 * (i + 1), columnspan=2) |
|
self.imgt_preds = [CanvasImage(self, os.path.join(self.master.figure_path, self.master.figure_experiments[i], 'output/imgs_test', |
|
self.master.dataset_path, img_path, |
|
'imgt_pred.png'), 2, 3 * (i + 1), self.width, self.height) for i in |
|
range(len(self.master.figure_experiments))] |
|
self.imgt_label = tk.Label(self, text=f'GT') |
|
self.imgt_label.grid(row=1, column=3 * (len(self.master.figure_experiments) + 1), columnspan=2) |
|
self.imgt = CanvasImage(self, os.path.join(self.master.figure_path, self.master.figure_experiments[0], 'output/imgs_test', |
|
self.master.dataset_path, img_path, |
|
'imgt.png'), 2, 3 * (len(self.master.figure_experiments) + 1), self.width, self.height) |
|
button_next = tk.Button(self, text='next', command=self.next, width=10) |
|
button_prev = tk.Button(self, text='prev', command=self.prev, width=10) |
|
self.entry_goto = tk.Entry(self, width=20) |
|
self.entry_goto.bind('<Return>', self.go_to) |
|
button_prev.grid(row=4, column=0, sticky='nsew') |
|
button_next.grid(row=4, column=1, sticky='nsew') |
|
self.label = tk.Label(self, text='go to:', width=10) |
|
self.label.grid(row=4, column=3, sticky='nsew') |
|
self.entry_goto.grid(row=4, column=4, sticky='nsew') |
|
self.button_open_eog = tk.Button(self, text='open in eog', command=self.open_eog) |
|
self.button_open_eog.grid(row=5, column=0, sticky='nsew') |
|
|
|
def destroy_all(self): |
|
self.overlayed.destroy() |
|
self.imgt.destroy() |
|
for imgt_pred in self.imgt_preds: |
|
imgt_pred.destroy() |
|
|
|
|
|
if __name__ == "__main__": |
|
app = SampleApp() |
|
app.mainloop() |
|
|