1. 程式人生 > >OpenCV 距離變換的筆記

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;  
}