[Image Processing] - Distance Transform Algorithm

foreword

Through this article you can learn

  • What is the distance of the image?
  • What is Distance Transform
  • Calculation of distance transformation
  • Implementation of distance transformation in OpenCV

What is the distance of the image?

Distance is a measure that describes the distance between two pixels in an image. Common distance measures include Euchildean distance, City block distance, and Chessboard distance .

  • Euclidean distance

The definition of Euclidean distance comes from classical geometry, which is consistent with the distance between two points of simple geometry we learn in mathematics, and is the square root of the coordinates of two pixels . The advantage of Euclidean distance is that its definition is very intuitive and obvious, but the disadvantage is that the calculation of the square root is very time-consuming.

De = sqrt(((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)));
  • city ​​block distance

The city block distance is also called the D4 distance . This distance describes the horizontal and vertical movement distance only allowed in the pixel coordinate system plane. 4 means that under this definition, the pixel points are adjacent to 4, that is, each point is only connected to its The distance between the upper, lower, left and right adjacent 4 points is 1. which is defined as

D4 = abs(x1 - x2) + abs(y1 - y2);
  • chessboard distance

If you allow the movement of the diagonal direction of the pixel in the image coordinate system , you can get the chessboard distance, also known as the D8 distance , 8 means that under this definition, the pixel is 8 adjacent, that is, each point is only connected to it The distance between the 8 adjacent points in the upper, lower, left, right, and four diagonal directions of is 1.

D8 = max(abs(x1 - x2), (y1 - y2));

Figure 1 shows the relationship between the three distances

insert image description here

​ Figure 1 Schematic diagram of three distances

What is distance transform?

The distance transform is also called the distance function or the skew algorithm. It is an application of the concept of distance, and some algorithms of image processing are based on distance transformation. The distance transformation describes the distance between a pixel point in an image and a certain area block. The value of a pixel point in an area block is 0, the value of a pixel point in an adjacent area block is small, and the farther away it is, the larger the value is.

Figure 2 is an example of a simple distance transformation, using the checkerboard distance
insert image description here

​ Figure 2 Example of chessboard distance transformation

Distance Transform Algorithm Steps

intuitive computing

After determining the distance formula, how should it be calculated based on intuitive understanding? Still take the above example for illustration, B represents a collection of black backgrounds, which contain b 1 , b 2 , b 3 ... and other points; W represents a collection of white objects, which contain w 1 , w 2 , w 3 ...wait a while. Now we want to calculate the distance from each point in the W set to the B set, then we take a w i in W each time, and then calculate the distance from w i to each point b j in the B set according to the distance formula D ij , take the value with the smallest distance D ij as the final measurement result, until every point in the W set is calculated once. In fact, before the step of calculating the distance, it is necessary to traverse the image to divide the pixel points into two sets. This intuitive calculation is relatively complicated and takes a long time . Therefore, the predecessors invented a fast calculation distance transformation Methods.

quick calculation

For the Euclidean distance transform algorithm, relevant scholars have studied the faster chamfering distance transform algorithm to approximate the effect of the Euclidean distance transform. The specific process is as follows:

  • Use the forward template as shown in the left template in Figure 3 3*3to scan the image from top to bottom and from left to right. If the pixel value corresponding to the 0 point in the center of the template is 0, skip it, and if it is 1, calculate each element in the template. The sum of the corresponding pixel values ​​are Sum1, Sum2, Sum3, Sum4, Sum5 respectively, and the central pixel value is the minimum value among these five sum values.
  • Use the backward template as shown in the right 3*3template in Fig.3 to scan the image from bottom to top and from right to left.
  • Generally, the chamfering distance transformation templates we use are 3 * 3and 5 * 5, respectively, as shown in the following figure:

insert image description here

​ Figure 3 Chamfer distance transformation template

example

AL AL
AL P          mask1
AL
   BR
P  BR         mask2
BR BR
  • Binarize the image, the sub-image value is 0, and the background is 255;
  • Use Mask 1 to scan from left to right and top to bottom, point p is the current pixel point, point q is the point in the AL neighborhood in Mask 1, and D is the distance calculation, including chessboard distance, city distance and Euclidean distance. Fp is the pixel value of p point, calculate
