File size: 7,489 Bytes
aec5cde
f4daba4
 
 
9d84578
aec5cde
9d84578
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501be52
9d84578
 
 
9ec4178
 
 
95cb277
 
d94003f
95cb277
9d84578
aec5cde
 
f118b28
 
aec5cde
 
 
 
ab86061
 
aec5cde
 
 
 
 
 
9d84578
 
aec5cde
 
ab86061
aec5cde
 
9d84578
aec5cde
f118b28
 
aec5cde
 
 
 
 
 
9d84578
 
ab86061
 
 
 
aec5cde
 
9d84578
aec5cde
f118b28
 
 
 
 
 
ab86061
 
f118b28
 
 
 
 
 
 
ab86061
 
f118b28
 
 
ab86061
f118b28
ab86061
 
 
 
 
f118b28
 
 
 
 
 
 
 
 
0570674
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f118b28
 
 
 
 
 
 
 
aa2570e
 
 
ebd5c74
aa2570e
 
aec5cde
aa2570e
 
 
 
 
d6a7010
aa2570e
d6a7010
 
5ff8b91
d6a7010
 
 
 
 
 
aa2570e
d6a7010
 
 
 
 
 
aa2570e
d6a7010
 
 
 
 
 
5ff8b91
d6a7010
 
 
 
 
 
 
aa2570e
d6a7010
0570674
d6a7010
aa2570e
aec5cde
f118b28
 
aec5cde
 
 
 
 
0570674
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
import gradio as gr
from PIL import Image
import numpy as np

# Modified matrices with improved red/cyan separation
matrices = {
    'true': [
        [0.299, 0.587, 0.114, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0.299, 0.587, 0.114]
    ],
    'mono': [
        [0.299, 0.587, 0.114, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0.299, 0.587, 0.114, 0.299, 0.587, 0.114]
    ],
    'color': [
        [1, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 1]
    ],
    'halfcolor': [
        [0.299, 0.587, 0.114, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 1]
    ],
    'optimized': [
        [0, 0.450, 1.050, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 1]
    ],
    # New matrices with improved red/cyan separation
    'dubois': [
        [0.456, 0.5, 0.176, -0.04, -0.038, -0.016, -0.015, -0.021, -0.005],     # Left image
        [-0.043, -0.088, -0.002, 0.378, 0.734, -0.018, -0.072, -0.113, 1.226]  # Right image
    ],
    'dubois_optimized': [
        [0.4561, 0.500484, 0.176381, -0.0400822, -0.0378246, -0.0157589, -0.0152161, -0.0205971, -0.00546856],
        [-0.0434706, -0.0879388, -0.00155529, 0.378476, 0.73364, -0.0184503, -0.0721527, -0.112961, 1.2264]
    ]
}

def make_anaglyph(left_img, right_img, color_method):
    """Generate an anaglyph from left and right images using the specified color method"""
    if left_img is None or right_img is None:
        return None
    
    # Convert from numpy array (from Gradio) to PIL Image
    left = Image.fromarray(left_img)
    right = Image.fromarray(right_img)
    
    # Check if both images have the same dimensions
    if left.size != right.size:
        # Resize right image to match left image dimensions
        right = right.resize(left.size, Image.LANCZOS)
    
    # Create a new image for the result (important change)
    result = Image.new("RGB", left.size)
    
    # Get the pixel maps
    width, height = left.size
    leftMap = left.load()
    rightMap = right.load()
    resultMap = result.load()
    
    # Use the selected color matrix
    m = matrices[color_method]
    
    # Apply the anaglyph transformation
    for y in range(0, height):
        for x in range(0, width):
            r1, g1, b1 = leftMap[x, y]
            r2, g2, b2 = rightMap[x, y]
            
            resultMap[x, y] = (
                int(r1*m[0][0] + g1*m[0][1] + b1*m[0][2] + r2*m[1][0] + g2*m[1][1] + b2*m[1][2]),
                int(r1*m[0][3] + g1*m[0][4] + b1*m[0][5] + r2*m[1][3] + g2*m[1][4] + b2*m[1][5]),
                int(r1*m[0][6] + g1*m[0][7] + b1*m[0][8] + r2*m[1][6] + g2*m[1][7] + b2*m[1][8])
            )
    
    # Convert back to numpy array for Gradio
    return np.array(result)

