Project

General

Profile

Files » main_alpha_blender.py

KAUNG Zin Thu, 12/25/2025 02:44 PM

 
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3

    
4
## @file main_alpha_blender.py
5
## @brief This program applies gamma-corrected alpha blending to overlapping projector images.
6
## @details
7
## This program finds the overlap between two projected images and computes
8
## a spatially smooth alpha mask. The mask is gamma-corrected to compensate for
9
## projector luminance behavior and to avoid visible seams in the blended region.
10

    
11
import cv2
12
import numpy as np
13
from config_reader import ConfigReader
14

    
15

    
16
class MainAlphaBlender(object):
17
    """
18
    @brief This is the main controller class for executing the alpha-blending pipeline.
19
    @details
20
    This class loads configuration parameters imported from config_reader, generates a gamma-corrected
21
    alpha mask, applies it to the selected side of the image, and outputs
22
    a seamless PNG with adjusted transparency.
23
    """
24

    
25
    def __init__(self):
26
        """
27
        @brief This is the constructor for MainAlphaBlender.
28
        @details This initializes and loads configuration parameters through ConfigReader.
29
        @see config_reader.py
30
        """
31
        self.__configReader = None
32
        self.__configReader = ConfigReader()
33

    
34
    def run(self):
35
        """
36
        @brief This executes the entire alpha blending process.
37
        @details
38
        This function performs the following steps:
39
          1. Load parameters from configuration.
40
          2. Load the input image (must be PNG/BGRA).
41
          3. Estimate the overlap region in pixels.
42
          4. Generate a linear alpha curve depending on 'left' or 'right' side.
43
          5. Apply gamma correction (crucial for projector luminance).
44
          6. Write the alpha-corrected output image.
45
        @return this will return None.
46
        @see config_reader.py
47
        """
48

    
49
        print("--- Starting Alpha Blend Process (Gamma Corrected) ---")
50

    
51
        # 1. Get Configuration
52
        image_name = self.__configReader.getImageName()
53
        side = self.__configReader.getSide()
54
        proj_width_phys = self.__configReader.getProjectedImageWidth()
55
        dist_phys = self.__configReader.getDistanceBetweenProjectors()
56
        gamma = self.__configReader.getGamma()
57

    
58
        # 2. Loading Image using OpenCV
59
        img = cv2.imread(image_name, cv2.IMREAD_UNCHANGED)
60
        if img is None:
61
            print(f"Error: Could not load image '{image_name}'")
62
            return
63

    
64
        # Ensure BGRA format for safe alpha manipulation
65
        if img.shape[2] == 3:
66
            img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
67

    
68
        height, width = img.shape[:2]
69

    
70
        # 3. Calculate Overlap Ratio and Pixels
71
        overlap_ratio = 1.0 - (dist_phys / proj_width_phys) if proj_width_phys > 0 else 0.0
72

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

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

    
80
        # 4. Create a linear fade mask for the overlap region
81
        ## @brief Linear ramp values (0 → 1)
82
        linear_ramp = np.linspace(0, 1, overlap_pixels)
83

    
84
        # Determine mask direction based on side
85
        if side == "left":
86
            ## @brief Left side fades out (1 → 0)
87
            base_curve = 1.0 - linear_ramp
88
        elif side == "right":
89
            ## @brief Right side fades in (0 → 1)
90
            base_curve = linear_ramp
91
        else:
92
            print("Error: Side must be 'left' or 'right'")
93
            return
94

    
95
        # 5. Apply Gamma Correction
96
        ## @details Raises the curve to power (1/gamma) to compensate projector response.
97
        mask_curve = np.power(base_curve, 1.0 / gamma)
98

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

    
103
        if side == "left":
104
            alpha_channel[:, width - overlap_pixels:] *= mask_block
105
        else:
106
            alpha_channel[:, :overlap_pixels] *= mask_block
107

    
108
        img[:, :, 3] = (alpha_channel * 255).astype(np.uint8)
109

    
110
        # Optional RGB burn-in (debug visualization)
111
        for c in range(3):
112
            if side == "left":
113
                roi = img[:, :, c][:, width - overlap_pixels:]
114
                img[:, :, c][:, width - overlap_pixels:] = (roi * mask_block).astype(np.uint8)
115
            else:
116
                roi = img[:, :, c][:, :overlap_pixels]
117
                img[:, :, c][:, :overlap_pixels] = (roi * mask_block).astype(np.uint8)
118

    
119
        # 7. Save Output
120
        output_name = f"output_{side}.png"
121
        cv2.imwrite(output_name, img)
122
        print(f"Saved: {output_name}")
123

    
124

    
125
if __name__ == "__main__":
126
    """
127
    @brief Program entry point.
128
    @details Instantiates MainAlphaBlender and runs the pipeline.
129
    """
130
    blender = MainAlphaBlender()
131
    blender.run()
(1-1/4)