是否有一种算法在填充样本图像上的孔方面表现良好?膨胀不能很好地工作,因为在我最终设法连接这些曲线的末端之前,曲线变得非常厚。我想避免加粗线条。谢谢你的帮助。
是的,图像中可能只有任何字母或形状有这样的洞。
答案 0 :(得分:5)
另一种更简单的方法,可能会更好地转换为OpenCV
,因为它使用卷积而不是顺序Perl / C代码。
基本上将所有黑色像素设置为值10
,将所有白色像素设置为值0
,然后将图像与以下3x3内核进行卷积:
1 1 1
1 10 1
1 1 1
现在,内核中间的黑色像素将给出100(10x10),并且邻域中的任何其他黑色像素将给出10(10x1)。因此,如果我们想要具有仅具有一个相邻黑色像素的中心黑色像素的点,则其值将为110(100 + 10)。因此,让所有具有红色值110的像素着色。这给出了这个命令:
convert EsmKh.png -colorspace gray -fill gray\(10\) -opaque black -fill gray\(0\) -opaque white -morphology convolve '3x3: 1,1,1 1,10,1 1,1,1' -fill red -opaque gray\(110\) out.png
生成的图像(您可能需要放大间隙才能看到红色):
如果您需要红色像素列表,请将输出文件名替换为txt:
并按以下方式搜索:
convert EsmKh.png -colorspace gray -fill rgb\(10,10,10\) -opaque black -fill rgb\(0,0,0\) -opaque white -morphology convolve '3x3: 1,1,1 1,10,1 1,1,1' txt: | grep "110,110,110"
给出:
86,55: (110,110,110) #6E6E6E grey43
459,55: (110,110,110) #6E6E6E grey43
83,56: (110,110,110) #6E6E6E grey43
507,59: (110,110,110) #6E6E6E grey43
451,64: (110,110,110) #6E6E6E grey43
82,65: (110,110,110) #6E6E6E grey43
134,68: (110,110,110) #6E6E6E grey43
519,75: (110,110,110) #6E6E6E grey43
245,81: (110,110,110) #6E6E6E grey43
80,83: (110,110,110) #6E6E6E grey43
246,83: (110,110,110) #6E6E6E grey43
269,84: (110,110,110) #6E6E6E grey43
288,85: (110,110,110) #6E6E6E grey43
315,87: (110,110,110) #6E6E6E grey43
325,87: (110,110,110) #6E6E6E grey43
422,104: (110,110,110) #6E6E6E grey43
131,116: (110,110,110) #6E6E6E grey43
524,116: (110,110,110) #6E6E6E grey43
514,117: (110,110,110) #6E6E6E grey43
122,118: (110,110,110) #6E6E6E grey43
245,122: (110,110,110) #6E6E6E grey43
76,125: (110,110,110) #6E6E6E grey43
456,128: (110,110,110) #6E6E6E grey43
447,129: (110,110,110) #6E6E6E grey43
245,131: (110,110,110) #6E6E6E grey43
355,135: (110,110,110) #6E6E6E grey43
80,146: (110,110,110) #6E6E6E grey43
139,151: (110,110,110) #6E6E6E grey43
80,156: (110,110,110) #6E6E6E grey43
354,157: (110,110,110) #6E6E6E grey43
144,160: (110,110,110) #6E6E6E grey43
245,173: (110,110,110) #6E6E6E grey43
246,183: (110,110,110) #6E6E6E grey43
76,191: (110,110,110) #6E6E6E grey43
82,197: (110,110,110) #6E6E6E grey43
126,200: (110,110,110) #6E6E6E grey43
117,201: (110,110,110) #6E6E6E grey43
245,204: (110,110,110) #6E6E6E grey43
248,206: (110,110,110) #6E6E6E grey43
297,209: (110,110,110) #6E6E6E grey43
309,210: (110,110,110) #6E6E6E grey43
现在你可以处理红点列表,并为每一个红点找到最近的其他红点并用直线连接它们 - 或者如果你感觉非常敏锐的话,做一些曲线拟合。当然,可能需要进行一些改进,您可能希望设置最大间隙填充线长度。
答案 1 :(得分:2)
我对此有点尝试。它可能需要一些调整,但这是一个想法。我的算法如下:
查找所有具有1个黑色相邻像素的黑色像素,将其着色为红色并将其放在pixels at ends
列表中。
浏览所有红色像素的列表,找到最近的其他红色像素,并在两者之间绘制直线。
顺便说一下,我只实现了第一部分 - 要留给读者做的东西; - )
#!/usr/bin/perl
use strict;
use warnings;
use Image::Magick;
use Data::Dumper;
my $im=Image::Magick->new();
$im->Read('EsmKh.png');
my ($width,$height)=$im->Get('width','height');
my $out=Image::Magick->new();
$out->Read('EsmKh.png');
my @pixels;
# Iterate over pixels
for my $y (0..($height-1)){
for my $x (0..($width-1)){
my (@pixel) = split(/,/, $im->Get("pixel[$x,$y]"));
$pixels[$x][$y]=$pixel[0];
}
}
# Find black pixels that have precisely 1 black neighbour
for my $y (1..($height-2)){
for my $x (1..($width-2)){
next if $pixels[$x][$y]!=0;
my $neighbours=0;
for(my $i=$x-1;$i<=$x+1;$i++){
for(my $j=$y-1;$j<=$y+1;$j++){
$neighbours++ if $pixels[$i][$j]==0;
}
}
$neighbours--; # Uncount ourself !
if($neighbours==1){
$out->Set("pixel[$x,$y]"=>'red');
}
}
}
$out->Write(filename=>'out.png');
<强>结果强>
您必须放大才能看到红色像素...
缩放图片
答案 2 :(得分:1)
在获得加厚的图像后,您可以恢复“瘦身”#34;形状使用skeletonization。我发现了一个骨架化here的实现。
如果你想避免太多的增厚(因为它会扭曲图像并将形状的某些部分合并在一起),请使用温和的侵蚀和骨架化,直到你填满孔。
答案 3 :(得分:0)
这是Mark Setchell算法的OpenCV
,C++
实现。这非常简单,使用相同的内核,并通过cv::filter2D
函数对输入图像进行卷积。我有选择地反转了输入图像,因此目标像素的值为255
:
//Read input Image
cv::Mat inputImage = cv::imread( "C://opencvImages//blobs.png", cv::IMREAD_GRAYSCALE );
//Invert the image:
inputImage = 255 - inputImage;
//Threshold the image so that white pixels get a value of 0 and
//black pixels a value of 10:
cv::threshold( inputImage, inputImage, 128, 10, cv::THRESH_BINARY );
现在,设置内核并对图像进行卷积,如下所示:
//Set up the end-point kernel:
cv::Mat kernel = ( cv::Mat_<int>(3, 3) <<
1, 1, 1,
1, 10, 1,
1, 1, 1
);
//Convolute image with kernel:
cv::filter2D( inputImage, inputImage, -1 , kernel, cv::Point( -1, -1 ), 0, cv::BORDER_DEFAULT );
卷积的直接结果是这样,端点上的像素现在具有110
的值,可以在此输出中(几乎)看到它:
让这些像素阈值化并将它们覆盖在原始图像上。结果就是(红色像素):
此外,可以在开始时计算图像的skeleton
。骨架的归一化线宽为1 pixel
。该函数是OpenCV的Extended Image Processing module
的一部分:
#include <opencv2/ximgproc.hpp>
//Compute the skeleton of the input:
cv::Mat skel;
int algorithmType = 1;
cv::ximgproc::thinning( inputImage, skel, algorithmType );