目前正在学习<图像处理,分析与机器视觉>里面有提到距离变换计算,以此笔记记录生活。
距离变换的定义 :计算图像中像素点到最近零像素点的距离,也就是零像素点的最短距离。
Mat srcImage = imread("/Users/hanoi/Desktop/hand.jpg",0);
Mat dist_image(size,CV_32FC1);
distanceTransform(srcImage, dist_image, CV_DIST_L2, CV_DIST_MASK_3);
int main(int argc, const char * argv[])
{
Mat srcImage = imread("/Users/hanoi/Desktop/hand.jpg",0);
printf("step=%ld\n",srcImage.step[0]);
if (!srcImage.data)
{
cout << "读入图片错误!" << endl;
system("pause");
return -1;
}
Size size;
size.width = srcImage.cols;
size.height = srcImage.rows;
Mat dist_image(size,CV_32FC1);
Mat bi_src(size,CV_8SC1);
distanceTransform(srcImage, dist_image, CV_DIST_L2, CV_DIST_MASK_3);
normalize(dist_image, dist_image, 0, 1, NORM_MINMAX);
imshow("src",dist_image);
//imwrite("/Users/hanoi/Desktop/gray1.jpg", dist_image );
Mat myImage(size,CV_8UC1);
for (int j=0; j<dist_image.rows; j++)
{
for (int i=0; i<dist_image.cols; i++)
{
double m = dist_image.at<float>(j,i );
int y = m * 255;
if(y > 255)
{
y = 255;
}
*(myImage.data + j*myImage.step[0] + i) = y;
}
}
imwrite("/Users/hanoi/Desktop/gray1.bmp", myImage);
waitKey();
return 0;
}
我们的type是CV_32FC1,所以每个像素是4个字节,为了能保存图像,在进行归一化处理之后,需要用距离*255,然后作为该像素点的亮度值。
distanceTransform是opencv的提供的计算距离转换的函数,个人觉得要主要是dest的输出,存的是距离的矩阵,所以这里我们创建的时候type是CV_32FC1。
其中距离的计算方法:
为了减少计算了量,我们采用的是一种倒角模版的算法,只需要对图像进行两次扫描玖可以实现距离变换,该方法被称为chamfer倒角距离变换,该模版如下:
网络代码参考:
//距离变换的扫描实现
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;
using namespace std;
//计算欧氏距离的函数
float calcEuclideanDiatance(int x1, int y1, int x2, int y2)
{
return sqrt(float((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)));
}
//计算棋盘距离的函数
int calcChessboardDistance(int x1, int y1, int x2, int y2)
{
return cv::max(abs(x1 - x2), (y1 - y2));
}
//计算麦哈顿距离(街区距离)
int calcBlockDistance(int x1, int y1, int x2, int y2)
{
return abs(x1 - x2) + abs(y1 - y2);
}
//距离变换函数的实现
void distanceTrans(Mat &srcImage, Mat &dstImage)
{
CV_Assert(srcImage.data != nullptr);
//CV_Assert()若括号中的表达式值为false,则返回一个错误信息。
//定义灰度图像的二值图像
Mat grayImage, binaryImage;
//将原图像转换为灰度图像
cvtColor(srcImage, grayImage, CV_BGR2GRAY);
//将灰度图像转换为二值图像
threshold(grayImage, binaryImage, 100, 255, THRESH_BINARY);
imshow("二值化图像", binaryImage);
int rows = binaryImage.rows;
int cols = binaryImage.cols;
uchar *pDataOne;
uchar *pDataTwo;
float disPara = 0;
float fDisMIn = 0;
//第一遍遍历图像,使用左模板更新像素值
for (int i = 1; i < rows - 1; i++)
{
//图像的行指针的获取
pDataOne = binaryImage.ptr<uchar>(i);
for (int j = 1; j < cols; j++)
{
//分别计算左模板掩码的相关距离
//PL PL
//PL P
//PL
pDataTwo = binaryImage.ptr<uchar>(i - 1);
disPara = calcEuclideanDiatance(i, j, i - 1, j - 1);
fDisMIn = cv::min((float)pDataOne[j], disPara + pDataTwo[j - 1]);
disPara = calcEuclideanDiatance(i, j, i - 1, j);
fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j]);
pDataTwo = binaryImage.ptr<uchar>(i);
disPara = calcEuclideanDiatance(i, j, i, j - 1);
fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j-1]);
pDataTwo = binaryImage.ptr<uchar>(i+1);
disPara = calcEuclideanDiatance(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 = binaryImage.ptr<uchar>(i);
for (int j = cols - 1; j >= 0; j--)
{
//分别计算右模板掩码的相关距离
//pR pR
//pR p
//pR
pDataTwo = binaryImage.ptr<uchar>(i + 1);
disPara = calcEuclideanDiatance(i, j, i + 1, j);
fDisMIn = cv::min((float)pDataOne[j], disPara + pDataTwo[j]);
disPara = calcEuclideanDiatance(i, j, i + 1, j + 1);
fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j+1]);
pDataTwo = binaryImage.ptr<uchar>(i);
disPara = calcEuclideanDiatance(i, j, i, j +1);
fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j + 1]);
pDataTwo = binaryImage.ptr<uchar>(i - 1);
disPara = calcEuclideanDiatance(i, j, i - 1, j + 1);
fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j + 1]);
pDataOne[j] = (uchar)cvRound(fDisMIn);
}
}
dstImage = binaryImage.clone();
}
//主函数
int main()
{
Mat srcImage = imread("2345.jpg");
if (!srcImage.data)
{
cout << "读入图片错误!" << endl;
system("pause");
return -1;
}
Mat dstImage;
distanceTrans(srcImage, dstImage);
imshow("距离变换图像", dstImage);
waitKey();
return 0;
}