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): """Process images based on the selected method""" 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: 1340px; } """ # 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(): with gr.Column(): with gr.Row(): with gr.Column(): left_input = gr.Image(label="Left Image") with gr.Column(): 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="optimized", info="Select the color processing method" ) generate_btn = gr.Button("Generate 3D Image", variant="primary") 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 """) 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()