Created
November 16, 2020 05:55
-
-
Save as8190255/f72346a4e2290de7a59a3d83ef0545a4 to your computer and use it in GitHub Desktop.
Kinect深度摄像头Opencv识别触摸点位
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//============================================================================ | |
// Name : KinectTouch.cpp | |
// Author : github.com/robbeofficial | |
// Version : 0.something | |
// Description : recognizes touch points on arbitrary surfaces using kinect | |
// and maps them to TUIO cursors | |
// (turns any surface into a touchpad) | |
//============================================================================ | |
/* | |
* 1. point your kinect from a higher place down to your table | |
* 2. start the program (keep your hands off the table for the beginning) | |
* 3. use your table as a giant touchpad | |
*/ | |
#include <iostream> | |
#include <vector> | |
#include <map> | |
using namespace std; | |
// openCV | |
#include <opencv/highgui.h> | |
#include <opencv/cv.h> | |
using namespace cv; | |
// openNI | |
#include <XnOpenNI.h> | |
#include <XnCppWrapper.h> | |
using namespace xn; | |
#define CHECK_RC(rc, what) \ | |
if (rc != XN_STATUS_OK) \ | |
{ \ | |
printf("%s failed: %s\n", what, xnGetStatusString(rc)); \ | |
return rc; \ | |
} | |
// TUIO | |
/* | |
#include "TuioServer.h" | |
using namespace TUIO; | |
*/ | |
// TODO smoothing using kalman filter | |
//--------------------------------------------------------------------------- | |
// Globals | |
//--------------------------------------------------------------------------- | |
// OpenNI | |
xn::Context xnContext;//实例 | |
xn::DepthGenerator xnDepthGenerator;//深度数据 | |
xn::ImageGenerator xnImgeGenertor;//色彩数据 | |
bool mousePressed = false; | |
//--------------------------------------------------------------------------- | |
// Functions | |
//--------------------------------------------------------------------------- | |
int initOpenNI(const XnChar* fname) { | |
XnStatus nRetVal = XN_STATUS_OK; | |
ScriptNode scriptNode; | |
// initialize context | |
nRetVal = xnContext.InitFromXmlFile(fname, scriptNode);//初始化 配置文件 | |
CHECK_RC(nRetVal, "InitFromXmlFile");//打印日志 | |
// initialize depth generator | |
nRetVal = xnContext.FindExistingNode(XN_NODE_TYPE_DEPTH, xnDepthGenerator);//初始化 深度摄像头 xnDepthGenerator深度数据 | |
CHECK_RC(nRetVal, "FindExistingNode(XN_NODE_TYPE_DEPTH)");//打印日志 | |
// initialize image generator | |
nRetVal = xnContext.FindExistingNode(XN_NODE_TYPE_IMAGE, xnImgeGenertor);//初始化 图像摄像头 xnImgeGenertor图像数据 | |
CHECK_RC(nRetVal, "FindExistingNode(XN_NODE_TYPE_IMAGE)");//打印日志 | |
return 0; | |
} | |
//转换 | |
void average(vector<Mat1s>& frames, Mat1s& mean) { | |
Mat1d acc(mean.size()); | |
Mat1d frame(mean.size()); | |
for (unsigned int i=0; i<frames.size(); i++) { | |
frames[i].convertTo(frame, CV_64FC1); | |
acc = acc + frame; | |
} | |
acc = acc / frames.size(); | |
acc.convertTo(mean, CV_16SC1); | |
} | |
int main() { | |
const unsigned int nBackgroundTrain = 30; | |
const unsigned short touchDepthMin = 10; | |
const unsigned short touchDepthMax = 20; | |
const unsigned int touchMinArea = 50; | |
const bool localClientMode = true; // connect to a local client | |
const double debugFrameMaxDepth = 4000; // maximal distance (in millimeters) for 8 bit debug depth frame quantization 8位调试深度帧量化的最大距离(毫米) | |
const char* windowName = "Debug"; | |
const Scalar debugColor0(0,0,128); | |
const Scalar debugColor1(255,0,0); | |
const Scalar debugColor2(255,255,255); | |
int xMin = 110; | |
int xMax = 560; | |
int yMin = 120; | |
int yMax = 320; | |
Mat1s depth(480, 640); // 16 bit depth (in millimeters) | |
Mat1b depth8(480, 640); // 8 bit depth | |
Mat3b rgb(480, 640); // 8 bit depth | |
Mat3b debug(480, 640); // debug visualization | |
Mat1s foreground(640, 480); | |
Mat1b foreground8(640, 480); | |
Mat1b touch(640, 480); // touch mask | |
Mat1s background(480, 640); | |
vector<Mat1s> buffer(nBackgroundTrain); | |
initOpenNI("../niConfig.xml"); | |
// TUIO server object | |
/* | |
TuioServer* tuio; | |
if (localClientMode) { | |
tuio = new TuioServer(); | |
} else { | |
tuio = new TuioServer("192.168.0.2",3333,false); | |
} | |
TuioTime time; | |
*/ | |
// create some sliders | |
namedWindow(windowName); | |
createTrackbar("xMin", windowName, &xMin, 640); | |
createTrackbar("xMax", windowName, &xMax, 640); | |
createTrackbar("yMin", windowName, &yMin, 480); | |
createTrackbar("yMax", windowName, &yMax, 480); | |
// create background model (average depth) //头一帧 深度数据 作为模型帧。 | |
for (unsigned int i=0; i<nBackgroundTrain; i++) { | |
xnContext.WaitAndUpdateAll();//刷新 | |
depth.data = (uchar*) xnDepthGenerator.GetDepthMap();//获取深度数据 | |
buffer[i] = depth; | |
} | |
average(buffer, background); | |
while ( (char) waitKey(1) != (char) 27 ) {//ESC 按键 退出 | |
// read available data | |
xnContext.WaitAndUpdateAll();//刷新 | |
// update 16 bit depth matrix | |
depth.data = (uchar*) xnDepthGenerator.GetDepthMap(); | |
//xnImgeGenertor.GetGrayscale8ImageMap() | |
// update rgb image | |
//rgb.data = (uchar*) xnImgeGenertor.GetRGB24ImageMap(); // segmentation fault here | |
//cvtColor(rgb, rgb, CV_RGB2BGR); | |
// extract foreground by simple subtraction of very basic background model | |
foreground = background - depth;//计算深度差值 | |
// find touch mask by thresholding (points that are close to background = touch points) | |
touch = (foreground > touchDepthMin) & (foreground < touchDepthMax);//判断深度差在指定阀值内 Mask遮罩 | |
// extract ROI | |
Rect roi(xMin, yMin, xMax - xMin, yMax - yMin); | |
Mat touchRoi = touch(roi);//画框Rect | |
// find touch points | |
vector< vector<Point2i> > contours; | |
vector<Point2f> touchPoints;//touchPoints 存储 点位 | |
findContours(touchRoi, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, Point2i(xMin, yMin));//轮廓检测,自动画框findContours | |
for (unsigned int i=0; i<contours.size(); i++) {//轮询检测结果 | |
Mat contourMat(contours[i]); | |
// find touch points by area thresholding 按区域阈值查找接触点 | |
if ( contourArea(contourMat) > touchMinArea ) {//contourArea 计算图像轮廓的面积 | |
Scalar center = mean(contourMat); | |
Point2i touchPoint(center[0], center[1]); | |
touchPoints.push_back(touchPoint); | |
} | |
} | |
// send TUIO cursors | |
/* | |
time = TuioTime::getSessionTime(); | |
tuio->initFrame(time); | |
for (unsigned int i=0; i<touchPoints.size(); i++) { // touch points | |
float cursorX = (touchPoints[i].x - xMin) / (xMax - xMin); | |
float cursorY = 1 - (touchPoints[i].y - yMin)/(yMax - yMin); | |
TuioCursor* cursor = tuio->getClosestTuioCursor(cursorX,cursorY); | |
// TODO improve tracking (don't move cursors away, that might be closer to another touch point) | |
if (cursor == NULL || cursor->getTuioTime() == time) { | |
tuio->addTuioCursor(cursorX,cursorY); | |
} else { | |
tuio->updateTuioCursor(cursor, cursorX, cursorY); | |
} | |
} | |
tuio->stopUntouchedMovingCursors(); | |
tuio->removeUntouchedStoppedCursors(); | |
tuio->commitFrame(); | |
*/ | |
// draw debug frame | |
depth.convertTo(depth8, CV_8U, 255 / debugFrameMaxDepth); // render depth to debug frame 转颜色 | |
cvtColor(depth8, debug, CV_GRAY2BGR); | |
debug.setTo(debugColor0, touch); // touch mask | |
rectangle(debug, roi, debugColor1, 2); // surface boundaries | |
for (unsigned int i=0; i<touchPoints.size(); i++) { // touch points 绘画框/圆 | |
circle(debug, touchPoints[i], 5, debugColor2, CV_FILLED); | |
} | |
// render debug frame (with sliders) | |
imshow(windowName, debug);//显示 | |
//imshow("image", rgb); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment