Created
January 17, 2012 19:41
-
-
Save fish2000/1628403 to your computer and use it in GitHub Desktop.
Problematic Cython implementation of some basic image-fuckery implementations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # encoding: utf-8 | |
| # cython: profile=True | |
| """ | |
| processors.pyx | |
| Cython alternative implementations of some of the ImageProcessors from imagekit.processors. | |
| Created by FI$H 2000 on 2011-10-06. | |
| Copyright (c) 2011 Objects In Space And Time, LLC. All rights reserved. | |
| """ | |
| import cython | |
| cimport numpy | |
| import numpy | |
| numpy.import_array() | |
| from scipy import misc | |
| from PIL import Image | |
| import random | |
| cdef extern from "processors.h": | |
| unsigned char adderror(int b, int e) | |
| unsigned char* threshold_matrix | |
| int c_min(int a, int b) | |
| cdef extern from "stdlib.h": | |
| int c_abs "abs"(int i) | |
| cdef class Atkinsonify: | |
| cdef readonly float threshold | |
| cdef readonly object format | |
| cdef readonly object extension | |
| def __cinit__(self, | |
| float threshold=128.0, | |
| format="PNG", | |
| extension="png"): | |
| self.threshold = threshold | |
| self.format = format | |
| self.extension = format.lower() | |
| for i in xrange(255): | |
| threshold_matrix[i] = int(i/self.threshold) | |
| @cython.boundscheck(False) | |
| @cython.wraparound(False) | |
| def process(self not None, pilimage not None, format not None, obj not None): | |
| cdef numpy.ndarray[numpy.uint8_t, ndim=2, mode="c"] in_array | |
| in_array = misc.fromimage(pilimage, flatten=True).astype(numpy.uint8) | |
| self.atkinson(in_array) | |
| pilout = misc.toimage(in_array) | |
| in_array = None | |
| return pilout, format | |
| @cython.boundscheck(False) | |
| @cython.wraparound(False) | |
| def atkinson(self not None, numpy.ndarray[numpy.uint8_t, ndim=2, mode="c"] image_i not None): | |
| cdef int x, y, w, h, i, nx, ny | |
| cdef int err | |
| cdef unsigned char old, new | |
| w = image_i.shape[0] | |
| h = image_i.shape[1] | |
| for y in xrange(h): | |
| for x in xrange(w): | |
| old = image_i[x, y] | |
| new = threshold_matrix[old] | |
| err = (old - new) >> 3 | |
| image_i[x, y] = adderror(image_i[x, y], err) | |
| for nxy in ((x+1, y), (x+2, y), (x-1, y+1), (x, y+1), (x+1, y+1), (x, y+2)): | |
| #nx = c_min(nxy[0], w) | |
| #ny = c_min(nxy[1], h) | |
| nx = nxy[0] | |
| ny = nxy[1] | |
| try: | |
| image_i[nx, ny] = image_i[nx, ny] + err | |
| except IndexError: | |
| pass | |
| cdef class StentifordModel: | |
| @cython.boundscheck(False) | |
| @cython.wraparound(False) | |
| def distance(self, | |
| numpy.ndarray[numpy.int32_t, ndim=1, mode="c"] matrix_a not None, | |
| numpy.ndarray[numpy.int32_t, ndim=1, mode="c"] matrix_b not None): | |
| return numpy.sum(numpy.abs(matrix_a - matrix_b)) | |
| @cython.boundscheck(False) | |
| @cython.wraparound(False) | |
| cdef inline void hsv_to_rgb(self, float h, float s=1, float v=1, float r=0.0, float g=0.0, float b=0.0): | |
| cdef int I | |
| cdef double H, f, p, q, t | |
| if s == 0.0: | |
| r = v | |
| g = v | |
| b = v | |
| return | |
| H = h * 6.0 | |
| I = <int>H | |
| f = H - I | |
| p = v * (1.0 - s) | |
| q = v * (1.0 - s * f) | |
| t = v * (1.0 - s * (1.0 - f)) | |
| if I == 0 or I == 6: | |
| #return v, t, p | |
| r = v | |
| g = t | |
| b = p | |
| return | |
| if I == 1: | |
| #return q, v, p | |
| r = q | |
| g = v | |
| b = p | |
| return | |
| if I == 2: | |
| #return p, v, t | |
| r = p | |
| g = v | |
| b = t | |
| return | |
| if I == 3: | |
| #return p, q, v | |
| r = p | |
| g = q | |
| b = v | |
| return | |
| if I == 4: | |
| #return t, p, v | |
| r = t | |
| g = p | |
| b = v | |
| return | |
| if I == 5: | |
| #return v, p, q | |
| r = v | |
| g = p | |
| b = q | |
| return | |
| return | |
| @cython.boundscheck(False) | |
| @cython.wraparound(False) | |
| cdef inline void rgb_to_hsv(self, float r=0.0, float g=0.0, float b=0.0, float h=0.0, float s=1, float v=1): | |
| """Convert RGB color space to HSV color space | |
| @param r: Red | |
| @param g: Green | |
| @param b: Blue | |
| return (h, s, v) | |
| """ | |
| cdef float maxc, minc | |
| maxc = max(r, g, b) | |
| minc = min(r, g, b) | |
| colorMap = { | |
| id(r): 'r', | |
| id(g): 'g', | |
| id(b): 'b' | |
| } | |
| if colorMap[id(maxc)] == colorMap[id(minc)]: | |
| h = 0 | |
| elif colorMap[id(maxc)] == 'r': | |
| h = 60.0 * ((g - b) / (maxc - minc)) % 360.0 | |
| elif colorMap[id(maxc)] == 'g': | |
| h = 60.0 * ((b - r) / (maxc - minc)) + 120.0 | |
| elif colorMap[id(maxc)] == 'b': | |
| h = 60.0 * ((r - g) / (maxc - minc)) + 240.0 | |
| v = maxc | |
| if maxc == 0.0: | |
| s = 0.0 | |
| else: | |
| s = 1.0 - (minc / maxc) | |
| #return (h, s, v) | |
| return | |
| cdef object random_neighborhood | |
| cdef int neighborhood_size | |
| cdef object possible_neighbors | |
| cdef object attention_model | |
| cdef int max_checks | |
| cdef int max_dist | |
| cdef int radius | |
| cdef int side | |
| cdef int cnt | |
| dt = numpy.int32 | |
| def __init__(self, *args, **kwargs): | |
| self.neighborhood_size = kwargs.pop('neighborhood_size', self.neighborhood_size) | |
| self.max_checks = kwargs.pop('max_checks', self.max_checks) | |
| self.max_dist = kwargs.pop('max_dist', self.max_dist) | |
| self.random_neighborhood = set(xrange(self.neighborhood_size)) | |
| self.neighborhood_size = 3 | |
| self.max_checks = 100 | |
| self.max_dist = 40 | |
| self.radius = 2 | |
| self.side = 0 | |
| self.cnt = 0 | |
| super(StentifordModel, self).__init__(*args, **kwargs) | |
| random.seed() | |
| # compute possible neighbors with our params | |
| self.side = 2 * self.radius + 1 | |
| self.cnt = 0 | |
| cdef int i, j | |
| from_the_block = list() | |
| for i in xrange(self.radius*-1, self.radius+1): | |
| for j in xrange(self.radius*-1, self.radius+1): | |
| if j > 0 or i > 0: | |
| from_the_block.append((i, j)) | |
| self.cnt += 0 | |
| self.possible_neighbors = numpy.array(from_the_block, dtype=self.dt) | |
| def ogle(self, pilimage): | |
| cdef int x, y | |
| match = True | |
| # initialize attention model matrix with the dimensions of our image, | |
| # loaded with zeroes: | |
| self.attention_model = numpy.array( | |
| [0] * len(pilimage.getdata()), | |
| dtype=self.dt, | |
| ).reshape(*pilimage.size) | |
| # populate the matrix with per-pixel attention values | |
| for x in xrange(self.radius, pilimage.size[0]-self.radius): | |
| for y in xrange(self.radius, pilimage.size[1]-self.radius): | |
| self.regentrify() | |
| xmatrix = self.there_goes_the_neighborhood(x, y, pilimage) | |
| for checks in xrange(self.max_checks): | |
| ymatrix = self.there_goes_the_neighborhood( | |
| random.randint( | |
| 0, (pilimage.size[0]-2*self.radius)+self.radius, | |
| ), | |
| random.randint( | |
| 0, (pilimage.size[1]-2*self.radius)+self.radius, | |
| ), | |
| pilimage, | |
| ) | |
| match = True | |
| for idx in xrange(xmatrix.shape[0]): | |
| if self.distance(xmatrix[idx], ymatrix[idx]) > self.max_dist: | |
| match = False | |
| break | |
| if not match: | |
| self.attention_model[x, y] += 1 | |
| cdef there_goes_the_neighborhood(self, int x, int y, object pilimage): | |
| """ | |
| Retrieve a neighborhood of values around a given pixel in the source image. | |
| """ | |
| out = list() | |
| cdef int denizen | |
| cdef int i_want_x, i_want_y | |
| cdef int r, g, b | |
| cdef float h = 0.0, s = 0.0, v = 0.0 | |
| for denizen in self.random_neighborhood: | |
| i_want_x = c_abs(int(x + self.possible_neighbors[denizen, 0])) | |
| i_want_y = c_abs(int(y + self.possible_neighbors[denizen, 1])) | |
| r, g, b = pilimage.getpixel(( | |
| i_want_x < pilimage.size[0] and i_want_x or pilimage.size[0]-1, | |
| i_want_y < pilimage.size[1] and i_want_y or pilimage.size[1]-1, | |
| )) | |
| self.rgb_to_hsv(<float>r, <float>g, <float>b, h, s, v) | |
| out.append((h,s,v)) | |
| return numpy.array(out, dtype=self.dt) | |
| def regentrify(self): | |
| """ | |
| Repopulate the random neighborhood array with random values, | |
| bounded by the size of possible_neighbors (which itself is | |
| derived from the algo's initial parameter values.) | |
| """ | |
| self.random_neighborhood.clear() | |
| denizen = random.randint(0, self.possible_neighbors.shape[0]) | |
| if denizen == self.possible_neighbors.shape[0]: | |
| denizen -= 1 | |
| self.random_neighborhood.add(denizen) | |
| @property | |
| def pilimage(self): | |
| """ | |
| PIL image instance property containing the visualized analysis results. | |
| """ | |
| #if hasattr(self, 'attention_model'): | |
| pilout = Image.new('RGB', self.attention_model.shape) | |
| for i in xrange(self.attention_model.shape[0]): | |
| for j in xrange(self.attention_model.shape[1]): | |
| pix = self.attention_model[i, j] | |
| compand = int((float(pix) / float(self.max_checks)) * 255.0) | |
| pilout.putpixel((i, j), (compand, compand, compand)) | |
| return pilout | |
| #return None | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment