cv2.connectedComponents doesn't work properly - python

I want to use the function cv2.connectedComponents to connect components on a binary image, like the following...
.
Everything works, except the outputted labels array. In this array are only zeros and not sequential numbers as indicated, according to the identified components.
import cv2
import numpy as np
img = cv2.imread('eGaIy.jpg', 0)
img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)[1] # ensure binary
ret, labels = cv2.connectedComponents(img)
# Map component labels to hue val
label_hue = np.uint8(179*labels/np.max(labels))
blank_ch = 255*np.ones_like(label_hue)
labeled_img = cv2.merge([label_hue, blank_ch, blank_ch])
# cvt to BGR for display
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)
# set bg label to black
labeled_img[label_hue==0] = 0
cv2.imshow('labeled.png', labeled_img)
cv2.waitKey()
outputted labels --> labels.shape: (256L, 250L)
[[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
...,
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]]

It works for me:
And you should be careful that the function only find the component of nonzero. In the source image, the components are the edges. And the returned are labeled image as the same size of source.
The output of
[[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
...,
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]]
only represent the 4 corner regions(3x3) are all zeros, but it doesn't mean all elements are zeros.
If you call this after you call the cv2.connectedComponents:
print(set(labels.reshape(-1).tolist()))
You will get:
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
It means there exist 14 components(edges), and 1 background(0).

Related

get non zero ROI from numpy array

I want to extract a rectangular ROI from an image.
The image contains a single connected non zero part.
I need it to be efficient in run time.
I was thinking maybe:
Summing along each direction.
Finding first non zero and last non zero.
Slicing the image accordingly.
Is there a better way?
My code:
First is a function to find the first and last non zero:
import numpy as np
from PIL import Image
def first_last_nonzero(boolean_vector):
first = last = -1
for idx,val in enumerate(boolean_vector):
if val == True and first == -1:
first = idx
if val == False and first != -1:
last = idx
return first , last
Then creating an image:
np_im = np.array([[ 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 255 154 251 60 0 0 0]
[ 0 0 0 0 4 66 0 0 255 0 0 0]
[ 0 0 0 0 0 0 0 134 48 0 0 0]
[ 0 0 0 0 0 0 236 70 0 0 0 0]
[ 0 0 0 0 1 255 0 0 0 0 0 0]
[ 0 0 0 0 255 24 24 24 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0]])
Then running our function on the sum along each axis:
y_start, y_end = first_last_nonzero(np.sum(np_im, 1)>0)
x_start, x_end = first_last_nonzero(np.sum(np_im, 0)>0)
cropped_np_im = np_im[y_start:y_end, x_start:x_end]
# show the cropped image
Image.fromarray(cropped_np_im).show()
This works but there are probably a plenty of unnecessary calculations.
Is there a better way to do this? Or maybe more pythonic way?
You can make use of the functions from this post:
Numpy: How to find first non-zero value in every column of a numpy array?
def first_nonzero(arr, axis, invalid_val=-1):
mask = arr!=0
return np.where(mask.any(axis=axis), mask.argmax(axis=axis), invalid_val)
def last_nonzero(arr, axis, invalid_val=-1):
mask = arr!=0
val = arr.shape[axis] - np.flip(mask, axis=axis).argmax(axis=axis) - 1
return np.where(mask.any(axis=axis), val, invalid_val)
arr = np.array([
[0, 0, 0, 0, 1, 1],
[0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 0, 0] ])
y_Min, y_Max, x_Min, x_Max = (0, 0, 0, 0)
y_Min = first_nonzero(arr, axis = 0, invalid_val = -1)
y_Min = (y_Min[y_Min >= 0]).min()
x_Min = first_nonzero(arr, axis = 1, invalid_val = -1)
x_Min = (x_Min[x_Min >= 0]).min()
y_Max = last_nonzero(arr, axis = 0, invalid_val = -1)
y_Max = (y_Max[y_Max >= 0]).max()
x_Max = last_nonzero(arr, axis = 1, invalid_val = -1)
x_Max = (x_Max[x_Max >= 0]).max()
print(x_Min)
print(y_Min)
print(x_Max)
print(y_Max)
For this example of mine, the code will return 1, 0, 5, 4.
As a general rule of thumb in python: Try to avoid loops at all costs. From my own experience that statement is true in 99 out of 100 cases

Python plot 2D array with black and white cells

I want to plot 2D array of 1's and 0's in Python with black and white cells using pyplot.imshow(). If there is '1' then the cell color should be black and if it's '0' the cell color should be white.
I tried this:
grid = np.zeros((4, 4, 4), int)
choices = np.random.choice(grid.size, 6, replace=False)
grid.ravel()[choices] = 1
plt.imshow(grid, cmap='gray')
plt.show()
This is how the output looks like with this code
If you meant to create a 3-dimensional grid, than you are probably interested in plotting all slices:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(2020)
grid = np.zeros((4, 4, 4), int)
choices = np.random.choice(grid.size, 6, replace=False)
grid.ravel()[choices] = 1
print(grid)
fig,ax=plt.subplots(2,2,figsize=(6,6))
for i,a in enumerate(ax.flatten()):
a.imshow(grid[i,:,:], cmap='gray_r',)
a.set_title(f"slice {i}")
plt.show()
yields:
[[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[1 1 0 0]]
[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]]
[[0 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 0]]
[[0 1 0 0]
[1 0 0 0]
[0 0 0 0]
[0 0 0 0]]]
and this image:
If, however, you wanted to plot in 2d, then use:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(2020)
grid = np.zeros((4, 4), int)
choices = np.random.choice(grid.size, 6, replace=False)
grid.ravel()[choices] = 1
print(grid)
plt.imshow(grid,cmap='gray_r')
plt.show()
yields:
[[0 1 1 0]
[1 0 0 0]
[0 1 1 0]
[1 0 0 0]]

