Code » History » Revision 6
Revision 5 (Faiq Sayyidan SETIAWAN, 10/24/2024 01:23 PM) → Revision 6/9 (Faiq Sayyidan SETIAWAN, 10/24/2024 01:23 PM)
--- *[[/|Home]]* *[[Home]]* | *[[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>