OpenCV 距离变换的笔记

目前正在学习<图像处理,分析与机器视觉>里面有提到距离变换计算,以此笔记记录生活。
距离变换的定义 :计算图像中像素点到最近零像素点的距离,也就是零像素点的最短距离。

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;  
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章