Numpy Arrays - Replacing Elements - python

I am new to numpy and I want to replace specefic elements in a 3D numpy array. My 3D numpy array represents an image. The shape of the array is:
(1080, 1920, 3). The number 3 represents RGB of each pixel in the image.
All I want to know is how to change all the elements that are equal to [0,0,0] into [255,255,255]
Which means i want all black pixels in the image to be white.. How can i do it?
Thanks!

Say you have stored your array in data; this should work:
data[(data == 0).all(axis=2)] = [255, 255, 255]
This is due to numpy's broadcasting rules, which compare each value to 0, resulting in a boolean array with True values where they compare equal and False elsewhere.
The next step is to take only those sub-arrays where all of the individual values do compare equal, with .all(axis=2) - the last axis, which is the one you want.
Then, with the resulting boolean array, you can index back into data, which will give you only those sub-arrays equal to [0, 0, 0], and set those to [255, 255, 255].

Related

Binarize image "manually"

I am trying to modify an image by looping through the pixels. The code is this
for x in range(image.shape[0]):
for y in range(image.shape[1]):
if tuple(image[x, y]) in possible_colors_rgb:
image[x, y] = [255, 255, 255]
else:
image[x, y] = [0, 0, 0]
In the array possible_colors_bg I have a list of tuples of 3 elements that represent rgb values. The problem is that the if never evaluates as true, even if I am sure that there are some pixels that should satisfy the equality. How can I understand what's wrong?
cv2 keeps pixel values in BGR order. If your tuples in possible_colors_rgb are in RGB order, they won't match.
possible_colors_bgr = [(b,g,r) for r,g,b in possible_colors_rgb]
If the number of colors is large you might consider using a set instead of a tuple or list for the possible colors, for better efficiency.

Use a matrix to inform where pixels should be on or off (Numpy)

I am not sure how to word this. I am sure there is an operation that describes what I am trying to do I just don't have a lot of experience manipulating image arrays.
I have a 2D array (matrix) of 1s and 0s which specify if a group of pixels should be the color [255,255,255] or the color [0,0,0] in rbg. It seems like this should be a simple multiplication. I should be able to multiple my color by my matrix of 1s and 0s to make an image, but all the dots products and matrix multiplication I have tried has failed.
Here is a simple example my 2D numpy array and
# 2D pixels array
[[0,1],
[1, 1]]
# rbg array
[[255,255,255]]
What I would want is the following 3D array
[[[0,0,0],[255,255,255]],
[[255,255,255], [255,255,255]]]
This array has the shape 2X2X3.
Here are the arrays for reproducibility and to make it easy for anyone willing to help.
pixel = np.array([0,1,1,1]).reshape(2,2)
rgb = np.array([255,255,255]).reshape(1,3)
How about reshaping pixel into a 3D matrix and using dot?
pixel = np.array([0,1,1,1]).reshape(2,2,-1)
rgb = np.array([255,255,255]).reshape(1,3)
pixel.dot(rgb)
Output
array([[[ 0, 0, 0],
[255, 255, 255]],
[[255, 255, 255],
[255, 255, 255]]])
If you are multiplying matrices element-wise, they must be broadcastable, meaning that numpy can convert their size into a common size. From the numpy documentation:
Broadcasting can be understood by four rules:
All input arrays with ndim smaller than the input array of largest ndim, have 1’s prepended to their shapes.
The size in each dimension of the output shape is the maximum of all the input sizes in that dimension.
An input can be used in the calculation if its size in a particular dimension either matches the output size in that dimension, or has value exactly 1.
If an input has a dimension size of 1 in its shape, the first data entry in that dimension will be used for all calculations along that dimension. In other words, the stepping machinery of the ufunc will simply not step along that dimension (the stride will be 0 for that dimension).
Your desired dimension structuring boils down to a three-dimensional array, where the dimension indices have the meaning (y coordinate, x coordinate, colour channel), where the colour channel index is either 0, 1, or 2 (for red, green, and blue, respectively).
To abide by the rules shown above and get the desired dimension structuring explained above, we need to ensure that our pixel array has three dimensions, of which the third dimension can have size 1 (see rule 3). The rgb array can stay the same, as dimensions will automatically be added at the front (see rule 1). Because of rule 2, the resulting array will take the size of the pixel array for the first two dimensions, and the size of the rgb array for the third dimension.
To add a third dimension to the pixel array, you can either use reshape (as the other answers show), or the expand_dims function. Then, you can simply do an elementwise multiplication between the arrays, and numpy will automatically broadcast the arrays using the rules discussed above. See the following example:
>>> pixel = np.array([
... [0, 1],
... [1, 1]
... ])
>>> rgb = np.array([255, 255, 255])
>>> pixel.shape
(2, 2)
>>> rgb.shape
(3,)
>>> pixel = np.expand_dims(pixel, axis=2) # add a third axis to get the correct shape
>>> pixel.shape
(2, 2, 1)
>>> image = rgb * pixel
>>> image.shape
(2, 2, 3)
>>> image
array([[[ 0, 0, 0],
[255, 255, 255]],
[[255, 255, 255],
[255, 255, 255]]])
Note that numpy prints the array a bit differently than you might expect, but you can simply verify that the array matches your desired array:
>>> image[0, 0, :]
array([0, 0, 0])
>>> image[0, 1, :]
array([255, 255, 255])
>>> image[1, 0, :]
array([255, 255, 255])
>>> image[1, 1, :]
array([255, 255, 255])
When multiplying matrices, the columns of the first matrix must match the rows of the 2nd matrix. And the Rows of the first matrix and column of the 2nd matrix will be the new dimension of your result.
An example is if you have a 1x3 matrix and you multiply by a 3x4 matrix. this is a valid multiplication since the first matrix has 3 columns and the 2nd matrix has 3 rows. The result will then be a 1x4 matrix due to the first matrix having a single row, and the 2nd has 4 columns.
For your example I think you need a nested loop to multiply each element in your pixel array with the RBG values. I don't know a great way to do this, but I think this may work.
pixel = np.array([0,1,1,1]).reshape(2,2)
rgb = np.array([255,255,255]).reshape(1,3)
arr = np.zeros((2,2,3))
for i in range(2):
for j in range(2):
for k in range(3):
arr[i][j][k] = pixel[i][j] * rgb[0][k]

