图像的距离变换实现了像素与图像区域的距离变换,使得最后生成的图像在该自己元素位置处的像素为0,临近的背景的像素具有较小的值,且随着距离的增大它的的数值也就越大。
对于距离图像来说,图像中的每个像素的灰度值为该像素与距离其最近的背景像素间的距离,也就是说,给每个像素赋值为离它最近的背景像素点与其距离,一幅二值图像的距离变换可以提供每个像素到最近的非零像素的距离
根据度量距离的方法不同,距离变换有几种不同的方法,假设像素点为p1(x1, y1),p2(x2, y2),计算距离的方法常见的有:
- 1.欧几里德距离
- 2.曼哈顿距离(City Block Distance)
Distance = |x2-x1|+|y2-y1| - 3.象棋格距离(Chessboard Distance)
Distance = max(|x2-x1|,|y2-y1|)
而这个方法在官方网站上有可以直接使用的方法,于是我就用官网的方法来试了一下,结果如下:
这里要注意需要转为灰度图才可以进行下面的操作。
上面的滑动条调节的是图片亮度,随着图片亮度增大,打火机的轮廓变得模糊,而原来没有出现的白色圆盒的轮廓开始出现。
图像距离算法还可以应用于目标细化,骨架提取,粘连物体的分离等,并不仅仅是表示出图像中目标与背景的距离。
代码自取:
// CVE6.2.cpp: 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdio.h>
using namespace cv;
int maskSize0 = CV_DIST_MASK_5;
int voronoiType = -1;
int edgeThresh = 100;
int distType0 = CV_DIST_L1;
// The output and temporary images
Mat gray;
// threshold trackbar callback
static void onTrackbar(int, void*)
{
static const Scalar colors[] =
{
Scalar(0,0,0),
Scalar(255,0,0),
Scalar(255,128,0),
Scalar(255,255,0),
Scalar(0,255,0),
Scalar(0,128,255),
Scalar(0,255,255),
Scalar(0,0,255),
Scalar(255,0,255)
};
int maskSize = voronoiType >= 0 ? CV_DIST_MASK_5 : maskSize0;
int distType = voronoiType >= 0 ? CV_DIST_L2 : distType0;
Mat edge = gray >= edgeThresh, dist, labels, dist8u;
if (voronoiType < 0)
distanceTransform(edge, dist, distType, maskSize);
else
distanceTransform(edge, dist, labels, distType, maskSize, voronoiType);
if (voronoiType < 0)
{
// begin "painting" the distance transform result
dist *= 5000;
pow(dist, 0.5, dist);
Mat dist32s, dist8u1, dist8u2;
dist.convertTo(dist32s, CV_32S, 1, 0.5);
dist32s &= Scalar::all(255);
dist32s.convertTo(dist8u1, CV_8U, 1, 0);
dist32s *= -1;
dist32s += Scalar::all(255);
dist32s.convertTo(dist8u2, CV_8U);
Mat planes[] = { dist8u1, dist8u2, dist8u2 };
merge(planes, 3, dist8u);
}
else
{
dist8u.create(labels.size(), CV_8UC3);
for (int i = 0; i < labels.rows; i++)
{
const int* ll = (const int*)labels.ptr(i);
const float* dd = (const float*)dist.ptr(i);
uchar* d = (uchar*)dist8u.ptr(i);
for (int j = 0; j < labels.cols; j++)
{
int idx = ll[j] == 0 || dd[j] == 0 ? 0 : (ll[j] - 1) % 8 + 1;
float scale = 1.f / (1 + dd[j] * dd[j] * 0.0004f);
int b = cvRound(colors[idx][0] * scale);
int g = cvRound(colors[idx][1] * scale);
int r = cvRound(colors[idx][2] * scale);
d[j * 3] = (uchar)b;
d[j * 3 + 1] = (uchar)g;
d[j * 3 + 2] = (uchar)r;
}
}
}
imshow("Distance Map", dist8u);
}
static void help()
{
printf("\nProgram to demonstrate the use of the distance transform function between edge images.\n"
"Usage:\n"
"./distrans [image_name -- default image is stuff.jpg]\n"
"\nHot keys: \n"
"\tESC - quit the program\n"
"\tC - use C/Inf metric\n"
"\tL1 - use L1 metric\n"
"\tL2 - use L2 metric\n"
"\t3 - use 3x3 mask\n"
"\t5 - use 5x5 mask\n"
"\t0 - use precise distance transform\n"
"\tv - switch to Voronoi diagram mode\n"
"\tp - switch to pixel-based Voronoi diagram mode\n"
"\tSPACE - loop through all the modes\n\n");
}
const char* keys =
{
"{1| |stuff.jpg|input image file}"
};
int main(int argc, const char** argv)
{
help();
CommandLineParser parser(argc, argv, keys);
//string filename = parser.get<string>("1");
//gray = imread("stuff.jpg", 0);
cv::Mat img = imread("E:/C++/CVE6.2/图片1.png");
imshow("原图", img);
gray = imread("E:/C++/CVE6.2/图片1.png", 0);
imshow("灰度图", gray);
resize(gray, gray, Size(), 0.25, 0.25, 1);
//if (gray.empty())
//{
// printf("Cannot read image file: %s\n", filename.c_str());
// help();
// return -1;
//}
namedWindow("Distance Map", CV_WINDOW_NORMAL);
createTrackbar("Brightness Threshold", "Distance Map", &edgeThresh, 255, onTrackbar, 0);
for (;;)
{
// Call to update the view
onTrackbar(0, 0);
int c = waitKey(0) & 255;
if (c == 27)
break;
if (c == 'c' || c == 'C' || c == '1' || c == '2' ||
c == '3' || c == '5' || c == '0')
voronoiType = -1;
if (c == 'c' || c == 'C')
distType0 = CV_DIST_C;
else if (c == '1')
distType0 = CV_DIST_L1;
else if (c == '2')
distType0 = CV_DIST_L2;
else if (c == '3')
maskSize0 = CV_DIST_MASK_3;
else if (c == '5')
maskSize0 = CV_DIST_MASK_5;
else if (c == '0')
maskSize0 = CV_DIST_MASK_PRECISE;
else if (c == 'v')
voronoiType = 0;
else if (c == 'p')
voronoiType = 1;
else if (c == ' ')
{
if (voronoiType == 0)
voronoiType = 1;
else if (voronoiType == 1)
{
voronoiType = -1;
maskSize0 = CV_DIST_MASK_3;
distType0 = CV_DIST_C;
}
else if (distType0 == CV_DIST_C)
distType0 = CV_DIST_L1;
else if (distType0 == CV_DIST_L1)
distType0 = CV_DIST_L2;
else if (maskSize0 == CV_DIST_MASK_3)
maskSize0 = CV_DIST_MASK_5;
else if (maskSize0 == CV_DIST_MASK_5)
maskSize0 = CV_DIST_MASK_PRECISE;
else if (maskSize0 == CV_DIST_MASK_PRECISE)
voronoiType = 0;
}
}
return 0;
}