存储在矩阵中的图像的边缘检测

时间:2015-04-03 14:54:20

标签: python algorithm numpy edge-detection

我以二维数组的形式表示图像。我有这张照片:

original

如何获得直接位于灰色区域边界的像素并将其着色?

colorized

我想分别用绿色和红色得到矩阵元素的坐标。矩阵上只有白色,黑色和灰色区域。

6 个答案:

答案 0 :(得分:7)

以下内容应该可以满足您的需求(或者至少是帮助)。想法是使用基于阈值的逻辑检查分成各个区域。然后可以使用numpy roll检测这些区域之间的边缘,以移动x和y中的像素,并比较以查看我们是否处于边缘,

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()

这给出了following。为简单起见,我使用roll与每个区域的倒数。您可以将每个连续区域滚动到下一个区域以检测边缘

感谢@Kabyle提供奖励,这是我花了一段时间寻找解决方案的问题。我尝试了scipy skeletonize,feature.canny,拓扑模块和openCV,但成效有限......这种方式对我来说是最强大的(Droplet界面跟踪)。希望它有所帮助!

答案 1 :(得分:4)

对此有一个非常简单的解决方案:根据定义,任何同时具有白色和灰色邻居的像素都在你的&#34; red&#34;边缘,灰色和黑色的邻居在&#34;绿色&#34;边缘。最轻/最暗的邻居由skimage.filters.rank中的最大/最小滤波器返回,并且具有最亮/最暗邻居(白色/灰色或灰色/黑色)的像素掩模的二进制组合分别产生边缘。 / p>

结果:

enter image description here

一个有效的解决方案:

import numpy
import skimage.filters.rank
import skimage.morphology
import skimage.io

# convert image to a uint8 image which only has 0, 128 and 255 values
# the source png image provided has other levels in it so it needs to be thresholded - adjust the thresholding method for your data
img_raw = skimage.io.imread('jBD9j.png', as_grey=True)
img = numpy.zeros_like(img, dtype=numpy.uint8)
img[:,:] = 128
img[ img_raw < 0.25 ] = 0
img[ img_raw > 0.75 ] = 255

# define "next to" - this may be a square, diamond, etc
selem = skimage.morphology.disk(1)

# create masks for the two kinds of edges
black_gray_edges = (skimage.filters.rank.minimum(img, selem) == 0) & (skimage.filters.rank.maximum(img, selem) == 128)
gray_white_edges = (skimage.filters.rank.minimum(img, selem) == 128) & (skimage.filters.rank.maximum(img, selem) == 255)

# create a color image
img_result = numpy.dstack( [img,img,img] )

# assign colors to edge masks
img_result[ black_gray_edges, : ] = numpy.asarray( [ 0, 255, 0 ] )
img_result[ gray_white_edges, : ] = numpy.asarray( [ 255, 0, 0 ] )

imshow(img_result)

P.S。具有黑色和白色邻居或所有三种颜色邻居的像素处于未定义的类别中。上面的代码并没有给那些颜色着色。您需要弄清楚在这些情况下您希望输出如何着色;但是很容易扩展上面的方法来制作另外一个或两个面罩。

P.S。边缘宽两个像素。如果没有更多信息,就无法解决这个问题:边缘位于两个区域之间,并且您还没有定义两个区域中的哪一个在每种情况下都要重叠,因此唯一对称的解决方案是重叠两个区域一个像素。

P.S。这将像素本身计为其自己的邻居。灰色上的孤立白色或黑色像素(反之亦然)将被视为边缘(以及其周围的所有像素)。

答案 2 :(得分:2)

虽然plonser的答案可能相当直接实现,但我认为它在锐利和薄边缘方面失败了。不过,我建议你使用他的部分方法作为预处理。
在第二步中,您要使用Marching Squares Algorithm。根据{{​​3}}的文档,它是

  

