4

I have a numpy 2-d array which I divided in several numpy 2-d blocks. All blocks have the same shape. On these blocks I performed K-means segementation using the scikit-learn module. The edges of each block are overlapping (each block has one row/column overlap with the adjacent block). What I want is to give the overlapping segments in two adjacent blocks the same value. My current code can be downloaded here.

Image of the blocks and their position in the original image:

Image of the blocks

Blocks in python code

blockNW=np.array([[ 0.,  0.,  0.,  0.,  5.],
   [ 0.,  0.,  4.,  5.,  5.],
   [ 0.,  4.,  4.,  5.,  2.],
   [ 0.,  4.,  5.,  5.,  2.],
   [ 5.,  5.,  2.,  2.,  2.]])

blockNE=np.array([[ 1.,  18.,  18.,  18.,  6.],
   [ 1.,  18.,  7.,  6.,  6.],
   [ 3.,  7.,  7.,  7.,  6.],
   [ 3.,  3.,  3.,  7.,  7.],
   [ 3.,  3.,  7.,  7.,  7.]])

blockSW=np.array([[ 8., 8.,  8.,  10.,  10.],
   [ 8.,  8.,  9.,  10.,  10.],
   [ 8.,  8.,  9.,  9.,  10.],
   [ 8.,  8.,  8.,  9.,  10.],
   [ 8.,  8.,  9.,  9.,  11.]])

blockSE=np.array([[ 12., 12.,  12., 12.,  12.],
   [ 12., 12.,  12., 12.,  13.],
   [ 12., 12.,  12.,  13.,  13.],
   [ 12., 12.,  13.,  13.,  13.],
   [ 12., 13.,  13.,  13.,  13.]])

blocksStacked=np.array([blockNW,blockNE,blockSW,blockSE])

What I want is to connect the overlapping segments. For this I would like to use as few for-loops as possible, because they are slowing down the code. My current steps are:

import math
import numpy as np
from scipy import ndimage,stats

n_blocks,blocksize = np.shape(blocksStacked)[0],np.shape(blocksStacked)[1]
# shape of original image
out_shp = (8,8)
# horizontal and vertical blocks
horizontal_blocks=math.ceil(out_shp[1]/float(blocksize))
vertical_blocks=math.ceil(out_shp[0]/float(blocksize))
# numpy 2_d array in the shape of the image with an unique ID for each block
blockindex=np.arange(horizontal_blocks*vertical_blocks).reshape(-1,horizontal_blocks)

Block index

enter image description here

def find_neighbours(values,neighbourslist):
    '''function to find the index of neighbouring blocks'''
    mode=stats.mode(values)
    if mode.count>1:
        values=np.delete(values,np.where(values==mode[0]))
    else:
        values=np.delete(values,np.where(values==np.median(values)))
    neighbourslist.append(values)
    return 0 

#Locate overlapping rows and columns per block
neighbourlist=[]
kernel=np.array([[0,1,0],[1,1,1],[0,1,0]],dtype='uint8')
_ =ndimage.generic_filter(blockindex, find_neighbours, footprint=kernel,extra_arguments=(neighbourlist,))

#output (block 0 has neighbours 1 and 2, etc.):
>>> neighbourlist
[array([ 1.,  2.]), array([ 0.,  3.]), array([ 0.,  3.]), array([ 1.,  2.])]

Now the next step could be is to loop through all blocks and neighbors and select the overlapping rows or columns (If possible I would also like to remove these loops).

# First I create masks to select overlapping rows or columns:
upmask=np.ones((blocksize,blocksize),dtype=bool)
upmask[1:,:]=0
downmask=np.ones((blocksize,blocksize),dtype=bool)
downmask[:-1,:]=0
rightmask=np.ones((blocksize,blocksize),dtype=bool)
rightmask[:,:-1]=0
leftmask=np.ones((blocksize,blocksize),dtype=bool)
leftmask[:,1:]=0

# Now loop through all blocks and neighbours and select the overlapping rows/columsn
for i in range(n_blocks):
    n_neighbours = len(neighbourlist[i])
    block=blocksStacked[i,:,:]
    for j in range(n_neighbours):
        neighborindex=neighbourlist[i][j]
        block_neighbour=blocksStacked[neighborindex,:,:]
        if i+1==neighborindex:
            blockvals=block[rightmask]
            neighbourvals=block_neighbour[leftmask]
        elif i-1==neighborindex:
            blockvals=block[leftmask]
            neighbourvals=block_neighbour[rightmask]
        elif i+horizontal_blocks==neighborindex:
            blockvals=block[downmask]
            neighbourvals=block_neighbour[upmask]
        elif i-horizontal_blocks==neighborindex:
            blockvals=block[upmask]
            neighbourvals=block_neighbour[downmask]

