Optimizing speed for large-array inter-element algorithms (specifically, color space conversion)
Hello all, I'm scratching my head over how to make this image color space conversion from "RGB" to "HSV" quicker. It requires input from all three bands of a given pixel at each pixel, and thus can't be easily flattened. I've already implemented psyco and removed the extra step of calling colorsys's rgb_to_hsv function by adapting the code into my primary for loop. Right now it takes around 9 seconds for a single 1600x1200 RGB image, a conversion that I've seen implemented more or less instantly in, say, ImageJ. What can I do to make this conversion more efficient? Thank you in advance, Theodore Test ------------ #Necessary imports and hand-waving at psyco usage. import numpy from numpy import * from scipy.misc import pilutil import psyco psyco.full() # Read image file, cram it into a normalized array with one row per pixel # with each column holding the given pixel's R,G, or B band-value. s = pilutil.imread('A_Biggish_Image.tif') r,c,d = s.shape l = r*c t = t.reshape(l,3) t = t/255.0 # Cycle through all of the pixels, converting them to HSV space and putting them # back into the array. This is the big time-waster. # # The conversion is adapted directly from colorsys.rgb_to_hsv to reduce call time for x in range(0,l): tr = float(t[x,0]) tg = float(t[x,1]) tb = float(t[x,2]) maxc = max(tr, tg, tb) minc = min(tr, tg, tb) v = maxc if minc == maxc: t[x,0],t[x,1],t[x,2] = 0.0, 0.0, v else: s = (maxc-minc) / maxc rc = (maxc-tr) / (maxc-minc) gc = (maxc-tg) / (maxc-minc) bc = (maxc-tb) / (maxc-minc) if tr == maxc: h = bc-gc elif tg == maxc: h = 2.0+rc-bc else: h = 4.0+gc-rc h = (h/6.0) % 1.0 t[x,0],t[x,1],t[x,2] = h, s, v # Renormalize shape and contents to image-file standards. t = t*255.0 t = t.astype(uint8) t = t.reshape(r,c,d) finaloutputarray = t --------
Am Montag, 21. Januar 2008 15:39:21 schrieb theodore test:
Right now it takes around 9 seconds for a single 1600x1200 RGB image, a conversion that I've seen implemented more or less instantly in, say, ImageJ. What can I do to make this conversion more efficient?
You should "just remove" the loop. Try to convert all operations to elementwise array/matrix-operations, which are very fast. IOW, make the algorithm work on all pixels in parallel. The only tricky part is the conditional operation, but you can emulate that with the binary operators and e.g. put(). -- Ciao, / / /--/ / / ANS
Hi, I don't know the area but following your code I would suggest the completely untested code. I am not sure about the conditions to get h so I leave you get to the correct h and transform it appropriately. Bruce # First create 2 dimensional array/matrix (number of pixels by three): 'rows' are pixels and one 'columns' for each of r, g, b values, say RGB from the image # Compute v ie maximum across r, g and b for each pixel v=RGB.max(axis=1) #compute range between high and low values for each pixel maxmin=v-RGB.min(axis=1) #compute s s=maxmin/v #Compute h hr=(RGB[:,1]-RGB[:,2])/maxmin hg=2.0+(RGB[:,2]-RGB[:,0])/maxmin hb=4.0+(RGB[:,0]-RGB[:,1])/maxmin #Some code to get h #Final output HSV=255.0*numpy.column_stack(h,s,v) On Jan 21, 2008 8:39 AM, theodore test <tteststudent@gmail.com> wrote:
Hello all,
I'm scratching my head over how to make this image color space conversion from "RGB" to "HSV" quicker. It requires input from all three bands of a given pixel at each pixel, and thus can't be easily flattened. I've already implemented psyco and removed the extra step of calling colorsys's rgb_to_hsv function by adapting the code into my primary for loop.
Right now it takes around 9 seconds for a single 1600x1200 RGB image, a conversion that I've seen implemented more or less instantly in, say, ImageJ. What can I do to make this conversion more efficient?
Thank you in advance, Theodore Test
------------
#Necessary imports and hand-waving at psyco usage. import numpy from numpy import * from scipy.misc import pilutil
import psyco psyco.full()
# Read image file, cram it into a normalized array with one row per pixel # with each column holding the given pixel's R,G, or B band-value. s = pilutil.imread('A_Biggish_Image.tif') r,c,d = s.shape
l = r*c t = t.reshape(l,3) t = t/255.0
# Cycle through all of the pixels, converting them to HSV space and putting them # back into the array. This is the big time-waster. # # The conversion is adapted directly from colorsys.rgb_to_hsv to reduce call time for x in range(0,l): tr = float(t[x,0]) tg = float(t[x,1]) tb = float(t[x,2]) maxc = max(tr, tg, tb) minc = min(tr, tg, tb) v = maxc if minc == maxc: t[x,0],t[x,1],t[x,2] = 0.0, 0.0, v else: s = (maxc-minc) / maxc rc = (maxc-tr) / (maxc-minc) gc = (maxc-tg) / (maxc-minc) bc = (maxc-tb) / (maxc-minc) if tr == maxc: h = bc-gc elif tg == maxc: h = 2.0+rc-bc else: h = 4.0+gc-rc h = (h/6.0) % 1.0 t[x,0],t[x,1],t[x,2] = h, s, v
# Renormalize shape and contents to image-file standards. t = t*255.0 t = t.astype(uint8) t = t.reshape(r,c,d)
finaloutputarray = t
--------
_______________________________________________ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion
Hi Theodore Probably not the fastest, but a full example of how you may vectorize your loop. Ran just one test, and that speeded up the original code. Example: ---------------- from numpy import empty_like def vectorized_rgb2hsv(im): "im is a (m, n, 3) array.""" im = im/255. out = empty_like(im) im_max = im.max(-1) delta = im.ptp(-1) s = delta/im_max s[delta==0] = 0 index = im[:,:,0] == im_max # red is max out[index, 0] = (im[index, 1] - im[index, 2] ) / delta[index] index = im[:,:,1] == im_max # green is max out[index, 0] = 2 + (im[index, 2] - im[index, 0] ) / delta[index] index = im[:,:,2] == im_max # blue is max out[index, 0] = 4 + (im[index, 0] - im[index, 1] ) / delta[index] out[:,:,0] = (out[:,:,0]/6.0) % 1.0 out[:,:,1] = s out[:,:,2] = im_max out = (255.*out).astype(uint8) return out Timings (shape: 1202, 800, 3): ----------------- code in first post: %prun a = rgb2hsv.rgb2hsv(s) 1923207 function calls in 18.423 CPU seconds removing loop: %prun b = rgb2hsv.vectorized_rgb2hsv(s) 8 function calls in 4.630 CPU seconds Arnar
Hi, Also try NumExpr (http://www.scipy.org/SciPyPackages/NumExpr): "The scipy.sandbox.numexpr package supplies routines for the fast evaluation of array expressions elementwise by using a vector-based virtual machine. " However, this page is probably a little out of date. Regards Bruce On Jan 21, 2008 4:17 PM, Arnar Flatberg <arnar.flatberg@gmail.com> wrote:
Hi Theodore
Probably not the fastest, but a full example of how you may vectorize your loop. Ran just one test, and that speeded up the original code.
Example: ----------------
from numpy import empty_like
def vectorized_rgb2hsv(im): "im is a (m, n, 3) array.""" im = im/255. out = empty_like(im) im_max = im.max(-1) delta = im.ptp(-1) s = delta/im_max s[delta==0] = 0 index = im[:,:,0] == im_max # red is max out[index, 0] = (im[index, 1] - im[index, 2] ) / delta[index] index = im[:,:,1] == im_max # green is max out[index, 0] = 2 + (im[index, 2] - im[index, 0] ) / delta[index] index = im[:,:,2] == im_max # blue is max out[index, 0] = 4 + (im[index, 0] - im[index, 1] ) / delta[index] out[:,:,0] = (out[:,:,0]/6.0) % 1.0 out[:,:,1] = s out[:,:,2] = im_max out = (255.*out).astype(uint8) return out
Timings (shape: 1202, 800, 3): ----------------- code in first post: %prun a = rgb2hsv.rgb2hsv(s) 1923207 function calls in 18.423 CPU seconds
removing loop: %prun b = rgb2hsv.vectorized_rgb2hsv(s) 8 function calls in 4.630 CPU seconds
Arnar
_______________________________________________ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion
participants (4)
-
Arnar Flatberg -
Bruce Southey -
Hans Meine -
theodore test