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