我想自适应地对此图像进行阈值处理,以便使用OpenCV中的findContours()
找到外边框。由于通常的原因,我使用自适应阈值:全局阈值处理,即使使用Otsu的方法,也不能充分补偿图像不同部分之间的亮度差异。
不幸的是,自适应阈值处理会在一些具有粗网格线的交叉点处造成破坏。这是因为,对于交叉点处的像素,粗网格线占据周围区域的大部分,使得局部阈值升高到交叉点处的像素的(仅适度暗)值。令人惊讶的是,即使对于大的阈值窗口,这种效果仍然存在一定程度。
当然,这使得自适应阈值对于在这些类型的图像中找到轮廓是无用的。但是,它总体上比其他算法(如Canny)在提供连接的边缘时要好得多。
通过手动填充图像中的所有单像素和双像素间隙,我已经能够在自适应阈值处理后重新连接边缘(我实际上是缩小图像上的阈值以节省运行时间;间隙更大上面的全尺寸图片)。这是我使用的OpenCV代码(为Android绑定编写)。 0为黑色,-1为白色。
private void fillGaps(Mat image) {
int size = image.rows() * image.cols();
byte[] src = new byte[size], dst = new byte[size];
image.get(0, 0, src);
int c = image.cols();
int start = 2 * c + 2;
int end = size - start;
for (int i = start; i < end; i++) {
if (src[i+1] == -1 && src[i-1] == -1 || src[i+c] == -1 && src[i-c] ==-1){
// 1-pixel gap
dst[i] = -1;
} else if (src[i+1] == 0 && src[i+2 ] == -1 && src[i-1] == -1) {
// 2-pixel horizontal gap
dst[i] = -1; dst[i+1] = -1;
} else if (src[i+c] == 0 && src[i+2*c] == -1 && src[i-c] == -1) {
// 2-pixel vertical gap
dst[i] = -1; dst[i+c] = -1;
}
}
image.put(0, 0, dst);
}
这是填补空白之前和之后的缩小图像:
虽然这在这里运作得相当好,但它是一种粗糙的技术,没有填补所有空隙,有时会将网格与其他附近的轮廓连接起来。
在自适应阈值后避免断开轮廓的可靠方法是什么?
答案 0 :(得分:2)
你正在采取的自适应阈值方法是正常的,但作为下一步,你应该做一些形态学操作:侵蚀,扩张,开放,关闭。 对于您的特定情况,关闭操作将是合适的。如果您想手动查看此处:http://users.utcluj.ro/~raluca/ip_2013/ipl_07e.pdf 还有Open-CV内置方法cvDilate和cvErode。结构元素的形状无关紧要,但尺寸要小。
我看到你填补了空白的实施。在那里你没有考虑对角线元素。最好在每个像素周围采用3x3或5x5窗口,然后比较每个元素,然后决定结果。
counter=0;
for (int k=i-radius; k<=i+radius; k++)
{
for (int l=j-radius; l<=j+radius; l++)
{
if (src[k][l] == -1)
counter++;
}
}
if (counter > 0)
dest[k][l] = -1;
else
dest[k][l] = 0;
这是我用于扩张(或填补空白)的示例代码。半径可以是1(3x3)或2(5x5)。
答案 1 :(得分:0)
尝试在adaptiveThreshold之后扩展图像。