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()
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()
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)
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()
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)
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