F(p) = min( F(p),  F(q)+D(p,q) ), 其中,q属于AL.
  • Then use Mask 2 to scan from right to left, from bottom to top, and calculate
F(p) = min( F(p),  F(q)+D(p,q) ), 其中,q属于BR.

Code implementation of distance transformation algorithm

void imageprocess::DistanceTransform(const cv::Mat& src_image, cv::Mat& dst_image)
{
    
    
    //step1: check the input parameters: 检查输入参数
    assert(!src_image.empty());
    assert(src_image.channels() == 1);

    //step2: initialize dst_image : 初始化目标图像
    cv::threshold(src_image, dst_image, 100, 255, cv::THRESH_BINARY);


    //step3: pass throuth from top to bottom, left to right: 从上到下,从做到右遍历
    for (size_t i = 1; i < dst_image.rows - 1; i++)
    {
    
    
        for (size_t j = 1; j < dst_image.cols; j++)
        {
    
    
            //AL  AL    
            //AL  P
            //AL
            std::array<cv::Point, 4> AL;
            AL.at(0) =  cv::Point( i - 1, j - 1 );
            AL.at(1) =  cv::Point( i - 1, j );
            AL.at(2) =  cv::Point( i, j - 1 );
            AL.at(3) =  cv::Point(i + 1, j - 1 );

            int Fp = dst_image.at<uchar>(i, j);

            //Fq
            std::array<int, 4> Fq = {
    
     0 };
            Fq.at(0) = dst_image.at<uchar>(i - 1, j - 1);
            Fq.at(1) = dst_image.at<uchar>(i - 1, j);
            Fq.at(2) = dst_image.at<uchar>(i, j - 1);
            Fq.at(3) = dst_image.at<uchar>(i + 1, j - 1);

            std::array<int, 4> Dpq = {
    
     0 };
            std::array<int, 4> DpqAddFq = {
    
     0 };
        
            for (size_t k = 0; k < 4; k++)
            {
    
    
                //D(p, q)
                Dpq.at(k) = D4(i, AL.at(k).x, j, AL.at(k).y);
                //D(p,q) + F(q)
                DpqAddFq.at(k) = Dpq.at(k) + Fq.at(k);
            }
            //F(p) = min[F(p), D(p,q) + F(q)]
            std::sort(DpqAddFq.begin(), DpqAddFq.end());
            
            auto min = DpqAddFq.at(0);
            Fp = std::min(Fp, min);

            dst_image.at<uchar>(i, j) = Fp;

        }
    }

    
    //step4: pass throuth from bottom to top, right to left: 从下到上,从右到左遍历

    for (int i = dst_image.rows - 2; i > 0; i--)
    {
    
    
        for (int j = dst_image.cols -  2; j >= 0 ; j--)
        {
    
    
            //        BR
            //  P   BR
            //  BR  BR
            std::array<cv::Point, 4> BR;
            BR.at(0) = cv::Point( i - 1, j + 1 );
            BR.at(1) = cv::Point( i , j + 1 );
            BR.at(2) = cv::Point( i + 1, j + 1 );
            BR.at(3) = cv::Point( i + 1, j );

            int Fp = dst_image.at<uchar>(i, j);

            //Fq
            std::array<int, 4> Fq = {
    
     0 };
            Fq.at(0) = dst_image.at<uchar>(i - 1, j + 1);
            Fq.at(1) = dst_image.at<uchar>(i, j + 1);
            Fq.at(2) = dst_image.at<uchar>(i + 1, j + 1);
            Fq.at(3) = dst_image.at<uchar>(i + 1, j);

            std::array<int, 4> Dpq = {
    
     0 };
            std::array<int, 4> DpqAddFq = {
    
     0 };

            for (size_t k = 0; k < 4; k++)
            {
    
    
                //D(p, q)
                Dpq.at(k) = D4(i, BR.at(k).x, j, BR.at(k).y);
                //D(p,q) + F(q)
                DpqAddFq.at(k) = Dpq.at(k) + Fq.at(k);
            }

            //F(p) = min[F(p), D(p,q) + F(q)]
            std::sort(DpqAddFq.begin(), DpqAddFq.end());

            auto min = DpqAddFq.at(0);
            Fp = std::min(Fp, min);

            dst_image.at<uchar>(i, j) = static_cast<uchar>(Fp);

        }
    }
    
}

