2019-06-28 10:37:13 zhouzongzong 阅读数 535

距离变换是二值图像处理与操作中常用手段,在骨架提取,图像窄化中常有应用。距离变换的结果是得到一张与输入图像类似的灰度图像,但是灰度值只出现在前景区域。并且越远离背景边缘的像素灰度值越大。

根据度量距离的方法不同,距离变换有几种不同的方法,假设像素点p1(x1, y1), p2(x2, y2)计算距离的方法常见的有:

  1. 欧几里德距离(常用的距离),是点和点之间坐标的均方根。通常情况下人们所说到的距离,指的就是欧式距离:
    Distance =在这里插入图片描述

  2. 曼哈顿距离(City Block Distance),又称街区距离,表示对点与点之间在不同维度上的绝对距离的叠加,实质上是从一个点到另外一个点的步数,并不能走斜线, 公式如下:Distance = |x2-x1|+|y2-y1|

  3. 象棋格距离(Chessboard Distance),被用来衡量向量空间中两个点之间的距离,它是曼哈顿距离的加权版本。实质上是凑成一个正方形的对角线, 公式如下:Distance = max(|x2-x1|,|y2-y1|)
    在这里插入图片描述

一旦距离度量公式选择,就可以在二值图像的距离变换中使用。一个最常见的距离变换算法就是通过连续的腐蚀操作来实现,腐蚀操作的停止条件是所有前景像素都被完全腐蚀这样根据腐蚀的先后顺序,我们就得到各个前景像素点到前景中心骨架像素点的距离。根据各个像素点的距离值,设置为不同的灰度值。这样就完成了二值图像的距离

2014-03-10 23:05:42 qianchenglenger 阅读数 17119

问题:一张输入图片,图片上有两条平行线,求出这两条平行线之间的距离

解决思路:

1. 对图像中的直线进行细化

2. 提取直线的轮廓坐标

3. 对轮廓上的坐标进行直线集合,从而得到直线方程

4. 计算两条直线之间的距离

参考:

问题来源 http://www.opencvchina.com/thread-854-1-1.html

图像细化 http://blog.csdn.net/qianchenglenger/article/details/19332011

图像轮廓提取 http://blog.csdn.net/augusdi/article/details/9000893

直线拟合 http://blog.csdn.net/zhuoyue08/article/details/6803040

两条直线之间的距离公式3:http://zhidao.baidu.com/link?url=ef_DHNkjyq1qq7VgubX3afL2KIUQIB4ukd3zHGp0zz8iPPKC046azyvG5ltHR-i0WaLI72eO7j0sOJI4wZSE4q

工具:

 opencv 2.4.8 + VS2013

代码:

1.头文件 ProcessImage.h

//ProcessImage.h
#pragma once
#include <opencv2/highgui/highgui.hpp>

/* 对输入图像进行细化
 * src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
 * dst为对src细化后的输出图像,格式与src格式相同,调用前需要分配空间,元素中只有0与1,1代表有元素,0代表为空白
 * maxIterations限制迭代次数,如果不进行限制,默认为-1,代表不限制迭代次数,直到获得最终结果
 */
void thinImage(IplImage* src, IplImage* dst, int maxIterations = -1);

2.代码实现 ProcessImage.cpp

