Kinect For Windows V2开发日志七:照片合成与背景消除
上一篇里讲到了Kinect可以从环境中区分出人体来。因此可以利用这个功能,来把摄像头前的人合成进照片里,和利用Photoshop不同的是,这样合成进去的人是动态且实时的。
简单的思路
BodyIndex用的是深度数据,只能用来判断画面中的点属不属于人体而不能用来直接显示画面,Color图里的数据只能用来显示而没有其他功能。所以如果深度数据能和彩色数据配合的话,就能利用深度数据来识别出彩色数据中的哪些点属于人体。但是深度帧的分辨率是512 x 424,而彩色帧的分辨率是1920 x 1080 ,无法将他们对应起来。然而微软提供了一个叫ICoordinateMapper的类。
简单来说:将彩色图上的点转换到深度图的坐标系中->判断某点是否是人体->是的话从彩色图中取出此点,与背景替换。
代码
//这份代码累死哥了
#include <iostream>
#include <opencv2/imgproc.hpp>
#include <opencv2\highgui.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()这个函数。
首选,需要准备好三种数据源:Color、BodyIndex、Depth,其中前两个是完成功能本来就需要的,第三个是转换坐标系时需要,无法直接把Color的坐标系映射到BodyIndex中,只能映射到Depth中。
然后是读取背景图,读取之后也要转换成Color图的尺寸,这样把Color中的点贴过去时坐标就不用再转换,直接替换就行。接下来也要读取三种Frame,为了易读性,不如把准备工作在前面都做完,在这一步直接用Reader就行。
然后,利用MapColorFrameToDepthSpace(),将彩色帧映射到深度坐标系,它需要4个参数,第1个是深度帧的大小,第2个是深度数据,第3个是彩色帧的大小,第4个是一个DepthSpacePoint的数组,它用来储存彩色空间中的每个点对应到深度空间的坐标。
要注意,这个函数只是完成坐标系的转换,也就是说它对于彩色坐标系中的每个点,都给出了一个此点对应到深度坐标系中的坐标,并不涉及到具体的ColorFrame。
最后,遍历彩色图像,对于每一点,都取出它对应的深度坐标系的坐标,然后把这个坐标放入BodyIndex的数据中,判断此点是否属于人体,如果属于,就把这点从彩色图中取出,跟背景图中同一坐标的点替换。
要注意的是,DepthSpacePoint中的X和Y的值都是float的,用它们来计算在BodyIndex里的坐标时,需要强转成int,不然画面就会不干净,一些不属于人体的地方也被标记成了人体被替换掉。
效果图
背景为一张1920 * 1080的壁纸

Kinect For Windows V2开发日志七:照片合成与背景消除的更多相关文章
- Kinect For Windows V2开发日志一:开发环境的配置
算是正式进军Kinect了,前段时间学的东西现在就忘了,于是从此开始记录一下. 目前为止大部分的学习资料来自于Heresy的博客,写的非常优秀,清晰明了,十分感谢.开发语言为C++,应该会一直使用,但 ...
- Kinect For Windows V2开发日志八:侦测、追踪人体骨架
简介 Kinect一个很强大的功能就是它可以侦测到人体的骨骼信息并追踪,在Kinect V2的SDK 2.0中,它最多可以同时获取到6个人.每个人25个关节点的信息,并且通过深度摄像头,可以同时获取到 ...
- Kinect For Windows V2开发日志六:人体的轮廓的表示
Kinect中带了一种数据源,叫做BodyIndex,简单来说就是它利用深度摄像头识别出最多6个人体,并且用数据将属于人体的部分标记,将人体和背景区别开来.利用这一特性,就可以在环境中显示出人体的轮廓 ...
- Kinect For Windows V2开发日志五:使用OpenCV显示彩色图像及红外图像
彩色图像 #include <iostream> #include <Kinect.h> #include <opencv2\highgui.hpp> using ...
- Kinect For Windows V2开发日志九:侦测并绘制人体骨架
简介 在上一篇<侦测.追踪人体骨架>里,介绍了关节点的使用办法,这一篇记录将关节点与OpenCV结合的绘图方法. 代码 #include <iostream> #include ...
- Kinect For Windows V2开发日志四:使用OpenCV显示深度图像
代码示例: #include <Kinect.h> #include <iostream> #include <opencv2\highgui.hpp> using ...
- Kinect For Windows V2开发日志三:简单的深度读取
代码示例: #include <Kinect.h> #include <iostream> using namespace std; int main(void) { IKin ...
- Kinect For Windows V2开发日志二:Kinect V2的基本参数
以下内容节选自Heresy的博客: 彩色影像:1920 x 1080 @ 30 / 15 FPS(根据环境亮度) 深度影像:512 x 424 @ 30 FPS.16bit 距离值(mm).可侦测 ...
- Kinect for Windows V2开发教程
教程 https://blog.csdn.net/openbug/article/details/80921437 Windows版Kinect SDK https://docs.microsoft. ...
随机推荐
- 递归模式学习(recursion)
所谓递归,就是方法调用自身.对于递归模式来说,要有一个出口来让递归结束,避免出现死循环. 实例全排列: 从n中拿出m个元素进行排列,当n==m时为全排列. 利用递归就是:把n个元素轮流放入第一个位置, ...
- linux内核完全注释之微型计算机组成结构
计算机组成原理 1.传统计算机计算机组成框图 CPU通过地址线.数据线.控制线组成的本地总线(或内部总线),与系统的其他部分进行数据通信,地址线用于提供内存或I/O设备的地址,指明所需读写数据的具体操 ...
- 在sql中使用了 hashbytes 函数
在做项目的时候,在sql中使用了hashbytes函数,运用md5算法去加密一密码.代码如下 DECLARE @psw nvarchar(20) SET @psw = 'admin' SELECT h ...
- (剑指Offer)面试题21:包含min函数的栈
题目: 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数. 在该栈中,调用min,push,pop的时间复杂度都是O(1) 思路: 1.除了原来的栈s,增加一个辅助栈s_min,用 ...
- Flex 拾色器改变背景
package { import flash.display.*; import flash.geom.*; import flash.utils.*; import mx.core.EdgeMe ...
- cocos2dx Http网络编程
转自:http://blog.csdn.net/wangbin_jxust/article/details/9632771,http://blog.csdn.net/wangbin_jxust/art ...
- AS与JS相互通信(Flex中调用js函数)
转载自http://www.blogjava.net/Alpha/archive/2009/06/27/284373.html Flex中As调用Js的方法是: 1.导入包 (import f ...
- web自己主动保存表单
note:当中部分源代码来源网络 所用的框架:jquery 实现的功能: 1.自己主动保存表单 2.页面刷新的时候把自己主动保存的值赋值给表单元素 思路: 1.表单值改变的时候自己主动触发函数.保存表 ...
- Codeforces Educational Codeforces Round 5 A. Comparing Two Long Integers 高精度比大小,模拟
A. Comparing Two Long Integers 题目连接: http://www.codeforces.com/contest/616/problem/A Description You ...
- CloudStack的VO在调用setRemoved方法抛异常的原因
今天在开发中发现一个问题,本来想对一个VO对象的removed值赋值,然后去update一下这条记录,一个最简单的set方法,但是在调用时直接抛异常了. 1: public void setRemov ...