1

Guys I'm new to python and I'm stuck on this. Would really appreciate it if you could help me out.

I've got an image that has a number of colours in each pixel. They all denote a number.

enter image description here

The image was constructed using this range from 0 to 15625 pixels. Each of the pixels in the range from 0 to 15625 has a different colour and the above image was constructed using that.

image range (it's massive so you might need to download it to see the image)

What I'm trying to do is convert the RGB values from the range such as the first pixel value (5,5,5) to 1 and the next pixel value in the range to 2 and so on. Therefore, each pixel in the image above could correspond to a value.

It's similar to this question but I don't think it does what I want to do. How to Convert all pixel values of an image to a certain range -python

This is the code I used to create the range

#!/usr/local/bin/python3
from PIL import Image
import numpy as np

# Create array to hold output image
result=np.zeros([1,25*25*25,3],dtype=np.uint8)

#change the values above i.e. 51*51*51 done by (upperbound-lowerbound)/step i.e (255-0)/5
j=0
for r in range(5,255,10):
    for g in range(5,255,10):
        for b in range(5,255,10):
            result[0,j]= (r,g,b)
            j+=1

# Convert output array to image and save
im=Image.fromarray(result)
im.save("perfect1.png")

This is the code to find the RGB values of each pixel in the range

from PIL import Image

i = Image.open('perfect1.png')
pixels = i.load() # this is not a list, nor is it list()'able
width, height = i.size

all_pixels = []
for x in range(width):
    for y in range(height):
        cpixel = pixels[x, y]
        all_pixels.append(cpixel)

print all_pixels

This is the code for creating a sub array with no extra pixel values as each "pixel" value in the image has a number of pixels enclosed. a = array of the image values

rows_mask = np.insert(np.diff(a[:, 0]).astype(np.bool), 0, True)
columns_mask = np.insert(np.diff(a[0]).astype(np.bool), 0, True)
b = a[np.ix_(rows_mask, columns_mask)]
Abid Abdul Gafoor
  • 462
  • 1
  • 6
  • 18
  • so, let me see if I understood correctly, the image was generated mapping values from `0` to `15625` to a color defined in your LUT and you want to reverse map each color to its index in the table? – filippo Jun 18 '18 at 11:37
  • Yes, you're right! In the software called HDimaging, you put the range in and then it maps the range to the image as shown above. I want to get the values for each pixel. – Abid Abdul Gafoor Jun 18 '18 at 11:41
  • another thing, your image looks pixelated but each little square/rectangle actually contains multiple pixels, it that desired? I mean the image you posted has got the proper shape or has it been rescaled by mistake? – filippo Jun 18 '18 at 11:42
  • Unfortunately it has a number of pixels. What I'm trying to do is create an array from the initial image with all the pixelated values and then create a subarray that ignores any repeated values as a result creating an image with no extra pixelated values. I'll add the code where I created the subarray above. – Abid Abdul Gafoor Jun 18 '18 at 11:56
  • For a single pixel you could do something like `np.where(np.all(img[i] == lut, axis=1))` but you'd have to iterate over each single one to get the full reverse map. There's probably a more efficient way. – filippo Jun 18 '18 at 12:53
  • Hi filippo, I don't understand the code above? – Abid Abdul Gafoor Jun 18 '18 at 14:33
  • didn't really want to answer but it was difficult to explain in a comment, so see my answer ;-) if this is one off thing and performance is not an issue it might even solve your problem – filippo Jun 18 '18 at 16:22

1 Answers1

1

Here's some idea.

Let's load your images

import numpy as np
from scipy.misc import imread

img = imread("img.png")[..., :3]   # drop alpha channel
lut = imread("lut.png").squeeze()  # squeeze 1D first axis

print(img.shape)
print(lut.shape)

Which outputs

(589, 612, 3)
(15625, 3)

Now let's say we want to look up the first pixel in the image

print(img[0, 0])
[245 245  95]

You can find all pixels in the look up table with the same value (axis=1 to compare row by row)

np.all(img[0, 0] == lut, axis=1)

Which gives you a mask for all the pixels which is True for matches and False otherwise.

Now you can convert that to a list of indices (which in your case we can assume will have length 1) with np.where

idx = np.where(np.all(img[0, 0] == lut, axis=1))

And, assuming each pixel, has a unique mapping you will get

(array([15609]),)

Now this method is really slow and inefficient, you'll have to repeat it for each pixel of the image. There's probably some way to speed it up but at the moment I'm not seeing it, let's see if anyone else has some better input.

filippo
  • 5,197
  • 2
  • 21
  • 44
  • Thank you so much. I'm a bit confused. I'm trying to learn. I'm a bit confused on what squeeze is trying to do and is the alpha channel dropped because its a RGBa value? And also what does the output represent in the second block of code? I know 15625 is the length of the pixel range but what does 3 represent? Thank you so much – Abid Abdul Gafoor Jun 19 '18 at 08:43
  • @AbidAbdulGafoor the image you posted had an alpha channel but the look up table maps RGB values so I just kept the first three channels with `[..., :3]`. The lookup table is a `1x15625x3` array, but if you don't need the extra `1` dimension you can drop it with squeeze. The output represents the loaded image shapes, `3` is the number of channels for each pixel (red, green, blue). – filippo Jun 19 '18 at 08:47