我以二维数组的形式表示图像。我有这张照片:
如何获得直接位于灰色区域边界的像素并将其着色?
我想分别用绿色和红色得到矩阵元素的坐标。矩阵上只有白色,黑色和灰色区域。
答案 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()
这给出了。为简单起见,我使用roll与每个区域的倒数。您可以将每个连续区域滚动到下一个区域以检测边缘
感谢@Kabyle提供奖励,这是我花了一段时间寻找解决方案的问题。我尝试了scipy skeletonize,feature.canny,拓扑模块和openCV,但成效有限......这种方式对我来说是最强大的(Droplet界面跟踪)。希望它有所帮助!
答案 1 :(得分:4)
对此有一个非常简单的解决方案:根据定义,任何同时具有白色和灰色邻居的像素都在你的&#34; red&#34;边缘,灰色和黑色的邻居在&#34;绿色&#34;边缘。最轻/最暗的邻居由skimage.filters.rank
中的最大/最小滤波器返回,并且具有最亮/最暗邻居(白色/灰色或灰色/黑色)的像素掩模的二进制组合分别产生边缘。 / p>
结果:
一个有效的解决方案:
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. 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()
!
然而,从您的图像中没有明确定义的灰色区域,因此我在图像中采用了两个最大的强度计数并对其进行了阈值处理。有点令人不安的是白色区域中间的红色区域,但我认为这可以通过直方图程序中的区域数量进行调整。您也可以像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。为区分白色和灰色之间的边缘以及黑色和灰色之间的边缘,请尝试使用局部平均强度。