how to change pixel value in a numpy array

I have a big RGB image as a numpy array,i want to set all pixel that has R=0,G=0,B=0 to R=255,G=0,B=0.
what is the fastest way?
i tried:
for pix in result:
if np.all(np.logical_and(pix[0]==pix[1],pix[2]==0,pix[2]==pix[1])):
pix [0] = 255
but in this way i don't have a single pixel. there is a similar way that it is not to iterate the index?
Here is a vectorized solution. Your image is basically an w by h by 3(colors) array. We can make use of the broadcasting rules that are not easy to grasp but are very powerful.
Basically, we compare the whole array to a 3 vector with the values that you are looking for. Due to the broadcasting rules Numpy will then compare each pixel to that three vector and tell you if it matched (so in this specific case, if the red, green and blue matched). You will end up with an boolean array of trues and falses of the same size as the image.
now we only want to find the pixels where all three colors matched. For that we use the "all" method, which is true, if all values of an array are true. If we apply that to a certain axis -- in this case the color axis -- we get an w by h array that is true, wherever all the colors matched.
Now we can apply this 2D boolean mask back to our original w by h by 3 array and get the pixels that match our color. we can now reassign them -- again with broadcasting.
Here is the example code
import numpy as np
#create a 2x2x3 image with ones
img = np.ones( (2,2,3) )
#make the off diagonal pixels into zeros
img[0,1] = [0,0,0]
img[1,0] = [0,0,0]
#find the only zeros pixels with the mask
#(of course any other color combination would work just as well)
#... and apply "all" along the color axis
mask = (img == [0.,0.,0.]).all(axis=2)
#apply the mask to overwrite the pixels
img[ mask ] = [255,0,0]
Since all values are positive or null, a simple and efficient way is:
img[img.sum(axis=2)==0,0]=255
img.sum(axis=2)==0 select good pixels in the two first dimensions, 0 the red canal in the third.

numpy efficient array multiplication

