import numpy as np
import PIL.Image, PIL.ImageDraw, PIL.ImageFilter, PIL.ImageFont, PIL.ImageOps
import scipy.signal
from .rescale import rescale
from .utilities import _coord_circle
[docs]def image_blobs(width=500, height=500, n=100, sd=8, weight=1):
"""Returns a PIL image with blobs of the same standard deviations (SD).
Parameters
----------
width : int
Width of the returned image.
height : int
Height of the returned image.
n : int
Number of gaussian blobs drawn in the returned image.
sd : int
The standard deviation of the gaussian blob. Unit in pixel.
weight : int
A multiplication weight in case there are several layers of SDs.
Returns
-------
Image
Image of blob(s).
Examples
--------
>>> import pyllusion
>>>
>>> pyllusion.image_blobs(n=100)
>>> pyllusion.image_blobs(n=[5, 300, 1000], sd=[50, 10, 5], weight=[1, 1.5, 2])
"""
# Sanitize input
if isinstance(sd, (int, float)):
sd = [sd]
if isinstance(n, (int, float)):
n = [n]
if isinstance(weight, (int, float)):
weight = [weight]
if len(n) != len(sd):
raise TypeError("'n' must be of the same length as 'sd'.")
if len(n) != len(weight):
raise TypeError("'n' must be of the same length as 'weight'.")
if isinstance(width, tuple):
height = width[1]
width = width[0]
# Add layers
array = np.zeros((height, width))
parent_width = 3 * np.max([width, height])
for i, current_sd in enumerate(sd):
x = np.random.randint(width, size=n[i])
y = np.random.randint(height, size=n[i])
parent_blob = _image_blob_parent(sd=int(current_sd), parent_width=parent_width)
w = np.int(len(parent_blob) / 2)
for j in range(int(n[i])):
# Crop the blob and multiply by weight
array += (parent_blob[w - y[j] : (w - y[j]) + height, w - x[j] : (w - x[j]) + width] * weight[i])
array = (array - np.min(array)) / np.max(array) * 255
image = PIL.Image.fromarray(array.astype(np.uint8))
return image
[docs]def image_blob(x=450, y=100, width=800, height=600, sd=30):
"""Returns a PIL image of a blob.
Parameters
----------
x : int
x-coordinate of the center of the blob. Unit in pixel.
y : int
y-coordinate of the center of the blob. Unit in pixel.
width : int
Width of the returned image.
height : int
Height of the returned image.
sd : int
The standard deviation of the gaussian blob. Unit in pixel.
Returns
-------
Image
Image of blob.
>>> import pyllusion
>>>
>>> pyllusion.image_blob()
"""
blob = _image_blob(x=x, y=y, width=width, height=height)
blob = rescale(blob, to=[0, 255])
image = PIL.Image.fromarray(blob.astype(np.uint8))
return image
# =============================================================================
# Internal
# =============================================================================
def _image_blob(x=400, y=300, width=800, height=600, sd=30):
"""Returns a 2D Gaussian kernel.
>>> import pyllusion
>>> import matplotlib.pyplot as plt
>>> array = _image_blob(sd=8)
>>> plt.imshow(array) #doctest: +ELLIPSIS
"""
parent_blob = _image_blob_parent(x=x, y=y, width=width, height=height, sd=sd)
w = np.int(len(parent_blob) / 2)
return parent_blob[w - y : (w - y) + height, w - x : (w - x) + width]
def _image_blob_parent(x=400, y=300, width=800, height=600, sd=30, parent_width=None):
"""
>>> import matplotlib.pyplot as plt
>>> plt.imshow(_image_blob_parent(sd=30))
"""
if parent_width is None:
parent_width = 3 * (np.max([x, y, height - x, width - y]))
gkern1d = scipy.signal.gaussian(parent_width, std=sd).reshape(parent_width, 1)
return np.outer(gkern1d, gkern1d)
def _draw_blob(width, height=None, size=0.1, blur=0, color="black"):
# Retrieve dimensions
if height is None:
width, height = width
elif isinstance(width, PIL.Image.Image):
width, height = width.size
# Create mask of image size
blob = PIL.Image.new("RGBA", (width, height))
# Blob coordinates
coord = _coord_circle(
blob, diameter=size, x=np.random.uniform(-1, 1), y=np.random.uniform(-1, 1)
)
# Draw blob
draw = PIL.ImageDraw.Draw(blob)
draw.ellipse(coord, fill=color)
blob = blob.filter(PIL.ImageFilter.GaussianBlur(radius=blur * 0.01 * width))
return blob