Converting a PNG image to 2D array - python

I have a PNG file which when I convert the image to a numpy array, it is of the format that is 184 x 184 x 4. The image is 184 by 184 and each pixel is in RGBA format and hence the 3D array.
This a B&W image and the pixels are either [255, 255, 255, 255] or [0, 0, 0, 255].
I want to convert this to a 184 x 184 2D array where the pixels are now either 1 or 0, depending upon if it is [255, 255, 255, 255] or [0, 0, 0, 255].
Any ideas how to do a straightforward conversion of this.

There would be several ways to do the comparison to give us a boolean array and then, we just need to convert to int array with type conversion. So, for the comparison, one simple way would be to compare against 255 and check for ALL matches along the last axis. This would correspond to checking for [255, 255, 255, 255]. Thus, one approach would be like so -
((arr == 255).all(-1)).astype(int)
Sample run -
In [301]: arr
Out[301]:
array([[[255, 255, 255, 255],
[ 0, 0, 0, 255],
[ 0, 0, 0, 255]],
[[ 0, 0, 0, 255],
[255, 255, 255, 255],
[255, 255, 255, 255]]])
In [302]: ((arr == 255).all(-1)).astype(int)
Out[302]:
array([[1, 0, 0],
[0, 1, 1]])

If there are really only two values in the array as you say, simply scale and return one of the dimensions:
(arr[:,:,0] / 255).astype(int)

Related

Replacing ones and zeros in a 2D numpy array with another array?

I have a simple problem that I am trying to solve using numpy in an efficient manner. The jist of it is that I have a simple 2D array containing ones and zeros representing an image mask.
What I want to do is convert these ones and zeros into their RGB equivalent where one is a white pixel [255, 255, 255] and zero is a black pixel [0, 0, 0].
How would I go about doing this using NumPy?
mask = [[0, 0, 1],
[1, 0, 0]]
# something
result = [
[[0, 0, 0], [0, 0, 0], [255, 255, 255]],
[[255, 255, 255], [0, 0, 0], [0, 0, 0]]
]
The intent is to take the result and feed it into PIL to save into a PNG.
I've tried using numpy.where but can't seem to coax it into broadcasting another array out.
A possible solution:
np.stack([255 * mask, 255 * mask, 255 * mask], axis=2)
Output:
array([[[ 0, 0, 0],
[ 0, 0, 0],
[255, 255, 255]],
[[255, 255, 255],
[ 0, 0, 0],
[ 0, 0, 0]]])
As your image contains only two colours, I would suggest you consider saving it as a palette image, a.k.a. an indexed image.
Rather than needlessly inflating your image by a factor of 3 to enable it to store 16.7 million colours, you can just store one byte per pixel which will still enable you to have 256 colours which seems plenty when you only have 2 "colours", namely black and white.
That looks like this:
import numpy as np
from PIL import Image
# Make Numpy array "na" from your list
na = np.array(mask, dtype=np.uint8)
# Make PIL Image from Numpy array - this image will be 'L' mode
im = Image.fromarray(na)
# Now push a palette into the image that says:
# index 0 => black, i.e. [0,0,0]
# index 1 => white, i.e. [255,255,255]
#  all other 254 indices are black
# Afterwards the image will be 'P' mode
im.putpalette([0,0,0, 255,255,255] + [0,0,0]*254)
# Save
im.save('result.png')
Since you need to repeat each item three times, np.repeat in conjunction with reshape could be used:
mask = np.array([[0, 0, 1], [1, 0, 0]])
255 * np.repeat(mask, 3, axis=1).reshape(*mask.shape, -1)
>>> array([[[ 0, 0, 0],
[ 0, 0, 0],
[255, 255, 255]],
[[255, 255, 255],
[ 0, 0, 0],
[ 0, 0, 0]]])

How get unique pixels from 2d numpy array?

I have 2d array with rgb pixel data (2 row with 3 pixel in a row).
[[[255, 255, 255],[3, 0, 2],[255, 255, 255]],[[255, 255, 255],[3, 0, 2],[255, 255, 255]]]
How can I get unique pixel? I want to get
[[255, 255, 255], [3, 0, 2]]
I am trying to use np.unique and np.transpose with np.reshape but I wasn't able to get the desired result.
Reshape the array to 2D and then use np.unique with axis=0
arr = np.array([[[255, 255, 255],[3, 0, 2],[255, 255, 255]],[[255, 255, 255],[3, 0, 2],[255, 255, 255]]])
shape = arr.shape
arr = arr.reshape((shape[0] * shape[1], shape[2]))
print(np.unique(arr, axis=0))
Output
[[ 3 0 2]
[255 255 255]]
How about this?
import itertools
np.unique(np.array(list(itertools.chain(*arr))), axis=0)
array([[ 3, 0, 2],
[255, 255, 255]])

