Project

General

Profile

Codes » History » Version 5

Dylan WIDJAJA, 11/06/2024 10:26 PM

1 1 Dylan WIDJAJA
___
2
3 4 Ty Hikaru DAULTON
[[/|Home]] | [[About Us]] | [[About the Project]] | [[UML Diagrams]] | [[Codes]] | [[Outcome]] | [[ System Requirements]]
4 1 Dylan WIDJAJA
5
6
___
7
8 2 Ty Hikaru DAULTON
h1=. <pre>  Codes
9
</pre>
10 5 Dylan WIDJAJA
11
h2. main.py: This file initializes the entire project
12
<pre><code class="python">
13
import tkinter as tk
14
from GUI import GUI
15
if __name__ == "__main__":
16
    root = tk.Tk()
17
    root.geometry("200x200")
18
    app = GUI(root)
19
    root.mainloop()
20
21
</code></pre>
22
23
h2. Commands.py: A class to handle various commands related to image processing and display
24
<pre><code class="python">
25
import cv2 as cv
26
import numpy as np
27
import tkinter as tk
28
from tkinter import filedialog, messagebox
29
import os
30
from PIL import Image, ImageTk
31
import configparser
32
from ImageIO import ImageIO
33
34
class Commands:
35
    def __init__(self, gui, shared_data):
36
        self.gui = gui
37
        self.shared_data = shared_data
38
        self.io = ImageIO(shared_data)
39
40
    def load_left_image(self):
41
        filepath = filedialog.askopenfilename(title="Select Left Image")
42
        if filepath:
43
            self.shared_data.left_image = self.io.load_image(filepath)
44
            print("Left image loaded.")
45
46
    def load_right_image(self):
47
        filepath = filedialog.askopenfilename(title="Select Right Image")
48
        if filepath:
49
            self.shared_data.right_image = self.io.load_image(filepath)
50
            print("Right image loaded.")
51
52
    def smooth_gradient(self,length, rate):
53
        # Create a linear gradient that transitions from full brightness (1) to zero (0)
54
        x = np.linspace(0, 1, length)
55
        # Apply a non-linear function (e.g., quadratic) for a smoother transition
56
        gradient = 1 - (x ** 2)  # Quadratic falloff to create a smooth transition
57
        gradient = np.clip(gradient * rate, 0, 1)  # Scale the gradient by the rate
58
        return gradient
59
60
    def show_left_image(self):
61
        if self.shared_data.left_image is not None:
62
63
            
64
65
            self.shared_data.isLeft = True
66
            left = self.shared_data.left_image.copy()
67
            height = left.shape[0]
68
            left_overlap_start = left.shape[1] - self.shared_data.overlap_region
69
            
70
            # Create a smooth gradient alpha mask for the left image (1 to 0)
71
            gradient_alpha = np.linspace(1, 0, self.shared_data.overlap_region)
72
            gradient_alpha = np.tile(gradient_alpha, (height, 1))
73
74
            # Apply the alpha gradient to the overlapping region
75
            left_overlap = left[:, left_overlap_start:, :].copy()
76
            left_overlap = (left_overlap * gradient_alpha[:, :, np.newaxis]).astype(np.uint8)
77
78
            # Replace the modified overlapping region back into the original left image
79
            left[:, left_overlap_start:, :] = left_overlap
80
            
81
            #Debug code
82
            #Write the edited image to file
83
            self.io.write_image(self, img=left, fileName="left_Modified.png")
84
            print("Left image written to file.")
85
            # Show the modified image
86
            self.io.show_image(left, canvas=self.gui.canvas)
87
            print("Left image displayed.")
88
89
            
90
        else:
91
            messagebox.showerror("Error", "No left image loaded!")
92
93
    def show_right_image(self):
94
        if self.shared_data.right_image is not None:
95
            self.shared_data.isLeft = False
96
            right = self.shared_data.right_image.copy()
97
            height = right.shape[0]
98
            right_overlap_end = self.shared_data.overlap_region
99
            
100
            # Create a smooth gradient alpha mask for the right image (0 to 1)
101
            gradient_alpha = np.linspace(0, 1, right_overlap_end)
102
            gradient_alpha = np.tile(gradient_alpha, (height, 1))
103
104
            # Apply the alpha gradient to the overlapping region
105
            right_overlap = right[:, :right_overlap_end, :].copy()
106
            right_overlap = (right_overlap * gradient_alpha[:, :, np.newaxis]).astype(np.uint8)
