OpenCV:distanceTransform距离变换函数

2025-10-14 08:10:45

1、OpenCV函数distanceTransform():

功能:用来计算原图像中距离变换图像;

void distanceTransform( InputArray src,  

                    OutputArray dst,

OutputArray labels,

int distanceType,

int maskSize,

int labelType=DIST_LABEL_CCOMP ); 

函数说明:

用于计算图像中每一个非零点像素与其最近的零点像素之间的距离,输出的是保存每一个非零点与最近零点的距离信息;图像上越亮的点,代表了离零点的距离越远。

参数:

src是单通道的8bit的二值图像(只有0或1)

dst表示的是计算距离的输出图像,可以使单通道32bit浮点数据

distanceType表示的是选取距离的类型,可以设置为CV_DIST_L1,CV_DIST_L2,CV_DIST_C等,具体如下:

  DIST_L1       = 1,   //!< distance = |x1-x2| + |y1-y2| 

  DIST_L2       = 2,   //!< the simple euclidean distance 

  DIST_C        = 3,   //!< distance = max(|x1-x2|,|y1-y2|) 

  DIST_L12      = 4,   //!< L1-L2 metric: distance =2(sqrt(1+x*x/2) - 1)) 

  DIST_FAIR     = 5,   //!< distance = c^2(|x|/c-log(1+|x|/c)),c = 1.3998 

  DIST_WELSCH = 6,  //!< distance = c^2/2(1-exp(-(x/c)^2)), c= 2.9846 

  DIST_HUBER  = 7   //!< distance = |x|<c ? x^2/2 :c(|x|-c/2), c=1.345 

maskSize表示的是距离变换的掩膜模板,可以设置为3,5或CV_DIST_MASK_PRECISE,对 CV_DIST_L1 或CV_DIST_C 的情况,参数值被强制设定为 3, 因为3×3 mask 给出5×5 mask 一样的结果,而且速度还更快。

labels表示可选输出2维数组;

labelType表示的是输出二维数组的类型;

OpenCV:distanceTransform距离变换函数

2、细化轮廓:

代码如下:

#include <opencv2\opencv.hpp>

#include <opencv2\highgui\highgui.hpp>

#include <opencv2\features2d\features2d.hpp>

#include <opencv2\core\core.hpp>

using namespace std; 

using namespace cv; 

int main() 

       Mat srcImg=imread("raw.jpg",1); 

       Mat imageGray; 

       cvtColor(srcImg,imageGray,CV_RGB2GRAY); 

       imageGray=~imageGray;  //对灰度图取反 

       GaussianBlur(imageGray,imageGray,Size(5,5),2); //滤波 

       threshold(imageGray,imageGray,20,200,CV_THRESH_BINARY); //阈值 

       imshow("threshold",imageGray); 

      

       Mat distanceImg(imageGray.size(),CV_32FC1); //距离变换结果的Mat矩阵 

       distanceTransform(imageGray,distanceImg,CV_DIST_L2,3); //距离变换 

       Mat dist;

       normalize(distanceImg,dist, 0, 1, NORM_MINMAX); 

       imshow("distanceImg",dist);

       Mat distShowImg; 

       distShowImg=Mat::zeros(imageGray.size(),CV_8UC1); //定义细化后的字符轮廓 

       float maxValue=0;//距离变换矩阵中的最大值 

       for(int i=0;i<distanceImg.rows;i++) 

       { 

              for(int j=0;j<distanceImg.cols;j++) 

              { 

                     if(distanceImg.at<float>(i,j) > maxValue) 

                     { 

                            maxValue=distanceImg.at<float>(i,j);  //获取距离变换的极大值 

                     } 

              } 

       } 

       for(int i=0;i<distanceImg.rows;i++) 

       { 

              for(int j=0;j<distanceImg.cols;j++) 

              { 

                     if(distanceImg.at<float>(i,j) > maxValue/1.9) 

                     { 

                            distShowImg.at<unsigned char>(i,j)=255;   //符合距离大于最大值一定比例条件的点设为255 

                     } 

              } 

       } 

       imshow("Source Image",srcImg); 

       imshow("thinImg",distShowImg); 

       waitKey(0); 

       return 0; 

}  

