Skip to content

Instantly share code, notes, and snippets.

@badjano
Created May 10, 2025 02:17
Show Gist options
  • Save badjano/f402048ad6ea7fd2bc650530554497af to your computer and use it in GitHub Desktop.
Save badjano/f402048ad6ea7fd2bc650530554497af to your computer and use it in GitHub Desktop.
import sys
import numpy as np
from PIL import Image
import argparse
import os
def create_autostereogram(pattern_path, depth_map_path, output_path, repeat_width=128):
"""
Generate an autostereogram from a pattern image and a depth map.
Args:
pattern_path (str): Path to the pattern image
depth_map_path (str): Path to the depth map image
output_path (str): Path to save the output autostereogram
repeat_width (int): Width of the repeating pattern in pixels
"""
# Load pattern and depth map
try:
pattern = Image.open(pattern_path).convert('RGB')
depth_map = Image.open(depth_map_path).convert('L') # Convert depth map to grayscale
except Exception as e:
print(f"Error loading images: {e}")
return False
# resize pattern to a fixed size
pattern = pattern.resize((repeat_width, repeat_width))
# Resize depth map to match the pattern's height while maintaining aspect ratio
pattern_width, pattern_height = pattern.size
depth_width, depth_height = depth_map.size
# Convert images to numpy arrays for faster processing
pattern_array = np.array(pattern)
depth_array = np.array(depth_map)
output_array = np.zeros((depth_height, depth_width, 3), dtype=np.uint8)
# Calculate maximum pixel shift based on depth factor (as a fraction of pattern width)
depth_factor = 0.2 # Adjust this value to control depth effect intensity
max_shift = int(pattern_width * depth_factor)
# get array from pattern
strip = np.zeros((pattern_height, pattern_width, 3), dtype=np.uint8)
for y in range(pattern_height):
for x in range(pattern_width):
# Get the color from the pattern
strip[y, x] = pattern_array[y % pattern_height, x % pattern_width]
output_height = depth_height
output_width = depth_width
# Generate autostereogram
for y in range(output_height):
# For each row of the output image
for x in range(output_width):
# Skip the first pattern width of pixels (reference strip)
if x < pattern_width:
output_array[y, x] = strip[y % repeat_width, x % repeat_width]
else:
# Calculate shift based on depth
if x < depth_array.shape[1]:
# Normalize depth value (0-255) to (0-max_shift)
shift = int((depth_array[y, x] / 255.0) * max_shift)
# Look up the color from the left, shifted by depth
sample_x = x - pattern_width + shift
# Ensure sample_x is within bounds
if sample_x < 0:
sample_x = 0
elif sample_x >= output_width:
sample_x = output_width - 1
output_array[y, x] = output_array[y, sample_x]
else:
# For areas beyond the depth map, just repeat the pattern
output_array[y, x] = strip[y, x % pattern_width]
# Convert back to PIL image and save
output = Image.fromarray(output_array)
output.save(output_path)
print(f"Autostereogram saved to {output_path}")
return True
def main():
if len(sys.argv) < 3:
create_autostereogram("pattern.png", "depth.png", "autostereogram.png", 256)
return
parser = argparse.ArgumentParser(description='Generate an autostereogram from a pattern and depth map.')
parser.add_argument('pattern', help='Path to the pattern image')
parser.add_argument('depth_map', help='Path to the depth map image')
parser.add_argument('--output', '-o', default='autostereogram.png', help='Output image path')
parser.add_argument('--depth_factor', '-d', type=float, default=0.2,
help='Depth effect intensity (0.0-1.0)')
parser.add_argument('--repeat_width', '-r', type=int, default=128,
help='Number of times to repeat the pattern horizontally')
args = parser.parse_args()
if not os.path.exists(args.pattern):
print(f"Error: Pattern file not found: {args.pattern}")
return
if not os.path.exists(args.depth_map):
print(f"Error: Depth map file not found: {args.depth_map}")
return
create_autostereogram(args.pattern, args.depth_map, args.output,
args.depth_factor, args.repeat_width)
if __name__ == "__main__":
main()
@badjano
Copy link
Author

badjano commented May 10, 2025

Autostereogram Generator

This Python script builds a single-image random dot autostereogram (SIRDS) from:

  1. A repeating pattern (any RGB image).
  2. A depth map (grayscale image, where brighter pixels appear “closer” in the final 3D effect).

It tiles the pattern across the width of the depth map, then shifts pixels according to depth values to produce the familiar “Magic Eye” effect.


Features

  • Customizable repeat width: Control how wide the base pattern strip is before tiling.
  • Adjustable depth factor: Tune how pronounced the 3D effect appears.
  • Command-line arguments with sensible defaults for easy quick runs or batch processing.

Dependencies

Install with:

pip install numpy pillow

Usage

python main.py <pattern.png> <depth_map.png> [-o OUTPUT] [-d DEPTH_FACTOR] [-r REPEAT_WIDTH]

Example

Generate stereogram with default parameters

python main.py pattern.png depth_map.png

Custom output

python main.py pattern.png depth_map.png \
    --output magic_eye.png \
    --repeat_width 256

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment