Project

General

Profile

Code » History » Revision 8

Revision 7 (VALECHA Bharat, 01/07/2026 09:10 PM) → Revision 8/9 (WONGKAI Briana Monika Luckyta, 01/08/2026 01:53 PM)

[Wiki|Home]]| [[Wiki|Home]]    |    [[Project Details|Project Details]]    |    [[Group Members|Group Members]]    |    [[UML Diagrams|UML Diagrams]] |    [[Weekly Progress|Weekly Progress]]    | [[UML_Diagrams|UML Diagrams]]    | **[[Code|Code]]** 

  

 --- 

 > h1. Code 

 *config_reader.py* 

 <pre><code class="python"> 

 #!/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)) 
         # ˄ 
 </code></pre> 

 *config.json* 

 <pre><code class="python"> 

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

 *graph_gen _IND.py* 

 <pre><code class="python"> 

 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() 

 </code></pre> 

 *graph_gen.py* 

 <pre><code class="python"> 

 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() 

 </code></pre> 

 *main_alpha_blender.py* 

 <pre><code class="python"> 

 #!/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() 
 # ˄ 

 </code></pre> 

 *x_curve.py* 

 <pre><code class="python"> 
 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() 

 </code></pre>