############################### # Complexity - Pixel Symmetry # ############################### # # V1.0 # 29/05/2017 # # Implemented by: # Thomas Langerak # (hello@thomaslangerak.nl) # # Supervisor: # Antti Oulasvirta # # This work was funded by Technology Industries of Finland in a three-year # project grant on self-optimizing web services. The principal investigator # is Antti Oulasvirta of Aalto University (antti.oulasvirta@aalto.fi) # ########### # Summary # ########### # # One of the Gestalt principles, mirror symmetry - the similarity of an object reflection across a straight axis - # was claimed to improve interface design. However, quantifying symmetry might be problematic in HCI. Psychologists # mainly studied mirror symmetry of relatively simple objects, such as dot and line patterns, or human faces. # ############# # Technical # ############# # # Inputs: PNG image (base64) # Returns: List of 1 item: Normalized Symmetry (float) # ############## # References # ############## # # 1. Miniukovich, A. and De Angeli, A. Quantification of interface visual complexity. Proceedings of the 2014 # International Working Conference on Advanced Visual Interfaces - AVI '14, (2014). # ############## # Change Log # ############## # ############### # Bugs/Issues # ############### # # Limiting the number of pixels is not working yet. # import cv2 from skimage import util, color import numpy as np import base64 from PIL import Image from io import BytesIO def get_pixels_in_radius(x, y, width, height, radius): # Get x border if x < radius: rad_x_left = -x rad_x_right = radius elif width - x < radius: rad_x_right = 1 * (width - x) rad_x_left = -radius else: rad_x_left = -radius rad_x_right = radius # Get y borders if y < radius: rad_y_top = -y rad_y_bottom = radius elif height - y < radius: rad_y_bottom = 1 * (height - y) rad_y_top = -radius else: rad_y_top = -radius rad_y_bottom = radius pixels = [] for m in range(rad_x_left, rad_x_right): for n in range(rad_y_top, rad_y_bottom): if m != 0 or n != 0: pixel = [x + m, y + n] pixels.append(pixel) return pixels def execute(b64): b64 = base64.b64decode(b64) b64 = BytesIO(b64) img = Image.open(b64) img = np.array(img) img_la = color.rgb2gray(img) img_la = util.img_as_ubyte(img_la) # See sigma here: https://dsp.stackexchange.com/questions/4716/differences-between-opencv-canny-and-matlab-canny img_la = cv2.GaussianBlur(img_la, (7, 7), 2) edges = cv2.Canny(img_la, 0.11, 0.27) height, width = edges.shape # Set all pixels in radius of an edge pixel to 0 # This is not going good yet radius = 3 all_key = 0 for y in range(height): for x in range(width): if edges[y][x] != 0: all_key += 1 pixels_in_radius = get_pixels_in_radius(x, y, width, height, radius) for pixel in pixels_in_radius: edges[pixel[1], pixel[0]] = 0 img = Image.fromarray(edges, 'L') # img.show() symmetry_radius = 4 # Check vertical symmetry sym_key = 0 for y in range(height): for x in range(width / 2): if edges[y][x] != 0: vertical_pixels = get_pixels_in_radius(width - x, y, width, height, symmetry_radius) horizontal_pixels = get_pixels_in_radius(x, height - y, width, height, symmetry_radius) for pixel in vertical_pixels: if edges[int(pixel[1]), int(pixel[0])] != 0: sym_key += 1 break for pixel in horizontal_pixels: if edges[int(pixel[1]), int(pixel[0])] != 0: sym_key += 1 break try: sym_normalized = (float(sym_key) / float(all_key)) * ((float((all_key - 1) * symmetry_radius) / float(width * height)) ** -1) except ZeroDivisionError: sym_normalized = 0 return [sym_normalized]