Source code for pyllusion.Autostereogram.Autostereogram
import numpy as np
import PIL.Image, PIL.ImageDraw, PIL.ImageFilter, PIL.ImageFont, PIL.ImageOps
from ..image import image_noise, image_text
[docs]class Autostereogram:
"""
A class to generate Autostereograms based on a given depth map.
Autostereograms are images made of a pattern that is horizontally repeated (with slight variations)
which, when watched with the appropriate focus, will generate an illusion of depth.
"""
def __init__(
self, stimulus="Hello", pattern=None, n_repetitions=14, depth=1, invert=False, **kwargs
):
self.stimulus = stimulus
self.pattern = pattern
self.n_repetitions = n_repetitions
self.depth = depth
self.invert = invert
# If '/' and '.' in string, we assume it's a path
if "/" in self.stimulus and "." in self.stimulus:
depth_map = PIL.Image.open(self.stimulus)
else: # Else a text
depth_map = image_text(text=self.stimulus, **kwargs)
# Convert to black and white
depth_map = depth_map.convert('L')
depth_map = PIL.ImageOps.autocontrast(depth_map)
if invert is False:
self.depth_map = PIL.ImageOps.invert(depth_map)
# Get size of depth map
self.width, self.height = self.depth_map.size
# We want the strip width to be a multiple of the tile
# width so it repeats cleanly.
self.strip_width = int(self.width / self.n_repetitions)
# Fix conflicting arguments
conflicting_args = ["width", "height", "font"]
kwargs = {key: kwargs[key] for key in kwargs if key not in conflicting_args}
# Create strip of pattern
if pattern is None:
strip = image_noise(width=self.strip_width, height=self.height, **kwargs)
else:
strip = self.pattern(width=self.strip_width, height=self.height, **kwargs)
self.strip_pixels = strip.load()
[docs] def draw(self, guide=True):
"""Create a PIL image of Autostereograms.
Parameters
----------
guide : bool
Defaults to 'True' to activate two red dots as guidance, and 'False' to disable the guide.
Returns
-------
Image
Image of the Autostereograms illusion, defaults to 800 x 600 pixels.
Can be resized
(`resize()`, See https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.resize)
and saved in different file formats
(`save()` See https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html).
Examples
---------
>>> import pyllusion
>>>
>>> autostereograms = pyllusion.Autostereogram(stimulus="3D", width=1000, height=500, font="arialbd.ttf")
>>> autostereograms.draw(guide=True)
"""
image = PIL.Image.new("RGB", (self.width, self.height))
# Load pixels for easy replacement
depth_pixels = self.depth_map.load()
image_pixels = image.load()
for x in range(self.width):
for y in range(self.height):
# Need one full strip's worth to borrow from.
if x < self.strip_width:
image_pixels[x, y] = self.strip_pixels[x, y]
else:
shift_amplitude = self.depth * (depth_pixels[x, y] / self.n_repetitions)
image_pixels[x, y] = image_pixels[x - self.strip_width + shift_amplitude, y]
# Add guide
if guide is True:
draw = PIL.ImageDraw.Draw(image)
for i in [-2, 0]:
diameter = 0.005 * self.width
center_x = (self.width / 2) + (i * self.strip_width / 2)
center_y = 0.5 * self.height
draw.ellipse([center_x-diameter, center_y-diameter, center_x+diameter, center_y+diameter], fill=(255, 0, 0))
return image