Project

General

Profile

Code » History » Version 6

Faiq Sayyidan SETIAWAN, 10/24/2024 01:23 PM

1 1 Faiq Sayyidan SETIAWAN
---
2
3 6 Faiq Sayyidan SETIAWAN
*[[/|Home]]*   |   *[[Team Members]]*   |   *[[Project Description]]*   |   *[[Code]]*   |   *[[UML Diagrams]]*   |   *[[Results]]*   |
4 1 Faiq Sayyidan SETIAWAN
5
---
6 2 Faiq Sayyidan SETIAWAN
7
h1=. <pre>
8 3 Faiq Sayyidan SETIAWAN
Code </pre>
9 4 Faiq Sayyidan SETIAWAN
10
h2. alpha_blending_v2.py: everything
11
12
<pre><code class="python">
13
import tkinter as tk
14
15
from tkinter import filedialog, messagebox
16
17
from tkinter import ttk
18
19
import configparser
20
21
import cv2
22
23
import numpy as np
24
25
import os
26
27
import logging
28
29
from PIL import Image, ImageTk
30
31
# Setup logging
32
33
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
34
35
# ConfigReader class definition
36
37
class ConfigReader:
38
39
    def __init__(self, config_path):
40
41
        self.config_parser = configparser.ConfigParser()
42
43
        self.config_parser.read(config_path)
44
45
    def getImageName(self):
46
47
        return str(self.config_parser['DEFAULT']['image_name'])
48
49
    def getProjectedImageWidth(self):
50
51
        return int(self.config_parser['DEFAULT']['projected_image_width'])
52
53
    def getProjectedOverlapWidth(self):
54
55
        return int(self.config_parser['DEFAULT']['projected_overlap_width'])
56
57
    def getGamma(self):
58
59
        return float(self.config_parser['DEFAULT']['gamma'])
60
61
    def getImageSide(self):
62
63
        return int(self.config_parser['DEFAULT']['image_side'])
64
65
    def getTransparencyFactor(self):
66
67
        return float(self.config_parser['DEFAULT'].get('transparency_factor', 1.0))
68
69
# MainDisplay class definition
70
71
class MainDisplay:
72
73
    def readImage(self, image_path):
74
75
        # Loads an image from the specified path with 3 channels (BGR)
76
77
        image = cv2.imread(image_path, cv2.IMREAD_COLOR)
78
79
        if image is None:
80
81
            logging.error(f"Error loading image at {image_path}")
82
83
        else:
84
85
            logging.info(f"Loaded image {image_path} with shape: {image.shape}")
86
87
        return image
88
89
    def setImage(self, image):
90
91
        # Placeholder for image processing if necessary
92
93
        # Here you can modify or prepare the image before displaying
94
95
        logging.info(f"Setting image with shape: {image.shape}")
96
97
        return image
98
99
# MaskCreator class definition
100
101
class MaskCreator:
102
103
    def __init__(self, image, transparency_factor):
104
105
        self.__image = image
106
107
        self.__alpha_gradient = None
108
109
        self.__gamma_corrected = None
110
111
        self.result_image = None
112
113
        self.__mask = None
114
115
        self.transparency_factor = transparency_factor
116
117
    def smoothstep(self, edge0, edge1, x):
118
119
        # Smoothstep function for smoother transition (non-linear gradient)
120
121
        x = np.clip((x - edge0) / (edge1 - edge0), 0, 1)
122
123
        return x * x * (3 - 2 * x)
124
125
    def create_mask(self, image_side, mask_width, image_width):
126
127
        self.__mask = self.__image.shape[1] * mask_width // image_width
128
129
        gradient = np.linspace(0, 1, self.__mask)
130
131
        # Apply smoothstep to create a smoother gradient
132
133
        smooth_gradient = self.smoothstep(0, 1, gradient)
134
135
        # Apply transparency factor to adjust the strength of the blending
136
137
        smooth_gradient = smooth_gradient * self.transparency_factor
138
139
        if image_side == 1:
140
141
            # Gradient from transparent to opaque (middle to right)
142
143
            self.__alpha_gradient = smooth_gradient
144
145
        elif image_side == 0:
146
147
            # Gradient from opaque to transparent (left to middle)
148
149
            self.__alpha_gradient = smooth_gradient[::-1]  # Reverse for the left side
150
151
    def gammaCorrection(self, gamma):
152
153
        # Apply gamma correction
154
155
        inv_gamma = 1.0 / gamma
156
157
        table = np.array([((i / 255.0) ** inv_gamma) * 255
158
159
                          for i in np.arange(256)]).astype("uint8")
160
161
        self.__gamma_corrected = cv2.LUT(self.__image, table)
162
163
        logging.info(f"Applied gamma correction with gamma={gamma}")
164
165
        # Save gamma corrected image for inspection
166
167
        cv2.imwrite("gamma_corrected.png", self.__gamma_corrected)
168
169
    def alpha_blending(self, image_side):
170
171
        """
172
173
        Applies alpha blending on the gamma-corrected image.
174
175
        Combines the gamma-corrected part of the image with a black background using the alpha gradient mask.
176
177
        """
178
179
        # Initialize result_image to be the gamma-corrected image
180
181
        self.result_image = self.__gamma_corrected.copy()
182
183
        if image_side == 1:  # Right side
184
185
            # Create a region of interest (ROI) where blending will occur (right side of the image)
186
187
            roi = self.result_image[:, :self.__mask].astype(np.float32)
188
189
            # Create black background for blending
190
191
            black_background = np.zeros_like(roi, dtype=np.float32)
192
193
            # Apply the alpha mask to blend gamma-corrected image with black background
194
195
            alpha = self.__alpha_gradient.reshape(1, -1, 1).astype(np.float32)
196
197
            blended = (alpha * roi + (1 - alpha) * black_background)
198
199
            blended = np.clip(blended, 0, 255).astype(np.uint8)
200
201
            # Place the blended region back in the result image
202
203
            self.result_image[:, :self.__mask] = blended
204
205
            logging.info(f"Applied alpha blending on the right side with mask width {self.__mask}")
206
207
            # Save blended region for debugging
208
209
            cv2.imwrite("blended_right_side.png", blended)
210
211
        elif image_side == 0:  # Left side
212
213
            # Create a region of interest (ROI) where blending will occur (left side of the image)
214
215
            roi = self.result_image[:, -self.__mask:].astype(np.float32)
216
217
            # Create black background for blending
218
219
            black_background = np.zeros_like(roi, dtype=np.float32)
220
221
            # Apply the alpha mask to blend gamma-corrected image with black background
222
223
            alpha = self.__alpha_gradient.reshape(1, -1, 1).astype(np.float32)
224
225
            blended = (alpha * roi + (1 - alpha) * black_background)
226
227
            blended = np.clip(blended, 0, 255).astype(np.uint8)
228
229
            # Place the blended region back in the result image
230
231
            self.result_image[:, -self.__mask:] = blended
232
233
            logging.info(f"Applied alpha blending on the left side with mask width {self.__mask}")
234
235
            # Save blended region for debugging
236
237
            cv2.imwrite("blended_left_side.png", blended)
238
239
def process_image(config_path, main_display):
240
241
    """
242
243
    Processes an image based on the provided configuration.
244
245
    Args:
246
247
        config_path (str): Path to the configuration file.
248
249
        main_display (MainDisplay): Instance of MainDisplay for image operations.
250
251
    Returns:
252
253
        tuple: Processed image and its corresponding name.
254
255
    """
256
257
    # Load configuration
258
259
    config_reader = ConfigReader(config_path)
260
261
    # Retrieve configuration parameters
262
263
    mask_width = config_reader.getProjectedOverlapWidth()
264
265
    image_width = config_reader.getProjectedImageWidth()
266
267
    gamma = config_reader.getGamma()
268
269
    image_side = config_reader.getImageSide()
270
271
    image_path = config_reader.getImageName()
272
273
    transparency_factor = config_reader.getTransparencyFactor()
274
275
    # Determine image side name
276
277
    if image_side == 0:
278
279
        image_name = 'left'
280
281
    elif image_side == 1:
282
283
        image_name = 'right'
284
285
    else:
286
287
        logging.error(f"Invalid ImageSide value in {config_path}. Use 0 for left image, 1 for right image.")
288
289
        return None, None
290
291
    # Load image
292
293
    image = main_display.readImage(image_path)
294
295
    if image is None:
296
297
        logging.error(f"Image loading failed for {image_path}. Skipping...")
298
299
        return None, None
300
301
    # Initialize result image
302
303
    result_image = main_display.setImage(image).copy()
304
305
    cv2.imwrite("initial_result_image.png", result_image)  # Save initial image
306
307
    # Initialize MaskCreator
308
309
    mask_creator = MaskCreator(image, transparency_factor)
310
311
    # Apply image modifications
312
313
    mask_creator.create_mask(image_side, mask_width, image_width)
314
315
    mask_creator.gammaCorrection(gamma)
316
317
    mask_creator.result_image = result_image
