first go at multithreading the mixer.

This commit is contained in:
sccolbert
2009-11-08 05:28:24 +01:00
parent a3737fa34c
commit 196ab7530a
2 changed files with 173 additions and 99 deletions
+100 -91
View File
@@ -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 = <np.int16_t>(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] = <np.uint8_t>op_result
with nogil:
for i from 0 <= i < height:
for j from 0 <= j < width:
op_result = <np.int16_t>(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] = <np.uint8_t>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 = <float>(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] = <np.uint8_t>op_result
with nogil:
for i from 0 <= i < height:
for j from 0 <= j < width:
op_result = <float>(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] = <np.uint8_t>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 = <float>((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 = <float>((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] = <np.uint8_t>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] = <np.uint8_t>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 = <float>stateimg[i,j,0] / 255.
g = <float>stateimg[i,j,1] / 255.
b = <float>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 = <float>stateimg[i,j,0] / 255.
g = <float>stateimg[i,j,1] / 255.
b = <float>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] = <np.uint8_t>(r * 255)
img[i,j,1] = <np.uint8_t>(g * 255)
img[i,j,2] = <np.uint8_t>(b * 255)
b = 1 / (1 + exp(beta - b * alpha))
b = (b - c1) / c2
img[i,j,0] = <np.uint8_t>(r * 255)
img[i,j,1] = <np.uint8_t>(g * 255)
img[i,j,2] = <np.uint8_t>(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 = <float>stateimg[i,j,0] / 255.
g = <float>stateimg[i,j,1] / 255.
b = <float>stateimg[i,j,2] / 255.
img[i,j,0] = <np.uint8_t>(pow(r, gamma) * 255)
img[i,j,1] = <np.uint8_t>(pow(g, gamma) * 255)
img[i,j,2] = <np.uint8_t>(pow(b, gamma) * 255)
with nogil:
for i from 0 <= i < height:
for j from 0 <= j < width:
r = <float>stateimg[i,j,0] / 255.
g = <float>stateimg[i,j,1] / 255.
b = <float>stateimg[i,j,2] / 255.
img[i,j,0] = <np.uint8_t>(pow(r, gamma) * 255)
img[i,j,1] = <np.uint8_t>(pow(g, gamma) * 255)
img[i,j,2] = <np.uint8_t>(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] = <np.uint8_t>RGB[0]
img[i, j, 1] = <np.uint8_t>RGB[1]
img[i, j, 2] = <np.uint8_t>RGB[2]
img[i, j, 0] = <np.uint8_t>RGB[0]
img[i, j, 1] = <np.uint8_t>RGB[1]
img[i, j, 2] = <np.uint8_t>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] = <np.uint8_t>RGB[0]
img[i, j, 1] = <np.uint8_t>RGB[1]
img[i, j, 2] = <np.uint8_t>RGB[2]
img[i, j, 0] = <np.uint8_t>RGB[0]
img[i, j, 1] = <np.uint8_t>RGB[1]
img[i, j, 2] = <np.uint8_t>RGB[2]
+73 -8
View File
@@ -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