def make_stereopair(left_img, right_img, color_method):
    """Generate a stereo pair from left and right images"""
    if left_img is None or right_img is None:
        return None
    
    # Convert from numpy array (from Gradio) to PIL Image
    left = Image.fromarray(left_img)
    right = Image.fromarray(right_img)
    
    # Check if both images have the same dimensions
    if left.size != right.size:
        # Resize right image to match left image dimensions
        right = right.resize(left.size, Image.LANCZOS)
    
    width, height = left.size
    leftMap = left.load()
    rightMap = right.load()
    
    # Create a new image twice as wide
    pair = Image.new('RGB', (width * 2, height))
    pairMap = pair.load()
    
    # Copy the left and right images side by side
    for y in range(0, height):
        for x in range(0, width):
            pairMap[x, y] = leftMap[x, y]
            pairMap[x + width, y] = rightMap[x, y]
    
    # Convert to monochrome if required
    if color_method == 'mono':
        pair = pair.convert('L')
    
    # Convert back to numpy array for Gradio
    return np.array(pair)

def process_images(left_img, right_img, method, color_method):

    """Generate a 3D visualization from left and right stereo images.

    This function processes a pair of stereo images (left and right views) and 
    generates a 3D image using the specified visualization method:
    
    - "anaglyph": Combines the two images using a red-cyan color encoding, suitable 
      for viewing with 3D glasses.
    - "parallel": Places the images side by side for stereoscopic viewing with the 
      parallel-eye method.
    - "crossed": Similar to parallel, but with the left and right images swapped, 
      suitable for cross-eyed viewing.

    You can also specify the color processing style (e.g., "optimized", "mono", "true", etc.)
    for finer control over color blending or grayscale conversion.

    Args:
        left_img (numpy.ndarray): The left-eye image as a NumPy array.
        right_img (numpy.ndarray): The right-eye image as a NumPy array.
        method (str): The 3D generation method, one of: "anaglyph", "parallel", or "crossed".
        color_method (str): The color matrix to use for processing. Options include: "optimized", "true", "mono", "color", "halfcolor", "dubois", "dubois_optimized".

    Returns:
        numpy.ndarray: The resulting 3D image, ready to be displayed or saved.
    """

    if method == "anaglyph":
        return make_anaglyph(left_img, right_img, color_method)
    elif method == "parallel":
        return make_stereopair(left_img, right_img, color_method)
    elif method == "crossed":
        return make_stereopair(right_img, left_img, color_method)
    return None

css="""
div#col-container{
    margin: 0 auto;
    max-width: 840px;
}
"""
# Create the Gradio interface
with gr.Blocks(css=css) as app:
    with gr.Column(elem_id="col-container"):
        gr.Markdown("# 3D Anaglyph Image Generator")
        gr.Markdown("Upload left and right images to create 3D images using different methods.")
        
        
        with gr.Row():
            left_input = gr.Image(label="Left Image")
            right_input = gr.Image(label="Right Image")

        method = gr.Radio(
            ["anaglyph", "parallel", "crossed"], 
            label="Method", 
            value="anaglyph",
            info="Select the 3D image creation method"
        )
                    
        color_method = gr.Radio(
            ["optimized", "true", "mono", "color", "halfcolor", "dubois", "dubois_optimized"], 
            label="Color Method", 
            value="dubois",
            info="Select the color processing method"
        )

        with gr.Accordion("Details about methods", open=False):
            gr.Markdown("""
             ### Methods:
            - **anaglyph**: Creates a red-cyan 3D image (requires 3D glasses)
            - **parallel**: Creates side-by-side images for parallel viewing
            - **crossed**: Creates side-by-side images for cross-eyed viewing
                
            ### Color Methods:
            - **optimized**: Best for most images (default)
            - **true**: True color anaglyph
            - **mono**: Monochrome output
            - **color**: Full color (may cause ghosting)
            - **halfcolor**: Balance between color and depth
            """)
                
        generate_btn = gr.Button("Generate 3D Image", variant="primary")
        
        output = gr.Image(label="Generated 3D Anaglyph Image")
        
    generate_btn.click(
        fn=process_images,
        inputs=[left_input, right_input, method, color_method],
        outputs=output
    )

# Launch the app
if __name__ == "__main__":
    app.launch(mcp_server=True)