107
108
            # Replace the modified overlapping region back into the original right image
109
            right[:, :right_overlap_end, :] = right_overlap
110
            
111
            #Write modified image to file
112
            self.io.write_image(self, img=right, fileName="right_Modified.png")
113
            print("Right image written to file.")
114
            # Show the modified image
115
            self.io.show_image(right, canvas=self.gui.canvas)
116
            print("Right image displayed.")
117
            
118
        else:
119
            messagebox.showerror("Error", "No right image loaded!")
120
        
121
    def show_right_original(self):
122
        # Show original image
123
        if self.shared_data.right_image is not None:
124
            self.shared_data.isLeft = False
125
            right = self.shared_data.right_image.copy()
126
            self.io.show_image(right, canvas=self.gui.canvas)
127
            print("Right image displayed.")
128
        else:
129
            messagebox.showerror("Error", "No right image loaded!")
130
131
    def show_left_original(self):
132
        # Show original image
133
        if self.shared_data.left_image is not None:
134
            self.shared_data.isLeft = True
135
            left = self.shared_data.left_image.copy()
136
            self.io.show_image(left, canvas=self.gui.canvas)
137
            print("Left image displayed.")
138
        else:
139
            messagebox.showerror("Error", "No left image loaded!")
140
141
142
    def set_overlap_region(self):
143
        self.shared_data.overlap_region = self.gui.overlap.get()
144
        print(f"Overlap region set to {self.shared_data.overlap_region}")
145
        if(self.shared_data.isLeft):
146
            self.show_left_image()
147
        else:
148
            self.show_right_image()
149
150
    def increase_overlap(self):
151
        current_overlap = self.gui.overlap.get()
152
        self.gui.overlap.set(current_overlap + 1)
153
        self.set_overlap_region()
154
        if(self.shared_data.isLeft):
155
            self.show_left_image()
156
        else:
157
            self.show_right_image()
158
        
159
        
160
        
161
162
    def decrease_overlap(self):
163
        current_overlap = self.gui.overlap.get()
164
        self.gui.overlap.set(max(0, current_overlap - 1))  # Prevents negative overlap
165
        self.set_overlap_region()
166
        if(self.shared_data.isLeft):
167
            self.show_left_image()
168
        else:
169
            self.show_right_image()
170
171
</code></pre>
172
173
h2. GUI.py: This class creates a graphical user interface for the image projector 
174
<pre><code class="python">
175
import cv2 as cv
176
import numpy as np
177
import tkinter as tk
178
from tkinter import filedialog, messagebox
179
import os
180
from PIL import Image, ImageTk
181
import configparser
182
from SharedData import SharedData
183
from Commands import Commands
184
class GUI:
185
    def __init__(self, master):
186
        self.master = master
187
        self.master.title("Image Projector")
188
        self.canvas = tk.Canvas(self.master, bg="black")
189
        self.canvas.pack(fill=tk.BOTH, expand=True)
190
        self.shared_data = SharedData()
191
        self.commands = Commands(self, self.shared_data)
192
        self.control_window()
193
194
    def control_window(self):
195
        self.control_window = tk.Toplevel(self.master)
196
        self.control_window.title("Controls")
197
        
198
        self.overlap = tk.IntVar(value=200)
199
200
        # Label and entry for overlap region width
201
        self.overlapLabel = tk.Label(self.control_window, text='Overlap region Width', font=('calibre', 10, 'bold'))
202
        self.overlapBox = tk.Entry(self.control_window, textvariable=self.overlap)
203
        
204
        self.overlapLabel.grid(row=0, column=0, padx=5, pady=5)
205
        self.overlapBox.grid(row=0, column=1, padx=5, pady=5)
206
207
        # Button to set overlap region
208
        self.set_overlap_button = tk.Button(self.control_window, text="Set Overlap Region", command=self.commands.set_overlap_region)
209
        self.set_overlap_button.grid(row=1, column=0, columnspan=2, padx=5, pady=5)
210
211
        # New buttons for increasing and decreasing the overlap region
212
        self.increase_overlap_button = tk.Button(self.control_window, text="Increase Overlap", command=self.commands.increase_overlap)
213
        self.increase_overlap_button.grid(row=2, column=0, padx=5, pady=5)
214
215
        self.decrease_overlap_button = tk.Button(self.control_window, text="Decrease Overlap", command=self.commands.decrease_overlap)
216
        self.decrease_overlap_button.grid(row=2, column=1, padx=5, pady=5)
217
218
        # Load left and right image buttons
