forked from emackev/emackev.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
anamorphic.py
139 lines (133 loc) · 5.58 KB
/
anamorphic.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import pylab as pyl
import matplotlib as mpl
import numpy as npy
import matplotlib.image as im
import time
from Tkinter import *
import Image
from PIL import Image
from numpy import log
import tkFileDialog
import tkSimpleDialog as tksd
WINDOW = 600 # window size
def idle(parent, canvas):
print "idle"
def load_png():
# the user chooses which .png file to open
global pngName
pngName = tkFileDialog.askopenfilename()
canvas.itemconfigure("text",text="view image, and/or calculate")
canvas.update()
print pngName
return pngName
def set_radius():
# the user sets the radius
global r_in
r_in = tksd.askfloat("set radius", "enter the cylinder radius in inches:", maxvalue = 60, minvalue = .01, parent = root)
canvas.itemconfigure("text",text="radius (in inches) = %.1f"%r_in)
canvas.update()
time.sleep(1)
canvas.itemconfigure("text",text="please choose a .png")
canvas.update()
return r_in
def see_orig():
# the user can view the image
global pngName
Orig = pyl.asarray(Image.open(pngName))
Im = Image.fromarray(Orig); Im.show();
def calc_transform():
# this transforms x -> theta, and y -> r, and gives you then new image
try:
global pngName, r_in, Im
print "calculating..."
# loading the image
Orig = pyl.asarray(Image.open(pngName))
Im = Image.fromarray(Orig, 'RGB');
# checking the dpi
try:
dpi = Im.info["dpi"]
print "dpi = ", dpi
except:
dpi = 72
print "couldn't find dpi, using default dpi = 72"
print "calculating, please wait..."
canvas.itemconfigure("text",text="Calculating, may take several mins...")
canvas.update()
# getting the size of the original image. sy is the number of pixels in the y dimension, sx in the x direction, and sc is the number of colors (usually 3 for RGB or 4 if it also includes alpha)
(sy,sx,sc) = (Orig.shape[0], Orig.shape[1], Orig.shape[2])
# calculates the radius in pixels
r_cylinder = int(r_in*dpi);
# sets theta to be between 0 and pi (maybe user should be able to set this?)
th_range = (pyl.pi)
# sets the maximum radius (in pixels) as the radius of the cylinder plus the height of the original image. Maybe the user should be able to set this, but then we'd have to interpolate to resize the image.
max_r = r_cylinder+sy
# the final image has dimensions 2*max_r x 2*max_r, because this is the widest that a circle with radius max_r will be
fy = 2*max_r
fx = 2*max_r
# initialize the final image to 255s (white background)
Final = 255*pyl.ones((fy,fx,sc), dtype = pyl.uint8)
for y in range(fy):
# x and y index into the final image
# xc and yc are centered versions of x and y, so the origin is in the middle
yc = fy/2 - y
for x in range(fx):
xc = fx/2 - x
# calculate r from xc and yc
r = pyl.sqrt(xc*xc+yc*yc)
# calculate theta with arctan2, which works even for angles that are bigger than 90
th = (pyl.arctan2(1.*yc,(1.*xc)))
# check if r and theta are within range
if ((th<th_range)&(th>0)):
if ((r>r_cylinder)&(r<(max_r))):
# x_orig and y_orig are the indices you want for the original image
x_orig = int(th*sx/th_range)
y_orig = int(r - r_cylinder)
# assign the appropriate pixels of the final image based on the original image, flipping up down and left right (mirror image)
Final[y,x,:] = Orig[sy-y_orig-1,sx - x_orig -1,:];
# make an image out of the array, and set the dpi
Im = Image.fromarray(Final); Im.info["dpi"] = dpi;
canvas.itemconfigure("text",text="calculated, view and/or save the anamorphic image")
canvas.update()
except:
canvas.itemconfigure("text",text="Sorry, there was an error. Please check .png")
canvas.update()
def see_final():
global Im
# show the image
Im.show();
return Im
def save_final():
global Im
canvas.itemconfigure("text",text="saving...")
canvas.update()
# open a dialog to save the image
Im.save(tkFileDialog.asksaveasfilename())
canvas.itemconfigure("text",text="saved!")
canvas.update()
# GUI setup
root = Tk()
root.title("Emily's Anamorphics (press q to exit)")
root.bind('q','exit')
canvas = Canvas(root, width=WINDOW, height=.25*WINDOW, background='white')
# button to set radius
set_r = Button(root, text = "set radius", command = set_radius)
set_r.pack()
# button to choose .png
pickfile = Button(root, text = "choose .png", command = load_png)
pickfile.pack()
# button to view .png
see_im = Button(root, text = "view original .png", command = see_orig)
see_im.pack()
# button to run the calculations
calc = Button(root, text = "calculate anamorphic image", command=calc_transform)
calc.pack()
# button to view the results
see_fin = Button(root, text = "view anamorphic image", command = see_final)
see_fin.pack()
# button to save the results
save_fin = Button(root, text = "save anamorphic image", command =save_final)
save_fin.pack()
canvas.create_text(.5*WINDOW,.05*WINDOW,text="~Morph an image so it only looks right when viewed in a refective cylinder~",font=("Helvetica", 14),tags="explanation",fill="#0000b0")
canvas.create_text(.5*WINDOW,.1*WINDOW,text="please set the cylinder radius",font=("Helvetica", 24),tags="text",fill="#0000b0")
canvas.pack()
root.mainloop()