Project

General

Profile

Actions

Home | About Us | About the Project | UML Diagrams | Codes | Outcome | System Requirements


  Codes

main.py: This file initializes the entire project
import tkinter as tk
from GUI import GUI
if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("200x200")
    app = GUI(root)
    root.mainloop()

Commands.py: A class to handle various commands related to image processing and display
import cv2 as cv
import numpy as np
import tkinter as tk
from tkinter import filedialog, messagebox
import os
from PIL import Image, ImageTk
import configparser
from ImageIO import ImageIO

class Commands:
    def __init__(self, gui, shared_data):
        self.gui = gui
        self.shared_data = shared_data
        self.io = ImageIO(shared_data)

    def load_left_image(self):
        filepath = filedialog.askopenfilename(title="Select Left Image")
        if filepath:
            self.shared_data.left_image = self.io.load_image(filepath)
            print("Left image loaded.")

    def load_right_image(self):
        filepath = filedialog.askopenfilename(title="Select Right Image")
        if filepath:
            self.shared_data.right_image = self.io.load_image(filepath)
            print("Right image loaded.")

    def smooth_gradient(self,length, rate):
        # Create a linear gradient that transitions from full brightness (1) to zero (0)
        x = np.linspace(0, 1, length)
        # Apply a non-linear function (e.g., quadratic) for a smoother transition
        gradient = 1 - (x ** 2)  # Quadratic falloff to create a smooth transition
        gradient = np.clip(gradient * rate, 0, 1)  # Scale the gradient by the rate
        return gradient

    def show_left_image(self):
        if self.shared_data.left_image is not None:

            self.shared_data.isLeft = True
            left = self.shared_data.left_image.copy()
            height = left.shape[0]
            left_overlap_start = left.shape[1] - self.shared_data.overlap_region

            # Create a smooth gradient alpha mask for the left image (1 to 0)
            gradient_alpha = np.linspace(1, 0, self.shared_data.overlap_region)
            gradient_alpha = np.tile(gradient_alpha, (height, 1))

            # Apply the alpha gradient to the overlapping region
            left_overlap = left[:, left_overlap_start:, :].copy()
            left_overlap = (left_overlap * gradient_alpha[:, :, np.newaxis]).astype(np.uint8)

            # Replace the modified overlapping region back into the original left image
            left[:, left_overlap_start:, :] = left_overlap

            #Debug code
            #Write the edited image to file
            self.io.write_image(self, img=left, fileName="left_Modified.png")
            print("Left image written to file.")
            # Show the modified image
            self.io.show_image(left, canvas=self.gui.canvas)
            print("Left image displayed.")

        else:
            messagebox.showerror("Error", "No left image loaded!")

    def show_right_image(self):
        if self.shared_data.right_image is not None:
            self.shared_data.isLeft = False
            right = self.shared_data.right_image.copy()
            height = right.shape[0]
            right_overlap_end = self.shared_data.overlap_region

            # Create a smooth gradient alpha mask for the right image (0 to 1)
            gradient_alpha = np.linspace(0, 1, right_overlap_end)
            gradient_alpha = np.tile(gradient_alpha, (height, 1))

            # Apply the alpha gradient to the overlapping region
            right_overlap = right[:, :right_overlap_end, :].copy()
            right_overlap = (right_overlap * gradient_alpha[:, :, np.newaxis]).astype(np.uint8)

            # Replace the modified overlapping region back into the original right image
            right[:, :right_overlap_end, :] = right_overlap

            #Write modified image to file
            self.io.write_image(self, img=right, fileName="right_Modified.png")
            print("Right image written to file.")
            # Show the modified image
            self.io.show_image(right, canvas=self.gui.canvas)
            print("Right image displayed.")

        else:
            messagebox.showerror("Error", "No right image loaded!")

    def show_right_original(self):
        # Show original image
        if self.shared_data.right_image is not None:
            self.shared_data.isLeft = False
            right = self.shared_data.right_image.copy()
            self.io.show_image(right, canvas=self.gui.canvas)
            print("Right image displayed.")
        else:
            messagebox.showerror("Error", "No right image loaded!")

    def show_left_original(self):
        # Show original image
        if self.shared_data.left_image is not None:
            self.shared_data.isLeft = True
            left = self.shared_data.left_image.copy()
            self.io.show_image(left, canvas=self.gui.canvas)
            print("Left image displayed.")
        else:
            messagebox.showerror("Error", "No left image loaded!")

    def set_overlap_region(self):
        self.shared_data.overlap_region = self.gui.overlap.get()
        print(f"Overlap region set to {self.shared_data.overlap_region}")
        if(self.shared_data.isLeft):
            self.show_left_image()
        else:
            self.show_right_image()

    def increase_overlap(self):
        current_overlap = self.gui.overlap.get()
        self.gui.overlap.set(current_overlap + 1)
        self.set_overlap_region()
        if(self.shared_data.isLeft):
            self.show_left_image()
        else:
            self.show_right_image()

    def decrease_overlap(self):
        current_overlap = self.gui.overlap.get()
        self.gui.overlap.set(max(0, current_overlap - 1))  # Prevents negative overlap
        self.set_overlap_region()
        if(self.shared_data.isLeft):
            self.show_left_image()
        else:
            self.show_right_image()