219
        self.button_load_left = tk.Button(self.control_window, text="Load Left Image", command=self.commands.load_left_image)
220
        self.button_load_left.grid(row=3, column=0, padx=5, pady=5)
221
222
        self.button_load_right = tk.Button(self.control_window, text="Load Right Image", command=self.commands.load_right_image)
223
        self.button_load_right.grid(row=3, column=1, padx=5, pady=5)
224
225
        # Show left and right image buttons
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.grid(row=4, column=0, padx=5, pady=5)
228
229
        self.button_show_right = tk.Button(self.control_window, text="Show Right Image", command=self.commands.show_right_image)
230
        self.button_show_right.grid(row=4, column=1, padx=5, pady=5)
231
232
        # Show original left and right image buttons
233
        self.button_show_left_original = tk.Button(self.control_window, text="Show Left Original", command=self.commands.show_left_original)
234
        self.button_show_left_original.grid(row=5, column=0, padx=5, pady=5)
235
236
        self.button_show_right_original = tk.Button(self.control_window, text="Show Right Original", command=self.commands.show_right_original)
237
        self.button_show_right_original.grid(row=5, column=1, padx=5, pady=5)
238
239
        self.save_config = tk.Button(self.control_window, text="Save Config", command=self.shared_data.write_to_config)
240
        self.save_config.grid(row=6, column=0, padx=5, pady=5)
241
242
        self.load_config = tk.Button(self.control_window, text="Load Config", command=self.shared_data.read_from_config)
243
        self.load_config.grid(row=6, column=1, padx=5, pady=5)
244
245
</code></pre>
246
247
h2. ImageIO.py: This class is in charge of image input and output 
248
<pre><code class="python">
249
import cv2 as cv
250
import numpy as np
251
import os
252
from PIL import Image
253
import tkinter as tk
254
255
class ImageIO:
256
    def __init__(self, shared_data):
257
        self.shared_data = shared_data
258
259
    def load_image(self, path):
260
        return cv.imread(path, cv.IMREAD_COLOR)
261
262
    def write_image(self, img, fileName="image.png"):
263
        if cv.imwrite(fileName, img):
264
            print(f"File {fileName} Written Successfully")
265
        else:
266
            print("File writing failed")
267
268
    def show_image(self, img, title="Image Title", canvas=None):
269
        # Set up OpenCV fullscreen window in the main thread
270
        cv.namedWindow(title, cv.WINDOW_NORMAL)
271
        cv.setWindowProperty(title, cv.WND_PROP_FULLSCREEN, cv.WINDOW_FULLSCREEN)
272
        
273
        # Resize the image to 1920x1080 resolution
274
        img_resized = cv.resize(img, (1920, 1080), interpolation=cv.INTER_LINEAR)
275
        
276
        # Display the resized image in OpenCV and keep it open without blocking Tkinter
277
        cv.imshow(title, img_resized)
278
279
        # Run a non-blocking loop to keep OpenCV window and Tkinter GUI responsive
280
        while cv.getWindowProperty(title, cv.WND_PROP_VISIBLE) >= 1:
281
            cv.waitKey(1)
282
            if canvas:
283
                canvas.update_idletasks()
284
                canvas.update()
285
286
        # Close the OpenCV window once the loop ends
287
        cv.destroyAllWindows()
288
289
</code></pre>
290
291
h2. SharedData.py: This class is used to store shared data between different classes 
292
<pre><code class="python">
293
import cv2 as cv
294
import numpy as np
295
import tkinter as tk
296
from tkinter import filedialog, messagebox
297
import os
298
from PIL import Image, ImageTk
299
import configparser
300
301
class SharedData:
302
    def __init__(self):
303
        self.left_image = None
304
        self.right_image = None
305
        self.overlap_region = 200
306
        self.leftSide = True
307
308
    def write_to_config(self, filename='config.ini'):
309
        config = configparser.ConfigParser()
310
        config['DEFAULT'] = {
311
            'overlap_region': str(self.overlap_region),
312
            'leftSide': str(self.leftSide)
313
        }
314
        with open(filename, 'w') as configfile:
315
            config.write(configfile)
316
317
    def read_from_config(self, filename='config.ini'):
318
        config = configparser.ConfigParser()
319
        config.read(filename)
320
        self.overlap_region = config['DEFAULT'].getint('overlap_region', self.overlap_region)
321
        self.leftSide = config['DEFAULT'].getboolean('leftSide', self.leftSide)
322
323
</code></pre>
324