Search code examples
python-3.ximage-processingscikit-image

When labelling an image using a function I wrote, I don't obtain the same result compared to the skimage measure.label function


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.


Solution

  • 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