|
1
|
#!/usr/bin/env python
|
|
2
|
# -*- coding: utf-8 -*-
|
|
3
|
|
|
4
|
##
|
|
5
|
# @file blending_mechanics_visualization.py
|
|
6
|
# @brief Visualizes spatial alpha blending mechanics with and without gamma correction.
|
|
7
|
#
|
|
8
|
# @details
|
|
9
|
# This program generates a detailed visualization of projector edge blending
|
|
10
|
# in an overlap region. It compares:
|
|
11
|
# - Linear alpha blending (baseline / incorrect approach)
|
|
12
|
# - Gamma-corrected alpha blending (final solution)
|
|
13
|
#
|
|
14
|
# The graph highlights how gamma correction compensates for nonlinear
|
|
15
|
# projector luminance response, preventing visible dark seams
|
|
16
|
# in the overlap region.
|
|
17
|
#
|
|
18
|
# The output image is designed for inclusion in technical documentation
|
|
19
|
# or a project wiki to explain blending mechanics visually.
|
|
20
|
|
|
21
|
import numpy as np
|
|
22
|
import matplotlib.pyplot as plt
|
|
23
|
|
|
24
|
|
|
25
|
##
|
|
26
|
# @brief Plots spatial alpha blending behavior in an overlap region.
|
|
27
|
#
|
|
28
|
# @details
|
|
29
|
# This function simulates the intensity distribution of two overlapping
|
|
30
|
# projectors across a horizontal overlap zone.
|
|
31
|
#
|
|
32
|
# Two blending strategies are visualized:
|
|
33
|
# 1. Linear alpha blending (y = x)
|
|
34
|
# 2. Gamma-corrected alpha blending (y = x^(1/gamma))
|
|
35
|
#
|
|
36
|
# The resulting plot illustrates how gamma correction increases effective
|
|
37
|
# light energy in the overlap region, correcting the dark band artifact
|
|
38
|
# caused by naive linear blending.
|
|
39
|
#
|
|
40
|
# The shaded regions emphasize the difference in projected light energy
|
|
41
|
# between linear and gamma-corrected approaches.
|
|
42
|
#
|
|
43
|
# @note
|
|
44
|
# This visualization is analytical and explanatory in nature.
|
|
45
|
# It does not represent raw pixel blending, but projected luminance behavior.
|
|
46
|
#
|
|
47
|
# @return None
|
|
48
|
def plot_blending_mechanics_unique():
|
|
49
|
##
|
|
50
|
# @brief Define overlap region configuration.
|
|
51
|
#
|
|
52
|
# @details
|
|
53
|
# overlap_pixels represents the horizontal width (in pixels)
|
|
54
|
# where two projectors overlap.
|
|
55
|
overlap_pixels = 256 # Example: Change this to your actual overlap width
|
|
56
|
|
|
57
|
##
|
|
58
|
# @brief Generate pixel coordinate ranges.
|
|
59
|
#
|
|
60
|
# @details
|
|
61
|
# x_pixels is used for spatial positioning,
|
|
62
|
# while x_normalized maps the same region into [0, 1]
|
|
63
|
# for alpha calculations.
|
|
64
|
x_pixels = np.linspace(0, overlap_pixels, 500)
|
|
65
|
x_normalized = np.linspace(0, 1, 500)
|
|
66
|
|
|
67
|
##
|
|
68
|
# @brief Compute linear alpha blending weights (baseline).
|
|
69
|
#
|
|
70
|
# @details
|
|
71
|
# These represent a naive linear fade-out / fade-in
|
|
72
|
# without accounting for projector gamma characteristics.
|
|
73
|
alpha_left_lin = 1.0 - x_normalized
|
|
74
|
alpha_right_lin = x_normalized
|
|
75
|
|
|
76
|
##
|
|
77
|
# @brief Compute gamma-corrected alpha blending weights.
|
|
78
|
#
|
|
79
|
# @details
|
|
80
|
# Gamma correction compensates for nonlinear luminance response
|
|
81
|
# of projectors.
|
|
82
|
#
|
|
83
|
# Formula:
|
|
84
|
# @f[
|
|
85
|
# \alpha_{corrected} = \alpha^{(1/\gamma)}
|
|
86
|
# @f]
|
|
87
|
gamma = 2.2 # Projector gamma value
|
|
88
|
alpha_left_corr = np.power(alpha_left_lin, 1.0 / gamma)
|
|
89
|
alpha_right_corr = np.power(alpha_right_lin, 1.0 / gamma)
|
|
90
|
|
|
91
|
##
|
|
92
|
# @brief Initialize plotting canvas.
|
|
93
|
plt.figure(figsize=(10, 6))
|
|
94
|
|
|
95
|
##
|
|
96
|
# @brief Plot gamma-corrected projector outputs.
|
|
97
|
#
|
|
98
|
# @details
|
|
99
|
# These curves represent the final projected light intensities
|
|
100
|
# after gamma compensation.
|
|
101
|
plt.plot(x_pixels, alpha_left_corr, 'b-', linewidth=3, label='Left Projector Output')
|
|
102
|
plt.plot(x_pixels, alpha_right_corr, 'r-', linewidth=3, label='Right Projector Output')
|
|
103
|
|
|
104
|
##
|
|
105
|
# @brief Plot linear blending reference curves.
|
|
106
|
#
|
|
107
|
# @details
|
|
108
|
# Dashed lines illustrate the baseline case where no gamma
|
|
109
|
# correction is applied.
|
|
110
|
plt.plot(x_pixels, alpha_left_lin, 'k:', linewidth=1.5, alpha=0.5,
|
|
111
|
label='Linear Reference (No Correction)')
|
|
112
|
plt.plot(x_pixels, alpha_right_lin, 'k:', linewidth=1.5, alpha=0.5)
|
|
113
|
|
|
114
|
##
|
|
115
|
# @brief Visualize additional light energy introduced by gamma correction.
|
|
116
|
#
|
|
117
|
# @details
|
|
118
|
# The shaded regions represent the difference between
|
|
119
|
# linear and gamma-corrected intensity distributions,
|
|
120
|
# emphasizing how gamma correction boosts luminance
|
|
121
|
# in the overlap region.
|
|
122
|
plt.fill_between(x_pixels, alpha_left_corr, alpha_left_lin,
|
|
123
|
color='blue', alpha=0.1)
|
|
124
|
plt.fill_between(x_pixels, alpha_right_corr, alpha_right_lin,
|
|
125
|
color='red', alpha=0.1)
|
|
126
|
|
|
127
|
##
|
|
128
|
# @brief Annotate gamma compensation effect.
|
|
129
|
#
|
|
130
|
# @details
|
|
131
|
# Highlights the central region where gamma correction
|
|
132
|
# mitigates the dark band artifact.
|
|
133
|
plt.text(overlap_pixels / 2, 0.8,
|
|
134
|
'Gamma Boost Region\n(Corrects Dark Band)',
|
|
135
|
horizontalalignment='center',
|
|
136
|
fontsize=10,
|
|
137
|
bbox=dict(facecolor='white', alpha=0.8, edgecolor='none'))
|
|
138
|
|
|
139
|
##
|
|
140
|
# @brief Configure graph labels and appearance.
|
|
141
|
plt.title(f'Spatial Intensity Distribution (Overlap: {overlap_pixels}px)',
|
|
142
|
fontsize=14, fontweight='bold')
|
|
143
|
plt.xlabel('Pixel Position in Overlap Zone', fontsize=12)
|
|
144
|
plt.ylabel('Projected Light Intensity (0.0 - 1.0)', fontsize=12)
|
|
145
|
plt.legend()
|
|
146
|
plt.grid(True, alpha=0.3)
|
|
147
|
|
|
148
|
##
|
|
149
|
# @brief Save visualization to file.
|
|
150
|
#
|
|
151
|
# @details
|
|
152
|
# The generated image is saved as a PNG file for
|
|
153
|
# easy embedding into documentation or wiki pages.
|
|
154
|
plt.savefig('custom_blending_curve.png', dpi=100)
|
|
155
|
print("Saved: custom_blending_curve.png")
|
|
156
|
|
|
157
|
|
|
158
|
##
|
|
159
|
# @brief Program entry point.
|
|
160
|
#
|
|
161
|
# @details
|
|
162
|
# Executes the blending mechanics visualization when the script
|
|
163
|
# is run directly.
|
|
164
|
if __name__ == "__main__":
|
|
165
|
plot_blending_mechanics_unique()
|