Skip to content

Instantly share code, notes, and snippets.

@akiross
Created January 18, 2016 14:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akiross/a94a57e84838744ede54 to your computer and use it in GitHub Desktop.
Save akiross/a94a57e84838744ede54 to your computer and use it in GitHub Desktop.
A very simple tool for making average of similar images
#!/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