Source code for pylissom.utils.stimuli

"""
Provides several functions that create and manipulate matrices representing different stimuli, mainly guassians disks
TODO: only use cv2 or scikit-image, not both
"""

import random

import cv2
import numpy as np
from skimage.transform import rotate

from pylissom.math import gaussian


[docs]def translate(bar, step=1, over_x=True): images = [] cols = bar.shape[0] half_cols = cols // 2 for x in range(-half_cols, half_cols + 1 if cols % 2 == 1 else half_cols, step): if over_x: matrix = np.float32([[1, 0, x], [0, 1, 0]]) else: matrix = np.float32([[1, 0, 0], [0, 1, x]]) dst = cv2.warpAffine(bar, matrix, bar.shape) images.append(dst) return images
[docs]def random_translation(img, x_offset=5, y_offset=5): random_x = random.uniform(-x_offset, x_offset) random_y = random.uniform(-y_offset, y_offset) matrix = np.float32([[1, 0, random_x], [0, 1, random_y]]) return cv2.warpAffine(img, matrix, img.shape)
[docs]def generate_horizontal_bar(size): line = cv2.line(np.zeros((size, size), dtype=np.float32), (0, size // 2), (size, size // 2), 1, 1) return cv2.GaussianBlur(line, (7, 7), 0)
[docs]def generate_gaussian(shape, mu_x=0.0, mu_y=0.0, sigma_x=1.0, sigma_y=None): return np.fromfunction(function=lambda x, y: gaussian(x, y, mu_x, mu_y, sigma=sigma_x, sigma_y=sigma_y), shape=shape, dtype=int)
[docs]def rotations(img, num=13): r""" Returns: Returns a dictionary of len 180 / num, representing {rotation_degrees: rotated_img} """ return {(int(degree), rotate(img, degree, mode='constant').astype(img.dtype)) for degree in np.linspace(0, 180, num)}
[docs]def gaussian_generator(size, mu_x, mu_y, sigma_x, sigma_y, orientation): r""" Args: orientation: It's actually redundant because orientation is a function of the sigmas, but make it easier to use Returns: A numpy matrix representing a gaussian of shape = (size, size) """ g = generate_gaussian((size, size), mu_x, mu_y, sigma_x, sigma_y) rot = rotate(g, orientation, mode='constant') return rot.astype(g.dtype)
[docs]def generate_random_gaussian(size): r""" Returns: A numpy matrix with a random gaussian of shape = (size, size) """ return gaussian_generator(size, mu_x=random.uniform(0, size - 1), mu_y=random.uniform(0, size - 1), sigma_x=0.75, sigma_y=2.5, orientation=random.uniform(0, 180) )
[docs]def random_gaussians_generator(size, gaussians=1): r""" Args: size: img will have shape = (size, size) gaussians: How many gaussians per matrix Returns: Yields a squared numpy matrix with gaussians disks """ while True: yield combine_matrices(*(generate_random_gaussian(size) for _ in range(gaussians)))
[docs]def generate_random_faces(size): # TODO: sigma should be a function of size return generate_three_dots(size, mu_x=random.uniform(0, size - 1), mu_y=random.uniform(0, size - 1), # sigma_x=2 * 1.21 / size / 1.7, sigma_x=1.2, # sigma_x=2.0, orientation=random.uniform(-15, 15) )
[docs]def generate_three_dots(size, mu_x, mu_y, sigma_x, orientation): # TODO: The spacing between eyes and mouth should be a function of the image size or sigma r""" Returns: A numpy matrix with 3 gaussian disks representing a face """ lefteye = generate_gaussian((size, size), mu_x=mu_x, mu_y=mu_y + 2.5 * 2, sigma_x=sigma_x) righteye = generate_gaussian((size, size), mu_x=mu_x, mu_y=mu_y - 2.5 * 2, sigma_x=sigma_x) mouth = generate_gaussian((size, size), mu_x=mu_x + 5 * 2, mu_y=mu_y, sigma_x=sigma_x) eyes = combine_matrices(lefteye, righteye) face = combine_matrices(eyes, mouth) return rotate(face, orientation, mode='constant').astype(face.dtype)
[docs]def faces_generator(size, num=1): r""" Args: size: img will have shape = (size, size) gaussians: How many faces per matrix Returns: Yields a squared numpy matrix with 3-gaussians faces """ while True: yield combine_matrices(*(generate_random_faces(size) for _ in range(num)))
[docs]def combine_matrices(*matrices): r""" Returns: Merges matrices in one matrix using the maximum values in each pixel """ return np.maximum.reduce(matrices)
[docs]def sine_grating(size, freq, phase): vals = np.linspace(-np.pi, np.pi, size) xgrid, ygrid = np.meshgrid(vals, vals) return 0.5 + 0.5 * np.sin(freq * ygrid + phase, dtype=np.float32)