现在的位置: 首页 > 综合 > 正文

【矩阵算法】distance transform用以提取手掌中心区域

2018年04月12日 ⁄ 综合 ⁄ 共 3094字 ⁄ 字号 评论关闭

这几天在做一个手势识别的项目,其中最的关键一步是提取手掌中心。

获得手掌重心通常的做法是计算整个手部的重心,并以该重心位置近似手掌重心,这种方法只适用于没有手指伸出或只有一个手指伸出的情况,否则获得的手掌重心位置将严重偏离真实位置。

    距离变换的基本含义是计算一个图像中非零像素点到最近的零像素点的距离,也就是到零像素点的最短距离。因此可以基于距离变换提取手掌重心。

算法基本思想:

1)将手掌图像二值化,手掌内的区域设为白色,外部区域设为黑色。

2)将二值化后的图像经过distanceTransform变换,得到dist_image,其中每个像素点的值是该像素点到其最近的零像素点的距离。

3)找到dist_image的最大值(即圆的半径R),并记录下位置(即圆心坐标)。

代码如下:

 

#include "opencv2/opencv.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <vector>
using namespace cv;  
using namespace std; 

pair<Point,double> DetectInCircles(vector<Point> contour,Mat src)
{
	
	Mat dist_image;
	distanceTransform(src,dist_image,CV_DIST_L2,3);
	int temp=0,R=0,cx=0,cy=0;
	int d;
	for (int i=0;i<src.rows;i++)
		for (int j=0;j<src.cols;j++)
		{
			/* checks if the point is inside the contour. Optionally computes the signed distance from the point to the contour boundary*/
		        d = pointPolygonTest(contour, Point2f(j, i), 0);
			if (d>0)
			{
				temp=(int)dist_image.ptr<float>(i)[j];
				if (temp>R)
				{
					R=temp;
					cy=i;
					cx=j;
				}
						
			}			
		}
    
    
	return make_pair(Point(cx,cy),R);
	   
}

int main()  
{  
	// Read input binary image  
	Mat src= imread("D:\\mycode\\6.jpg",1);
	Mat image;
	cvtColor(src,image,CV_BGR2GRAY);
	vector<vector<Point>> contours;  
	
	//findContours的输入是二值图像  
	findContours(image,   
		contours, // a vector of contours   
		CV_RETR_EXTERNAL, // retrieve the external contours  
		CV_CHAIN_APPROX_NONE); // retrieve all pixels of each contours  

	// Print contours' length轮廓的个数  
	cout << "Contours: " << contours.size() << endl;  
	vector<vector<Point>>::const_iterator itContours= contours.begin();  
	for ( ; itContours!=contours.end(); ++itContours) {  

		cout << "Size: " << itContours->size() << endl;//每个轮廓包含的点数  
	}  
    //找到最大轮廓
	int index=0,maxArea=0;	
	for(unsigned int i=0;i<contours.size();i++)
	{
		int area=contourArea(contours[i]);
		if (area>maxArea)
		{
			index=i;
			maxArea=area;
		}
	}
	// draw black contours on white image  
	Mat result(image.size(),CV_8U,Scalar(0));  
	drawContours(result,contours,      //画出轮廓  
		-1, // draw all contours  
		Scalar(255), // in black  
		2); // with a thickness of 2  


	pair<Point,double> m=DetectInCircles(contours[index],image);
	
	cout<<m.first.x<<" "<<m.first.y<<" "<<m.second<<endl;
	circle(src,m.first,3,Scalar(0,0,255),2);
	circle(src,m.first,m.second,Scalar(0,0,255),1);
	namedWindow("result");  
	imshow("result",src); 
	waitKey(0);
	
	return 0;
}

结果:

      原图                                dist_image                     结果

         

   

 

   

 

其中有一点需要注意:

distanceTransform(InputArray src, OutputArray dst, int distanceType, int maskSize)

Parameters:
src – 8-bit, single-channel (binary) source image.
dst – Output image with calculated distances. It is a 32-bit floating-point, single-channel
image of the same size as src .
distanceType – Type of distance. It can be CV_DIST_L1, CV_DIST_L2 , or CV_DIST_C .
maskSize – Size of the distance transform mask. It can be 3, 5, or CV_DIST_MASK_PRECISE
(the latter option is only supported by the first function). In case of the CV_DIST_L1 or
CV_DIST_C distance type, the parameter is forced to 3 because a 3  3 mask gives the same
result as 5  5 or any larger aperture.
labels – Optional output 2D array of labels (the discrete Voronoi diagram). It has the type
CV_32SC1 and the same size as src . See the details below.

dst为单通道的32-bit 浮点型矩阵,读取元素时需要用image.ptr<float>(i)[j],用imshow显示的时候需要用normalize(dist_image, magI, 0, 1, CV_MINMAX); 将float类型的矩阵转换到可显示图像范围 (float
[0, 1]).

 

另外还有一个博客值得参考:http://blog.csdn.net/wuhaibing_cver/article/details/8602461

抱歉!评论已关闭.