简体   繁体   中英

Manipulate RGB values in image

I would like to apply a simple algebraic operation to the RBG values of an image, that I have loaded via PIL. My current version works, but is slow:

from PIL import Image
import numpy as np

file_name = '1'
im = Image.open('data/' + file_name + '.jpg').convert('RGB')
pixels = np.array(im)
s = pixels.shape
p = pixels.reshape((s[0] * s[1], s[2]))


def update(ratio=0.5):
    p2 = np.array([[min(rgb[0] + rgb[0] * ratio, 1), max(rgb[1] - rgb[1] * ratio, 0), rgb[2]] for rgb in p])
    img = Image.fromarray(np.uint8(p2.reshape(s)))
    img.save('result/' + file_name + '_test.png')
    return 0

update(0.5)

Has someone a more efficient idea?

Make use of NumPy's vectorized operations to get rid of the loop.

I modified your original approach to compare performance between the following, different solutions. Also, I added a PIL only approach using ImageMath , if you want to get rid of NumPy completely.

Furthermore, I assume, there is/was a bug:

p2 = np.array([[min(rgb[0] + rgb[0] * ratio, 1), max(rgb[1] - rgb[1] * ratio, 0), rgb[2]] for rgb in p])

You actually do NOT convert to float , so it should be 255 instead of 1 in the min call.

Here's, what I've done:

import numpy as np
from PIL import Image, ImageMath
import time


# Modified, original implementation; fixed most likely wrong compare value in min (255 instead of 1)
def update_1(ratio=0.5):
    pixels = np.array(im)
    s = pixels.shape
    p = pixels.reshape((s[0] * s[1], s[2]))
    p2 = np.array([[min(rgb[0] + rgb[0] * ratio, 255), max(rgb[1] - rgb[1] * ratio, 0), rgb[2]] for rgb in p])
    img = Image.fromarray(np.uint8(p2.reshape(s)))
    img.save('result_update_1.png')
    return 0


# More efficient vectorized approach using NumPy
def update_2(ratio=0.5):
    pixels = np.array(im)
    pixels[:, :, 0] = np.minimum(pixels[:, :, 0] * (1 + ratio), 255)
    pixels[:, :, 1] = np.maximum(pixels[:, :, 1] * (1 - ratio), 0)
    img = Image.fromarray(pixels)
    img.save('result_update_2.png')
    return 0


# More efficient approach only using PIL
def update_3(ratio=0.5):
    (r, g, b) = im.split()
    r = ImageMath.eval('min(float(r) / 255 * (1 + ratio), 1) * 255', r=r, ratio=ratio).convert('L')
    g = ImageMath.eval('max(float(g) / 255 * (1 - ratio), 0) * 255', g=g, ratio=ratio).convert('L')
    Image.merge('RGB', (r, g, b)).save('result_update_3.png')
    return 0


im = Image.open('path/to/your/image.png')

t1 = time.perf_counter()
update_1(0.5)
print(time.perf_counter() - t1)

t1 = time.perf_counter()
update_2(0.5)
print(time.perf_counter() - t1)

t1 = time.perf_counter()
update_3(0.5)
print(time.perf_counter() - t1)

The performance on a [400, 400] RGB image on my machine:

1.723889293 s    # your approach
0.055316339 s    # vectorized NumPy approach
0.062502050 s    # PIL only approach

Hope that helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM