距离变换实现图像的每个像素到最近前景目标或到图像边界的距离,距离变换的步骤如下:
1.将图像进行二值化,子图像值为0,背景为255;
2.利用maskL从左向右,从上到下扫描,p点是当前像素点,q点是maskL中邻域的点,D()为距离计算,包括棋盘距离、城市距离和欧式距离。F(p)为p点的像素值,计算F(p) = min( F(p), F(q)+D(p,q) ), 其中,q属于maskL
3.再利用maskR从右向左,从下向上扫描,计算F(p) = min( F(p), F(q)+D(p,q) ), 其中q属于maskR
4.F(p) 则为距离变换后的图像。
代码实现:
//计算欧式距离
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 cv::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(cv::Mat &image, cv::Mat &result)
{
//CV_Assert()若括号中的表达式值为false,则返回一个错误信息
CV_Assert(!image.empty());
cv::Mat imageGray, imageBinary;
//转换成灰度图像
cv::cvtColor(image, imageGray, CV_RGB2GRAY);
//转换成二值图
cv::threshold(imageGray, imageBinary, 100, 255, cv::THRESH_BINARY);
cv::imshow("imageBinary", imageBinary);
int rows = imageBinary.rows, cols = imageBinary.cols;
uchar* pDataOne, *pDataTwo;
float disPara = 0, fDisMin = 0;
//第一遍遍历图像,用左模板更新像素值
for (int i = 1; i < rows - 1; i++)
{
//图像指针获取
pDataOne = imageBinary.ptr(i);
for (int j = 1; j < cols; j++)
{
//分别计算其左掩码的相关距离
//maskL
// pL pL
// pL p
// pL
pDataTwo = imageBinary.ptr(i - 1);
disPara = calcEuclideanDistance(i, j, i - 1, j - 1);
fDisMin = cv::min((float)pDataOne[j], disPara + pDataTwo[j - 1]);
disPara = calcEuclideanDistance(i, j, i - 1, j);
fDisMin = cv::min(fDisMin, disPara + pDataTwo[j]);
pDataTwo = imageBinary.ptr(i);
disPara = calcEuclideanDistance(i, j, i, j - 1);
fDisMin = cv::min(fDisMin, disPara + pDataTwo[j - 1]);
pDataTwo = imageBinary.ptr(i + 1);
disPara = calcEuclideanDistance(i, j, i + 1, j - 1);
fDisMin = cv::min(fDisMin, disPara + pDataTwo[j - 1]);
pDataOne[j] = (uchar)cvRound(fDisMin);
}
}
//第二遍遍历图像,用右模板更新图像值
for (int i = rows - 2; i > 0; i--)
{
//图像指针获取
pDataOne = imageBinary.ptr(i);
for (int j = cols - 2; j >= 0; j--)
{
//分别计算其右掩码的相关距离
//maskR
// pR
// p pR
// pR pR
pDataTwo = imageBinary.ptr(i + 1);
disPara = calcEuclideanDistance(i, j, i + 1, j);
fDisMin = cv::min((float)pDataOne[j], disPara + pDataTwo[j]);
disPara = calcEuclideanDistance(i, j, i + 1, j + 1);
fDisMin = cv::min(fDisMin, disPara + pDataTwo[j + 1]);
pDataTwo = imageBinary.ptr(i);
disPara = calcEuclideanDistance(i, j, i, j + 1);
fDisMin = cv::min(fDisMin, disPara + pDataTwo[j + 1]);
pDataTwo = imageBinary.ptr(i - 1);
disPara = calcEuclideanDistance(i, j, i - 1, j + 1);
fDisMin = cv::min(fDisMin, disPara + pDataTwo[j + 1]);
pDataOne[j] = (uchar)cvRound(fDisMin);
}
}
result = imageBinary.clone();
}
运行结果:
OpenCV距离变换函数:
void distanceTransform(InputArray src, OutputArray dst, int distanceType, int maskSize, int dstType=CV_32F )
src:输入矩阵
代码实现:
int main()
{
cv::Mat image = cv::imread("1.jpg");
if (image.empty()) return -1;
cv::imshow("image", image);
//转换为灰度图
cv::Mat imageGray, imageBinary, result1;
cv::cvtColor(image, imageGray, CV_RGB2GRAY);
//转换为二值图
cv::threshold(imageGray, imageBinary, 160, 255, cv::THRESH_BINARY);
//距离变换
cv::distanceTransform(imageBinary, result1, CV_DIST_L2, CV_DIST_MASK_PRECISE);
//归一化矩阵
cv::normalize(result1, result1, 0, 1, cv::NORM_MINMAX);
cv::imshow("imageBinary1", imageBinary);
cv::imshow("result1", result1);
cv::waitKey(0);
return 0;
}
运行结果: