diff --git a/main.py b/main.py new file mode 100644 index 0000000..5281843 --- /dev/null +++ b/main.py @@ -0,0 +1,217 @@ + +import os +import cv2 +import numpy as np +import tkinter as tk +from tkinter import filedialog +from PIL import Image, ImageTk + +OPENED_IMAGE = None + +def padding(file_path, border_width): + image = cv2.imread(file_path) + if image is None: + print("Failed to load the image.") + return + padded_image = cv2.copyMakeBorder( + image, + top=border_width, + bottom=border_width, + left=border_width, + right=border_width, + borderType=cv2.BORDER_REFLECT + ) + _cache_image(padded_image, file_path) + +def crop(file_path): + image = cv2.imread(file_path) + height, width = image.shape[:2] + # crop 80 px from top & left, 130 px from bottom & right + x_0 = 80 + x_1 = width - 130 + y_0 = 80 + y_1 = height - 130 + cropped_image = image[y_0:y_1, x_0:x_1] + + _cache_image(cropped_image, file_path) + +def resize(file_path): + width = 200 + height = 200 + image = cv2.imread(file_path) + resized_image = cv2.resize(image, (width, height)) + _cache_image(resized_image, file_path) + + +def copy(file_path): + image = cv2.imread(file_path) + + height, width, channels = image.shape + emptyPictureArray = np.zeros((height, width, 3), dtype=np.uint8) + + for y in range(height): + for x in range(width): + for c in range(channels): + emptyPictureArray[y, x, c] = image[y, x, c] + _cache_image(emptyPictureArray, file_path) + + +def grayscale(file_path): + image = cv2.imread(file_path) + gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + _cache_image(gray_image, file_path) + +def hsv(file_path): + image = cv2.imread(file_path) + hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) + _cache_image(hsv_image, file_path) + + + +def hue_shifted(file_path, hue=50): + image = cv2.imread(file_path) + height, width, channels = image.shape + emptyPictureArray = np.zeros((height, width, 3), dtype=np.uint8) + for y in range(height): + for x in range(width): + for c in range(channels): + new_value = image[y, x, c] + hue + # Clip the value to stay within [0, 255] + emptyPictureArray[y, x, c] = np.clip(new_value, 0, 255) + _cache_image(emptyPictureArray, file_path) + +def smoothing(file_path): + image = cv2.imread(file_path) + smoothed_image = cv2.blur(image, ksize=(15, 15)) + _cache_image(smoothed_image, file_path) + + +def rotation(file_path, rotation_angle=90): + image = cv2.imread(file_path) + if rotation_angle == 90: + rotated_image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE) + elif rotation_angle == 180: + rotated_image = cv2.rotate(image, cv2.ROTATE_180) + else: + print("Invalid angle. Use 90 or 180.") + return + _cache_image(rotated_image, file_path) + +def open_image(): + """ + Function called from the menu to open an image. + """ + # Ask user to choose an image file + file_path = filedialog.askopenfilename( + filetypes=[("Image files", "*.jpg *.jpeg *.png *.gif *.bmp")] + ) + if file_path: + _open_image(file_path) + +def _cache_image(image, file_path): + opened_image_basename = os.path.basename(file_path) + opened_image_path = os.path.dirname(file_path) + newfname = "cache/" + opened_image_basename + cv2.imwrite(newfname, image) + print(f"Image cached as '{newfname}'.") + _open_image(newfname) + + +def _save_image(file_path): + image = cv2.imread(file_path) + opened_image_basename = os.path.basename(file_path) + opened_image_path = os.path.dirname(file_path) + newfname = opened_image_path + '/../' + opened_image_basename + cv2.imwrite(newfname, image) + print(f"Image saved as '{newfname}'.") + _open_image(newfname) + + +def _open_image(file_path): + """ + Open and display an image in the GUI. + Params: + - file_path: Path to the image file. + """ + # Open with cv2 to get dimensions + imgcv2 = cv2.imread(file_path) + height, width, channels = imgcv2.shape + + # Open and resize image (optional) + img = Image.open(file_path) + img = img.resize((height, width), Image.LANCZOS) # Resize to fit frame + tk_img = ImageTk.PhotoImage(img) + + # Update label inside frame with the correct dimensions + image_label.config(image=tk_img, height=height, width=width) + + global OPENED_IMAGE + OPENED_IMAGE = file_path # Store opened image in global variable, to access later if needed + + print("Opened image:", file_path) + + +if __name__ == "__main__": + +# Main window + root = tk.Tk() + root.title("Image Viewer") + root.geometry("800x600") + root.config(bg="white") + icon = tk.PhotoImage(file='icon.png') + root.tk.call('wm', 'iconphoto', root._w, icon) + root.minsize(800, 600) + +# Menus + menu = tk.Menu(root) + root.config(menu=menu) + file_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="File", menu=file_menu) + file_menu.add_command(label="Open Image", command=open_image) + file_menu.add_command(label="Save Image", command=lambda: _save_image(OPENED_IMAGE)) + file_menu.add_command(label="Exit", command=root.quit) + + test_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Test", menu=test_menu) + test_menu.add_command(label="Padding", command=lambda: padding(OPENED_IMAGE, border_width=50) if OPENED_IMAGE is not None else print("No image opened.")) + test_menu.add_command(label="Crop", command=lambda: crop(OPENED_IMAGE) if OPENED_IMAGE is not None else print("No image opened.")) + test_menu.add_command(label="Resize", command=lambda: resize(OPENED_IMAGE) if OPENED_IMAGE is not None else print("No image opened.")) + test_menu.add_command(label="Copy", command=lambda: copy(OPENED_IMAGE) if OPENED_IMAGE is not None else print("No image opened.")) + test_menu.add_command(label="Greyscale", command=lambda: grayscale(OPENED_IMAGE) if OPENED_IMAGE is not None else print("No image opened.")) + test_menu.add_command(label="Hsv", command=lambda: hsv(OPENED_IMAGE) if OPENED_IMAGE is not None else print("No image opened.")) + test_menu.add_command(label="Hue Shifted", command=lambda: hue_shifted(OPENED_IMAGE, hue=50) if OPENED_IMAGE is not None else print("No image opened.")) + test_menu.add_command(label="Smoothed", command=lambda: smoothing(OPENED_IMAGE) if OPENED_IMAGE is not None else print("No image opened.")) + test_menu.add_command(label="Rotated", command=lambda: rotation(OPENED_IMAGE, rotation_angle=90) if OPENED_IMAGE is not None else print("No image opened.")) + + + clipboard_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Clipboard", menu=clipboard_menu) + + image_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Image", menu=image_menu) + + tools_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Tools", menu=tools_menu) + + shapes_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Shapes", menu=shapes_menu) + + colors_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Colors", menu=colors_menu) + + layer_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Layer", menu=layer_menu) + + filter_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Filter", menu=filter_menu) +# + +# Frame to hold image + imgframe = tk.Frame(root, width=500, height=500, bg="lightgray", relief="sunken", bd=2) + imgframe.pack(side="top", pady=10) + +# Label to display image + image_label = tk.Label(imgframe, width=500, height=500, bg="white") + image_label.pack(expand=True) + + root.mainloop()