Edge detection for image stored in matrix

user4632747 picture user4632747 · Apr 3, 2015 · Viewed 11.4k times · Source

I represent images in the form of 2-D arrays. I have this picture:

original

How can I get the pixels that are directly on the boundaries of the gray region and colorize them?

colorized

I want to get the coordinates of the matrix elements in green and red separately. I have only white, black and gray regions on the matrix.

Answer

Ed Smith picture Ed Smith · Apr 7, 2015

The following should hopefully be okay for your needs (or at least help). The idea is to split into the various regions using logical checks based on threshold values. The edge between these regions can then be detected using numpy roll to shift pixels in x and y and comparing to see if we are at an edge,

import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
from skimage.morphology import closing

thresh1 = 127
thresh2 = 254

#Load image
im = sp.misc.imread('jBD9j.png')

#Get threashold mask for different regions
gryim = np.mean(im[:,:,0:2],2)
region1 =  (thresh1<gryim)
region2 =  (thresh2<gryim)
nregion1 = ~ region1
nregion2 = ~ region2

#Plot figure and two regions
fig, axs = plt.subplots(2,2)
axs[0,0].imshow(im)
axs[0,1].imshow(region1)
axs[1,0].imshow(region2)

#Clean up any holes, etc (not needed for simple figures here)
#region1 = sp.ndimage.morphology.binary_closing(region1)
#region1 = sp.ndimage.morphology.binary_fill_holes(region1)
#region1.astype('bool')
#region2 = sp.ndimage.morphology.binary_closing(region2)
#region2 = sp.ndimage.morphology.binary_fill_holes(region2)
#region2.astype('bool')

#Get location of edge by comparing array to it's 
#inverse shifted by a few pixels
shift = -2
edgex1 = (region1 ^ np.roll(nregion1,shift=shift,axis=0))
edgey1 = (region1 ^ np.roll(nregion1,shift=shift,axis=1))
edgex2 = (region2 ^ np.roll(nregion2,shift=shift,axis=0)) 
edgey2 = (region2 ^ np.roll(nregion2,shift=shift,axis=1))

#Plot location of edge over image
axs[1,1].imshow(im)
axs[1,1].contour(edgex1,2,colors='r',lw=2.)
axs[1,1].contour(edgey1,2,colors='r',lw=2.)
axs[1,1].contour(edgex2,2,colors='g',lw=2.)
axs[1,1].contour(edgey2,2,colors='g',lw=2.)
plt.show()

Which gives the following. For simplicity I've use roll with the inverse of each region. You could roll each successive region onto the next to detect edges

Thank you to @Kabyle for offering a reward, this is a problem that I spent a while looking for a solution to. I tried scipy skeletonize, feature.canny, topology module and openCV with limited success... This way was the most robust for my case (droplet interface tracking). Hope it helps!