Actions
Code » History » Revision 5
« Previous |
Revision 5/9
(diff)
| Next »
Faiq Sayyidan SETIAWAN, 10/24/2024 01:23 PM
Home | Team Members | Project Description | Code | UML Diagrams | Results |
Code
alpha_blending_v2.py: everything¶
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()
Updated by Faiq Sayyidan SETIAWAN 6 months ago · 5 revisions