Project

General

Profile

Actions

Home| Project Details | Group Members | Weekly Progress | UML Diagrams | Code


Code

config_reader.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ˅
import json
import os
# ˄

class ConfigReader(object):
    # ˅
    CONFIG_FILENAME = "config.json" 
    # ˄

    def __init__(self):
        self.__config = {}
        # ˅
        self._load_config()
        # ˄

    # ˅
    def _load_config(self):
        """Loads the configuration from the JSON file.""" 
        if not os.path.exists(self.CONFIG_FILENAME):
            print(f"Warning: {self.CONFIG_FILENAME} not found. Using defaults.")
            return

        with open(self.CONFIG_FILENAME, 'r') as f:
            self.__config = json.load(f)
    # ˄

    def getProjectedImageWidth(self):
        # ˅
        # The physical width of the image projected by ONE projector
        return float(self.__config.get("projected_image_width", 0))
        # ˄

    def getDistanceBetweenProjectors(self):
        # ˅
        # The physical distance between the lenses of the two projectors
        return float(self.__config.get("distance_between_projectors", 0))
        # ˄

    def getImageWidth(self):
        # ˅
        # The pixel width of the source image file
        return int(self.__config.get("image_width", 1920))
        # ˄

    def getImageHeight(self):
        # ˅
        # The pixel height of the source image file
        return int(self.__config.get("image_height", 1080))
        # ˄

    def getImageName(self):
        # ˅
        return self.__config.get("image_name", "")
        # ˄

    def getSide(self):
        # ˅
        # 'left' or 'right'
        return self.__config.get("side", "left").lower()
        # ˄

    def getGamma(self):
        # ˅
        # Usually 2.2 for standard displays
        return float(self.__config.get("gamma", 2.2))
        # ˄

config.json


{
    "image_name": "right.jpg",
    "image_width": 1920,
    "image_height": 1080,
    "projected_image_width": 200,
    "distance_between_projectors": 150,
    "gamma": 3,
    "side": "right" 
}

graph_gen _IND.py


import numpy as np
import matplotlib.pyplot as plt

def generate_individual_graphs():
    # Input gradient (0 to 1)
    x = np.linspace(0, 1, 500)

    # The three specified Gamma values
    gamma_values = [0.5, 3, 50]

    for gamma in gamma_values:
        plt.figure(figsize=(8, 6))

        # 1. Reference Line (Linear - No Correction)
        # This is the 'Reference Curve'. It shows the output without blending/correction.
        plt.plot(x, x, 'k--', label='Linear Reference (No Correction)', alpha=0.5)

        # 2. Gamma Correction Curve
        # Logic: y = x^(1/gamma)
        y = np.power(x, 1.0 / gamma)

        # Plotting the curve
        plt.plot(x, y, color='blue', linewidth=3, label=f'Gamma Correction (γ={gamma})')

        # Decoration
        plt.title(f'Gamma Correction Curve: γ = {gamma}', fontsize=14)
        plt.xlabel('Input Pixel Intensity (0 to 1)', fontsize=12)
        plt.ylabel('Corrected Output Intensity', fontsize=12)
        plt.legend(loc='best')
        plt.grid(True, alpha=0.3)
        plt.xlim(0, 1.0)
        plt.ylim(0, 1.05)

        # Saving separate files for each gamma
        filename = f"gamma_curve_{gamma}.png" 
        plt.savefig(filename)
        print(f"Generated graph: {filename}")

        # Close plot to start fresh for next gamma
        plt.close()

if __name__ == "__main__":
    generate_individual_graphs()

graph_gen.py


import numpy as np
import matplotlib.pyplot as plt

def plot_gamma_curves():
    # Generate input gradient values from 0 to 1
    x = np.linspace(0, 1, 500)

    # Defined gamma values for comparison
    gamma_values = [0.5, 3, 50]

    plt.figure(figsize=(10, 6))

    # Reference Line (Linear - No Correction)
    plt.plot(x, x, 'k--', label='Linear (No Correction)', alpha=0.5)

    # Blending logic: y = x^(1/gamma)
    for gamma in gamma_values:
        # Formula derived from the provided code
        y = np.power(x, 1.0 / gamma)

        # Plotting
        plt.plot(x, y, linewidth=2.5, label=f'Gamma = {gamma} (Exp: 1/{gamma} ≈ {1.0 / gamma:.2f})')

    # Graph Formatting
    plt.title('Gamma Correction Curves Comparison', fontsize=14)
    plt.xlabel('Input Value (Gradient 0 to 1)', fontsize=12)
    plt.ylabel('Corrected Output Value', fontsize=12)
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.ylim(0, 1.05)
    plt.xlim(0, 1.0)

    # Save chart
    output_filename = 'gamma_comparison_graph.png'
    plt.savefig(output_filename)
    print(f"Graph successfully saved as: {output_filename}")
    plt.show()

if __name__ == "__main__":
    plot_gamma_curves()

main_alpha_blender.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ˅
import cv2
import numpy as np
from config_reader import ConfigReader

# ˄

