Project

General

Profile

Files » ver2_commented.py

1st draft of commented file. Needs improve - Wonil KIM, 10/27/2024 07:53 PM

 
1
import cv2 as cv
2
import numpy as np
3
import tkinter as tk
4
from tkinter import filedialog, messagebox
5
import os
6
from PIL import Image, ImageTk
7

    
8
class SharedData:
9
    def __init__(self):
10
        self.left_image = None
11
        self.right_image = None
12
        self.overlap_region = 0
13

    
14
##
15
# @class This class is in charge of image input and output
16
#
17
class ImageIO:
18
    ##
19
    # @breif
20
    # initializator for whole method.
21
    # get parsed config.cfg data from the class 'SharedData'
22
    # 
23
    # @param self the class itself
24
    # @param shared_data parsed config.cfg data
25
    # @return None
26
    def __init__(self, shared_data):
27
        self.shared_data = shared_data
28

    
29
    ##
30
    # @breif function for loading image
31
    # @param path path of the image
32
    # @return numpy array, image
33
    def load_image(self, path):
34
        return cv.imread(path, cv.IMREAD_COLOR)
35

    
36
    ##
37
    # @brief function to manipulate the image
38
    # @param img, loaded image, numpy matrix
39
    # @param fileName, name of the image. "image.png" will be the default value if there are no input
40
    # @return None
41
    def write_image(self, img, fileName="image.png"):
42
        if cv.imwrite(fileName, img):
43
            print(f"File {fileName} Written Successfully")
44
        else:
45
            print("File writing failed")
46

    
47
    ## 
48
    # @breif function to print image through the projector
49
    # @param img numpy array, image matrix
50
    # @param title title of the window. 'Image Title' will be the default value when there is no input.
51
    # @param canvas parameter needed for library 'Tinker Canvas'. None is default value.
52
    # @return None
53
    def show_image(self, img, title="Image Title", canvas=None):
54
        img_rgb = cv.cvtColor(img, cv.COLOR_BGR2RGB)
55
        img_pil = Image.fromarray(img_rgb)
56
        img_tk = ImageTk.PhotoImage(img_pil)
57
        
58
        canvas.update_idletasks()  # Force the canvas to update its dimensions
59
        canvas_width = canvas.winfo_width()  # Get the updated canvas width
60
        
61
        if self.shared_data.isLeft:
62
            # Align image to the top-right corner
63
            canvas.create_image(canvas_width, 0, anchor=tk.NE, image=img_tk)
64
        else:
65
            # Align image to the top-left corner
66
            canvas.create_image(0, 0, anchor=tk.NW, image=img_tk)
67
        
68
        canvas.image = img_tk  # Prevent image from being garbage collected
69

    
70
class Commands:
71
    ## \brief A class to handle various commands related to image processing.
72
    #
73
    # The Commands class provides methods to load, display, and set overlap regions
74
    # for images using a graphical user interface (GUI) and shared data. It integrates
75
    # with an ImageIO class to handle image input and output operations.
76
    #
77
    # \author 
78
    # \version 1.0
79
    # \date 2024-10-24
80
    def __init__(self, gui, shared_data):
81
        ## \brief Initializes the Commands instance.
82
        #
83
        # Sets up the GUI interface and shared data. It also initializes
84
        # an ImageIO instance for handling image operations.
85
        #
86
        # \param gui The GUI instance used for displaying images and getting user input.
87
        # \param shared_data The shared data structure that stores images and other state information.
88
        self.gui = gui
89
        self.shared_data = shared_data
90
        self.io = ImageIO(shared_data)
91
    def load_left_image(self):
92
        ## \brief Loads the left image from a file.
93
        #
94
        # Opens a file dialog for the user to select a left image file. If a valid file path
95
        # is provided, the left image is loaded into the shared data. A message is printed
96
        # to confirm the loading process.
97
        # 
98
        # \warning This method does not check the file format or image content.
99
        filepath = filedialog.askopenfilename(title="Select Left Image")
100
        if filepath:
101
            self.shared_data.left_image = self.io.load_image(filepath)
102
            print("Left image loaded.")
103
    def load_right_image(self):
104
        ## \brief Loads the right image from a file.
105
        #
106
        # Opens a file dialog for the user to select a right image file. If a valid file path
107
        # is provided, the right image is loaded into the shared data. A message is printed
108
        # to confirm the loading process.
109
        #
110
        # \warning This method does not check the file format or image content.
111
        filepath = filedialog.askopenfilename(title="Select Right Image")
112
        if filepath:
113
            self.shared_data.right_image = self.io.load_image(filepath)
114
            print("Right image loaded.")
115
    def show_left_image(self):
116
        ## \brief Displays the left image on the GUI.
117
        #
118
        # If the left image is available in the shared data, it is shown on the GUI canvas.
119
        # Otherwise, an error message is displayed to the user.
120
        #
121
        # \exception Shows a message box error if no left image is loaded.
122
        if self.shared_data.left_image is not None:
123
            self.io.show_image(self.shared_data.left_image, canvas=self.gui.canvas)
124
            print("Left image displayed.")
125
        else:
126
            messagebox.showerror("Error", "No left image loaded!")
127
    def show_right_image(self):
128
        ## \brief Displays the right image on the GUI.
129
        #
130
        # If the right image is available in the shared data, it is shown on the GUI canvas.
