Created
January 18, 2016 14:13
-
-
Save akiross/a94a57e84838744ede54 to your computer and use it in GitHub Desktop.
A very simple tool for making average of similar images
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import os | |
import re | |
import numpy as np | |
from ast import literal_eval as make_tuple | |
from tkinter import * | |
from tkinter import ttk | |
from tkinter import filedialog | |
from PIL import ImageTk, Image | |
def get_image_data(img_path, mode='RGB'): | |
'''Returns the image as floating point data''' | |
img = Image.open(img_path).convert(mode) | |
return (np.array(img).astype(float), img.size) | |
def average_images(paths_iter, mode='RGB'): | |
count = 1 | |
tot, size = get_image_data(next(paths_iter)) | |
for path in paths_iter: | |
dat, dim = get_image_data(path) | |
if dim != size: | |
print(path, 'incompatible shape', dim, 'should be', size) | |
continue | |
tot += dat | |
count += 1 | |
if not count: | |
return None | |
# Compute average | |
avg = (tot / count).astype(np.uint8) | |
return Image.fromarray(avg) | |
def get_file_list(*args): | |
'''Called when the user wants to load a new dataset''' | |
global loaded_files | |
dirname = filedialog.askdirectory() | |
# If user didn't cancel | |
if dirname: | |
cur_path.set(dirname) | |
# Get the files in the list | |
loaded_files = tuple(os.listdir(dirname)) | |
file_list.set(loaded_files) | |
# run_vhs_ttarget03_.*-pop_snapshot-0 | |
def make_avg(*args): | |
'''This sorts the files currently in the list''' | |
global avg_image_src, avg_image | |
print('Averaging files in directory', cur_path.get()) | |
fl = make_tuple(file_list.get()) | |
for f in fl: | |
print(os.path.join(cur_path.get(), f)) | |
avg_image_src = average_images(os.path.join(cur_path.get(), fp) for fp in fl) | |
if avg_image_src: | |
avg_image = ImageTk.PhotoImage(avg_image_src) | |
# print("setting the image", avg_image) | |
label_img.config(image=avg_image) | |
else: | |
avg_image, avg_image_src = None, None | |
def save_avg(*args): | |
if avg_image: | |
print('Saving image') | |
fpath = filedialog.asksaveasfilename() | |
print(fpath) | |
if fpath: | |
avg_image_src.save(fpath) | |
else: | |
print('No image to save!') | |
def scale_pic(s): | |
'''Scale the currently displayed picture''' | |
global avg_image | |
if avg_image and avg_image_src: | |
w, h = avg_image_src.size | |
new_size = (max(int(w * s), 1), max(int(h * s), 1)) | |
avg_image = ImageTk.PhotoImage(avg_image_src.resize(new_size, Image.ANTIALIAS)) | |
label_img.config(image=avg_image) | |
def zoom_pic(): | |
'''Scales the picture using current zoom level''' | |
zoom_scale = (cur_zoom / (max_zoom // 2)) ** 4 | |
scale_pic(zoom_scale) | |
def zoom_in(*args): | |
global cur_zoom | |
cur_zoom = min(cur_zoom + 1, max_zoom) | |
zoom_pic() | |
def zoom_out(*args): | |
global cur_zoom | |
cur_zoom = max(cur_zoom - 1, 0) | |
zoom_pic() | |
def zoom_reset(*args): | |
global cur_zoom | |
cur_zoom = max_zoom // 2 | |
zoom_pic() | |
def filter_changed(*args): | |
'''When filter change''' | |
pat = str(filter_expr.get()) | |
try: | |
expr = re.compile(pat) | |
file_list.set(tuple(filter(lambda x: re.match(expr, x), loaded_files))) | |
except re.error: | |
#print('Invalid regex') TODO this should become a yellow-ish input box | |
file_list.set(tuple(filter(lambda x: x.startswith(pat), loaded_files))) | |
if __name__ == '__main__': | |
############################################################## | |
# +-------------+ +-------------------------------------+ # | |
# | load_files | | path_dir | # | |
# +-------------+ +-------------------------------------+ # | |
# +-------------------------------------------------------+ # | |
# | | # | |
# | loaded file list | # | |
# | | # | |
# +-------------------------------------------------------+ # | |
# +-----------------------------+ +----------+ +---------+ # | |
# | filter expression | | make avg | | save | # | |
# +-----------------------------+ +----------+ +---------+ # | |
# +-------------------------------------------------------+ # | |
# | | # | |
# | generated avg img | # | |
# | | # | |
# +-------------------------------------------------------+ # | |
############################################################## | |
root = Tk() | |
root.title("Average images") | |
root.columnconfigure(0, weight=1) | |
root.rowconfigure(0, weight=1) | |
mainframe = ttk.Frame(root) | |
mainframe.grid(column=0, row=0, sticky=(W, E, N, S)) | |
# for i in [1, 2, 3, 4]: | |
mainframe.columnconfigure(2, weight=1) | |
# Only the last row (label with image) expands | |
mainframe.rowconfigure(4, weight=1) | |
# This variable keeps the list of loaded files | |
loaded_files = ('spie_pt_100.jpg', 'anguria.jpg', 'babe1.png', 'literature.jpg') | |
# This variable keeps the current image path | |
images_path = '/home/akiross/Pictures/' | |
# This variable keeps the average image | |
avg_image, avg_image_src = None, None | |
# These keep the current zoom level and the max zoom level | |
cur_zoom, max_zoom = 10, 20 | |
# The variables required | |
filter_expr = StringVar() | |
cur_path = StringVar(value=images_path) | |
file_list = StringVar(value=loaded_files) | |
filter_expr.trace('w', filter_changed) | |
load_files = ttk.Button(mainframe, text='Chose directory', command=get_file_list) | |
load_files.grid(column=1, row=1, sticky=(W, E)) | |
load_files.focus() | |
path_dir = ttk.Label(mainframe, textvariable=cur_path) | |
path_dir.grid(column=2, row=1, columnspan=3, sticky=(W, E)) | |
candidate_files = Listbox(mainframe, height=8, listvariable=file_list) | |
candidate_files.grid(column=1, row=2, columnspan=4, sticky=(W, E, N, S)) | |
file_filter = ttk.Entry(mainframe, textvariable=filter_expr) | |
file_filter.grid(column=1, row=3, columnspan=2, sticky=(W, E)) | |
btn_avg = ttk.Button(mainframe, text='Make Avg', command=make_avg) | |
btn_avg.grid(column=3, row=3, sticky=(W, E)) | |
btn_save = ttk.Button(mainframe, text='Save', command=save_avg) | |
btn_save.grid(column=4, row=3, sticky=(W, E)) | |
label_img = ttk.Label(mainframe, image=None) | |
label_img.grid(column=1, row=4, columnspan=4, sticky=(N, S, W, E)) | |
root.bind('-', zoom_out) | |
root.bind('+', zoom_in) | |
root.bind('=', zoom_reset) | |
root.mainloop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment