Skip to content Skip to sidebar Skip to footer

Find Closest Rgb Color For Every Pixel In Image

First of all: I'm a programming beginner. I have a NumPy array with the RGB data for each pixel. im = np.asarray(Image.open('image.jpg')) I'm passing each pixel to my getNearestC

Solution 1:

Your (corrected) function is:

def findNearest(rgb):
    a = []
    for i in range(len(rgbValues)):
        d = ((rgbValues[i][0]-rgb[0])*0.3)**2 + ((rgbValues[i][1]-rgb[1])*0.59)**2 + ((rgbValues[i][2]-rgb[2])*0.11)**2
        a.append([d,i])
    list.sort(a)
    return rgbValues[a[0][1]]

It returns the correct rgbValues; this is now possible because its index get stored in a as well. This – in an admittedly roughly timed framework – processes some 27,085 pixels per second.

A straightforward implementation, adjusted to remember only the nearest index:

deffindNearest(rgb):
    dist = ((rgbValues[0][0]-rgb[0])*0.3)**2 + ((rgbValues[0][1]-rgb[1])*0.59)**2 + ((rgbValues[0][2]-rgb[2])*0.11)**2
    index = 0for i inrange(1,len(rgbValues)):
        d = ((rgbValues[i][0]-rgb[0])*0.3)**2 + ((rgbValues[i][1]-rgb[1])*0.59)**2 + ((rgbValues[i][2]-rgb[2])*0.11)**2if d < dist:
            dist = d
            index = i
    return rgbValues[index]

already performs much better: 37,175 pixels per second, a speed improvement of 37%. Can we do better with a more Pythonic approach?

deffindNearest(rgb):
    dist = [(((rgbValues[i][0]-rgb[0])*0.3)**2 + ((rgbValues[i][1]-rgb[1])*0.59)**2 + ((rgbValues[i][2]-rgb[2])*0.11)**2,i) for i inrange(22)]
    return rgbValues[min(dist)[1]]

Nope. With the same image and the same timing mechanism it goes down to 33,417 pixels/sec.


Complete test program, using a random image from an earlier question (it uses PIL to load, access pixels, and display the image, but that is not relevant for the distance calculations):

import random
from PIL import Image
from time import time

deffindNearest_org(rgb):
    a = []
    for i inrange(len(rgbValues)):
        d = ((rgbValues[i][0]-rgb[0])*0.3)**2 + ((rgbValues[i][1]-rgb[1])*0.59)**2 + ((rgbValues[i][2]-rgb[2])*0.11)**2
        a.append([d,i])
    list.sort(a)
    return rgbValues[a[0][1]]

deffindNearest_raw(rgb):
    dist = ((rgbValues[0][0]-rgb[0])*0.3)**2 + ((rgbValues[0][1]-rgb[1])*0.59)**2 + ((rgbValues[0][2]-rgb[2])*0.11)**2
    index = 0for i inrange(1,len(rgbValues)):
        d = ((rgbValues[i][0]-rgb[0])*0.3)**2 + ((rgbValues[i][1]-rgb[1])*0.59)**2 + ((rgbValues[i][2]-rgb[2])*0.11)**2if d < dist:
            dist = d
            index = i
    return rgbValues[index]

deffindNearest_list(rgb):
    dist = [(((rgbValues[i][0]-rgb[0])*0.3)**2 + ((rgbValues[i][1]-rgb[1])*0.59)**2 + ((rgbValues[i][2]-rgb[2])*0.11)**2,i) for i inrange(22)]
    return rgbValues[min(dist)[1]]

image = Image.open('output-2.png')
pixels = image.load()
width, height = image.size

rgbValues = [tuple(random.randrange(0,256) for _ inrange(3)) for _ inrange(22)]

start = time()
for y inrange(height):
    for x inrange(width):
        # fetch the rgb value
        color = pixels[x,y]
        # replace with nearest
        pixels[x,y] = findNearest_list (color)
print ('pixels/sec:', (width*height)/(time()-start))

image.show()

and test images before and after:

test image before

test image after

If you are only interested in results, use whatever native method your image library allows. This short piece using PIL's own quantize

rgbValues = list(sum(rgbValues, ()))*12
rgbValues = rgbValues[:768]
palimage = Image.new('P', (width, height))
palimage.putpalette(rgbValues)
newimage = image.quantize(palette=palimage)

outsources the calculations to native code, and the results are quite staggeringly better: 18,443,414 pixels/sec – a whopping 500 times faster than my native (/naïve) implementation. (Hyper-fancy tuple-to-list comes from https://stackoverflow.com/a/3205524)

Post a Comment for "Find Closest Rgb Color For Every Pixel In Image"