OpenCV:distanceTransform距离变换函数

OpenCV:distanceTransform距离变换函数

3、查找物体质心:

int main() 

       Mat srcImg=imread("raw1.jpg",1); 

       imshow("原图",srcImg);

       Mat imgGray; 

       cvtColor(srcImg,imgGray,CV_RGB2GRAY); 

       imgGray=~imgGray;

       GaussianBlur(imgGray,imgGray,Size(5,5),2); //滤波 

       threshold(imgGray,imgGray,20,200,CV_THRESH_BINARY); //阈值化    

      

       Mat distanceImg(imgGray.size(),CV_32FC1); //距离变换结果的Mat矩阵 

       distanceTransform(imgGray,distanceImg,CV_DIST_L2,3);  //距离变换 

       Mat dist;

       normalize(distanceImg,dist, 0, 1, NORM_MINMAX); 

       imshow("distanceImg",dist);

       Mat distShow; 

       distShow=Mat::zeros(imgGray.size(),CV_8UC1); //细化后的字符轮廓 

       float maxValue=0;  //距离变换矩阵中的最大值 

       Point Pt(0,0); 

       for(int i=0;i<distanceImg.rows;i++) 

       { 

              for(int j=0;j<distanceImg.cols;j++) 

              { 

                     distShow.at<unsigned char>(i,j)=distanceImg.at<float>(i,j); 

                     if(distanceImg.at<float>(i,j) > maxValue) 

                     { 

                            maxValue=distanceImg.at<float>(i,j);  //获取距离变换的极大值 

                            Pt=Point(j,i);//坐标 

                     } 

              } 

       } 

       circle(srcImg,Pt,maxValue,Scalar(0,0,255),3);     

       circle(srcImg,Pt,3,Scalar(0,255,0),3); 

       imshow("SImage",srcImg); 

       imshow("Thin Image",distShow); 

       waitKey(); 

       return 0; 

}  

OpenCV:distanceTransform距离变换函数

4、编程思想:

图像距离变换的一般步骤如下:

①将输入图片转换为二值图像,前景设置为1,背景设置为0;

②先遍历图像:左,左上,上,左下

使用下面的公式进行计算。

其中,D表示距离包括欧式距离,棋盘距离和麦哈顿距离;

掩膜模板mask为maskL;

f(p)为像素点p的像素值;

③再次遍历图像,右,右上,右下,下;

④根据模板maskL和maskR的扫描结果得到最终的距离变换图像。

为了减少计算了量,采用了一种倒角模版的算法,只需要对图像进行两次扫描玖可以实现距离变换,该方法被称为chamfer倒角距离变换,该模版如下:

OpenCV:distanceTransform距离变换函数

OpenCV:distanceTransform距离变换函数

5、实现:

#include <opencv2\opencv.hpp>

#include <opencv2\highgui\highgui.hpp>

#include <opencv2\features2d\features2d.hpp>

#include <opencv2\core\core.hpp>

using namespace std; 

using namespace cv; 

//计算欧氏距离的函数 

float calEuclideanDiatance(int x1, int y1, int x2, int y2) 

       return sqrt(float((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1))); 

//计算棋盘距离的函数 

int calChessboardDistance(int x1, int y1, int x2, int y2) 

       return max(abs(x1 - x2), (y1 - y2)); 

//计算麦哈顿距离(街区距离) 

int calBlockDistance(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);     

       Mat grayImage, binaryImage; 

       cvtColor(srcImage, grayImage, CV_BGR2GRAY); 

       grayImage = ~grayImage;

       threshold(grayImage, binaryImage, 20, 200, THRESH_BINARY); 

       imshow("二值化图像", binaryImage); 

       int rows = binaryImage.rows; 

       int cols = binaryImage.cols; 

       unsigned char *pDataOne; 

       unsigned char *pDataTwo; 

       float disPara = 0; 

       float fDisMIn = 0; 

       //第一遍:遍历图像,使用左模板更新像素值 

       for (int i = 1; i < rows - 1; i++) 

       { 

              //图像的行指针的获取 

              pDataOne = binaryImageNaNr<uchar>(i); 

              for (int j = 1; j < cols; j++) 

              { 

                     //分别计算左模板掩码的相关距离 

                     //PL  PL 

                     //PL  P 

                     //PL 

                     pDataTwo = binaryImageNaNr<uchar>(i - 1);//上一行 

                     disPara = calEuclideanDiatance(i, j, i - 1, j - 1);//当前像素 左上角像素 

                     fDisMIn = min((float)pDataOne[j], disPara + pDataTwo[j - 1]);//模板 

                     disPara = calEuclideanDiatance(i, j, i - 1, j);//当前像素 上方像素 

                     fDisMIn = min(fDisMIn, disPara + pDataTwo[j]); 

                     pDataTwo = binaryImageNaNr<uchar>(i);// 

                     disPara = calEuclideanDiatance(i, j, i, j - 1);//当前像素 左方像素 

                     fDisMIn = min(fDisMIn, disPara + pDataTwo[j-1]); 

                    

                     pDataTwo = binaryImageNaNr<uchar>(i+1); 

                     disPara = calEuclideanDiatance(i, j, i+1,j-1);//当前像素 左下方像素 

                     fDisMIn = min(fDisMIn, disPara + pDataTwo[j - 1]); 

                     pDataOne[j] = (unsigned char)cvRound(fDisMIn); 

              } 

       } 

       //第二遍:遍历图像,使用右模板更新像素值 

       for (int i = rows - 2; i > 0; i--)//hang 

       { 

              pDataOne = binaryImageNaNr<uchar>(i); 

              for (int j = cols - 1; j >= 0; j--) //lie

              { 

                     //分别计算右模板掩码的相关距离 

                     //pR  pR 

                     //pR  p 

                     //pR 

                     pDataTwo = binaryImageNaNr<uchar>(i + 1);//下一行 

                     disPara = calEuclideanDiatance(i, j, i + 1, j); //下方

                     fDisMIn = min((float)pDataOne[j], disPara + pDataTwo[j]); 

                    

                     disPara = calEuclideanDiatance(i, j, i + 1, j + 1);//右下方 

                     fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j+1]);

                     pDataTwo = binaryImageNaNr<uchar>(i); 

                     disPara = calEuclideanDiatance(i, j, i, j+1);//右方 

                     fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j + 1]); 

                    

                     pDataTwo = binaryImageNaNr<uchar>(i - 1); 

                     disPara = calEuclideanDiatance(i, j, i - 1, j + 1);  //右上方

                     fDisMIn = min(fDisMIn, disPara + pDataTwo[j + 1]); 

                    

                     pDataOne[j] = (unsigned char)cvRound(fDisMIn); 

              } 

       } 

       dstImage = binaryImage.clone(); 

////距离变换的扫描实现

int main() 

       Mat srcImage = imread("raw1.jpg"); 

       imshow("srcImg",srcImage);

       if (!srcImage.data) 

       { 

              cout << "读入图片错误!" << endl; 

              system("pause"); 

              return -1; 

       } 

       Mat dstImage; 

       distanceTrans(srcImage, dstImage); 

       imshow("距离变换图像", dstImage); 

       waitKey(); 

       return 0; 

OpenCV:distanceTransform距离变换函数

声明:本网站引用、摘录或转载内容仅供网站访问者交流或参考,不代表本站立场,如存在版权或非法内容,请联系站长删除,联系邮箱:site.kefu@qq.com。
猜你喜欢