I need to separate an array into sub arrays where each sub array represents a distinct line on an image

Viewed 58

My goal with this project is to start with one large 2d array and separate it into multiple arrays. Here is the original array:

arr = np.array([[0,0,0,0,0,0,0,0,0],
                [0,0,0,0,1,0,0,0,0],
                [0,1,0,0,1,0,0,0,0],
                [0,0,0,0,1,0,0,0,0],
                [0,0,0,0,1,0,0,1,0],
                [0,0,0,0,1,0,0,1,0],
                [0,1,0,0,1,0,0,1,0],
                [0,0,0,0,1,0,0,1,0],
                [0,0,0,0,1,0,0,1,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]])

This array represents two vertical lines (one line with len = 8 and the other with len = 5). The other two 1's are considered error. Firstly, I get rid of the error using this bit of code. It's job is to check neighboring pixels/cells and if all neighboring cells are zero then it is changed to zero.

noise = []
for i, e in enumerate(data):
    if arr[e[1],e[0]+1] == 0 and arr[e[1],e[0]-1] == 0 and arr[e[1]-1,e[0]] == 0 and arr[e[1]+1,e[0]] == 0 \
    and arr[e[1]+1,e[0]+1] == 0 and arr[e[1]+1,e[0]-1] == 0 and arr[e[1]-1,e[0]+1] == 0 and arr[e[1]-1,e[0]-1] == 0:
        noise.append(i)
data = np.delete(data, noise, axis = 0) 

Next, I want to separate the array into two sub arrays where sub array 1 is the x,y coordinates (column, row) of the first line and sub array 2 is the x,y coordinates of the second line. To do this, I first made a loop which records the positions of the end points of the two lines. I did this using a similar loop to the one I used to find and delete the single pixels/ noise.

Finally, once I have my array of end point coordinates, I made a loop which starts at one endpoint, checks in what direction the next pixel is, appends the coordinates, then repeats. Below you can find the loop I used to do this.

allfibers = []
end_points = np.array(end_points)
for i, e in enumerate(end_points):
    fiber = []
    row = data[end_points[i]][1]
    column = data[end_points[i]][0]
    fiber.append([row,column])
    _ = 0
    z = f'start -> row, column = {row}, {column}'
    while True:
        print(z)
        if arr[row][column+1] == 1 and z !='left': #right of pix
            fiber.append([row,column+1])
            column += 1 
            z = 'right'
        elif arr[row][column-1] == 1 and z !='right': #left of pix
            fiber.append([row,column-1]) 
            column -= 1
            z = 'left'
        elif arr[row+1][column] == 1 and z !='down': #above pix
            fiber.append([row+1,column])
            row += 1 
            z = 'up'
        elif arr[row-1][column] == 1 and z !='up': #below pix
            fiber.append([row-1,column])
            row -= 1
            z = 'down'
        if (np.any(endpoint_coord) == np.all([row,column]) or np.any(endpoint_coord) == 
        np.all([row,column-1]) \
        or np.any(endpoint_coord) == np.all([row-1,column])) and _ > 1:
            break
        _ += 1
    allfibers.append(np.array(fiber))
data_type = np.dtype(np.int64)
allfibers = np.array(allfibers, dtype=data_type)

print(allfibers)
`

In this code directly above lies the problem. The program returns this:

[[[1 4]
  [2 4]
  [3 4]
  [4 4]]

 [[4 7]
  [5 7]
  [6 7]
  [7 7]]

 [[8 4]
  [7 4]
  [6 4]
  [5 4]]

 [[8 7]
  [7 7]
  [6 7]
  [5 7]]]

This problem is not there there are 4 sub arrays and only 2 lines in the original array. Because each fiber has two endpoints this is expected and can easily be handled later on. The problem is that the lengths don't match. I do not know why this is the case. If anyone has any ideas about how to fix this please let me know. Also I am relatively new to this so forgive me if the explanation isn't the best and please let me know if there is anything I should clarify.

1 Answers

If you don't mind using pre-built function, you could use the scipy library:

import numpy as np
from scipy.signal import convolve2d
from scipy.ndimage import label 

## Example matrix
arr = [...]

## Check if neighboring cells are zero using a 2d convolution:
# The kernel
ker = np.array([[0,1,0],
                [1,0,1],
                [0,1,0]])

res = convolve2d(arr,ker,mode='same')

# Eliminate the noise
res = np.int32((res>0)&(arr>0))

# Using label your can segment an image and since an image is basically a matrix:
lab = label(res,ker)

# Get the coordinate of each line.
coord = []
for ii in range(lab[1]):
    coord.append(np.vstack(np.where(lab[0]==(ii+1))))

And the output is a list of numpy array, one array per line:

[array([[1, 2, 3, 4, 5, 6, 7, 8],
        [4, 4, 4, 4, 4, 4, 4, 4]], dtype=int64),
 array([[4, 5, 6, 7, 8],
        [7, 7, 7, 7, 7]], dtype=int64)]

Here I use two function:

convolve2d: compute the 2d convolution between arr and ker. The option mode='same' return a matrix with the same shape as the original matrix.

label: Segment a matrix, the neighboring cells that have to be considered are define by the kernel ker.

If you also want to consider diagonal line, then your could simply change the kernel.

Related