My goal is to implement the skimage measure.label function from first principles. I would like to do this in order to obtain a labelled image.
There are three functions. The first, "ccl", iterates through each and every pixel checking whether or not the pixel has been visited before and setting the appropriate labelling value. The second function, "check_neighbours", looks at all the surrounding pixels to compare whether or not they form part of the same label in the image. It also determines which values are related. The final function, "join_neighbours" is used to join sections which are related.
import cv2
import numpy as np
from skimage import measure
from skimage.filters import threshold_otsu
bgr_image=cv2.imread('filename')
gray_image = np.dot(bgr_image[..., :3], [0.114, 0.587, 0.299])
threshold = threshold_otsu(gray_image)
binary_image = gray_image > threshold
def check_neighbours(i, j, label,label_image,image,row,col):
label_image[i,j] = label
positions=[[i+1, j], [i-1, j],[i-1, j-1], [i + 1, j + 1],[i-1, j +1], [i + 1, j-1],[i, j+1], [i, j-1]]
for pos in positions:
if pos[0]>=0 and pos[0]<row and pos[1]>=0 and pos[1]<col and label_image[pos[0],pos[1]]>0 and image[pos[0],pos[1]] == 1:
if label_image[pos[0],pos[1]] != label_image[i,j]:
neighbours=sorted([label_image[pos[0],pos[1]],label_image[i,j]])
if not neighbours in dup and not neighbours[::-1] in dup:
dup.append(neighbours)
if pos[0]>=0 and pos[0]<row and pos[1]>=0 and pos[1]<col and image[pos[0],pos[1]] == 1:
label_image[pos[0],pos[1]]=label
return label_image
def ccl(image):
row, col = image.shape
count=0
label_image=np.asarray(np.zeros((row,col)), np.uint8)
for i in range(row):
for j in range(col):
if image[i,j] == 1:
if label_image[i,j]>0:
label = label_image[i,j]
else:
label=count+1
count=label
label_image=check_neighbours(i, j, label, label_image,image, row, col)
return label_image
def join_neighbours(image):
row, col = image.shape
for q in dup:
for i in range(row):
for j in range(col):
if image[i,j]==q[1]:
image[i,j]=q[0]
return image
#labelling image
dup=[]
print("Theirs")
print(measure.label(binary_image))
print()
print("Mine")
print(join_neighbours(ccl(binary_image)))
test_image=np.asarray([[True,True,True,True,False],[True,False,False,False,False],[False,False,False,True,True],[False,False,False,True,False],[True,False,False,True,True],[True,False,False,True,True],[True,False,False,True,False],[True,False,False,True,False],[True,False,False,True,False],[True,False,False,False,False],[True,False,True,False,True],[True,False,True,True,True]])
#labelling test data
dup=[]
print("Theirs")
print(measure.label(test_image))
print()
print("Mine")
print(join_neighbours(ccl(test_image)))
When I test the functions with small data sets the outputs are the same as to the measure.label function, however, when I test an image I don't seem to get the same outputs.
Image:
measure.label output:
[[ 0 0 0 ... 7 7 7]
[ 0 0 0 ... 7 7 7]
[ 0 0 0 ... 7 7 7]
...
[ 0 0 0 ... 107 107 107]
[ 0 0 0 ... 107 107 107]
[ 0 0 0 ... 107 107 107]]
My output:
[[ 0 0 0 ... 8 8 8]
[ 0 0 0 ... 8 8 8]
[ 0 0 0 ... 8 8 8]
...
[ 0 0 0 ... 36 36 36]
[ 0 0 0 ... 36 36 36]
[ 0 0 0 ... 36 36 36]]
Test data:
test_image=[[True,True,True,True,False],[True,False,False,False,False],[False,False,False,True,True],[False,False,False,True,False],[True,False,False,True,True],[True,False,False,True,True],[True,False,False,True,False],[True,False,False,True,False],[True,False,False,True,False],[True,False,False,False,False],[True,False,True,False,True],[True,False,True,True,True]]
measure.label output:
[[1 1 1 1 0]
[1 0 0 0 0]
[0 0 0 2 2]
[0 0 0 2 0]
[3 0 0 2 2]
[3 0 0 2 2]
[3 0 0 2 0]
[3 0 0 2 0]
[3 0 0 2 0]
[3 0 0 0 0]
[3 0 4 0 4]
[3 0 4 4 4]]
My output:
[[1 1 1 1 0]
[1 0 0 0 0]
[0 0 0 2 2]
[0 0 0 2 0]
[3 0 0 2 2]
[3 0 0 2 2]
[3 0 0 2 0]
[3 0 0 2 0]
[3 0 0 2 0]
[3 0 0 0 0]
[3 0 4 0 4]
[3 0 4 4 4]]
Thus if anyone can point me in the right direction with regards to what iI'm doing wrong it would be greatly appreciated.
So after a few more attempts I finally coded something that works. I followed the algorithm "One component at a time" laid out in:[https://en.wikipedia.org/wiki/Connected-component_labeling][1].
My resulting code looked as follow:
Python
def check_neighbours(queue,label,labelled_image,image,row,col):
while queue:
temp_value=queue.pop()
i,j=temp_value
positions=[[i+1, j], [i-1, j],[i-1, j-1], [i + 1, j + 1],[i-1, j +1], [i + 1, j-1],[i, j+1], [i, j-1]]
for pos in positions:
if pos[0]>=0 and pos[0]<row and pos[1]>=0 and pos[1]<col and image[pos[0],pos[1]]==1 and labelled_image[pos[0],pos[1]]==0:
labelled_image[pos[0],pos[1]]=label
queue.append([pos[0],pos[1]])
return labelled_image
def ccl(image):
row, col = image.shape
label=1
queue=[]
labelled_image=np.asarray(np.zeros((row,col)), np.uint8)
for i in range(row):
for j in range(col):
if image[i,j] == 1 and labelled_image[i,j]==0:
labelled_image[i,j]=label
queue.append([i,j])
labelled_image=check_neighbours(queue,label,labelled_image,image,row,col)
label+=1
return labelled_image