I have a three dimensional array img of shape [1200,1600,3] and a two dimensional array labels of shape [1200,1600]. The first array is from an image, the second one is from labels in the image. Location [i,j] in the img array corresponds to an image pixel.
I want to create a new array of the same dimension as the img array, such that for the pixels with label 0, the original array is unchanged, but all other pixels are whitened (255,255,255).
The code I am using is:
import numpy as np
newimg=np.zeros((img.shape[0],img.shape[1],img.shape[2]))
for i in range(0,img.shape[0]):
for j in range(0,img.shape[1]):
if labels[i][j]==0:
newimg[i][j]=img[i][j]
else:
newimg[i][j]=np.array([255,255,255])
Is there a faster way of doing this?
Generally speaking, you'd do something similar to:
newimg = img.copy()
newimg[labels != 0, :] = 255
or alternatively:
newimg = np.where(labels[..., None] != 0, img, 255)

Fast pixel comparisons with opencv and python [duplicate]

I'm looping through this image pixel by pixel and it's really slow. I have the 2 images I'm comparing sliced and flattened so each element is a 3 dimensional rgb value named e1 and e2. It is very slow though. Is there some method using opencv or numpy that can speed this up?
What I'm doing here is performing pixel comparisons on images with binned colors (8 colors).
I'm reading from a jpeg though so what should be [255,0,0] becomes [230,12,11] so what clean_key does is threshold the values to the cleaner ones. Then I append the number of times this combination occurs to a dictionary. So for example dict["255,0,0 0,0,255"] might occur 300 times in this image which means there were 300 instances where im1 had a red pixel and im2 had a blue pixel.
for e1,e2 in itertools.izip(im1_slice.reshape(-1,3),im2_slice.reshape(-1,3)):
key = str(clean_key(e1_row)) + str(clean_key(e2_row))
if key in proportion_dict:
proportion_dict[key] += 1
else:
proportion_dict[key] = 1
return (proportion_dict,total)
The way you want to do this is first compare each image to the color you want to see in that image, which makes a boolean mask where that image is the given color. You don't need to flatten the images to do this. This can be done by saying:
image == color
This works fine for grayscale images, but if color is actually along a third dimension, you want to make sure everything along that dimension matches (i.e., you want all of the r, g, and b components to match, so you use np.all along the last axis (-1 gives the last axis):
np.all(image == color, axis=-1)
Which gives a 2d array of booleans where each element is True if that pixel matches color and False if not. Do this for both images (and both colors) and then you'll have a mask where the color matches both images:
np.all(im1==c1, -1) & np.all(im2==c2, -1)
This not only tells you how many pixels match, but where they are (you could plot the above line and see dot at the points where they match). If you just want the count, just use np.sum on the mask which counts True as 1, and False as 0. All together:
def compare_colors(im1, im2, c1, c2):
matches = np.all(im1==c1, -1) & np.all(im2==c2, -1)
return matches.sum()
And to use/test it with random data:
>>> a = np.random.choice([0, 255], (20,20,3))
>>> b = np.random.choice([0, 255], (20,20,3))
>>> compare_colors(a, b, [255, 0, 255], [0, 255, 0])
12
But before you do that, with your real input, you want to "clean" your colors by a threshold. You could easily do that with np.where which looks at each element of an array, and if a condition is met, gives one thing, and if not, gives another. Here, if the value is less than 128, it uses 0, and otherwise uses 255:
np.where(a<128, 0, 255)
In general, you could write a function like this, with the values above as defaults:
def clean(a, thresh=128, under=0, over=255):
return np.where(a<128, under, over)
Of course to build up your dict of counts, you still have to loop through each color combination, but that's a short loop (8*8). Here's a full run through:
# some fake data (has values between 0 and 255 for r, g, and b)
H, W = 20, 20
a = np.random.randint(0, 256, (H,W,3))
b = np.random.randint(0, 256, (H,W,3))
# clean the images:
ac = clean(a)
bc = clean(b)
# build a list of all pairs of all 8 colors using itertools.product:
col_combos = itertools.product(itertools.product((0,255), repeat=3), repeat=2)
# now apply the comparison to the images for each pair of colors
col_dict = { (c1,c2): compare_colors(ac, bc, c1, c2) for c1,c2 in col_combos }
Then, the keys for col_dict are actually tuples of tuples, which are much easier to deal with than strings, in my opinion. Here's how you'd access an example key:
>>> col_dict[((0, 255, 255), (255, 0, 255))]
8

Categories

Resources