How to convert all pixel values ​​of an image to a specific range -python

I have an rgb image with 12 different colors, but I don't know the colors (pixel values) in advance. I want to convert all pixel values ​​between 0 and 11, each symbolizing a unique color of the original rgb image.

eg. all [230, 100, 140] are converted to [0,0,0], all [130,90,100] are converted to [0,0,1] and so on ... all [210,80,50] are converted to [ 0,0, 11].

+3


source to share


2 answers


A quick and dirty app. Much could be improved, especially if you go through the whole pixel of the image pixel by pixel, it is not very much, but very difficult, but I was too lazy to remember the threshold exactly and replace the RGB pixels.



import cv2
import numpy as np

#finding unique rows
#comes from this answer : http://stackoverflow.com/questions/8560440/removing-duplicate-columns-and-rows-from-a-numpy-2d-array
def unique_rows(a):
    a = np.ascontiguousarray(a)
    unique_a = np.unique(a.view([('', a.dtype)]*a.shape[1]))
    return unique_a.view(a.dtype).reshape((unique_a.shape[0], a.shape[1]))

img=cv2.imread(your_image)

#listing all pixels
pixels=[]
for p in img:
    for k in p:
        pixels.append(k)

#finding all different colors
colors=unique_rows(pixels)

#comparing each color to every pixel
res=np.zeros(img.shape)
cpt=0
for color in colors:
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            if (img[i,j,:]==color).all(): #if pixel is this color
                res[i,j,:]=[0,0,cpt] #set the pixel to [0,0,counter]
    cpt+=1

      

+2


source


You can use np.unique

with a bit of trickery:

import numpy as np

def safe_method(image, k):
    # a bit of black magic to make np.unique handle triplets
    out = np.zeros(image.shape[:-1], dtype=np.int32)
    out8 = out.view(np.int8)
    # should really check endianness here
    out8.reshape(image.shape[:-1] + (4,))[..., 1:] = image
    uniq, map_ = np.unique(out, return_inverse=True)
    assert uniq.size == k
    map_.shape = image.shape[:-1]
    # map_ contains the desired result. However, order of colours is most
    # probably different from original
    colours = uniq.view(np.uint8).reshape(-1, 4)[:, 1:]
    return colours, map_

      

However, if the number of pixels is much larger than the number of colors, the following heuristic algorithm can provide huge speedups. It tries to find a cheap hash function (for example, just looking at the red channel), and if it does, it uses that to create a lookup table. If it doesn't fall back to the above safe method.

CHEAP_HASHES = [lambda x: x[..., 0], lambda x: x[..., 1], lambda x: x[..., 2]]

def fast_method(image, k):
    # find all colours
    chunk = int(4 * k * np.log(k)) + 1
    colours = set()
    for chunk_start in range(0, image.size // 3, chunk):
        colours |= set(
            map(tuple, image.reshape(-1,3)[chunk_start:chunk_start+chunk]))
        if len(colours) == k:
            break
    colours = np.array(sorted(colours))
    # find hash method
    for method in CHEAP_HASHES:
        if len(set(method(colours))) == k:
            break
    else:
        safe_method(image, k)
    # create lookup table
    hashed = method(colours)
    # should really provide for unexpected colours here
    lookup = np.empty((hashed.max() + 1,), int)
    lookup[hashed] = np.arange(k)
    return colours, lookup[method(image)]

      



Testing and timings:

from timeit import timeit

def create_image(k, M, N):
    colours = np.random.randint(0, 256, (k, 3)).astype(np.uint8)
    map_ = np.random.randint(0, k, (M, N))
    image = colours[map_, :]
    return colours, map_, image

k, M, N = 12, 1000, 1000

colours, map_, image = create_image(k, M, N)

for f in fast_method, safe_method:
    print('{:16s} {:10.6f} ms'.format(f.__name__, timeit(
        lambda: f(image, k), number=10)*100))
    rec_colours, rec_map_ = f(image, k)
    print('solution correct:', np.all(rec_colours[rec_map_, :] == image))

      

Sample output (12 colors, 1000x1000 pixels):

fast_method        3.425885 ms
solution correct: True
safe_method       73.622813 ms
solution correct: True

      

+1


source







All Articles