131
        # Otherwise, an error message is displayed to the user.
132
        #
133
        # \exception Shows a message box error if no right image is loaded.
134
        if self.shared_data.right_image is not None:
135
            self.io.show_image(self.shared_data.right_image, canvas=self.gui.canvas)
136
            print("Right image displayed.")
137
        else:
138
            messagebox.showerror("Error", "No right image loaded!")
139
    def set_overlap_region(self):
140
        ## \brief Sets the overlap region for image processing.
141
        #
142
        # Retrieves the overlap region value from the GUI input and updates the shared data
143
        # accordingly. A message is printed to confirm the overlap region setting.
144
        #
145
        # \note This method assumes that the GUI input is a valid overlap region value.
146
        self.shared_data.overlap_region = self.gui.overlap.get()
147
        print(f"Overlap region set to {self.shared_data.overlap_region}")
148

    
149
# \class GUI
150
 # \brief This class creates a graphical user interface for the image projector.
151
 #
152
 # The GUI class is responsible for initializing the main window and setting up the control window. It provides
153
 # a canvas for displaying images and buttons for loading and displaying images. 
154
 #
155
 # \author
156
 # \date 2024-10-27
157
class GUI:
158
    # \brief Constructor for the GUI class.
159
    #
160
    # This constructor initializes the main window and sets up the control window.
161
    #
162
    # \param master the parent window
163
    def __init__(self, master):
164
        self.master = master
165
        # \brief Set the title of the main window
166
        self.master.title("Image Projector")
167
        # \brief Create a canvas for displaying images
168
        #
169
        # The canvas is used to project images loaded through the control window. It is set to black.
170
        self.canvas = tk.Canvas(self.master, bg="black")
171
        # \brief Pack the canvas to fill the window.
172
        #
173
        # The canvas is packed to fill the window. The "fill = tk.BOTH" parameter stretches the canvas both horizontally and vertically.
174
        self.canvas.pack(fill=tk.BOTH, expand=True)
175
        # \brief Initialize the shared data class.
176
        #
177
        # Holds information about the images and overlap region.
178
        self.shared_data = SharedData()
179
        # \brief Initialize the commands class.
180
        #
181
        # Handles the commands for loading and displaying images.
182
        # \param self the object pointer
183
        # \param self.shared_data the shared data class
184
        self.commands = Commands(self, self.shared_data)
185
        self.control_window()
186
    # \brief Call the function to set up the control window.
187
    #
188
    # A seperate window which allows loading and setting up images and overlap region. It contains buttons and entry
189
    # fields for loading images, setting the overlap region and control the display of images.
190
    def control_window(self):
191
        # \brief Initalize the control window.
192
        self.control_window = tk.Toplevel(self.master)
193
        # \brief Set the title of the control window.
194
        self.control_window.title("Controls")
195
        # \brief Initialize the overlap variable to store the overlap region width.
196
        #
197
        # The overlap variable will determine the region of overlap between the left and right images.
198
        self.overlap = tk.IntVar()
199
        # \brief Create a label for overlap region input box.
200
        self.label = tk.Label(self.control_window, text='Overlap region Width', font=('calibre', 10, 'bold'))
201
        # \brief Create an entry box for user to input the overlap region width.
202
        #
203
        # The entry box will take the user input for the overlap region width.
204
        self.overlapBox = tk.Entry(self.control_window, textvariable=self.overlap)
205
        # \brief Pack the label and entry box into the control window.
206
        self.label.pack()
207
        self.overlapBox.pack()
208
        # \brief Create a button to set the overlap region based on user input.
209
        #
210
        # This button will call the set_overlap_region function from the commands class.
211
        self.set_overlap_button = tk.Button(self.control_window, text="Set Overlap Region", command=self.commands.set_overlap_region)
212
        self.set_overlap_button.pack()
213
        # \brief Create a button to load the left image.
214
        #
215
        # This button will call the load_left_image function from the commands class.
216
        self.button_load_left = tk.Button(self.control_window, text="Load Left Image", command=self.commands.load_left_image)
217
        self.button_load_left.pack()
218
        # \brief Create a button to load the right image.
219
        #
220
        # This button will call the load_right_image function from the commands class.
221
        self.button_load_right = tk.Button(self.control_window, text="Load Right Image", command=self.commands.load_right_image)
222
        self.button_load_right.pack()
223
        # \brief Create a button to display the left image.
224
        #
225
        # This button will call the show_left_image function from the commands class.
226
        self.button_show_left = tk.Button(self.control_window, text="Show Left Image", command=self.commands.show_left_image)
227
        self.button_show_left.pack()
228
        # \brief Create a button to display the right image.
229
        #
230
        # This button will call the show_right_image function from the commands class.
231
        self.button_show_right = tk.Button(self.control_window, text="Show Right Image", command=self.commands.show_right_image)
232
        self.button_show_right.pack()
233
# \brief Main program entry point.
234
#
235
# This block initializes the tkinter root window, sets the window size and initializes the GUI class.
236
if __name__ == "__main__":
237
# \brief Initialize the tkinter root window.
238
    root = tk.Tk()
239
    # \brief Set the window size to 1280x800.
240
    root.geometry("1280x800")
241
    # \brief Create an instance of the GUI class passing the root window.
242
    app = GUI(root)
243
    # \brief Start the tkinter main loop to display the window and handle events.
244
    root.mainloop()
(21-21/49)