318
319
    mask_creator.alpha_blending(image_side)
320
321
    # Save final result for inspection
322
323
    cv2.imwrite("final_result_image.png", mask_creator.result_image)
324
325
    return mask_creator.result_image, image_name
326
327
def save_image(image, name):
328
329
    """
330
331
    Save the processed image to the project folder.
332
333
    Args:
334
335
        image (np.ndarray): The image to be saved.
336
337
        name (str): Name of the image (left or right).
338
339
    """
340
341
    # Define the output path
342
343
    output_dir = os.path.join(os.getcwd(), "processed_images")
344
345
    os.makedirs(output_dir, exist_ok=True)
346
347
    # Create the output file name
348
349
    output_path = os.path.join(output_dir, f"processed_image_{name}.png")
350
351
    # Save the image
352
353
    cv2.imwrite(output_path, image)
354
355
    logging.info(f"Saved processed image: {output_path}")
356
357
# GUI Application class definition
358
359
class ImageProcessingApp:
360
361
    def __init__(self, root):
362
363
        self.root = root
364
365
        self.root.title("Image Processing Application")
366
367
        # UI elements
368
369
        self.config_left = ""
370
371
        self.config_right = ""
372
373
        self.left_image_label = tk.Label(root, text="Left Image: None", anchor="w")
374
375
        self.left_image_label.pack(fill="x", padx=5, pady=5)
376
377
        self.right_image_label = tk.Label(root, text="Right Image: None", anchor="w")
378
379
        self.right_image_label.pack(fill="x", padx=5, pady=5)
380
381
        self.select_left_button = tk.Button(root, text="Select Left Config", command=self.select_left_config)
382
383
        self.select_left_button.pack(pady=5)
384
385
        self.select_right_button = tk.Button(root, text="Select Right Config", command=self.select_right_config)
386
387
        self.select_right_button.pack(pady=5)
388
389
        self.process_button = tk.Button(root, text="Process Images", command=self.process_images, state="disabled")
390
391
        self.process_button.pack(pady=10)
392
393
        self.progress_bar = ttk.Progressbar(root, orient="horizontal", length=300, mode="determinate")
394
395
        self.progress_bar.pack(pady=10)
396
397
        self.image_display = tk.Label(root)
398
399
        self.image_display.pack()
400
401
    def select_left_config(self):
402
403
        self.config_left = filedialog.askopenfilename(title="Select Left Config File",
404
405
                                                      filetypes=[("INI files", "*.ini")])
406
407
        if self.config_left:
408
409
            self.left_image_label.config(text=f"Left Image Config: {os.path.basename(self.config_left)}")
410
411
            self.enable_process_button()
412
413
    def select_right_config(self):
414
415
        self.config_right = filedialog.askopenfilename(title="Select Right Config File",
416
417
                                                       filetypes=[("INI files", "*.ini")])
418
419
        if self.config_right:
420
421
            self.right_image_label.config(text=f"Right Image Config: {os.path.basename(self.config_right)}")
422
423
            self.enable_process_button()
424
425
    def enable_process_button(self):
426
427
        if self.config_left and self.config_right:
428
429
            self.process_button.config(state="normal")
430
431
    def process_images(self):
432
433
        config_files = [self.config_left, self.config_right]
434
435
        processed_images = {}
436
437
        self.progress_bar["value"] = 0
438
439
        self.root.update_idletasks()
440
441
        for i, config_file in enumerate(config_files):
442
443
            image, name = process_image(config_file, MainDisplay())
444
445
            if image is not None and name is not None:
446
447
                processed_images[name] = image
448
449
            self.progress_bar["value"] += 50
450
451
            self.root.update_idletasks()
452
453
        for name, img in processed_images.items():
454
455
            save_image(img, name)
456
457
        self.display_image(processed_images)
458
459
        self.progress_bar["value"] = 100
460
461
        self.root.update_idletasks()
462
463
        messagebox.showinfo("Processing Complete", "Images processed and saved successfully.")
464
465
    def display_image(self, processed_images):
466
467
        for name, img in processed_images.items():
468
469
            img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
470
471
            pil_img = Image.fromarray(img_rgb)
472
473
            img_tk = ImageTk.PhotoImage(pil_img)
474
475
            self.image_display.config(image=img_tk)
476
477
            self.image_display.image = img_tk
478
479
def main():
480
481
    root = tk.Tk()
482
483
    app = ImageProcessingApp(root)
484
485
    root.mainloop()
486
487
if __name__ == "__main__":
488
489
    main()
490
491
</code></pre>