行进立方体算法的特例(Lorensen,William和   哈维E.克莱因。行进立方体:高分辨率3D表面   构造算法。计算机图形学(SIGGRAPH 87会议录)   1987年7月21日(4),p。 163-170

甚至存在Python实现作为scikit-image包的一部分。我一直在使用这种算法(我自己的Fortran实现)成功地进行通信工程中眼图的边缘检测。

广告1:预处理
创建图像副本并使其仅为两种颜色,例如黑,白。坐标保持不变,但您确保算法可以正确地做出是/否决定,而不依赖于您在图像矩阵表示中使用的值。

广告2:边缘检测
维基百科以及各种博客为您提供了各种语言的漂亮scikit-image算法,因此我不会详细介绍它。但是,让我给你一些实用的建议:

  1. 您的图片底部有开放边界。您可以人工添加另一行像素(黑色或灰色以限制白色/灰色区域),而不是修改算法。
  2. 选择起点至关重要。如果没有太多图像需要处理,我建议您手动选择。否则,您需要定义规则。由于Marching Squares算法可以从有界区域内的任何地方开始,因此您可以选择给定颜色/值的任何像素来检测相应的边缘(它最初将开始沿一个方向行走以找到边缘)。
  3. 该算法返回精确的2D位置,例如(X / Y)元组。你也可以
    • 遍历列表并通过指定不同的值或
    • 为相应的像素着色
    • 创建elaborate description以选择矩阵的某些部分,并指定与不同颜色对应的值,例如绿色或红色。
  4. 最后:一些后期处理 我建议在图像上添加一个人工边界。这有两个好处:  1. Marching Squares算法开箱即用。  2.无需区分图像边界和图像内两个区域之间的界面。完成设置彩色边缘后,只需删除人工边界 - 这将删除图像边界处的彩色线条。

答案 3 :(得分:1)

基本上遵循pyStarter建议使用scikit-image中的行进方形算法,可以使用以下代码提取所需的轮廓:

import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
from skimage import measure
import scipy.ndimage as ndimage
from skimage.color import rgb2gray
from pprint import pprint
#Load image
im = rgb2gray(sp.misc.imread('jBD9j.png'))

n, bins_edges = np.histogram(im.flatten(),bins = 100)
# Skip the black area, and assume two distinct regions, white and grey
max_counts = np.sort(n[bins_edges[0:-1] > 0])[-2:]
thresholds = np.select(
    [max_counts[i] == n for i in range(max_counts.shape[0])],
    [bins_edges[0:-1]] * max_counts.shape[0]
)
# filter our the non zero values
thresholds = thresholds[thresholds > 0] 


fig, axs = plt.subplots()
# Display image
axs.imshow(im, interpolation='nearest', cmap=plt.cm.gray)
colors = ['r','g']
for i, threshold in enumerate(thresholds):
    contours = measure.find_contours(im, threshold)

    # Display  all contours found for this threshold
    for n, contour in enumerate(contours):
        axs.plot(contour[:,1], contour[:,0],colors[i], lw = 4)

axs.axis('image')
axs.set_xticks([])
axs.set_yticks([])        
plt.show()

Edges

然而,从您的图像中没有明确定义的灰色区域,因此我在图像中采用了两个最大的强度计数并对其进行了阈值处理。有点令人不安的是白色区域中间的红色区域,但我认为这可以通过直方图程序中的区域数量进行调整。您也可以像Ed Smith那样手动设置这些。

答案 4 :(得分:0)

也许有更优雅的方式来做到这一点...... 但如果您的数组是numpy数组,其维度为(N,N)(灰度),则可以

import numpy as np

# assuming black -> 0 and white -> 1 and grey -> 0.5
black_reg = np.where(a < 0.1, a, 10)
white_reg = np.where(a > 0.9, a, 10)

xx_black,yy_black = np.gradient(black_reg)
xx_white,yy_white = np.gradient(white_reg)

# getting the coordinates
coord_green = np.argwhere(xx_black**2 + yy_black**2>0.2)
coord_red   = np.argwhere(xx_white**2 + yy_white**2>0.2)

数字0.2只是一个阈值,需要进行调整。

答案 5 :(得分:0)

我认为您可能正在寻找灰度图像的边缘检测方法。有很多方法可以做到这一点。也许这可以帮助http://en.m.wikipedia.org/wiki/Edge_detection。为区分白色和灰色之间的边缘以及黑色和灰色之间的边缘,请尝试使用局部平均强度。