Code » History » Version 3
VALECHA Bharat, 01/07/2026 09:06 PM
| 1 | 3 | VALECHA Bharat | [[Wiki|Home]] | [[Project Details|Project Details]] | [[Group Members|Group Members]] | [[UML Diagrams|UML Diagrams]] | [[Weekly Progress|Weekly Progress]] | **[[Code|Code]]** |
|---|---|---|---|
| 2 | 2 | VALECHA Bharat | |
| 3 | --- |
||
| 4 | |||
| 5 | > h1. Code |
||
| 6 | |||
| 7 | *config_reader.py* |
||
| 8 | |||
| 9 | <pre><code class="python"> |
||
| 10 | |||
| 11 | #!/usr/bin/env python |
||
| 12 | # -*- coding: utf-8 -*- |
||
| 13 | # ˅ |
||
| 14 | import json |
||
| 15 | import os |
||
| 16 | # ˄ |
||
| 17 | |||
| 18 | |||
| 19 | class ConfigReader(object): |
||
| 20 | # ˅ |
||
| 21 | CONFIG_FILENAME = "config.json" |
||
| 22 | # ˄ |
||
| 23 | |||
| 24 | def __init__(self): |
||
| 25 | self.__config = {} |
||
| 26 | # ˅ |
||
| 27 | self._load_config() |
||
| 28 | # ˄ |
||
| 29 | |||
| 30 | # ˅ |
||
| 31 | def _load_config(self): |
||
| 32 | """Loads the configuration from the JSON file.""" |
||
| 33 | if not os.path.exists(self.CONFIG_FILENAME): |
||
| 34 | print(f"Warning: {self.CONFIG_FILENAME} not found. Using defaults.") |
||
| 35 | return |
||
| 36 | |||
| 37 | with open(self.CONFIG_FILENAME, 'r') as f: |
||
| 38 | self.__config = json.load(f) |
||
| 39 | # ˄ |
||
| 40 | |||
| 41 | def getProjectedImageWidth(self): |
||
| 42 | # ˅ |
||
| 43 | # The physical width of the image projected by ONE projector |
||
| 44 | return float(self.__config.get("projected_image_width", 0)) |
||
| 45 | # ˄ |
||
| 46 | |||
| 47 | def getDistanceBetweenProjectors(self): |
||
| 48 | # ˅ |
||
| 49 | # The physical distance between the lenses of the two projectors |
||
| 50 | return float(self.__config.get("distance_between_projectors", 0)) |
||
| 51 | # ˄ |
||
| 52 | |||
| 53 | def getImageWidth(self): |
||
| 54 | # ˅ |
||
| 55 | # The pixel width of the source image file |
||
| 56 | return int(self.__config.get("image_width", 1920)) |
||
| 57 | # ˄ |
||
| 58 | |||
| 59 | def getImageHeight(self): |
||
| 60 | # ˅ |
||
| 61 | # The pixel height of the source image file |
||
| 62 | return int(self.__config.get("image_height", 1080)) |
||
| 63 | # ˄ |
||
| 64 | |||
| 65 | def getImageName(self): |
||
| 66 | # ˅ |
||
| 67 | return self.__config.get("image_name", "") |
||
| 68 | # ˄ |
||
| 69 | |||
| 70 | def getSide(self): |
||
| 71 | # ˅ |
||
| 72 | # 'left' or 'right' |
||
| 73 | return self.__config.get("side", "left").lower() |
||
| 74 | # ˄ |
||
| 75 | |||
| 76 | def getGamma(self): |
||
| 77 | # ˅ |
||
| 78 | # Usually 2.2 for standard displays |
||
| 79 | return float(self.__config.get("gamma", 2.2)) |
||
| 80 | # ˄ |
||
| 81 | </code></pre> |
||
| 82 | |||
| 83 | *config.json* |
||
| 84 | |||
| 85 | <pre><code class="python"> |
||
| 86 | |||
| 87 | { |
||
| 88 | "image_name": "right.jpg", |
||
| 89 | "image_width": 1920, |
||
| 90 | "image_height": 1080, |
||
| 91 | "projected_image_width": 200, |
||
| 92 | "distance_between_projectors": 150, |
||
| 93 | "gamma": 3, |
||
| 94 | "side": "right" |
||
| 95 | } |
||
| 96 | </code></pre> |
||
| 97 | |||
| 98 | *graph_gen _IND.py* |
||
| 99 | |||
| 100 | <pre><code class="python"> |
||
| 101 | |||
| 102 | import numpy as np |
||
| 103 | import matplotlib.pyplot as plt |
||
| 104 | |||
| 105 | |||
| 106 | def generate_individual_graphs(): |
||
| 107 | # Input gradient (0 to 1) |
||
| 108 | x = np.linspace(0, 1, 500) |
||
| 109 | |||
| 110 | # The three specified Gamma values |
||
| 111 | gamma_values = [0.5, 3, 50] |
||
| 112 | |||
| 113 | for gamma in gamma_values: |
||
| 114 | plt.figure(figsize=(8, 6)) |
||
| 115 | |||
| 116 | # 1. Reference Line (Linear - No Correction) |
||
| 117 | # This is the 'Reference Curve'. It shows the output without blending/correction. |
||
| 118 | plt.plot(x, x, 'k--', label='Linear Reference (No Correction)', alpha=0.5) |
||
| 119 | |||
| 120 | # 2. Gamma Correction Curve |
||
| 121 | # Logic: y = x^(1/gamma) |
||
| 122 | y = np.power(x, 1.0 / gamma) |
||
| 123 | |||
| 124 | # Plotting the curve |
||
| 125 | plt.plot(x, y, color='blue', linewidth=3, label=f'Gamma Correction (γ={gamma})') |
||
| 126 | |||
| 127 | # Decoration |
||
| 128 | plt.title(f'Gamma Correction Curve: γ = {gamma}', fontsize=14) |
||
| 129 | plt.xlabel('Input Pixel Intensity (0 to 1)', fontsize=12) |
||
| 130 | plt.ylabel('Corrected Output Intensity', fontsize=12) |
||
| 131 | plt.legend(loc='best') |
||
| 132 | plt.grid(True, alpha=0.3) |
||
| 133 | plt.xlim(0, 1.0) |
||
| 134 | plt.ylim(0, 1.05) |
||
| 135 | |||
| 136 | # Saving separate files for each gamma |
||
| 137 | filename = f"gamma_curve_{gamma}.png" |
||
| 138 | plt.savefig(filename) |
||
| 139 | print(f"Generated graph: {filename}") |
||
| 140 | |||
| 141 | # Close plot to start fresh for next gamma |
||
| 142 | plt.close() |
||
| 143 | |||
| 144 | |||
| 145 | if __name__ == "__main__": |
||
| 146 | generate_individual_graphs() |
||
| 147 | |||
| 148 | </code></pre> |
||
| 149 | |||
| 150 | *graph_gen.py* |
||
| 151 | |||
| 152 | <pre><code class="python"> |
||
| 153 | |||
| 154 | import numpy as np |
||
| 155 | import matplotlib.pyplot as plt |
||
| 156 | |||
| 157 | |||
| 158 | def plot_gamma_curves(): |
||
| 159 | # Generate input gradient values from 0 to 1 |
||
| 160 | x = np.linspace(0, 1, 500) |
||
| 161 | |||
| 162 | # Defined gamma values for comparison |
||
| 163 | gamma_values = [0.5, 3, 50] |
||
| 164 | |||
| 165 | plt.figure(figsize=(10, 6)) |
||
| 166 | |||
| 167 | # Reference Line (Linear - No Correction) |
||
| 168 | plt.plot(x, x, 'k--', label='Linear (No Correction)', alpha=0.5) |
||
| 169 | |||
| 170 | # Blending logic: y = x^(1/gamma) |
||
| 171 | for gamma in gamma_values: |
||
| 172 | # Formula derived from the provided code |
||
| 173 | y = np.power(x, 1.0 / gamma) |
||
| 174 | |||
| 175 | # Plotting |
||
| 176 | plt.plot(x, y, linewidth=2.5, label=f'Gamma = {gamma} (Exp: 1/{gamma} ≈ {1.0 / gamma:.2f})') |
||
| 177 | |||
| 178 | # Graph Formatting |
||
| 179 | plt.title('Gamma Correction Curves Comparison', fontsize=14) |
||
| 180 | plt.xlabel('Input Value (Gradient 0 to 1)', fontsize=12) |
||
| 181 | plt.ylabel('Corrected Output Value', fontsize=12) |
||
| 182 | plt.legend() |
||
| 183 | plt.grid(True, alpha=0.3) |
||
| 184 | plt.ylim(0, 1.05) |
||
| 185 | plt.xlim(0, 1.0) |
||
| 186 | |||
| 187 | # Save chart |
||
| 188 | output_filename = 'gamma_comparison_graph.png' |
||
| 189 | plt.savefig(output_filename) |
||
| 190 | print(f"Graph successfully saved as: {output_filename}") |
||
| 191 | plt.show() |
||
| 192 | |||
| 193 | |||
| 194 | if __name__ == "__main__": |
||
| 195 | plot_gamma_curves() |
||
| 196 | |||
| 197 | </code></pre> |
||
| 198 | |||
| 199 | *main_alpha_blender.py* |
||
| 200 | |||
| 201 | <pre><code class="python"> |
||
| 202 | |||
| 203 | #!/usr/bin/env python |
||
| 204 | # -*- coding: utf-8 -*- |
||
| 205 | # ˅ |
||
| 206 | import cv2 |
||
| 207 | import numpy as np |
||
| 208 | from config_reader import ConfigReader |
||
| 209 | |||
| 210 | |||
| 211 | # ˄ |
||
| 212 | |||
| 213 | |||
| 214 | class MainAlphaBlender(object): |
||
| 215 | # ˅ |
||
| 216 | |||
| 217 | # ˄ |
||
| 218 | |||
| 219 | def __init__(self): |
||
| 220 | self.__configReader = None |
||
| 221 | # ˅ |
||
| 222 | self.__configReader = ConfigReader() |
||
| 223 | # ˄ |
||
| 224 | |||
| 225 | # ˅ |
||
| 226 | def run(self): |
||
| 227 | print("--- Starting Alpha Blend Process (Gamma Corrected) ---") |
||
| 228 | |||
| 229 | # 1. Get Configuration |
||
| 230 | image_name = self.__configReader.getImageName() |
||
| 231 | side = self.__configReader.getSide() |
||
| 232 | proj_width_phys = self.__configReader.getProjectedImageWidth() |
||
| 233 | dist_phys = self.__configReader.getDistanceBetweenProjectors() |
||
| 234 | gamma = self.__configReader.getGamma() |
||
| 235 | |||
| 236 | # 2. Load Image |
||
| 237 | img = cv2.imread(image_name, cv2.IMREAD_UNCHANGED) |
||
| 238 | if img is None: |
||
| 239 | print(f"Error: Could not load image '{image_name}'") |
||
| 240 | return |
||
| 241 | |||
| 242 | # Ensure image is BGRA |
||
| 243 | if img.shape[2] == 3: |
||
| 244 | img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA) |
||
| 245 | |||
| 246 | height, width = img.shape[:2] |
||
| 247 | |||
| 248 | # 3. Calculate Overlap |
||
| 249 | if proj_width_phys > 0: |
||
| 250 | overlap_ratio = 1.0 - (dist_phys / proj_width_phys) |
||
| 251 | else: |
||
| 252 | overlap_ratio = 0.0 |
||
| 253 | |||
| 254 | if overlap_ratio <= 0: |
||
| 255 | print("Warning: No overlap detected.") |
||
| 256 | return |
||
| 257 | |||
| 258 | overlap_pixels = int(width * overlap_ratio) |
||
| 259 | print(f"Processing '{side}' | Overlap: {overlap_pixels} pixels | Gamma: {gamma}") |
||
| 260 | |||
| 261 | # 4. Generate Mask |
||
| 262 | # Create a linear ramp from 0 to 1 |
||
| 263 | linear_ramp = np.linspace(0, 1, overlap_pixels) |
||
| 264 | |||
| 265 | # Determine the base curve direction BEFORE gamma |
||
| 266 | if side == "left": |
||
| 267 | # Left image fades OUT (1 -> 0) on the right side |
||
| 268 | base_curve = 1.0 - linear_ramp |
||
| 269 | elif side == "right": |
||
| 270 | # Right image fades IN (0 -> 1) on the left side |
||
| 271 | base_curve = linear_ramp |
||
| 272 | else: |
||
| 273 | print("Error: Side must be 'left' or 'right'") |
||
| 274 | return |
||
| 275 | |||
| 276 | # 5. Apply Gamma Correction |
||
| 277 | # This "boosts" the midtones. 50% gray becomes ~73% gray. |
||
| 278 | # This is crucial because 0.73^2.2 (projector gamma) + 0.73^2.2 = ~1.0 Light |
||
| 279 | mask_curve = np.power(base_curve, 1 / gamma) |
||
| 280 | |||
| 281 | # 6. Apply to Image |
||
| 282 | alpha_channel = img[:, :, 3].astype(float) / 255.0 |
||
| 283 | mask_block = np.tile(mask_curve, (height, 1)) |
||
| 284 | |||
| 285 | if side == "left": |
||
| 286 | # Apply to the right-most columns |
||
| 287 | alpha_channel[:, width - overlap_pixels:] *= mask_block |
||
| 288 | elif side == "right": |
||
| 289 | # Apply to the left-most columns |
||
| 290 | alpha_channel[:, :overlap_pixels] *= mask_block |
||
| 291 | |||
| 292 | # 7. Save Output |
||
| 293 | img[:, :, 3] = (alpha_channel * 255).astype(np.uint8) |
||
| 294 | |||
| 295 | # Optional: Burn transparency into RGB for easier debugging/viewing |
||
| 296 | # (This makes the transparent parts black, ensuring no 'ghosting' if alpha is ignored) |
||
| 297 | for c in range(3): |
||
| 298 | if side == "left": |
||
| 299 | roi = img[:, :, c][:, width - overlap_pixels:] |
||
| 300 | img[:, :, c][:, width - overlap_pixels:] = (roi * mask_block).astype(np.uint8) |
||
| 301 | else: |
||
| 302 | roi = img[:, :, c][:, :overlap_pixels] |
||
| 303 | img[:, :, c][:, :overlap_pixels] = (roi * mask_block).astype(np.uint8) |
||
| 304 | |||
| 305 | output_name = f"output_{side}.png" |
||
| 306 | cv2.imwrite(output_name, img) |
||
| 307 | print(f"Saved: {output_name}") |
||
| 308 | |||
| 309 | # ˄ |
||
| 310 | |||
| 311 | |||
| 312 | # ˅ |
||
| 313 | if __name__ == "__main__": |
||
| 314 | blender = MainAlphaBlender() |
||
| 315 | blender.run() |
||
| 316 | # ˄ |
||
| 317 | |||
| 318 | </code></pre> |
||
| 319 | |||
| 320 | *x_curve.py* |
||
| 321 | |||
| 322 | <pre><code class="python"> |
||
| 323 | import numpy as np |
||
| 324 | import matplotlib.pyplot as plt |
||
| 325 | |||
| 326 | |||
| 327 | def plot_blending_mechanics_unique(): |
||
| 328 | # Setup - Use your specific project values here if you have them |
||
| 329 | overlap_pixels = 256 # Example: Change this to your actual overlap width |
||
| 330 | x_pixels = np.linspace(0, overlap_pixels, 500) |
||
| 331 | x_normalized = np.linspace(0, 1, 500) |
||
| 332 | |||
| 333 | # 1. Linear Alpha (The "Wrong" Way) |
||
| 334 | alpha_left_lin = 1.0 - x_normalized |
||
| 335 | alpha_right_lin = x_normalized |
||
| 336 | |||
| 337 | # 2. Gamma Corrected Alpha (Your Solution) |
||
| 338 | gamma = 2.2 # Your specific gamma |
||
| 339 | alpha_left_corr = np.power(alpha_left_lin, 1.0 / gamma) |
||
| 340 | alpha_right_corr = np.power(alpha_right_lin, 1.0 / gamma) |
||
| 341 | |||
| 342 | plt.figure(figsize=(10, 6)) |
||
| 343 | |||
| 344 | # Plot Gamma Corrected (Solid Colors) - The "Hero" of the graph |
||
| 345 | plt.plot(x_pixels, alpha_left_corr, 'b-', linewidth=3, label='Left Projector Output') |
||
| 346 | plt.plot(x_pixels, alpha_right_corr, 'r-', linewidth=3, label='Right Projector Output') |
||
| 347 | |||
| 348 | # Plot Linear (Dashed) - The "Baseline" |
||
| 349 | plt.plot(x_pixels, alpha_left_lin, 'k:', linewidth=1.5, alpha=0.5, label='Linear Reference (No Correction)') |
||
| 350 | plt.plot(x_pixels, alpha_right_lin, 'k:', linewidth=1.5, alpha=0.5) |
||
| 351 | |||
| 352 | # Unique Touch: Shade the area to show "Light Energy" |
||
| 353 | plt.fill_between(x_pixels, alpha_left_corr, alpha_left_lin, color='blue', alpha=0.1) |
||
| 354 | plt.fill_between(x_pixels, alpha_right_corr, alpha_right_lin, color='red', alpha=0.1) |
||
| 355 | |||
| 356 | # Annotations specific to your analysis |
||
| 357 | plt.text(overlap_pixels / 2, 0.8, 'Gamma Boost Region\n(Corrects Dark Band)', |
||
| 358 | horizontalalignment='center', fontsize=10, |
||
| 359 | bbox=dict(facecolor='white', alpha=0.8, edgecolor='none')) |
||
| 360 | |||
| 361 | plt.title(f'Spatial Intensity Distribution (Overlap: {overlap_pixels}px)', fontsize=14, fontweight='bold') |
||
| 362 | plt.xlabel('Pixel Position in Overlap Zone', fontsize=12) |
||
| 363 | plt.ylabel('Projected Light Intensity (0.0 - 1.0)', fontsize=12) |
||
| 364 | plt.legend() |
||
| 365 | plt.grid(True, alpha=0.3) |
||
| 366 | |||
| 367 | plt.savefig('custom_blending_curve.png', dpi=100) |
||
| 368 | print("Saved: custom_blending_curve.png") |
||
| 369 | |||
| 370 | |||
| 371 | if __name__ == "__main__": |
||
| 372 | plot_blending_mechanics_unique() |
||
| 373 | |||
| 374 | </code></pre> |