In each loop I end up with two numpy 1d arrays representing the overlapping columns or rows. For the first loop I will end up with:

>>> blockvals
array([5., 5., 2., 2., 2.])
>>> neighbourvals
array([1., 1., 3., 3., 3.])

I want to relabel the values of the overlapping segments to the values of the segments in the block which is not a neighbour:

blockNW=np.array([[ 0.,  0.,  0.,  0.,  5.],
   [ 0.,  0.,  4.,  5.,  5.],
   [ 0.,  4.,  4.,  5.,  2.],
   [ 0.,  4.,  5.,  5.,  2.],
   [ 5.,  5.,  2.,  2.,  2.]])

blockNE=np.array([[ 5.,  18.,  18.,  18.,  6.],
   [ 5.,  18.,  7.,  6.,  6.],
   [ 2.,  7.,  7.,  7.,  6.],
   [ 2.,  2.,  2.,  7.,  7.],
   [ 2.,  2.,  7.,  7.,  7.]])

Any idea on how to detect and relabel these overlapping segments?

Also my code looks a bit too cumbersome, any ideas on how to improve my code?

A few remarks:

  • Some segments will not overlap for 100%, so it should be possible to set a threshold. For example is segments are overlapping for more than 70% they should be relabeled
  • The output shape of the function should be similar to the shape of the stacked blocks

The desired output will look like this:

enter image description here

EDIT

With for-loops the code to solve the question would look something like this:

from scipy.stats import itemfreq

# Locate and re-label overlapping segments
for k in range(len(np.unique(blockvals))):
    #Iterate over each value in the overlapping row/column of the block
    blockval=np.unique(blockvals)[k]
    #count of blockval
    block_val_count=len(blockvals[np.where(blockvals==blockval)])
    #Select values in neighbour on the same location
    overlap=neighbourvals[np.where(blockvals==blockval)]
    overlapfreq=itemfreq(overlap)
    #select neighboring value which overlaps the most
    neighval_overlap_count= np.max(overlapfreq[:,1])
    neighval=overlapfreq[np.where(overlapfreq[:,1]==neighval_overlap_count),0][0]
    # count occurence of selected neighboring value
    neigh_val_count=len(neighbourvals[np.where(neighbourvals==neighval)])
    #If overlap is more than 70% relabel the neigboring value to the value in the block
    thresh=0.7
    if (neighval_overlap_count/float(neigh_val_count)>=thresh) and (neighval_overlap_count/float(block_val_count)>=thresh):   
        blocksStacked[neighborindex,:,:,][np.where(blocksStacked[neighborindex,:,:]==neighval)]=blockval



#output
>>> blocksStacked
array([[[  0.,   0.,   0.,   0.,   5.],
    [  0.,   0.,   4.,   5.,   5.],
    [  0.,   4.,   4.,   5.,   2.],
    [  0.,   4.,   5.,   5.,   2.],
    [  5.,   5.,   2.,   2.,   2.]],

   [[  5.,  18.,  18.,  18.,   6.],
    [  5.,  18.,   7.,   6.,   6.],
    [  2.,   7.,   7.,   7.,   6.],
    [  2.,   2.,   2.,   7.,   7.],
    [  2.,   2.,   7.,   7.,   7.]],

   [[  8.,   8.,   8.,  10.,  10.],
    [  8.,   8.,   9.,  10.,  10.],
    [  8.,   8.,   9.,   9.,  10.],
    [  8.,   8.,   8.,   9.,  10.],
    [  8.,   8.,   9.,   9.,  11.]],

   [[ 10.,  10.,  10.,  10.,  10.],
    [ 10.,  10.,  10.,  10.,  13.],
    [ 10.,  10.,  10.,  13.,  13.],
    [ 10.,  10.,  13.,  13.,  13.],
    [ 10.,  13.,  13.,  13.,  13.]]])
Wilmar van Ommeren
  • 7,469
  • 6
  • 34
  • 65

0 Answers0