//ProcessImage.cpp
#include "ProcessImage.h"
#include <utility>
#include <vector>
void thinImage(IplImage* src, IplImage* dst, int maxIterations)
{
	using namespace cv;
	CvSize size = cvGetSize(src);
	cvCopy(src, dst);//将src中的内容拷贝到dst中  
	int count = 0;  //记录迭代次数  
	while (true)
	{
		count++;
		if (maxIterations != -1 && count > maxIterations) //限制次数并且迭代次数到达  
			break;
		//std::cout << count << ' ';输出迭代次数  
		std::vector<std::pair<int, int> > mFlag; //用于标记需要删除的点  
		//对点标记  
		for (int i = 0; i<size.height; ++i)
		{
			for (int j = 0; j<size.width; ++j)
			{
				//如果满足四个条件,进行标记  
				//  p9 p2 p3  
				//  p8 p1 p4  
				//  p7 p6 p5  
				int p1 = CV_IMAGE_ELEM(dst, uchar, i, j);
				int p2 = (i == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j);
				int p3 = (i == 0 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j + 1);
				int p4 = (j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j + 1);
				int p5 = (i == size.height - 1 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j + 1);
				int p6 = (i == size.height - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j);
				int p7 = (i == size.height - 1 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j - 1);
				int p8 = (j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j - 1);
				int p9 = (i == 0 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j - 1);

				if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
				{
					int ap = 0;
					if (p2 == 0 && p3 == 1) ++ap;
					if (p3 == 0 && p4 == 1) ++ap;
					if (p4 == 0 && p5 == 1) ++ap;
					if (p5 == 0 && p6 == 1) ++ap;
					if (p6 == 0 && p7 == 1) ++ap;
					if (p7 == 0 && p8 == 1) ++ap;
					if (p8 == 0 && p9 == 1) ++ap;
					if (p9 == 0 && p2 == 1) ++ap;

					if (ap == 1)
					{
						if (p2*p4*p6 == 0)
						{
							if (p4*p6*p8 == 0)
							{
								//标记  
								mFlag.push_back(std::make_pair(i, j));
							}
						}
					}
				}
			}
		}

		//将标记的点删除  
		for (std::vector<std::pair<int, int> >::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
		{
			CV_IMAGE_ELEM(dst, uchar, i->first, i->second) = 0;
		}

		//直到没有点满足,算法结束  
		if (mFlag.size() == 0)
		{
			break;
		}
		else
		{
			mFlag.clear();//将mFlag清空  
		}

		//对点标记  
		for (int i = 0; i<size.height; ++i)
		{
			for (int j = 0; j<size.width; ++j)
			{
				//如果满足四个条件,进行标记  
				//  p9 p2 p3  
				//  p8 p1 p4  
				//  p7 p6 p5  
				int p1 = CV_IMAGE_ELEM(dst, uchar, i, j);
				if (p1 != 1) continue;
				int p2 = (i == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j);
				int p3 = (i == 0 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j + 1);
				int p4 = (j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j + 1);
				int p5 = (i == size.height - 1 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j + 1);
				int p6 = (i == size.height - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j);
				int p7 = (i == size.height - 1 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j - 1);
				int p8 = (j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j - 1);
				int p9 = (i == 0 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j - 1);

				if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
				{
					int ap = 0;
					if (p2 == 0 && p3 == 1) ++ap;
					if (p3 == 0 && p4 == 1) ++ap;
					if (p4 == 0 && p5 == 1) ++ap;
					if (p5 == 0 && p6 == 1) ++ap;
					if (p6 == 0 && p7 == 1) ++ap;
					if (p7 == 0 && p8 == 1) ++ap;
					if (p8 == 0 && p9 == 1) ++ap;
					if (p9 == 0 && p2 == 1) ++ap;

					if (ap == 1)
					{
						if (p2*p4*p8 == 0)
						{
							if (p2*p6*p8 == 0)
							{
								//标记  
								mFlag.push_back(std::make_pair(i, j));
							}
						}
					}
				}
			}
		}
		//删除  
		for (std::vector<std::pair<int, int> >::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
		{
			CV_IMAGE_ELEM(dst, uchar, i->first, i->second) = 0;
		}

		//直到没有点满足,算法结束  
		if (mFlag.size() == 0)
		{
			break;
		}
		else
		{
			mFlag.clear();//将mFlag清空  
		}
	}
}
3.主函数所在文件 Source.cpp

//Source.cpp
#include "ProcessImage.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#define _TEST
using namespace cv;
int main(int argc, char * argv[])
{
	//判断输入是否满足要求
	if (argc != 2)
	{
		std::cout << "argument error!";
		return -1;
	}
	IplImage *pSrc = cvLoadImage(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
	if (!pSrc)
	{
		std::cout << "read file failed!";
		return -1;
	}

	//显示原图
	namedWindow("原图", CV_WINDOW_AUTOSIZE);
	cvShowImage("原图", pSrc);

	IplImage *pTemp = cvCreateImage(cvGetSize(pSrc), pSrc->depth, pSrc->nChannels);
	IplImage *pDst = cvCreateImage(cvGetSize(pSrc), pSrc->depth, pSrc->nChannels);

	//将原图像转换为二值图像
	cvThreshold(pSrc, pTemp, 128, 1, CV_THRESH_BINARY_INV);
	//细化
	thinImage(pTemp, pDst);

#ifdef _TEST
	//显示细化后的图像
	IplImage *pThinImage = cvCreateImage(cvGetSize(pSrc), pSrc->depth, pSrc->nChannels);
	cvCopy(pDst, pThinImage);
	cvThreshold(pThinImage, pThinImage, 0.5, 255,CV_THRESH_BINARY);
	namedWindow("1 图像细化的结果", CV_WINDOW_AUTOSIZE);
	cvShowImage("1 图像细化的结果", pThinImage);
	cvReleaseImage(&pThinImage);
#endif

	//求轮廓
	CvMemStorage* storage = cvCreateMemStorage(0);
	CvSeq* contours = 0;
	cvFindContours(pDst	, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(0, 0));

#ifdef _TEST
	//将轮廓画出来
	IplImage *pDrawing1 = cvCreateImage(cvGetSize(pSrc),8,3);
	cvZero(pDrawing1);
	cvDrawContours(pDrawing1, contours, Scalar(255, 0, 0), Scalar(0, 0, 255), 1, 2, 8, cvPoint(0, 0));
	namedWindow("2 求轮廓", CV_WINDOW_AUTOSIZE);
	cvShowImage("2 求轮廓", pDrawing1);
	cvReleaseImage(&pDrawing1);
#endif


	//轮廓已经寻找到,均在contours中存放,我们需要对轮廓进行拟合
	//FitLine函数的用法:
	// 二维空间点拟合时 是 float[4]
	// 三位空间点拟合时 是 float[6]	
	float *line1 = new float[4];
	float *line2 = new float[4];
	// 第一个参数: 存储点序列
	// 第二个参数: 拟合算法,其中 CV_DIST_L2 就是平常的最小二乘法
	// 第三,第四,第五参数推荐值是 0,   0.01,  0.01,
	// 第六参数: line中存储返回值
	// 二维空间时: line[0--3] 分别为 (vx, vy, x0, y0)
	//      其中 vx, vy 是正规化之后的斜率向量。 x0,y0 是直线经过的点。
	// 三维空间时: line[0--5]  分别是 (vx, vy, vz, x0, y0, z0) 。意义同上
	cvFitLine(contours, CV_DIST_L2, 0, 0.01, 0.01, line1);
	cvFitLine(contours->h_next, CV_DIST_L2, 0, 0.01, 0.01, line2);
	
	//输出四个点
	std::cout << "第一条线: " << line1[0] << " " << line1[1] << " " << line1[2] << " " << line1[3] << std::endl;
	std::cout << "第二条线: " << line2[0] << " " << line2[1] << " " << line2[2] << " " << line2[3] << std::endl;
	
#ifdef _TEST
	//根据直线方程公式,我们从直线上取点,并画出来
	IplImage *pDrawing2 = cvCreateImage(cvGetSize(pSrc), 8, 3);
	cvZero(pDrawing2);
	cvLine(pDrawing2, cvPoint(0, (int)(line1[3] - line1[1] / line1[0] * line1[2])),
		cvPoint(pDrawing2->width - 1, (int)((pDrawing2->width - 1 - line1[2])*line1[1] / line1[0] + line1[3])),
		cvScalar(255, 0, 0));
	cvLine(pDrawing2, cvPoint(0, (int)(line2[3] - line2[1] / line2[0] * line2[2])), 
		cvPoint(pDrawing2->width - 1, (int)((pDrawing2->width - 1 - line2[2])*line2[1] / line2[0] + line2[3])), 
		cvScalar(0, 0, 255));
	namedWindow("3 直线拟合", CV_WINDOW_AUTOSIZE);
	cvShowImage("3 直线拟合", pDrawing2);
	cvReleaseImage(&pDrawing2);
#endif

	//我们根据距离方程,求出两条直线的距离
	double distance = abs(line1[0] * (line2[3]-line1[3]) - line1[1] * (line2[2]-line1[2]));	//注意,vx,vy已经正规化了
	std::cout << "两条直线之间的距离为: " << distance << std::endl;
	delete[] line1;
	delete[] line2;

	cvReleaseMemStorage(&storage);
	cvReleaseImage(&pSrc);
	cvReleaseImage(&pTemp);
	cvReleaseImage(&pDst);

	waitKey(0);

	return 0;
}
运行效果:

输入:

输出:


2018-11-07 22:57:28 Godsolve 阅读数 425

图像的距离变换实现了像素与图像区域的距离变换,使得最后生成的图像在该自己元素位置处的像素为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;
}



2019-10-29 19:16:14 qq_33446100 阅读数 24

说在前面

  • 编译环境:vs2017
  • opencv版本:4.0.1
  • 参考:《图像处理、分析与机器视觉(第4版)》

概念

  • 数字图像

    对于一个图像函数f(x,y)f(x,y),若其定义域以及值域都是离散的,那么称之为数字的;
    通常,通过采样将连续图像进行数字化。
    在这里插入图片描述
    一个连续图像在采样点处被数字化;采样点之间构成的关系为栅格。
  • 距离

    • 定义
      满足以下条件的函数:
      D(p,q)0,p=q,D(p,q)=0D(p,q)=D(q,p)D(p,r)D(p,q)+D(q,r)D(\bold p,\bold q)\geq 0,当且仅当\bold p=\bold q时,D(\bold p,\bold q)=0\\ D(\bold p,\bold q)=D(\bold q,\bold p)\\ D(\bold p,\bold r)\leq D(\bold p,\bold q)+D(\bold q,\bold r)
    • 欧式距离
      DE[(i,j),(h,k)]=(ih)2+(jk)2D_E[(i,j),(h,k)]=\sqrt{(i-h)^2+(j-k)^2}
      在这里插入图片描述
    • 城市街区距离
      只允许横向以及纵向的移动
      在这里插入图片描述
      D4[(i,j),(h,k)]=ih+jkD_4[(i,j),(h,k)]=|i-h|+|j-k|
      在这里插入图片描述
    • 棋盘距离
      允许横向、纵向以及对角线上的移动
      在这里插入图片描述
      D8[(i,j),(h,k)]=max{ih+jk}D_8[(i,j),(h,k)]=max\{|i-h|+|j-k|\}
      在这里插入图片描述
  • 距离变换算法

    • 按照一种距离度量D,D是D4或者D8,对大小为MxN的图像的一个子集S进行距离变换,建立一个MxN的数组F并作初始化:子集S中的元素置为0,其他位置为无穷。
    • 按行遍历图像,从上到下,从左到右。对于上方和左边的邻接像素,设
      F(p)=min[F(p),D(p,q)+F(q)]F(\bold p)=min[F(\bold p), D(p,q)+F(q)]
      在这里插入图片描述
    • 按行遍历图像,从下到上,从右到左。对于下方和右边的邻接像素,设
      F(p)=min[F(p),D(p,q)+F(q)]F(\bold p)=min[F(\bold p), D(p,q)+F(q)]
      在这里插入图片描述
    • 数组F中得到的是子集S的斜切

Code

  • c++实现

    并未处理图像,仅用于展示算法;
    时间复杂度:O(n2)O(n^2)
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    /*
    @{func} 判断i,j是否在范围内
    */
    bool InArea(int i, int j, int rows, int cols)
    {
    	if (i<0 || i>=rows)
    		return false;
    	if (j<0 || j>=cols)
    		return false;
    
    	return true;
    }
    /*
    @{param: i j} 点位置
    @{param: rows cols} 图像大小
    @{param: f} 目标数组
    @{func} 处理上以及左边的近邻像素
    */
    void D4AL(int i,int j, int rows, int cols, vector<vector<int>> &f)
    {
    	//上
    	if (InArea(i - 1, j, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i - 1][j]);
    	//左上
    	if (InArea(i - 1, j - 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i - 1][j - 1]);
    	//左
    	if (InArea(i, j - 1, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i][j - 1]);
    	//左下
    	if (InArea(i + 1, j - 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i + 1][j - 1]);
    }
    /*
    @{param: i j} 点位置
    @{param: rows cols} 图像大小
    @{param: f} 目标数组
    @{func} 处理下以及右边的近邻像素
    */
    void D4BR(int i, int j, int rows, int cols, vector<vector<int>> &f)
    {
    	//下
    	if (InArea(i + 1, j, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i + 1][j]);
    	//右下
    	if (InArea(i + 1, j + 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i + 1][j + 1]);
    	//右
    	if (InArea(i, j + 1, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i][j + 1]);
    	//右上
    	if (InArea(i - 1, j + 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i - 1][j + 1]);
    }
    
    /*
    @{param:src} 源图像
    @{param:f}   目标数组
    */
    void DistanceTransformD4(vector<vector<int>> &src, vector<vector<int>> &f)
    {
    	int cols = src[0].size();
    	int rows = src.size();
    
    	//初始化
    	for (int i = 0; i < rows; ++i)
    		for (int j = 0; j < cols; ++j)
    			if (src[i][j] == 1)
    				f[i][j] = 0;
    			else
    				f[i][j] = INT_MAX - 2;//简单的防止溢出
    	//按行遍历图像,从上到下,从左到右
    	for (int i = 0; i < rows; ++i)
    		for (int j = 0; j < cols; ++j)
    			D4AL(i, j, rows, cols, f);
    
    	//按行遍历图像,从下到上,从右到左
    	for (int i = rows - 1; i >= 0; --i)
    		for (int j = cols - 1; j >= 0; --j)
    			D4BR(i, j, rows, cols, f);
    }
    
    int main()
    {
    	vector<vector<int>> src = { 
    	{0,1,1,0,0,0,1,0 },
    	{0,1,0,0,0,0,0,1 },
    	{0,1,0,0,0,0,0,0 },
    	{0,1,0,0,0,0,0,0 }
    	};
    	int rows = src.size();
    	int cols = src[0].size();
    	vector<vector<int>> f(rows, vector<int>(cols, 0));
    
    	cout << "SRC:" << endl;
    	for (int i = 0; i < rows; ++i)
    	{
    		for (int j = 0; j < cols; ++j)
    			cout << src[i][j] << " ";
    		cout << endl;
    	}
    
    	DistanceTransformD4(src, f);
    
    	cout << "\nResult:" << endl;
    	for (int i = 0; i < rows; ++i)
    	{
    		for (int j = 0; j < cols; ++j)
    			cout << f[i][j] << " ";
    		cout << endl;
    	}
    
    	return 0;
    }
    
    在这里插入图片描述
  • opencv

    void cv::distanceTransform	(	
    InputArray 	src,
    OutputArray 	dst,
    OutputArray 	labels,
    int 	distanceType,
    int 	maskSize,
    int 	labelType = DIST_LABEL_CCOMP 
    )	
    
    栗子?
    在这里插入图片描述

应用

  • 移动机器人领域的路径规划以及障碍躲避
  • 图像中寻找最近特征,骨架抽取
  • 待补充
2015-11-14 12:42:28 baimafujinji 阅读数 3147

全文目录请见

图像处理中的数学原理详解(Part1 总纲)

http://blog.csdn.net/baimafujinji/article/details/48467225


2.3.2 距离空间
        尽管在线性空间上我们已经可以完成简单的线性运算,但这仍然不能满足我们的需求。为了保证数学刻画的精确性,还必须引入距离的概念。本文最初是从极限开始讲起的,它是因此微积分的必备要素之一,而极限的概念显然也是基于距离上无限接近这样一种角度来描述的。



       由此,在距离空间中,可以引入“任意逼近”的概念,即极限概念。一般来说,一个集合如果能够在其中确切地引入任意逼近的概念,就称之为“拓扑空间”。而距离空间是一种最常用的拓扑空间。


我的“图像处理中的数学原理”专栏中之系列文章由“清华大学出版社”结集出版。欢迎关注我的新书《图像处理中的数学修炼》(Applied Mathematics in Digital Image Processing)——详细介绍图像处理中的数学原理,为你打开一道通往图像世界的数学之门,详细内容及目录请见 http://blog.csdn.net/baimafujinji/article/details/48467225

没有更多推荐了,返回首页