Find out indices of an array elements

I have this array as a result of subtracting two images after getting there RGB integer values as an arrays
arr = img1 - img2
[[[0 0 0]
[0 0 0]
[0 0 0]
...,
[0 0 0]
[0 0 0]
[0 0 0]]
...,
[[0 0 0]
[0 0 0]
[0 0 0]
...,
[0 0 0]
[0 0 0]
[0 0 0]]]
i used these lines of code to change the shape of array to add the indices of each pixel subtraction
x, y, z = arr.shape
indices = np.vstack(np.unravel_index(np.arange(x*y), (y, x))).T
result = np.hstack((arr.reshape(x*y, z), indices))
and here what the result looks like:
[[ 0 0 0 0 0]
[ 0 0 0 0 1]
[ 0 0 0 0 2]
...,
[ 0 0 0 511 509]
[ 0 0 0 511 510]
[ 0 0 0 511 511]]
the first three values in each row is the RGB difference and the last two values is the X and Y indices
my question here, is there an efficient way to find the indices of the non zero values?
If I understand what you're saying correctly, you want them per list in your list of lists...
Saying this is your list:
l=[[0,0,0,0,0],[0,0,0,0,1],[0,0,0,0,2],[0,0,0,511,509],[0,0,0,511,510],[0,0,0,511,511]]
try running:
import numpy as np
ans=[np.nonzero(l[i])[0] for i in range(1,len(l))]
print ans
returns:
[array([4]), array([4]), array([3, 4]), array([3, 4]), array([3, 4])]
So it's an array containing arrays that have the indices of each non-zero element in each list. Since it uses list comprehension it runs pretty quickly and accession is as simple as using the indices. It will just be ans[list in list of lists][number of non-zero indices] like so:
ans[2][1]
4

Am expecting a blue (red?) image from python-openCV, but get black

I'm just starting to use OpenCV with Python and am trying to do some simple things. To start with, I tried to create a solid Blue image (or possibly Red, if the image turns out to be RGB, instead of BGR).
I tried the following:
import numpy as np
import cv2
img1 = np.zeros((512,512,3), np.uint8) #Create black image
img1[0,:,:] = 200 #Add intenstity to blue (red?) plane
print img1 #Verify image array
cv2.imshow("II",img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
for _ in range (1,5):
cv2.waitKey(1)
But, what I got was a black image. I'm fairly certain the array was correct, because the print statement gave me the following:
[[[200 200 200]
[200 200 200]
[200 200 200]
...,
[200 200 200]
[200 200 200]
[200 200 200]]
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]
...,
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]]
Does it make sense that I'm seeing a black instead of a blue (or red?) image?
What you're doing is changing color of 0th row. Instead, you need to change the values of first or 0th channel.
img[:, :, 0] = 255
This will change all the values of first or 0th channel to 255 which would give you blue image as it is a BGR Image.
You need to specify the colors as a tuple ! if you want a RGB image because very Index in array is a pixel and you need 3 value for B,G,R ( opencv set the pixels as BGR)
import numpy as np
import cv2
img1 = np.zeros((512,512,3), np.uint8) #Create black image
img1[:,:] = (255,0,0) #Add intenstity to blue (red?) plane
print img1 #Verify image array
cv2.imshow("II",img1,)
cv2.waitKey(0)
cv2.destroyAllWindows()
for _ in range (1,5):
cv2.waitKey(1)
result:

outputting large matrix in python from a dictionary

I have a python dictionary formatted in the following way:
data[author1][author2] = 1
This dictionary contains an entry for every possible author pair (all pairs of 8500 authors), and I need to output a matrix that looks like this for all author pairs:
"auth1" "auth2" "auth3" "auth4" ...
"auth1" 0 1 0 3
"auth2" 1 0 2 0
"auth3" 0 2 0 1
"auth4" 3 0 1 0
...
I have tried the following method:
x = numpy.array([[data[author1][author2] for author2 in sorted(data[author1])] for author1 in sorted(data)])
print x
outf.write(x)
However, printing this leaves me with this:
[[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
...,
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]]
and the output file is just a blank text file. I am trying to format the output in a way to read into Gephi (https://gephi.org/users/supported-graph-formats/csv-format/)
You almost got it right, your list comprehension is inverted. This will give you the expected result:
d = dict(auth1=dict(auth1=0, auth2=1, auth3=0, auth4=3),
auth2=dict(auth1=1, auth2=0, auth3=2, auth4=0),
auth3=dict(auth1=0, auth2=2, auth3=0, auth4=1),
auth4=dict(auth1=3, auth2=0, auth3=1, auth4=0))
np.array([[d[i][j] for i in sorted(d.keys())] for j in sorted(d[k].keys())])
#array([[0, 1, 0, 3],
# [1, 0, 2, 0],
# [0, 2, 0, 1],
# [3, 0, 1, 0]])
You could use pandas. Using #Saullo Castro input:
import pandas as pd
df = pd.DataFrame.from_dict(d)
Result:
>>> df
auth1 auth2 auth3 auth4
auth1 0 1 0 3
auth2 1 0 2 0
auth3 0 2 0 1
auth4 3 0 1 0
And if you want to save you can just do df.to_csv(file_name)

Categories

Resources