0

I have a very simple method that converts an RGB image to HSL and adjusts the Hue. This works relatively quickly with small image files, but large image files require minutes to process. I am converting the imgdata to a numpy array, but this does not seem to speed it up at all. Do I have to use numpy functions exclusively inside of the loop to speed this up? I can't find exactly where the bottleneck is inside of the loop as it is just fairly simple math calculations.

from colorsys import rgb_to_hls, hls_to_rgb
from numpy import array

def reload_img():
    global img, sizew, sizeh, maxsize, imgdata
    img = Image.open(IMAGE_SRC)
    sizew, sizeh = img.size
    maxsize = ((sizew/2)**2 + (sizeh/2)**2)**0.5
    imgdata = list(img.getdata())
    # Convert to numpy array
    imgdata = array(imgdata)

IMAGE_SRC = "test.jpg"
reload_img()

# Adjust Hue
for i in range(0,len(imgdata)):
    r,g,b = imgdata[i]
    r /= 255.0
    g /= 255.0
    b /= 255.0
    (h, l, s) = rgb_to_hls(r,g,b)
    h = .50
    imgdata[i] = hls2rgb((h,l,s))
Michael
  • 6,561
  • 5
  • 38
  • 55
  • 1
    The trick with numpy is to not use a 'for loop' in python. Numpy defaults to iterating through the ndarray element-wise, so try an come up with a way to slice and use Boolean masks and you'll see a huge performance increase. – willnx Mar 03 '16 at 04:52
  • 2
    I had a somewhat similar question a while back, maybe it can help you: http://stackoverflow.com/questions/5414638/using-numpy-and-pil-to-convert-56516bit-color-to-88824bit-color -- it used to be a for-loop, but you can see how that was removed and array operations were used – pyInTheSky Mar 03 '16 at 05:01
  • 2
    Also, if you can install it, [scikit image](http://scikit-image.org/docs/dev/api/skimage.color.html#rgb2hsv) has already solved this problem via Numpy. – willnx Mar 03 '16 at 05:03

1 Answers1

2

Here's a fast but not super precise method:

import numpy as np
from PIL import Image

def set_hue(img, hue):
    """
    img - a PIL (pillow) image
    hue - an integer in the range [0, 255]

    Returns a new PIL (pillow) image in HSV mode
    """
    hsv = img.convert('HSV')
    hsv_ar = np.array(hsv)
    hsv_ar[...,0] = hue
    out = Image.fromarray(hsv_ar, mode='HSV')
    return out

For this to work a somewhat recent version of Pillow (PIL fork) is probably required. It's fast because it uses the buffer protocol to convert between PIL format and Numpy array and vice versa. But the precision of the hue modification is not perfect, because there's an intermediate result with only 24 bits per pixel.

Note that Pillow doesn't have a HSL mode, so I used HSV.