1. 程式人生 > >影象處理(六)——距離變換

影象處理(六)——距離變換

影象的距離變換實現了畫素與影象區域的距離變換,使得最後生成的影象在該自己元素位置處的畫素為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;
}