GUI.py: This class creates a graphical user interface for the image projector
import cv2 as cv
import numpy as np
import tkinter as tk
from tkinter import filedialog, messagebox
import os
from PIL import Image, ImageTk
import configparser
from SharedData import SharedData
from Commands import Commands
class GUI:
    def __init__(self, master):
        self.master = master
        self.master.title("Image Projector")
        self.canvas = tk.Canvas(self.master, bg="black")
        self.canvas.pack(fill=tk.BOTH, expand=True)
        self.shared_data = SharedData()
        self.commands = Commands(self, self.shared_data)
        self.control_window()

    def control_window(self):
        self.control_window = tk.Toplevel(self.master)
        self.control_window.title("Controls")

        self.overlap = tk.IntVar(value=200)

        # Label and entry for overlap region width
        self.overlapLabel = tk.Label(self.control_window, text='Overlap region Width', font=('calibre', 10, 'bold'))
        self.overlapBox = tk.Entry(self.control_window, textvariable=self.overlap)

        self.overlapLabel.grid(row=0, column=0, padx=5, pady=5)
        self.overlapBox.grid(row=0, column=1, padx=5, pady=5)

        # Button to set overlap region
        self.set_overlap_button = tk.Button(self.control_window, text="Set Overlap Region", command=self.commands.set_overlap_region)
        self.set_overlap_button.grid(row=1, column=0, columnspan=2, padx=5, pady=5)

        # New buttons for increasing and decreasing the overlap region
        self.increase_overlap_button = tk.Button(self.control_window, text="Increase Overlap", command=self.commands.increase_overlap)
        self.increase_overlap_button.grid(row=2, column=0, padx=5, pady=5)

        self.decrease_overlap_button = tk.Button(self.control_window, text="Decrease Overlap", command=self.commands.decrease_overlap)
        self.decrease_overlap_button.grid(row=2, column=1, padx=5, pady=5)

        # Load left and right image buttons
        self.button_load_left = tk.Button(self.control_window, text="Load Left Image", command=self.commands.load_left_image)
        self.button_load_left.grid(row=3, column=0, padx=5, pady=5)

        self.button_load_right = tk.Button(self.control_window, text="Load Right Image", command=self.commands.load_right_image)
        self.button_load_right.grid(row=3, column=1, padx=5, pady=5)

        # Show left and right image buttons
        self.button_show_left = tk.Button(self.control_window, text="Show Left Image", command=self.commands.show_left_image)
        self.button_show_left.grid(row=4, column=0, padx=5, pady=5)

        self.button_show_right = tk.Button(self.control_window, text="Show Right Image", command=self.commands.show_right_image)
        self.button_show_right.grid(row=4, column=1, padx=5, pady=5)

        # Show original left and right image buttons
        self.button_show_left_original = tk.Button(self.control_window, text="Show Left Original", command=self.commands.show_left_original)
        self.button_show_left_original.grid(row=5, column=0, padx=5, pady=5)

        self.button_show_right_original = tk.Button(self.control_window, text="Show Right Original", command=self.commands.show_right_original)
        self.button_show_right_original.grid(row=5, column=1, padx=5, pady=5)

        self.save_config = tk.Button(self.control_window, text="Save Config", command=self.shared_data.write_to_config)
        self.save_config.grid(row=6, column=0, padx=5, pady=5)

        self.load_config = tk.Button(self.control_window, text="Load Config", command=self.shared_data.read_from_config)
        self.load_config.grid(row=6, column=1, padx=5, pady=5)

ImageIO.py: This class is in charge of image input and output
import cv2 as cv
import numpy as np
import os
from PIL import Image
import tkinter as tk

class ImageIO:
    def __init__(self, shared_data):
        self.shared_data = shared_data

    def load_image(self, path):
        return cv.imread(path, cv.IMREAD_COLOR)

    def write_image(self, img, fileName="image.png"):
        if cv.imwrite(fileName, img):
            print(f"File {fileName} Written Successfully")
        else:
            print("File writing failed")

    def show_image(self, img, title="Image Title", canvas=None):
        # Set up OpenCV fullscreen window in the main thread
        cv.namedWindow(title, cv.WINDOW_NORMAL)
        cv.setWindowProperty(title, cv.WND_PROP_FULLSCREEN, cv.WINDOW_FULLSCREEN)

        # Resize the image to 1920x1080 resolution
        img_resized = cv.resize(img, (1920, 1080), interpolation=cv.INTER_LINEAR)

        # Display the resized image in OpenCV and keep it open without blocking Tkinter
        cv.imshow(title, img_resized)

        # Run a non-blocking loop to keep OpenCV window and Tkinter GUI responsive
        while cv.getWindowProperty(title, cv.WND_PROP_VISIBLE) >= 1:
            cv.waitKey(1)
            if canvas:
                canvas.update_idletasks()
                canvas.update()

        # Close the OpenCV window once the loop ends
        cv.destroyAllWindows()

SharedData.py: This class is used to store shared data between different classes
import cv2 as cv
import numpy as np
import tkinter as tk
from tkinter import filedialog, messagebox
import os
from PIL import Image, ImageTk
import configparser

class SharedData:
    def __init__(self):
        self.left_image = None
        self.right_image = None
        self.overlap_region = 200
        self.leftSide = True

    def write_to_config(self, filename='config.ini'):
        config = configparser.ConfigParser()
        config['DEFAULT'] = {
            'overlap_region': str(self.overlap_region),
            'leftSide': str(self.leftSide)
        }
        with open(filename, 'w') as configfile:
            config.write(configfile)

    def read_from_config(self, filename='config.ini'):
        config = configparser.ConfigParser()
        config.read(filename)
        self.overlap_region = config['DEFAULT'].getint('overlap_region', self.overlap_region)
        self.leftSide = config['DEFAULT'].getboolean('leftSide', self.leftSide)

Updated by Dylan WIDJAJA 6 months ago · 5 revisions