图像的距离变换实现了像素与图像区域的距离变换,使得最后生成的图像在该自己元素位置处的像素为0,临近的背景的像素具有较小的值,且随着距离的增大它的的数值也就越大。对于距离图像来说,图像中的每个像素的灰度值为该像素与距离其最近的背景像素间的距离,也就是说,给每个像素赋值为离它最近的背景像素点与其距离,一幅二值图像的距离变换可以提供每个像素到最近的非零像素的距离。
距离变换的一般步骤如下:
1.将输入图片转换为二值图像,前景设置为1,背景设置为0;
2.第一遍水平扫描从左上角开始,依次从左往右逐行扫描,扫描完一行自动跳转到下一行的最左端继续扫描,按行遍历图像。掩膜模板mask为maskL 使用下面的公式进行计算。
其中D表示距离,包括欧氏距离,棋盘距离或是麦哈顿距离,f(p)为像素点p的像素值。
3.第二遍水平扫描从右下角开始,依次从右往左逐行扫描,扫描完一行自动转到上一行的最右端继续扫描,按行遍历图像,掩膜模板mask为maskR,方法和上一步相同
4.根据模板maskL和maskR的扫描结果得到最终的距离变换图像。
具体的程序如下:
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;
//计算欧式距离
float calcEuclideanDistance(int x1, int y1, int x2, int y2)
{
return sqrt(float((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)));
}
//计算棋盘距离
int calcChessboardDistance(int x1, int y1, int x2, int y2)
{
return max(abs(x1 - x2), abs(y1 - y2));
}
//计算街区距离
int calcBlockDistance(int x1, int y1, int x2, int y2)
{
return abs(x1 - x2) + abs(y1 - y2);
}
//实现距离变换的函数
void distanceTrans(Mat &srcImage, Mat &resultImage)
{
Mat srcGray, srcBinary;
cvtColor(srcImage, srcGray, CV_BGR2GRAY);
threshold(srcGray, srcBinary, 130, 255, THRESH_BINARY_INV);
imshow("srcBinary", srcBinary);
int rows = srcBinary.rows;
int cols = srcBinary.cols;
uchar *pDataOne;
uchar *pDataTwo;
float disPara = 0.0;
float fDisMin = 0.0;
for (int i = 1; i < rows - 1; i++)
{
pDataOne = srcBinary.ptr<uchar>(i);
for (int j = 1; j < cols; j++)
{
pDataTwo = srcBinary.ptr<uchar>(i - 1);
disPara = calcEuclideanDistance(i, j, i - 1, j - 1);
fDisMin = min((float)pDataOne[j], disPara + pDataTwo[j - 1]);
disPara = calcEuclideanDistance(i, j, i - 1, j);
fDisMin = min(fDisMin, disPara + pDataTwo[j]);
pDataTwo = srcBinary.ptr<uchar>(i);
disPara = calcEuclideanDistance(i, j, i, j - 1);
fDisMin = min(fDisMin, disPara + pDataTwo[j - 1]);
pDataTwo = srcBinary.ptr<uchar>(i+1);
disPara = calcEuclideanDistance(i, j, i + 1, j - 1);
fDisMin = min(fDisMin, disPara + pDataTwo[j - 1]);
pDataOne[j] = (uchar)cvRound(fDisMin);
}
}
for (int i = rows - 2; i > 0; i--)
{
pDataOne = srcBinary.ptr<uchar>(i);
for (int j = cols - 1; j > 0; j--)
{
pDataTwo = srcBinary.ptr<uchar>(i + 1);
disPara = calcEuclideanDistance(i, j, i + 1, j);
fDisMin = min((float)pDataOne[j], disPara + pDataTwo[j]);
disPara = calcEuclideanDistance(i, j, i + 1, j + 1);
fDisMin = min(fDisMin, disPara + pDataTwo[j + 1]);
pDataTwo = srcBinary.ptr<uchar>(i);
disPara = calcEuclideanDistance(i, j, i, j + 1);
fDisMin = min(fDisMin, disPara + pDataTwo[j + 1]);
pDataTwo = srcBinary.ptr<uchar>(i - 1);
disPara = calcEuclideanDistance(i, j, i - 1, j + 1);
fDisMin = min(fDisMin, disPara + pDataTwo[j + 1]);
pDataOne[j] = (uchar)cvRound(fDisMin);
}
}
resultImage = srcBinary.clone();
}
int main()
{
Mat srcImage = imread("test.jpg");
if (!srcImage.data)
{
printf("could not load image...\n");
return -1;
}
Mat resultImage;
distanceTrans(srcImage, resultImage);
imshow("resultImage", resultImage);
waitKey(0);
return 0;
}
原图二值化图:
效果图: