Reputation: 763
I'm using Pillow to load 2 images (apparently black and white, however in RGB format) and convert to grayscale. I can't use opencv.
aImage = Image.open(imageA.visualFilename).convert("L")
bImage = Image.open(imageB.visualFilename).convert("L")
I convert them to numpy arrays.
aArray = np.array(aImage)
bArray = np.array(bImage)
What I ultimately want to do is (1) count all the black pixels in each array and (2) compare aArray to bArray pixel by pixel and count the number of matching black pixels.
What's currently confusing me is when I print one of the arrays print aArray.shape
, I don't understand what i'm looking at. I'm using np.set_printoptions(threshold='nan')
to print the full array but it appears to be a series of elements with way too many values than I would expect. I would think each element would contain either a single 255 or 0?
Am I doing this the correct way in order to work with a black and white pixels? When I attempted to convert to binary "1", I got "True"/"False" results which confused me a little more.
Also, assuming aArray and bArray look identical, but are actually off by a few pixels, what is the best way to incorporate a 'fuzzy logic' to the pixel by pixel comparison of the two?
Just for additional information, aImage.size
and aImage.mode
return
(184, 184)
L
Upvotes: 5
Views: 10474
Reputation: 236
For starters the L
mode in the Image.open(visualFilename).convert("L")
does not convert a image to Black&White, rather it converts the image to a gray scale using the following formula:
L = R * 299/1000 + G * 587/1000 + B * 114/1000
Being R, G and B, red, green and blue respectively
This formula goes throughout the image and change a 3 (RGB) channel pixel to a 1 (gray scale) channel pixel that best approximates the 3 channel color scheme. And that is why at the end you don't get only the values 0 and 255. Instead you get a pixel with values ranging from 0 to 255.
To compare all the black pixels between two gray scale images first you have to define what black really means. Is a value like 10 black enough or 25 is already black for you? You got to remember that color is not absolute, and its meaning can change depending what you are doing.
Therefore a way around this is to set a threshold in which you will define "what is black for you". Using a simple Threshold function would do. So, I think this simple code might solve your problem:
import numpy as np
def threshold(array, th=50, above_to_max = False):
#check type numpy
if type(array) is np.ndarray:
#Creates a copy to not mess with original
array = array.copy()
#set all values below threshold to 0
array[array<=th] = 0
if above_to_max:
#set all values above threshold to 0
array[array>th] = 255
return array
else:
raise Exception("Array must be a numpy array")
#Example images
image1 = [[ 0, 5, 10, 15, 20],
[ 25, 30, 35, 40, 45],
[ 50, 55, 60, 65, 70],
[175,180,185,190,195],
[200,210,215,240,255]]
image2 = [[ 5, 5, 110, 5, 0],
[ 25, 30, 35, 0, 15],
[150, 55, 60, 65, 70],
[275,280,285,290,295],
[ 20, 10, 15,240,255]]
#Transform to array
array1 = np.asarray(image1, dtype=np.uint8)
array2 = np.asarray(image2, dtype=np.uint8)
#Apply threshold
tsh_array1 = threshold(array1)
tsh_array2 = threshold(array2)
print(' Array1\n', array1, '\n', 'Thresholded1\n',tsh_array1,'\n' )
print(' Array2\n', array2, '\n', 'Thresholded2\n',tsh_array2,'\n' )
equals = (tsh_array1==0)[tsh_array2==0]
print("Number of pixels in the same position equal to zero: {}".format(sum(equals)))
This code renders the following:
Array1
[[ 0 5 10 15 20]
[ 25 30 35 40 45]
[ 50 55 60 65 70]
[175 180 185 190 195]
[200 210 215 240 255]]
Thresholded1
[[ 0 0 0 0 0]
[ 0 0 0 0 0]
[ 0 55 60 65 70]
[175 180 185 190 195]
[200 210 215 240 255]]
Array2
[[ 5 5 110 5 0]
[ 25 30 35 0 15]
[150 55 60 65 70]
[ 19 24 29 34 39]
[ 20 10 15 240 255]]
Thresholded2
[[ 0 0 110 0 0]
[ 0 0 0 0 0]
[150 55 60 65 70]
[ 0 0 0 0 0]
[ 0 0 0 240 255]]
Number of pixels in the same position equal to zero: 9
EDIT:
Let's take a better look at the following line: (tsh_array1==0)[tsh_array2==0]
(tsh_array1==0)
returns an array with the same shape as tsh_array1
with value True
in the position of a 0 value and False
otherwise
[tsh_array2==0]
is the same as a where clausule. It filters the previous item where tsh_array2==0
The result is an array that looks like that:
[ True True True True True True True True True False False False False False False False False]
Since True is the same as 1, the sum of this array will return the number of zeros in the same position in the two arrays
I hope it helped!
Upvotes: 6
Reputation: 833
Sounds like you want Numpy arrays to me. When I do that I load the images to array by using cv2, with
img = cv2.imread(filepath, cv2.IMREAD_UNCHANGED)
using a demo img of mine I get
>>> img.shape
(2576, 4592, 3)
>>> img.max()
255
>>> img.min()
0
Comparing it to a second img results in a Boolean for each pixel which says if these pixels are identical. See
>>> (img == img2).shape
(2576, 4592, 3)
You can determine if the images are the same, by checking if all pixels compared to True
:
>>> (img == img2).all()
False
Counting black pixels by:
>>> sum(sum(sum((img == 0).astype(int))))
6136
Here you sum three times for each axis once. You can also do it fast by using:
>>> ((img == 0).astype(int)).sum()
6136
EDIT:
second part of your question is a bit tougher.
1) offset in x
y
direction.
Then you probably could try to find that by embedding both images in larger arrays.
Now you could work with clustering approaches to find key areas which you then try to connect.
This should result in a shifting and maybe rotation, which should be the translation between the two images.
2) An offset in color on the other hand would require something like a subtraction of both images. Now you could use the sum and calculate ratios to extreme cases like black and white images.
3) Fuzzy logic. You could move away from the idea of comparing index to index, but sliding a window over each image and compare the sets that are shown in the windows.
EDIT 2:
for not using cv2:
you can skip the cv2 load and just use np.array(aArray)
like you did in your question. I just used cv2 because i had no PIL installed and wanted to show you demo.
Upvotes: 3