class MainAlphaBlender(object):
    # ˅

    # ˄

    def __init__(self):
        self.__configReader = None
        # ˅
        self.__configReader = ConfigReader()
        # ˄

    # ˅
    def run(self):
        print("--- Starting Alpha Blend Process (Gamma Corrected) ---")

        # 1. Get Configuration
        image_name = self.__configReader.getImageName()
        side = self.__configReader.getSide()
        proj_width_phys = self.__configReader.getProjectedImageWidth()
        dist_phys = self.__configReader.getDistanceBetweenProjectors()
        gamma = self.__configReader.getGamma()

        # 2. Load Image
        img = cv2.imread(image_name, cv2.IMREAD_UNCHANGED)
        if img is None:
            print(f"Error: Could not load image '{image_name}'")
            return

        # Ensure image is BGRA
        if img.shape[2] == 3:
            img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)

        height, width = img.shape[:2]

        # 3. Calculate Overlap
        if proj_width_phys > 0:
            overlap_ratio = 1.0 - (dist_phys / proj_width_phys)
        else:
            overlap_ratio = 0.0

        if overlap_ratio <= 0:
            print("Warning: No overlap detected.")
            return

        overlap_pixels = int(width * overlap_ratio)
        print(f"Processing '{side}' | Overlap: {overlap_pixels} pixels | Gamma: {gamma}")

        # 4. Generate Mask
        # Create a linear ramp from 0 to 1
        linear_ramp = np.linspace(0, 1, overlap_pixels)

        # Determine the base curve direction BEFORE gamma
        if side == "left":
            # Left image fades OUT (1 -> 0) on the right side
            base_curve = 1.0 - linear_ramp
        elif side == "right":
            # Right image fades IN (0 -> 1) on the left side
            base_curve = linear_ramp
        else:
            print("Error: Side must be 'left' or 'right'")
            return

        # 5. Apply Gamma Correction
        # This "boosts" the midtones. 50% gray becomes ~73% gray.
        # This is crucial because 0.73^2.2 (projector gamma) + 0.73^2.2 = ~1.0 Light
        mask_curve = np.power(base_curve, 1 / gamma)

        # 6. Apply to Image
        alpha_channel = img[:, :, 3].astype(float) / 255.0
        mask_block = np.tile(mask_curve, (height, 1))

        if side == "left":
            # Apply to the right-most columns
            alpha_channel[:, width - overlap_pixels:] *= mask_block
        elif side == "right":
            # Apply to the left-most columns
            alpha_channel[:, :overlap_pixels] *= mask_block

        # 7. Save Output
        img[:, :, 3] = (alpha_channel * 255).astype(np.uint8)

        # Optional: Burn transparency into RGB for easier debugging/viewing
        # (This makes the transparent parts black, ensuring no 'ghosting' if alpha is ignored)
        for c in range(3):
            if side == "left":
                roi = img[:, :, c][:, width - overlap_pixels:]
                img[:, :, c][:, width - overlap_pixels:] = (roi * mask_block).astype(np.uint8)
            else:
                roi = img[:, :, c][:, :overlap_pixels]
                img[:, :, c][:, :overlap_pixels] = (roi * mask_block).astype(np.uint8)

        output_name = f"output_{side}.png" 
        cv2.imwrite(output_name, img)
        print(f"Saved: {output_name}")

    # ˄

# ˅
if __name__ == "__main__":
    blender = MainAlphaBlender()
    blender.run()
# ˄

x_curve.py

import numpy as np
import matplotlib.pyplot as plt

def plot_blending_mechanics_unique():
    # Setup - Use your specific project values here if you have them
    overlap_pixels = 256  # Example: Change this to your actual overlap width
    x_pixels = np.linspace(0, overlap_pixels, 500)
    x_normalized = np.linspace(0, 1, 500)

    # 1. Linear Alpha (The "Wrong" Way)
    alpha_left_lin = 1.0 - x_normalized
    alpha_right_lin = x_normalized

    # 2. Gamma Corrected Alpha (Your Solution)
    gamma = 2.2  # Your specific gamma
    alpha_left_corr = np.power(alpha_left_lin, 1.0 / gamma)
    alpha_right_corr = np.power(alpha_right_lin, 1.0 / gamma)

    plt.figure(figsize=(10, 6))

    # Plot Gamma Corrected (Solid Colors) - The "Hero" of the graph
    plt.plot(x_pixels, alpha_left_corr, 'b-', linewidth=3, label='Left Projector Output')
    plt.plot(x_pixels, alpha_right_corr, 'r-', linewidth=3, label='Right Projector Output')

    # Plot Linear (Dashed) - The "Baseline" 
    plt.plot(x_pixels, alpha_left_lin, 'k:', linewidth=1.5, alpha=0.5, label='Linear Reference (No Correction)')
    plt.plot(x_pixels, alpha_right_lin, 'k:', linewidth=1.5, alpha=0.5)

    # Unique Touch: Shade the area to show "Light Energy" 
    plt.fill_between(x_pixels, alpha_left_corr, alpha_left_lin, color='blue', alpha=0.1)
    plt.fill_between(x_pixels, alpha_right_corr, alpha_right_lin, color='red', alpha=0.1)

    # Annotations specific to your analysis
    plt.text(overlap_pixels / 2, 0.8, 'Gamma Boost Region\n(Corrects Dark Band)',
             horizontalalignment='center', fontsize=10,
             bbox=dict(facecolor='white', alpha=0.8, edgecolor='none'))

    plt.title(f'Spatial Intensity Distribution (Overlap: {overlap_pixels}px)', fontsize=14, fontweight='bold')
    plt.xlabel('Pixel Position in Overlap Zone', fontsize=12)
    plt.ylabel('Projected Light Intensity (0.0 - 1.0)', fontsize=12)
    plt.legend()
    plt.grid(True, alpha=0.3)

    plt.savefig('custom_blending_curve.png', dpi=100)
    print("Saved: custom_blending_curve.png")

if __name__ == "__main__":
    plot_blending_mechanics_unique()

Updated by WONGKAI Briana Monika Luckyta 1 day ago · 9 revisions