Project

General

Profile

Code » History » Revision 4

Revision 3 (Faiq Sayyidan SETIAWAN, 10/17/2024 03:39 PM) → Revision 4/9 (Faiq Sayyidan SETIAWAN, 10/24/2024 01:18 PM)

 
 --- 

 *[[Wiki]]*     |     *[[Team Members]]*     |     *[[Project Description]]*     |     *[[Code]]*     |     *[[UML Diagrams]]*     |     *[[Results]]*     | 

 --- 

 h1=. <pre> 
 Code </pre> 

 h2. alpha_blending_v2.py: everything 

 <pre><code class="python"> 
 import tkinter as tk 

 from tkinter import filedialog, messagebox 

 from tkinter import ttk 

 import configparser 

 import cv2 

 import numpy as np 

 import os 

 import logging 

 from PIL import Image, ImageTk 

 # Setup logging 

 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') 

 # ConfigReader class definition 

 class ConfigReader: 

     def __init__(self, config_path): 

         self.config_parser = configparser.ConfigParser() 

         self.config_parser.read(config_path) 

     def getImageName(self): 

         return str(self.config_parser['DEFAULT']['image_name']) 

     def getProjectedImageWidth(self): 

         return int(self.config_parser['DEFAULT']['projected_image_width']) 

     def getProjectedOverlapWidth(self): 

         return int(self.config_parser['DEFAULT']['projected_overlap_width']) 

     def getGamma(self): 

         return float(self.config_parser['DEFAULT']['gamma']) 

     def getImageSide(self): 

         return int(self.config_parser['DEFAULT']['image_side']) 

     def getTransparencyFactor(self): 

         return float(self.config_parser['DEFAULT'].get('transparency_factor', 1.0)) 

 # MainDisplay class definition 

 class MainDisplay: 

     def readImage(self, image_path): 

         # Loads an image from the specified path with 3 channels (BGR) 

         image = cv2.imread(image_path, cv2.IMREAD_COLOR) 

         if image is None: 

             logging.error(f"Error loading image at {image_path}") 

         else: 

             logging.info(f"Loaded image {image_path} with shape: {image.shape}") 

         return image 

     def setImage(self, image): 

         # Placeholder for image processing if necessary 

         # Here you can modify or prepare the image before displaying 

         logging.info(f"Setting image with shape: {image.shape}") 

         return image 

 # MaskCreator class definition 

 class MaskCreator: 

     def __init__(self, image, transparency_factor): 

         self.__image = image 

         self.__alpha_gradient = None 

         self.__gamma_corrected = None 

         self.result_image = None 

         self.__mask = None 

         self.transparency_factor = transparency_factor 

     def smoothstep(self, edge0, edge1, x): 

         # Smoothstep function for smoother transition (non-linear gradient) 

         x = np.clip((x - edge0) / (edge1 - edge0), 0, 1) 

         return x * x * (3 - 2 * x) 

     def create_mask(self, image_side, mask_width, image_width): 

         self.__mask = self.__image.shape[1] * mask_width // image_width 

         gradient = np.linspace(0, 1, self.__mask) 

         # Apply smoothstep to create a smoother gradient 

         smooth_gradient = self.smoothstep(0, 1, gradient) 

         # Apply transparency factor to adjust the strength of the blending 

         smooth_gradient = smooth_gradient * self.transparency_factor 

         if image_side == 1: 

             # Gradient from transparent to opaque (middle to right) 

             self.__alpha_gradient = smooth_gradient 

         elif image_side == 0: 

             # Gradient from opaque to transparent (left to middle) 

             self.__alpha_gradient = smooth_gradient[::-1]    # Reverse for the left side 

     def gammaCorrection(self, gamma): 

         # Apply gamma correction 

         inv_gamma = 1.0 / gamma 

         table = np.array([((i / 255.0) ** inv_gamma) * 255 

                           for i in np.arange(256)]).astype("uint8") 

         self.__gamma_corrected = cv2.LUT(self.__image, table) 

         logging.info(f"Applied gamma correction with gamma={gamma}") 

         # Save gamma corrected image for inspection 

         cv2.imwrite("gamma_corrected.png", self.__gamma_corrected) 

     def alpha_blending(self, image_side): 

         """ 

         Applies alpha blending on the gamma-corrected image. 

         Combines the gamma-corrected part of the image with a black background using the alpha gradient mask. 

         """ 

         # Initialize result_image to be the gamma-corrected image 

         self.result_image = self.__gamma_corrected.copy() 

         if image_side == 1:    # Right side 

             # Create a region of interest (ROI) where blending will occur (right side of the image) 

             roi = self.result_image[:, :self.__mask].astype(np.float32) 

             # Create black background for blending 

             black_background = np.zeros_like(roi, dtype=np.float32) 

             # Apply the alpha mask to blend gamma-corrected image with black background 

             alpha = self.__alpha_gradient.reshape(1, -1, 1).astype(np.float32) 

             blended = (alpha * roi + (1 - alpha) * black_background) 

             blended = np.clip(blended, 0, 255).astype(np.uint8) 

             # Place the blended region back in the result image 

             self.result_image[:, :self.__mask] = blended 

             logging.info(f"Applied alpha blending on the right side with mask width {self.__mask}") 

             # Save blended region for debugging 

             cv2.imwrite("blended_right_side.png", blended) 

         elif image_side == 0:    # Left side 

             # Create a region of interest (ROI) where blending will occur (left side of the image) 

             roi = self.result_image[:, -self.__mask:].astype(np.float32) 

             # Create black background for blending 

             black_background = np.zeros_like(roi, dtype=np.float32) 

             # Apply the alpha mask to blend gamma-corrected image with black background 

             alpha = self.__alpha_gradient.reshape(1, -1, 1).astype(np.float32) 

             blended = (alpha * roi + (1 - alpha) * black_background) 

             blended = np.clip(blended, 0, 255).astype(np.uint8) 

             # Place the blended region back in the result image 

             self.result_image[:, -self.__mask:] = blended 

             logging.info(f"Applied alpha blending on the left side with mask width {self.__mask}") 

             # Save blended region for debugging 

             cv2.imwrite("blended_left_side.png", blended) 

 def process_image(config_path, main_display): 

     """ 

     Processes an image based on the provided configuration. 

     Args: 

         config_path (str): Path to the configuration file. 

         main_display (MainDisplay): Instance of MainDisplay for image operations. 

     Returns: 

         tuple: Processed image and its corresponding name. 

     """ 

     # Load configuration 

     config_reader = ConfigReader(config_path) 

     # Retrieve configuration parameters 

     mask_width = config_reader.getProjectedOverlapWidth() 

     image_width = config_reader.getProjectedImageWidth() 

     gamma = config_reader.getGamma() 

     image_side = config_reader.getImageSide() 

     image_path = config_reader.getImageName() 

     transparency_factor = config_reader.getTransparencyFactor() 

     # Determine image side name 

     if image_side == 0: 

         image_name = 'left' 

     elif image_side == 1: 

         image_name = 'right' 

     else: 

         logging.error(f"Invalid ImageSide value in {config_path}. Use 0 for left image, 1 for right image.") 

         return None, None 

     # Load image 

     image = main_display.readImage(image_path) 

     if image is None: 

         logging.error(f"Image loading failed for {image_path}. Skipping...") 

         return None, None 

     # Initialize result image 

     result_image = main_display.setImage(image).copy() 

     cv2.imwrite("initial_result_image.png", result_image)    # Save initial image 

     # Initialize MaskCreator 

     mask_creator = MaskCreator(image, transparency_factor) 

     # Apply image modifications 

     mask_creator.create_mask(image_side, mask_width, image_width) 

     mask_creator.gammaCorrection(gamma) 

     mask_creator.result_image = result_image 

     mask_creator.alpha_blending(image_side) 

     # Save final result for inspection 

     cv2.imwrite("final_result_image.png", mask_creator.result_image) 

     return mask_creator.result_image, image_name 

 def save_image(image, name): 

     """ 

     Save the processed image to the project folder. 

     Args: 

         image (np.ndarray): The image to be saved. 

         name (str): Name of the image (left or right). 

     """ 

     # Define the output path 

     output_dir = os.path.join(os.getcwd(), "processed_images") 

     os.makedirs(output_dir, exist_ok=True) 

     # Create the output file name 

     output_path = os.path.join(output_dir, f"processed_image_{name}.png") 

     # Save the image 

     cv2.imwrite(output_path, image) 

     logging.info(f"Saved processed image: {output_path}") 

 # GUI Application class definition 

 class ImageProcessingApp: 

     def __init__(self, root): 

         self.root = root 

         self.root.title("Image Processing Application") 

         # UI elements 

         self.config_left = "" 

         self.config_right = "" 

         self.left_image_label = tk.Label(root, text="Left Image: None", anchor="w") 

         self.left_image_label.pack(fill="x", padx=5, pady=5) 

         self.right_image_label = tk.Label(root, text="Right Image: None", anchor="w") 

         self.right_image_label.pack(fill="x", padx=5, pady=5) 

         self.select_left_button = tk.Button(root, text="Select Left Config", command=self.select_left_config) 

         self.select_left_button.pack(pady=5) 

         self.select_right_button = tk.Button(root, text="Select Right Config", command=self.select_right_config) 

         self.select_right_button.pack(pady=5) 

         self.process_button = tk.Button(root, text="Process Images", command=self.process_images, state="disabled") 

         self.process_button.pack(pady=10) 

         self.progress_bar = ttk.Progressbar(root, orient="horizontal", length=300, mode="determinate") 

         self.progress_bar.pack(pady=10) 

         self.image_display = tk.Label(root) 

         self.image_display.pack() 

     def select_left_config(self): 

         self.config_left = filedialog.askopenfilename(title="Select Left Config File", 

                                                       filetypes=[("INI files", "*.ini")]) 

         if self.config_left: 

             self.left_image_label.config(text=f"Left Image Config: {os.path.basename(self.config_left)}") 

             self.enable_process_button() 

     def select_right_config(self): 

         self.config_right = filedialog.askopenfilename(title="Select Right Config File", 

                                                        filetypes=[("INI files", "*.ini")]) 

         if self.config_right: 

             self.right_image_label.config(text=f"Right Image Config: {os.path.basename(self.config_right)}") 

             self.enable_process_button() 

     def enable_process_button(self): 

         if self.config_left and self.config_right: 

             self.process_button.config(state="normal") 

     def process_images(self): 

         config_files = [self.config_left, self.config_right] 

         processed_images = {} 

         self.progress_bar["value"] = 0 

         self.root.update_idletasks() 

         for i, config_file in enumerate(config_files): 

             image, name = process_image(config_file, MainDisplay()) 

             if image is not None and name is not None: 

                 processed_images[name] = image 

             self.progress_bar["value"] += 50 

             self.root.update_idletasks() 

         for name, img in processed_images.items(): 

             save_image(img, name) 

         self.display_image(processed_images) 

         self.progress_bar["value"] = 100 

         self.root.update_idletasks() 

         messagebox.showinfo("Processing Complete", "Images processed and saved successfully.") 

     def display_image(self, processed_images): 

         for name, img in processed_images.items(): 

             img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 

             pil_img = Image.fromarray(img_rgb) 

             img_tk = ImageTk.PhotoImage(pil_img) 

             self.image_display.config(image=img_tk) 

             self.image_display.image = img_tk 

 def main(): 

     root = tk.Tk() 

     app = ImageProcessingApp(root) 

     root.mainloop() 

 if __name__ == "__main__": 

     main() 

 </code></pre>