Pixel intensity of RGB images and how to multiply it with integers to view shades of gray Pixel intensity of RGB images and how to multiply it with integers to view shades of gray numpy numpy

Pixel intensity of RGB images and how to multiply it with integers to view shades of gray


There are a number of issues with your question and assumptions.


You cannot count colours with np.unique(image)

You cannot count the colours in an image with np.unique(im). Let's see why by making a random image with just 4 intensities: 0,1,2 and 3.

import numpy as npimport cv2# Ensure repeatable, deterministic randomness!np.random.seed(42)# Make a random imageim = np.random.randint(0,4,(480,640,3), dtype=np.uint8)

That looks like this where each row is the RGB triplet for one pixel:

array([[[2, 2, 3],    [3, 2, 1],    [2, 2, 0],    ...,    [3, 3, 2],    [0, 0, 1],    [1, 1, 1]],    ...,    [3, 3, 1],    [2, 3, 0],    [0, 1, 3]]], dtype=uint8)

Now, if you try and get the unique colours like this, it will not work, because each colour is a combination of 3 intensities:

np.unique(im)    # prints: array([0, 1, 2, 3], dtype=uint8)

Whereas, if you want the number of unique colours, you need to look for the number of unique combinations of three RGB/BGR values:

np.unique(im.reshape(-1, im.shape[2]), axis=0)

which gives a vector of the unique RGB/BGR triplets in the image - each row is a unique colour combination:

array([[0, 0, 0],       [0, 0, 1],       [0, 0, 2],       [0, 0, 3],       [0, 1, 0],       [0, 1, 1],       [0, 1, 2],       [0, 1, 3],       [0, 2, 0],       [0, 2, 1],       [0, 2, 2],       [0, 2, 3],       [0, 3, 0],       [0, 3, 1],       [0, 3, 2],       [0, 3, 3],       [1, 0, 0],       [1, 0, 1],       [1, 0, 2],       [1, 0, 3],       [1, 1, 0],       [1, 1, 1],       [1, 1, 2],       [1, 1, 3],       [1, 2, 0],       [1, 2, 1],       [1, 2, 2],       [1, 2, 3],       [1, 3, 0],       [1, 3, 1],       [1, 3, 2],       [1, 3, 3],       [2, 0, 0],       [2, 0, 1],       [2, 0, 2],       [2, 0, 3],       [2, 1, 0],       [2, 1, 1],       [2, 1, 2],       [2, 1, 3],       [2, 2, 0],       [2, 2, 1],       [2, 2, 2],       [2, 2, 3],       [2, 3, 0],       [2, 3, 1],       [2, 3, 2],       [2, 3, 3],       [3, 0, 0],       [3, 0, 1],       [3, 0, 2],       [3, 0, 3],       [3, 1, 0],       [3, 1, 1],       [3, 1, 2],       [3, 1, 3],       [3, 2, 0],       [3, 2, 1],       [3, 2, 2],       [3, 2, 3],       [3, 3, 0],       [3, 3, 1],       [3, 3, 2],       [3, 3, 3]], dtype=uint8)

Or, as a simple number of unique colours:

len(np.unique(im.reshape(-1, im.shape[2]), axis=0))    # prints 64

So, for your image:

# Open imageim = cv2.imread('image.png',cv2.IMREAD_UNCHANGED)# Count unique colourslen(np.unique(im.reshape(-1, im.shape[2]), axis=0)    # prints 790

There are more colours than you expect

Why do I have more colours than I expect? The two most common reasons are:

  • the image was saved as a JPEG
  • there is text or drawn shapes that were anti-aliased

Let's look at how saving as a JPEG messes you up!

# Load image and count coloursim = cv2.imread('image.png',cv2.IMREAD_UNCHANGED)len(np.unique(im.reshape(-1, im.shape[2]), axis=0))    # prints 790# Save as JPEGcv2.imwrite('temp.jpg',im)# Reload and recount just the sameim = cv2.imread('temp.jpg',cv2.IMREAD_UNCHANGED)len(np.unique(im.reshape(-1, im.shape[2]), axis=0))    # prints 4666 !!!

How can I palettise an image - (reduce colours to a fixed palette)?

If you want to palettise your image to your own specific palette, firstly you need to specify your palette in BGR order (!) to match OpenCV's ordering:

palette = np.array([   [0,0,0],                # Black   [93,136,106],           # Green   [208,224,64],           # Blue   [85,124,168]],          # Brown   dtype=np.uint8)

Then read your image discarding the completely pointless alpha channel:

test = cv2.imread("image.png",cv2.IMREAD_COLOR)

Then calculate the distance to each palette entry from each pixel:

distance = np.linalg.norm(test[:,:,None] - palette[None,None,:], axis=3)

Then choose whichever one of the palette colours is nearest for each pixel:

palettised = np.argmin(distance, axis=2).astype(np.uint8)

Your image is now in the array palettised and stored at each pixel location is the index of the nearest colour in your palette - so, as your palette has 4 entries (0..3), all elements of your image are 0, 1, 2 or 3.

So, now you can multiply by 85 with:

result = palettised * 85

enter image description here


I think the edges are throwing it off. Try writing a function to set the pixels that make up the edges of your shapes into the exact color of that shape.


I'm not completely sure what you're asking for here but to determine the RGB pixel intensity of images, you can isolate each R, G, and B channel while setting the other channels to 0.

Original image

enter image description here

import cv2image = cv2.imread('pikachu_smile.png')blue = image.copy()# Set green and red channels to 0blue[:, :, 1] = 0blue[:, :, 2] = 0green = image.copy() # Set blue and red channels to 0green[:, :, 0] = 0green[:, :, 2] = 0red = image.copy()# Set blue and green channels to 0red[:, :, 0] = 0red[:, :, 1] = 0cv2.imshow('blue', blue)cv2.imshow('green', green)cv2.imshow('red', red)cv2.waitKey(0)

Isolated blue (left), green (middle), and red (right) channels

enter image description hereenter image description hereenter image description here

To increase the intensity of a specific channel, you can add a fixed value to the entire channel. For example, with the green channel

green[:, :, 1] += 40

enter image description here