diff --git a/scikits/image/io/_plugins/_colormixer.pyx b/scikits/image/io/_plugins/_colormixer.pyx index 19f4fce0..48450473 100644 --- a/scikits/image/io/_plugins/_colormixer.pyx +++ b/scikits/image/io/_plugins/_colormixer.pyx @@ -15,8 +15,8 @@ cimport numpy as np import cython cdef extern from "math.h": - float exp(float) - float pow(float, float) + float exp(float) nogil + float pow(float, float) nogil @cython.boundscheck(False) @@ -46,15 +46,16 @@ def add(np.ndarray[np.uint8_t, ndim=3] img, cdef np.int16_t op_result cdef int i, j - for i in range(height): - for j in range(width): - op_result = (stateimg[i,j,k] + n) - if op_result > 255: - img[i, j, k] = 255 - elif op_result < 0: - img[i, j, k] = 0 - else: - img[i, j, k] = op_result + with nogil: + for i from 0 <= i < height: + for j from 0 <= j < width: + op_result = (stateimg[i,j,k] + n) + if op_result > 255: + img[i, j, k] = 255 + elif op_result < 0: + img[i, j, k] = 0 + else: + img[i, j, k] = op_result @cython.boundscheck(False) @@ -84,15 +85,16 @@ def multiply(np.ndarray[np.uint8_t, ndim=3] img, cdef float op_result cdef int i, j - for i in range(height): - for j in range(width): - op_result = (stateimg[i,j,k] * n) - if op_result > 255: - img[i, j, k] = 255 - elif op_result < 0: - img[i, j, k] = 0 - else: - img[i, j, k] = op_result + with nogil: + for i from 0 <= i < height: + for j from 0 <= j < width: + op_result = (stateimg[i,j,k] * n) + if op_result > 255: + img[i, j, k] = 255 + elif op_result < 0: + img[i, j, k] = 0 + else: + img[i, j, k] = op_result @cython.boundscheck(False) @@ -122,17 +124,18 @@ def brightness(np.ndarray[np.uint8_t, ndim=3] img, cdef float op_result cdef int i, j, k - for i in range(height): - for j in range(width): - for k in range(3): - op_result = ((stateimg[i,j,k] * factor + offset)) + with nogil: + for i from 0 <= i < height: + for j from 0 <= j < width: + for k from 0 <= k < 3: + op_result = ((stateimg[i,j,k] * factor + offset)) - if op_result > 255: - img[i, j, k] = 255 - elif op_result < 0: - img[i, j, k] = 0 - else: - img[i, j, k] = op_result + if op_result > 255: + img[i, j, k] = 255 + elif op_result < 0: + img[i, j, k] = 0 + else: + img[i, j, k] = op_result @cython.boundscheck(False) @@ -144,29 +147,30 @@ def sigmoid_gamma(np.ndarray[np.uint8_t, ndim=3] img, cdef int width = img.shape[1] cdef float c1, c2, r, g, b - cdef int i, j, k - for i in range(height): - for j in range(width): - r = stateimg[i,j,0] / 255. - g = stateimg[i,j,1] / 255. - b = stateimg[i,j,2] / 255. - c1 = 1 / (1 + exp(beta)) - c2 = 1 / (1 + exp(beta - alpha)) - c1 + with nogil: + for i from 0 <= i < height: + for j from 0 <= j < width: + r = stateimg[i,j,0] / 255. + g = stateimg[i,j,1] / 255. + b = stateimg[i,j,2] / 255. - r = 1 / (1 + exp(beta - r * alpha)) - r = (r - c1) / c2 + c1 = 1 / (1 + exp(beta)) + c2 = 1 / (1 + exp(beta - alpha)) - c1 - g = 1 / (1 + exp(beta - g * alpha)) - g = (g - c1) / c2 + r = 1 / (1 + exp(beta - r * alpha)) + r = (r - c1) / c2 - b = 1 / (1 + exp(beta - b * alpha)) - b = (b - c1) / c2 + g = 1 / (1 + exp(beta - g * alpha)) + g = (g - c1) / c2 - img[i,j,0] = (r * 255) - img[i,j,1] = (g * 255) - img[i,j,2] = (b * 255) + b = 1 / (1 + exp(beta - b * alpha)) + b = (b - c1) / c2 + + img[i,j,0] = (r * 255) + img[i,j,1] = (g * 255) + img[i,j,2] = (b * 255) @cython.boundscheck(False) @@ -182,19 +186,21 @@ def gamma(np.ndarray[np.uint8_t, ndim=3] img, cdef int i, j gamma = 1./gamma - for i in range(height): - for j in range(width): - r = stateimg[i,j,0] / 255. - g = stateimg[i,j,1] / 255. - b = stateimg[i,j,2] / 255. - img[i,j,0] = (pow(r, gamma) * 255) - img[i,j,1] = (pow(g, gamma) * 255) - img[i,j,2] = (pow(b, gamma) * 255) + with nogil: + for i from 0 <= i < height: + for j from 0 <= j < width: + r = stateimg[i,j,0] / 255. + g = stateimg[i,j,1] / 255. + b = stateimg[i,j,2] / 255. + + img[i,j,0] = (pow(r, gamma) * 255) + img[i,j,1] = (pow(g, gamma) * 255) + img[i,j,2] = (pow(b, gamma) * 255) -cdef void rgb_2_hsv(float* RGB, float* HSV): +cdef void rgb_2_hsv(float* RGB, float* HSV) nogil: cdef float R, G, B, H, S, V, MAX, MIN R = RGB[0] G = RGB[1] @@ -256,7 +262,7 @@ cdef void rgb_2_hsv(float* RGB, float* HSV): HSV[2] = V -cdef void hsv_2_rgb(float* HSV, float* RGB): +cdef void hsv_2_rgb(float* HSV, float* RGB) nogil: cdef float H, S, V cdef float f, p, q, t, r, g, b cdef int hi @@ -440,28 +446,30 @@ def hsv_add(np.ndarray[np.uint8_t, ndim=3] img, cdef int i, j - for i in range(height): - for j in range(width): - RGB[0] = stateimg[i, j, 0] - RGB[1] = stateimg[i, j, 1] - RGB[2] = stateimg[i, j, 2] + with nogil: + for i from 0 <= i < height: + for j from 0 <= j < width: + RGB[0] = stateimg[i, j, 0] + RGB[1] = stateimg[i, j, 1] + RGB[2] = stateimg[i, j, 2] - rgb_2_hsv(RGB, HSV) + rgb_2_hsv(RGB, HSV) - # Add operation - HSV[0] += h_amt - HSV[1] += s_amt - HSV[2] += v_amt + # Add operation + HSV[0] += h_amt + HSV[1] += s_amt + HSV[2] += v_amt - hsv_2_rgb(HSV, RGB) + hsv_2_rgb(HSV, RGB) - RGB[0] *= 255 - RGB[1] *= 255 - RGB[2] *= 255 + RGB[0] *= 255 + RGB[1] *= 255 + RGB[2] *= 255 + + img[i, j, 0] = RGB[0] + img[i, j, 1] = RGB[1] + img[i, j, 2] = RGB[2] - img[i, j, 0] = RGB[0] - img[i, j, 1] = RGB[1] - img[i, j, 2] = RGB[2] @cython.boundscheck(False) def hsv_multiply(np.ndarray[np.uint8_t, ndim=3] img, @@ -508,28 +516,29 @@ def hsv_multiply(np.ndarray[np.uint8_t, ndim=3] img, cdef int i, j - for i in range(height): - for j in range(width): - RGB[0] = stateimg[i, j, 0] - RGB[1] = stateimg[i, j, 1] - RGB[2] = stateimg[i, j, 2] + with nogil: + for i from 0 <= i < height: + for j from 0 <= j < width: + RGB[0] = stateimg[i, j, 0] + RGB[1] = stateimg[i, j, 1] + RGB[2] = stateimg[i, j, 2] - rgb_2_hsv(RGB, HSV) + rgb_2_hsv(RGB, HSV) - # Multiply operation - HSV[0] += h_amt - HSV[1] *= s_amt - HSV[2] *= v_amt + # Multiply operation + HSV[0] += h_amt + HSV[1] *= s_amt + HSV[2] *= v_amt - hsv_2_rgb(HSV, RGB) + hsv_2_rgb(HSV, RGB) - RGB[0] *= 255 - RGB[1] *= 255 - RGB[2] *= 255 + RGB[0] *= 255 + RGB[1] *= 255 + RGB[2] *= 255 - img[i, j, 0] = RGB[0] - img[i, j, 1] = RGB[1] - img[i, j, 2] = RGB[2] + img[i, j, 0] = RGB[0] + img[i, j, 1] = RGB[1] + img[i, j, 2] = RGB[2] diff --git a/scikits/image/io/_plugins/util.py b/scikits/image/io/_plugins/util.py index 951ec16f..6d976deb 100644 --- a/scikits/image/io/_plugins/util.py +++ b/scikits/image/io/_plugins/util.py @@ -1,8 +1,11 @@ import numpy as np import _colormixer import _histograms +import threading +import multiprocessing # utilities to make life easier for plugin writers. +CPU_COUNT = multiprocessing.cpu_count() class GuiLockError(Exception): def __init__(self, msg): @@ -173,6 +176,52 @@ def histograms(img, nbins): return _histograms.histograms(img, nbins) +class ImgThread(threading.Thread): + def __init__(self, func, *args): + super(ImgThread, self).__init__() + self.func = func + self.args = args + + def run(self): + self.func(*self.args) + +class ThreadDispatch(object): + def __init__(self, img, stateimg, func, *args): + + width = img.shape[1] + height = img.shape[0] + self.cores = CPU_COUNT + self.threads = [] + self.chunks = [] + if self.cores == 2: + self.chunks.append((img[:, :(width/2), :], + stateimg[:, :(width/2), :])) + self.chunks.append((img[:, (width/2):, :], + stateimg[:, (width/2):, :])) + elif self.cores == 4: + self.chunks.append((img[:(height/2), :(width/2), :], + stateimg[:(height/2), :(width/2), :])) + self.chunks.append((img[:(height/2), (width/2):, :], + stateimg[:(height/2), (width/2):, :])) + self.chunks.append((img[(height/2):, :(width/2), :], + stateimg[(height/2):, :(width/2), :])) + self.chunks.append((img[(height/2):, (width/2):, :], + stateimg[(height/2):, (width/2):, :])) + else: + raise ValueError('Cant handle your weird machine') + + for i in range(self.cores): + self.threads.append(ImgThread(func, self.chunks[i][0], + self.chunks[i][1], *args)) + + def run(self): + for t in self.threads: + t.start() + for t in self.threads: + t.join() + + + class ColorMixer(object): ''' a class to manage mixing colors in an image. The input array must be an RGB uint8 image. @@ -244,8 +293,11 @@ class ColorMixer(object): ''' assert channel in self.valid_channels + pool = ThreadDispatch(self.img, self.stateimg, + _colormixer.add, channel, ammount) + pool.run() + - _colormixer.add(self.img, self.stateimg, channel, ammount) def multiply(self, channel, ammount): '''Mutliply the indicated channel by the specified value. @@ -261,8 +313,10 @@ class ColorMixer(object): ''' assert channel in self.valid_channels + pool = ThreadDispatch(self.img, self.stateimg, + _colormixer.multiply, channel, ammount) + pool.run() - _colormixer.multiply(self.img, self.stateimg, channel, ammount) def brightness(self, factor, offset): '''Adjust the brightness off an image with an offset and factor. @@ -277,13 +331,21 @@ class ColorMixer(object): result = clip((pixel + offset)*factor) ''' - _colormixer.brightness(self.img, self.stateimg, factor, offset) + pool = ThreadDispatch(self.img, self.stateimg, + _colormixer.brightness, factor, offset) + pool.run() + def sigmoid_gamma(self, alpha, beta): - _colormixer.sigmoid_gamma(self.img, self.stateimg, alpha, beta) + pool = ThreadDispatch(self.img, self.stateimg, + _colormixer.sigmoid_gamma, alpha, beta) + pool.run() + def gamma(self, gamma): - _colormixer.gamma(self.img, self.stateimg, gamma) + pool = ThreadDispatch(self.img, self.stateimg, + _colormixer.gamma, gamma) + pool.run() def hsv_add(self, h_amt, s_amt, v_amt): '''Adjust the H, S, V channels of an image by a constant ammount. @@ -301,7 +363,9 @@ class ColorMixer(object): The ammount to add to the value (-1..1) ''' - _colormixer.hsv_add(self.img, self.stateimg, h_amt, s_amt, v_amt) + pool = ThreadDispatch(self.img, self.stateimg, + _colormixer.hsv_add, h_amt, s_amt, v_amt) + pool.run() def hsv_multiply(self, h_amt, s_amt, v_amt): '''Adjust the H, S, V channels of an image by a constant ammount. @@ -323,8 +387,9 @@ class ColorMixer(object): The ammount to multiply to the value (0..1) ''' - _colormixer.hsv_multiply(self.img, self.stateimg, h_amt, s_amt, v_amt) - + pool = ThreadDispatch(self.img, self.stateimg, + _colormixer.hsv_multiply, h_amt, s_amt, v_amt) + pool.run() def rgb_2_hsv_pixel(self, R, G, B): '''Convert an RGB value to HSV