zoukankan      html  css  js  c++  java
  • Kinect For Windows V2开发日志七:照片合成与背景消除

    上一篇里讲到了Kinect可以从环境中区分出人体来。因此可以利用这个功能,来把摄像头前的人合成进照片里,和利用Photoshop不同的是,这样合成进去的人是动态且实时的。


    简单的思路

    BodyIndex用的是深度数据,只能用来判断画面中的点属不属于人体而不能用来直接显示画面,Color图里的数据只能用来显示而没有其他功能。所以如果深度数据能和彩色数据配合的话,就能利用深度数据来识别出彩色数据中的哪些点属于人体。但是深度帧的分辨率是512 x 424,而彩色帧的分辨率是1920 x 1080 ,无法将他们对应起来。然而微软提供了一个叫ICoordinateMapper的类。

    简单来说:将彩色图上的点转换到深度图的坐标系中->判断某点是否是人体->是的话从彩色图中取出此点,与背景替换。


    代码

    //这份代码累死哥了
    
    #include <iostream>
    #include <opencv2/imgproc.hpp>
    #include <opencv2highgui.hpp>
    #include <Kinect.h>
    
    using	namespace	std;
    using	namespace	cv;
    
    int	main(void)
    {
    	IKinectSensor	* mySensor = nullptr;
    	GetDefaultKinectSensor(&mySensor);
    	mySensor->Open();
    
    //************************准备好彩色图像的Reader并获取尺寸*******************************
    
    	int	colorHeight = 0, colorWidth = 0;
    	IColorFrameSource	* myColorSource = nullptr;
    	IColorFrameReader	* myColorReader = nullptr;
    	IFrameDescription	* myDescription = nullptr;
    	{
    		mySensor->get_ColorFrameSource(&myColorSource);
    
    		myColorSource->OpenReader(&myColorReader);
    
    		myColorSource->get_FrameDescription(&myDescription);
    		myDescription->get_Height(&colorHeight);
    		myDescription->get_Width(&colorWidth);
    
    		myDescription->Release();
    		myColorSource->Release();
    	}
    
    //************************准备好深度图像的Reader并获取尺寸*******************************
    
    	int	depthHeight = 0, depthWidth = 0;
    	IDepthFrameSource	* myDepthSource = nullptr;
    	IDepthFrameReader	* myDepthReader = nullptr;
    	{
    		mySensor->get_DepthFrameSource(&myDepthSource);
    
    		myDepthSource->OpenReader(&myDepthReader);
    
    		myDepthSource->get_FrameDescription(&myDescription);
    		myDescription->get_Height(&depthHeight);
    		myDescription->get_Width(&depthWidth);
    
    		myDescription->Release();
    		myDepthSource->Release();
    	}
    	
    //************************准备好人体索引图像的Reader并获取尺寸****************************
    
    	int	bodyHeight = 0, bodyWidth = 0;
    	IBodyIndexFrameSource	* myBodyIndexSource = nullptr;
    	IBodyIndexFrameReader	* myBodyIndexReader = nullptr;
    	{
    		mySensor->get_BodyIndexFrameSource(&myBodyIndexSource);
    
    		myBodyIndexSource->OpenReader(&myBodyIndexReader);
    
    		myDepthSource->get_FrameDescription(&myDescription);
    		myDescription->get_Height(&bodyHeight);
    		myDescription->get_Width(&bodyWidth);
    
    		myDescription->Release();
    		myBodyIndexSource->Release();
    	}
    
    //************************为各种图像准备buffer,并且开启Mapper*****************************
    
    	UINT	colorDataSize = colorHeight * colorWidth;
    	UINT	depthDataSize = depthHeight * depthWidth;
    	UINT	bodyDataSize = bodyHeight * bodyWidth;
    	Mat	temp = imread("test.jpg"),background;			    //获取背景图
    	resize(temp,background,Size(colorWidth,colorHeight));	//调整至彩色图像的大小
    
    	ICoordinateMapper	* myMaper = nullptr;			    //开启mapper
    	mySensor->get_CoordinateMapper(&myMaper);
    
    	Mat	colorData(colorHeight, colorWidth, CV_8UC4);		//准备buffer
    	UINT16	* depthData = new UINT16[depthDataSize];
    	BYTE	* bodyData = new BYTE[bodyDataSize];
    	DepthSpacePoint	* output = new DepthSpacePoint[colorDataSize];
    
    //************************把各种图像读进buffer里,然后进行处理*****************************
    
    	while (1)
    	{
    		IColorFrame * myColorFrame = nullptr;
    		while (myColorReader->AcquireLatestFrame(&myColorFrame) != S_OK);	//读取color图
    		myColorFrame->CopyConvertedFrameDataToArray(colorDataSize * 4, colorData.data, ColorImageFormat_Bgra);
    		myColorFrame->Release();
    
    		IDepthFrame * myDepthframe = nullptr;
    		while (myDepthReader->AcquireLatestFrame(&myDepthframe) != S_OK);	//读取depth图
    		myDepthframe->CopyFrameDataToArray(depthDataSize, depthData);
    		myDepthframe->Release();
    
    		IBodyIndexFrame * myBodyIndexFrame = nullptr;				        //读取BodyIndex图
    		while (myBodyIndexReader->AcquireLatestFrame(&myBodyIndexFrame) != S_OK);
    		myBodyIndexFrame->CopyFrameDataToArray(bodyDataSize, bodyData);
    		myBodyIndexFrame->Release();
    
    		Mat	copy = background.clone();					//复制一份背景图来做处理
    		if (myMaper->MapColorFrameToDepthSpace(depthDataSize, depthData, colorDataSize, output) == S_OK)
    		{
    			for (int i = 0; i < colorHeight; ++ i)
    				for (int j = 0; j < colorWidth;++ j)
    				{
    					DepthSpacePoint	tPoint = output[i * colorWidth + j];	//取得彩色图像上的一点,此点包含了它对应到深度图上的坐标
    					if (tPoint.X >= 0 && tPoint.X < depthWidth && tPoint.Y >= 0 && tPoint.Y < depthHeight)	//判断是否合法
    					{
    						int	index = (int)tPoint.Y * depthWidth + (int)tPoint.X;	//取得彩色图上那点对应在BodyIndex里的值(注意要强转)
    						if (bodyData[index] <= 5)					//如果判断出彩色图上某点是人体,就用它来替换背景图上对应的点
    						{
    							Vec4b	color = colorData.at<Vec4b>(i, j);
    							copy.at<Vec3b>(i, j) = Vec3b(color[0], color[1], color[2]);
    						}
    					}
    				}
    			imshow("TEST",copy);
    		}
    		if (waitKey(30) == VK_ESCAPE)
    			break;
    	}
    	delete[] depthData;			//记得各种释放
    	delete[] bodyData;
    	delete[] output;
    
    
    	myMaper->Release();
    	myColorReader->Release();
    	myDepthReader->Release();
    	myBodyIndexReader->Release();
    	mySensor->Close();
    	mySensor->Release();
    
    	return	0;
    }
    

    详细说明:

    SDK中提供了一个叫ICoordinateMapper的类,功能就是坐标系之间的互相转换,用来解决数据源的分辨率不同导致点对应不起来的问题。我们需要的是将彩色图像中的点与深度图像中的点一一对应起来,因此使用其中的MapColorFrameToDepthSpace()这个函数。

    首选,需要准备好三种数据源:ColorBodyIndexDepth,其中前两个是完成功能本来就需要的,第三个是转换坐标系时需要,无法直接把Color的坐标系映射到BodyIndex中,只能映射到Depth中。

    然后是读取背景图,读取之后也要转换成Color图的尺寸,这样把Color中的点贴过去时坐标就不用再转换,直接替换就行。接下来也要读取三种Frame,为了易读性,不如把准备工作在前面都做完,在这一步直接用Reader就行。

    然后,利用MapColorFrameToDepthSpace(),将彩色帧映射到深度坐标系,它需要4个参数,第1个是深度帧的大小,第2个是深度数据,第3个是彩色帧的大小,第4个是一个DepthSpacePoint的数组,它用来储存彩色空间中的每个点对应到深度空间的坐标。
    要注意,这个函数只是完成坐标系的转换,也就是说它对于彩色坐标系中的每个点,都给出了一个此点对应到深度坐标系中的坐标,并不涉及到具体的ColorFrame

    最后,遍历彩色图像,对于每一点,都取出它对应的深度坐标系的坐标,然后把这个坐标放入BodyIndex的数据中,判断此点是否属于人体,如果属于,就把这点从彩色图中取出,跟背景图中同一坐标的点替换。

    要注意的是,DepthSpacePoint中的XY的值都是float的,用它们来计算在BodyIndex里的坐标时,需要强转成int,不然画面就会不干净,一些不属于人体的地方也被标记成了人体被替换掉。


    效果图

    背景为一张1920 * 1080的壁纸






  • 相关阅读:
    python-web微信实现
    Python---BeautifulSoup模块
    Python—requests模块详解
    KindEditor编辑器
    Django实现瀑布流,组合搜索
    利用(CMD)在Django中创建文件
    ppt插入声音
    高性能mysql 第1,2,3章。
    mysql复制
    最长DNA重复序列长度,并输出该序列。 JAVA
  • 原文地址:https://www.cnblogs.com/xz816111/p/5185766.html
Copyright © 2011-2022 走看看