Numpy array not copy

I am working with numpy and images. I have a big image which i want to process bit by bit.
So I want to create a reference to the original image, do something with it and move on. But when I change something in the frame the change does not transfer to the original, which is the opposite of everything I read online
for example here : https://stackoverflow.com/a/53939444/11333604 if you don't use copy if you change one array the other changes. That does NOT HAPPEN to me
here is some pseudo code that demonstrates my problem
import numpy as np
#create a "big" imgae total black
matrix = np.full((5,5),255,np.uint8)
# the frame that i want to process
h = 3
w = 3
sub = matrix[:h, :w]
#mask to make everything 0
mask = np.zeros((h,w),np.uint8)
sub = sub & mask
#also change something radom so i know the problem is not the & or the mask
sub[0][0] = 12
#sub is changes
print(sub)
#matrix is not
print(matrix)
output
[[12 0 0]
[ 0 0 0]
[ 0 0 0]]
[[255 255 255 255 255]
[255 255 255 255 255]
[255 255 255 255 255]
[255 255 255 255 255]
[255 255 255 255 255]]
I suspect it has something to do with my arrays being 2d but i cant think of how
That's because in this line
sub = sub & mask
you're making sub to "look" at some new array formed with sub & mask and it loses its connection to matrix. If you do this in-place instead
sub &= mask
then matrix will be affected too:
>>> matrix
array([[ 12, 0, 0, 255, 255],
[ 0, 0, 0, 255, 255],
[ 0, 0, 0, 255, 255],
[255, 255, 255, 255, 255],
[255, 255, 255, 255, 255]], dtype=uint8)

How to use numpy.where to change all pixels of an image?

I have an image of shape (300,300,3) consisting of these pixels [255, 194, 7],[224, 255, 8],[230, 230, 230],[11, 102, 255]. I want to change this pixel [230, 230, 230] to [255,255,255]. And rest other pixels to [0,0,0]. So I'm applying numpy where function to switch the pixels. Below is the code:
import numpy
im = numpy.array([[[255, 194, 7],[224, 255, 8],[230, 230, 230],[11, 102, 255]]])
im[np.where((im == [230, 230, 230]).all(axis = 2))] = [255,255,255]
im[np.where((im != [255,255,255]).all(axis = 2))] = [0,0,0]
The first code is working fine, but all the pixels that have 255 in it like [11, 102, 255] doesnot get flipped at all in the second line. and the image remains same. Can anyone tell me what I'm doing wrong ?
import numpy as np
im = np.array([[[255, 194, 7],[224, 255, 8],[230, 230, 230],[11, 102, 255]]])
Like this?
Make a mask and use it to change the values.
>>> mask = im == 230
>>> im[mask] = 255
>>> im[np.logical_not(mask)] = 0
>>> im
=> array([[[ 0, 0, 0],
[ 0, 0, 0],
[255, 255, 255],
[ 0, 0, 0]]])
Or using numpy.where
>>> np.where(im==230, 255, 0)
=> array([[[ 0, 0, 0],
[ 0, 0, 0],
[255, 255, 255],
[ 0, 0, 0]]])
try
np.array_equal(arr1, arr2)

Numpy: vectorized operations to create a 3D array

I am learning Python and would like to find an efficient way to solve this problem using Numpy.
I currently have a 4x8 array containing random integers:
import numpy as np
n = 3
k = np.random.randint(n, size = (4,8))
Each number represents a color defined by its RGB value in a nx3 array:
colors = np.array([[0 , 0 , 0 ],
[0 , 100, 255],
[255, 100, 0 ]])
I would like to use these numbers to create a new 4x8x3 array where the first two dimensions represent pixels locations, and the third dimension the color of each pixel. This could be thought of as number painting. For example, if k[3,4] = 2, then myArray[3,4,:] = [255 100 0].
I am getting familiar with Numpy tools, but I am unsure of what I should be looking for exactly. Since the array k will eventually be much larger (I'm thinking ~640x480) and contain more than n = 3 non-random colors, I would like to use vectorized operations in order to speed up the process (and learn a bit more about them). Is this the most efficient way to do it?
IIUC, all you need to do is index into colors with k:
>>> k = np.random.randint(n, size = (2,4))
>>> out = colors[k]
>>> out
array([[[ 0, 100, 255],
[255, 100, 0],
[255, 100, 0],
[255, 100, 0]],
[[ 0, 100, 255],
[ 0, 100, 255],
[255, 100, 0],
[255, 100, 0]]])
>>> out.shape
(2, 4, 3)
>>> all((out[i]==colors[c]).all() for i,c in np.ndenumerate(k))
True

Categories

Resources