int imageprocess::D4(const int& x1, const int& x2, const int& y1, const int& y2)
{
    
    
    return abs(x1 - x2) + abs(y1 - y2);
}

int imageprocess::D8(const int& x1, const int& x2, const int& y1, const int& y2)
{
    
    
    return cv::max(abs(x1 - x2), (y1 - y2));
}

OpenCV distanceTransform function

  • function signature
/** @overload
@param src 8-bit, single-channel (binary) source image.
@param dst Output image with calculated distances. It is a 8-bit or 32-bit floating-point,
single-channel image of the same size as src .
@param distanceType Type of distance, see #DistanceTypes
@param maskSize Size of the distance transform mask, see #DistanceTransformMasks. In case of the
#DIST_L1 or #DIST_C distance type, the parameter is forced to 3 because a \f$3\times 3\f$ mask gives
the same result as \f$5\times 5\f$ or any larger aperture.
@param dstType Type of output image. It can be CV_8U or CV_32F. Type CV_8U can be used only for
the first variant of the function and distanceType == #DIST_L1.
*/
CV_EXPORTS_W void distanceTransform( InputArray src, OutputArray dst,
                                     int distanceType, int maskSize, int dstType=CV_32F);

Detailed parameter explanation:

  • InputArray src: input image, generally a binary image ;
  • OutputArray dst: output image, distance transformation result, grayscale image ;
  • int distanceType: distance type used for distance transformation (Euclidean distance: DIST_L2 = 2; D 4 D_4D4Distance: DIST_L1 = 1; D 8 D_8D8distance: DIST_C = 3 etc);
  • int mask_size: the size of the distance transform mask, generally 3 or 5;
  • int dstType: The data type of the output image, which can be CV_8U or CV_32F.

test code

#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main()
{
    
    
    //初始化输入图像和变换结果图像
    Mat mat(480, 480, CV_8UC1, Scalar(0)), transMatE, transMatD4, transMatD8;

    //给输入图像指定三个像素点作为距离变换原点(区域块)
    mat.at<uchar>(100, 200) = 1;
    mat.at<uchar>(200, 100) = 1;
    mat.at<uchar>(300, 300) = 1;

    //将将输入图像中1和0调换,使得原点距离为0
    mat = 1 - mat;

    //显示原始图像(显示为黑色)
    imshow("原始图片", mat);

    //分别利用欧式距离、D4距离和D8距离作距离变换,将结果存入transMatD4、transMatD8和transMatE
    distanceTransform(mat, transMatE, DIST_L2, 0);
    distanceTransform(mat, transMatD4, DIST_L1, 0, CV_8U);
    distanceTransform(mat, transMatD8, DIST_C, 0);

    //欧式距离与D8距离作变换后,值为32位浮点数,以下代码将其值转为uchar类型
    transMatE.convertTo(transMatE, CV_8U);
    transMatD8.convertTo(transMatD8, CV_8U);

    //显示距离变换结果
    imshow("欧式距离变换后的图片", transMatE);
    imshow("D4距离变换后的图片", transMatD4);
    imshow("D8距离变换后的图片", transMatD8);


    waitKey();

    return 0;
}

operation result

insert image description here

References

  • https://mangoroom.cn/opencv/distance-transfer.html
  • https://blog.csdn.net/qq_35535744/article/details/95247601
  • https://zhuanlan.zhihu.com/p/38917770
  • https://medium.com/on-coding/euclidean-distance-transform-d37e06958216
  • https://blog.csdn.net/Trent1985/article/details/18081761

Guess you like

Origin blog.csdn.net/m0_46376148/article/details/128765671
Recommended