-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathgui-image-editor.py
335 lines (254 loc) · 12.1 KB
/
gui-image-editor.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
import ttkbootstrap as ttk
from tkinter import filedialog
from tkinter.messagebox import showerror, askyesno
from tkinter import colorchooser
from PIL import Image, ImageOps, ImageTk, ImageFilter, ImageGrab
# defining global variables
WIDTH = 750
HEIGHT = 560
file_path = ""
pen_size = 3
pen_color = "black"
# function to open the image file
def open_image():
global file_path
file_path = filedialog.askopenfilename(title="Open Image File", filetypes=[("Image Files", "*.jpg;*.jpeg;*.png;*.gif;*.bmp")])
if file_path:
global image, photo_image
image = Image.open(file_path)
new_width = int((WIDTH / 2))
image = image.resize((new_width, HEIGHT), Image.LANCZOS)
image = ImageTk.PhotoImage(image)
canvas.create_image(0, 0, anchor="nw", image=image)
# a global variable for checking the flip state of the image
is_flipped = False
def flip_image():
try:
global image, photo_image, is_flipped
if not is_flipped:
# open the image and flip it left and right
image = Image.open(file_path).transpose(Image.FLIP_LEFT_RIGHT)
is_flipped = True
else:
# reset the image to its original state
image = Image.open(file_path)
is_flipped = False
# resize the image to fit the canvas
new_width = int((WIDTH / 2))
image = image.resize((new_width, HEIGHT), Image.LANCZOS)
# convert the PIL image to a Tkinter PhotoImage and display it on the canvas
photo_image = ImageTk.PhotoImage(image)
canvas.create_image(0, 0, anchor="nw", image=photo_image)
except:
showerror(title='Flip Image Error', message='Please select an image to flip!')
# global variable for tracking rotation angle
rotation_angle = 0
# function for rotating the image
def rotate_image():
try:
global image, photo_image, rotation_angle
# open the image and rotate it
image = Image.open(file_path)
new_width = int((WIDTH / 2))
image = image.resize((new_width, HEIGHT), Image.LANCZOS)
rotated_image = image.rotate(rotation_angle + 90)
rotation_angle += 90
# reset image if angle is a multiple of 360 degrees
if rotation_angle % 360 == 0:
rotation_angle = 0
image = Image.open(file_path)
image = image.resize((new_width, HEIGHT), Image.LANCZOS)
rotated_image = image
# convert the PIL image to a Tkinter PhotoImage and display it on the canvas
photo_image = ImageTk.PhotoImage(rotated_image)
canvas.create_image(0, 0, anchor="nw", image=photo_image)
except:
showerror(title='Rotate Image Error', message='Please select an image to rotate!')
# function for applying filters to the opened image file
def apply_filter(filter):
global image, photo_image
try:
# check if the image has been flipped or rotated
if is_flipped:
# flip the original image left and right
flipped_image = Image.open(file_path).transpose(Image.FLIP_LEFT_RIGHT)
# rotate the flipped image
rotated_image = flipped_image.rotate(rotation_angle)
# apply the filter to the rotated image
if filter == "Black and White":
rotated_image = ImageOps.grayscale(rotated_image)
elif filter == "Blur":
rotated_image = rotated_image.filter(ImageFilter.BLUR)
elif filter == "Contour":
rotated_image = rotated_image.filter(ImageFilter.CONTOUR)
elif filter == "Detail":
rotated_image = rotated_image.filter(ImageFilter.DETAIL)
elif filter == "Emboss":
rotated_image = rotated_image.filter(ImageFilter.EMBOSS)
elif filter == "Edge Enhance":
rotated_image = rotated_image.filter(ImageFilter.EDGE_ENHANCE)
elif filter == "Sharpen":
rotated_image = rotated_image.filter(ImageFilter.SHARPEN)
elif filter == "Smooth":
rotated_image = rotated_image.filter(ImageFilter.SMOOTH)
else:
rotated_image = Image.open(file_path).transpose(Image.FLIP_LEFT_RIGHT).rotate(rotation_angle)
elif rotation_angle != 0:
# rotate the original image
rotated_image = Image.open(file_path).rotate(rotation_angle)
# apply the filter to the rotated image
if filter == "Black and White":
rotated_image = ImageOps.grayscale(rotated_image)
elif filter == "Blur":
rotated_image = rotated_image.filter(ImageFilter.BLUR)
elif filter == "Contour":
rotated_image = rotated_image.filter(ImageFilter.CONTOUR)
elif filter == "Detail":
rotated_image = rotated_image.filter(ImageFilter.DETAIL)
elif filter == "Emboss":
rotated_image = rotated_image.filter(ImageFilter.EMBOSS)
elif filter == "Edge Enhance":
rotated_image = rotated_image.filter(ImageFilter.EDGE_ENHANCE)
elif filter == "Sharpen":
rotated_image = rotated_image.filter(ImageFilter.SHARPEN)
elif filter == "Smooth":
rotated_image = rotated_image.filter(ImageFilter.SMOOTH)
else:
rotated_image = Image.open(file_path).rotate(rotation_angle)
else:
# apply the filter to the original image
image = Image.open(file_path)
if filter == "Black and White":
image = ImageOps.grayscale(image)
elif filter == "Blur":
image = image.filter(ImageFilter.BLUR)
elif filter == "Sharpen":
image = image.filter(ImageFilter.SHARPEN)
elif filter == "Smooth":
image = image.filter(ImageFilter.SMOOTH)
elif filter == "Emboss":
image = image.filter(ImageFilter.EMBOSS)
elif filter == "Detail":
image = image.filter(ImageFilter.DETAIL)
elif filter == "Edge Enhance":
image = image.filter(ImageFilter.EDGE_ENHANCE)
elif filter == "Contour":
image = image.filter(ImageFilter.CONTOUR)
rotated_image = image
# resize the rotated/flipped image to fit the canvas
new_width = int((WIDTH / 2))
rotated_image = rotated_image.resize((new_width, HEIGHT), Image.LANCZOS)
# convert the PIL image to a Tkinter PhotoImage and display it on the canvas
photo_image = ImageTk.PhotoImage(rotated_image)
canvas.create_image(0, 0, anchor="nw", image=photo_image)
except:
showerror(title='Error', message='Please select an image first!')
# function for drawing lines on the opened image
def draw(event):
global file_path
if file_path:
x1, y1 = (event.x - pen_size), (event.y - pen_size)
x2, y2 = (event.x + pen_size), (event.y + pen_size)
canvas.create_oval(x1, y1, x2, y2, fill=pen_color, outline="", width=pen_size, tags="oval")
# function for changing the pen color
def change_color():
global pen_color
pen_color = colorchooser.askcolor(title="Select Pen Color")[1]
# function for erasing lines on the opened image
def erase_lines():
global file_path
if file_path:
canvas.delete("oval")
def save_image():
global file_path, is_flipped, rotation_angle
if file_path:
# create a new PIL Image object from the canvas
image = ImageGrab.grab(bbox=(canvas.winfo_rootx(), canvas.winfo_rooty(), canvas.winfo_rootx() + canvas.winfo_width(), canvas.winfo_rooty() + canvas.winfo_height()))
# check if the image has been flipped or rotated
if is_flipped or rotation_angle % 360 != 0:
# Resize and rotate the image
new_width = int((WIDTH / 2))
image = image.resize((new_width, HEIGHT), Image.LANCZOS)
if is_flipped:
image = image.transpose(Image.FLIP_LEFT_RIGHT)
if rotation_angle % 360 != 0:
image = image.rotate(rotation_angle)
# update the file path to include the modifications in the file name
file_path = file_path.split(".")[0] + "_mod.jpg"
# apply any filters to the image before saving
filter = filter_combobox.get()
if filter:
if filter == "Black and White":
image = ImageOps.grayscale(image)
elif filter == "Blur":
image = image.filter(ImageFilter.BLUR)
elif filter == "Sharpen":
image = image.filter(ImageFilter.SHARPEN)
elif filter == "Smooth":
image = image.filter(ImageFilter.SMOOTH)
elif filter == "Emboss":
image = image.filter(ImageFilter.EMBOSS)
elif filter == "Detail":
image = image.filter(ImageFilter.DETAIL)
elif filter == "Edge Enhance":
image = image.filter(ImageFilter.EDGE_ENHANCE)
elif filter == "Contour":
image = image.filter(ImageFilter.CONTOUR)
# update the file path to include the filter in the file name
file_path = file_path.split(".")[0] + "_" + filter.lower().replace(" ", "_") + ".jpg"
# open file dialog to select save location and file type
file_path = filedialog.asksaveasfilename(defaultextension=".jpg")
if file_path:
if askyesno(title='Save Image', message='Do you want to save this image?'):
# save the image to a file
image.save(file_path)
root = ttk.Window(themename="cosmo")
root.title("Image Editor")
root.geometry("510x580+300+110")
root.resizable(0, 0)
icon = ttk.PhotoImage(file='icon.png')
root.iconphoto(False, icon)
# the left frame to contain the 4 buttons
left_frame = ttk.Frame(root, width=200, height=600)
left_frame.pack(side="left", fill="y")
# the right canvas for displaying the image
canvas = ttk.Canvas(root, width=WIDTH, height=HEIGHT)
canvas.pack()
# binding the Canvas to the B1-Motion event
canvas.bind("<B1-Motion>", draw)
# label
filter_label = ttk.Label(left_frame, text="Select Filter:", background="white")
filter_label.pack(padx=0, pady=2)
# a list of filters
image_filters = ["Contour", "Black and White", "Blur", "Detail", "Emboss", "Edge Enhance", "Sharpen", "Smooth"]
# combobox for the filters
filter_combobox = ttk.Combobox(left_frame, values=image_filters, width=15)
filter_combobox.pack(padx=10, pady=5)
# binding the apply_filter function to the combobox
filter_combobox.bind("<<ComboboxSelected>>", lambda event: apply_filter(filter_combobox.get()))
# loading the icons for the 4 buttons
image_icon = ttk.PhotoImage(file = 'add.png').subsample(12, 12)
flip_icon = ttk.PhotoImage(file = 'flip.png').subsample(12, 12)
rotate_icon = ttk.PhotoImage(file = 'rotate.png').subsample(12, 12)
color_icon = ttk.PhotoImage(file = 'color.png').subsample(12, 12)
erase_icon = ttk.PhotoImage(file = 'erase.png').subsample(12, 12)
save_icon = ttk.PhotoImage(file = 'saved.png').subsample(12, 12)
# button for adding/opening the image file
image_button = ttk.Button(left_frame, image=image_icon, bootstyle="light", command=open_image)
image_button.pack(pady=5)
# button for flipping the image file
flip_button = ttk.Button(left_frame, image=flip_icon, bootstyle="light", command=flip_image)
flip_button.pack(pady=5)
# button for rotating the image file
rotate_button = ttk.Button(left_frame, image=rotate_icon, bootstyle="light", command=rotate_image)
rotate_button.pack(pady=5)
# button for choosing pen color
color_button = ttk.Button(left_frame, image=color_icon, bootstyle="light", command=change_color)
color_button.pack(pady=5)
# button for erasing the lines drawn over the image file
erase_button = ttk.Button(left_frame, image=erase_icon, bootstyle="light", command=erase_lines)
erase_button.pack(pady=5)
# button for saving the image file
save_button = ttk.Button(left_frame, image=save_icon, bootstyle="light", command=save_image)
save_button.pack(pady=5)
root.mainloop()