OpenCV学习3-----利用鼠标、键盘回调函数实现标定人体关节点
最近做实验,需要一些人体关节点的ground truth,需要自己手动标定,于是尝试使用OpenCV的鼠标键盘回调函数实现。
期间遇到不少问题,记录一下。
首先就是鼠标回调函数注册,
namedWindow("calibration");
setMouseCallback("calibration", onMouse, &photo);
其中onMouse为处理鼠标事件的函数。里面需要用的一个索引selectIndex来标记当前鼠标选择的关节点是哪一个。然后当鼠标点击相应关节点并拖动时,
要时刻更新相应关节点的坐标,并更新画面。更新画面函数为:
void Public::DrawSkeleton(Mat& photo, vector<float>& x, vector<float>& y)
其中里面有一句代码特别重要就是第二行:
photo.copyTo(img);
要将当前画面拷贝到一个新的画面上,不能直接操作原来的就画面。否则画面不会更新。
还有就是在更新画面的函数里显示当前画面的imshow,不能在这个后面加上waitkey,否则就不能退出当前帧,直到栈溢出。。
imshow("calibration", img);//不能在这儿加waitkey 否则就没有退出这个函数。。栈溢出
要在主函数中使用waitkey(0),一直监听鼠标键盘的操作,写到while循环里面,否则只会更新一帧后就会卡住不动。
还有开始不知道键盘上的↑ ↓← →的ASCII码什么是什么。。于是 通过waikey返回值,将其输出到屏幕上,就这样得到了他们的键值。。。
如下是一些代码和效果图。
效果如图所示:

public.h头文件
#ifndef _PUBLIC_H
#define _PUBLIC_H #include <opencv/cv.h>
#include <opencv/cvaux.h>
#include <highgui.h>
#include <fstream>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using namespace cv;
//一些公共参数 工具函数放到此处
#define HEIGHT 480
#define WIDTH 640
#define JOINT_NUM 15
#define NUM_SUPERPIXEL 2500 class Public
{
public:
Mat InitMat(string matrixPath, int m, int n);
void WriteMat(Mat& m,string path);
void DrawSkeleton(Mat& img, Mat& jointsMatrix);
void DrawSkeleton(Mat& img, vector<float>& x, vector<float>& y);
vector<string> JOINTS;
Mat img;
int frame;
Public();
~Public(){} }; #endif
public.cpp文件
#include "public.h" Public::Public()
{
JOINTS = { "hip", "chest", "neck", "lShldr", "lForeArm", "lHand", "rShldr", "rForeArm", "rHand", "lThigh", "lShin", "lFoot", "rThigh", "rShin", "rFoot" };
}
// 读入一个txt 返回一个m*n的矩阵
Mat Public::InitMat(string matrixPath, int m, int n)
{
ifstream matrixFile(matrixPath);
float temp;
Mat mat(m, n, CV_32F);
vector<float>xVec;//保存所有坐标
if (matrixFile.is_open())
{
while (!matrixFile.eof())
{
matrixFile >> temp;
xVec.push_back(temp);
}
}
else
{
cout << "不能打开文件!" << matrixPath.c_str() << endl;
return mat;
}
xVec.erase(xVec.end() - );
for (int i = ; i < m; i++)
{
for (int j = ; j < n; j++)
{
mat.at<float>(i, j) = xVec[i * n + j];
}
}
return mat;
}
//将mat矩阵 float类型 保存到path中 格式为
void Public::WriteMat(Mat& m, string path)
{
if (m.empty())return;
ofstream of(path);
for (int i = ; i < m.rows;i++)
{
for (int j = ; j < m.cols;j++)
{
of << m.at<float>(i, j) << " ";
}
of << endl;
}
of.close();
} /*
虚拟数据将关节点位置连接起来 成为一个骨架模型
0 1 hip
2 3 chest
4 5 neck
6 7 lShldr
8 9 lForeArm
10 11 lHand
12 13 rShldr
14 15 rForeArm
16 17 rHand
18 19 lThigh
20 21 lShin
22 23 lFoot
24 25 rThigh
26 27 rShin
28 29 rFoot
*/void Public::DrawSkeleton(Mat& photo, vector<float>& x, vector<float>& y)
{
if (photo.empty())return;
photo.copyTo(img); int thickness = ;
int lineType = ;
Scalar sca(, , );
char index[]; int vy = ;
int vx = ;
putText(img, "frame:"+to_string(frame), Point(vx, vy += ), , , Scalar(, , ));
putText(img, "s--save", Point(vx, vy += ), , , Scalar(, , ));
putText(img, "n--next", Point(vx, vy += ), , , Scalar(, , ));
putText(img, "p--previous", Point(vx, vy += ), , , Scalar(, , )); for (int i = ; i < JOINT_NUM; i++)
{
///校验 x y坐标 可能在画面外
x[i] = max((float)20.0, x[i]);
x[i] = min((float)WIDTH - , x[i]);
y[i] = max((float)25.0, y[i]);
y[i] = min((float)HEIGHT - , y[i]);
} for (int i = ; i < JOINT_NUM; i++)
{
sprintf_s(index, "%d", i);
circle(img, Point(x[i], y[i]), , Scalar(, , ), , );
putText(img, JOINTS[i], Point(x[i], y[i]), , , Scalar(, , ));
putText(img, index, Point(x[i] + , y[i] - ), , , Scalar(, , )); if (i == || i == || i == || i == || i == )continue; line(img, Point(x[i], y[i]), Point(x[i + ], y[i + ]), sca, thickness, lineType);
}
line(img, Point(x[], y[]), Point(x[], y[]), sca, thickness, lineType);//hip--lthigh
line(img, Point(x[], y[]), Point(x[], y[]), sca, thickness, lineType);//hip--rthigh
line(img, Point(x[], y[]), Point(x[], y[]), sca, thickness, lineType);//neck--lshldr
line(img, Point(x[], y[]), Point(x[], y[]), sca, thickness, lineType);//neck--rshldr
imshow("calibration", img);//不能在这儿加waitkey 否则就没有退出这个函数。。栈溢出
}
calibration.cpp文件
/* 真实数据标定骨架点程序
1.首先没有groundtruth,只有之前产生的特征点
2.每一帧frame的特征点F乘以之前随便训练好的映射矩阵M
得到一个初步需要调整的骨架点信息S = F*M 保存起来
3.将每一个关节点编号1-15 连线 然后调整位置
4.将调整后的关节点保存 更新
*/
#include <cv.h>
#include <cvaux.h>
#include <highgui.h>
#include <fstream>
#include "ImageShow.h"
#include "Loading.h"
#include "public.h" using namespace std;
using namespace cv; //1首先load点云 显示出来
//2计算关节点位置 显示出来
//3调节关节点位置 保存 进入下一帧 vector<float>jointsX;
vector<float>jointsY;
Public tools;
int selectIndex = ; //读取关节点坐标存入 jointsX jointsY
void loadGroundTruth(string filePath)
{
ifstream infile(filePath);
jointsX.clear();
jointsY.clear();
if (infile.is_open())
{
float x, y;
while (!infile.eof())
{
infile >> x >> y;
jointsX.push_back(x);
jointsY.push_back(y);
}
}
else cout << "不能打开文件!" << endl;
jointsX.pop_back();
jointsY.pop_back();
}
//将jointsX jointsY 保存
void saveGroundTruth(string filePath)
{
ofstream outfile(filePath);
for (int i = ; i < JOINT_NUM;i++)
{
outfile << jointsX[i] << " "<< jointsY[i] << endl;
}
outfile.close();
} void onMouse(int event, int x, int y, int flags, void* param)
{
static bool isMouseDown = false;
//static int selectIndex = 0;
//Mat *photo = (Mat*)param;
//Mat temp = photo->clone();
if (event == CV_EVENT_LBUTTONDOWN)
{
for (int i = ; i < JOINT_NUM;i++)//选中某个关节
{
if (abs(jointsX[i] - x) < && abs(jointsY[i] - y) < )
{
cout << "选中关节:"<<i <<endl;
selectIndex = i;
isMouseDown = true;
break;
}
}
}
if (event == CV_EVENT_LBUTTONUP)
{
isMouseDown = false;
}
if (event == CV_EVENT_MOUSEMOVE)
{
if (isMouseDown)
{
jointsX[selectIndex] = x;
jointsY[selectIndex] = y; tools.DrawSkeleton(*(Mat *)param, jointsX, jointsY);//更新画面
}
}
return; }
Mat InitMat(string matrixPath, int m, int n);
//利用已经有的特征点 乘以 映射矩阵 生成初步估计的关节点
void generateJoints()
{
Mat projectMat = tools.InitMat("E:/MatrixT.txt", , );
char featurePath[];
ofstream errlog("output/errlog.txt", ios_base::app);
for (int model = ; model <= ;model++)
{
for (int action = ; action <= ;action++)
{
for (int frame = ; frame < ;frame++)
{
sprintf_s(featurePath, "E:/laboratory/dataset/realdataresults/model%d/action%d/%d/clusterpoint.txt",model,action,frame);
cout << featurePath << endl;
ifstream isexist(featurePath);
if (!isexist.is_open())//当前不存在
{
continue;
}
Mat featrueMat = tools.InitMat(featurePath, , );
if (featrueMat.empty())//说明为空
{
errlog << featurePath << " 不存在"<<endl;
cout << featurePath << " 不存在" << endl;
errlog.close();
continue;
}
Mat guessJoints = featrueMat * projectMat;
char temp[];
sprintf_s(temp, "E:/laboratory/dataset/realdataresults/model%d/action%d/%d/guessJoints.txt", model, action, frame);
tools.WriteMat(guessJoints, temp);
}
}
} } //读入关节点位置 并标号 连线 调整位置 更新
void calibration()
{
Mat projectMat = tools.InitMat("E:/MatrixT.txt", , );
char pointCloud[];
char joints[];
for (int action = ; action <= ; action++)
{
for (int model = ; model <= ; model++)
{
for (int frame = ; frame < ; frame++)
{
sprintf_s(pointCloud, "E:/laboratory/dataset/realdata/action%d/model%d/%dfinal.txt", action, model, frame);
ifstream isexist(pointCloud);
if (!isexist.is_open())continue;//当前不存在
cout << pointCloud << endl; ImageShow ShowTool;
Mat photo(HEIGHT, WIDTH, CV_8UC3);
vector<Data> src;
Loading Load;
Load.DownLoad_Info(pointCloud, src, );
photo = ShowTool.getPhoto(src);// 加载点云 sprintf_s(joints, "E:/laboratory/dataset/realdataresults/model%d/action%d/%d/guessJoints.txt", model, action, frame);
ifstream isexist2(joints);
if (!isexist2.is_open())continue;//当前不存在
cout << joints << endl;
loadGroundTruth(joints);//关节点 namedWindow("calibration");
setMouseCallback("calibration", onMouse, &photo);
tools.frame = frame;
tools.DrawSkeleton(photo, jointsX, jointsY); int keyValue = ;
bool processFlag = true;
while (processFlag)
{
keyValue = waitKey();//没有这句话会卡住不动。。。 switch (keyValue)
{
case 'p': //重新加载上一帧
if (frame >= )
{
if (frame == )frame = -;
else frame -= ;
processFlag = false;
}
break;
case 's'://save
case 'S':
saveGroundTruth(joints);
cout << "success save"<< endl;
break;
case 'n':
case 'N'://next frame
processFlag = false;
break;
case :////left jointsX[selectIndex] -= ;
break; case ://38://up
jointsY[selectIndex] -= ;
break; case ://39://right
jointsX[selectIndex] += ;
break; case ://40://down
jointsY[selectIndex] += ;
break;
default:
break;
} tools.DrawSkeleton(photo, jointsX, jointsY);
}
}
}
}
} int main()
{ //generateJoints(); calibration(); getchar();
return ;
}
OpenCV学习3-----利用鼠标、键盘回调函数实现标定人体关节点的更多相关文章
- Objective-C学习笔记 利用协议实现回调函数
来源:http://mobile.51cto.com/iphone-278354.htm Objective-C学习笔记 利用协议实现回调函数是本文要介绍的内容,主要是实现一个显示文字为测试的视图,然 ...
- Opencv 学习笔记之——鼠标,进度条操作
Opencv中提供一个鼠标调用的函数,SetMouseCallback()函数,它配合一个回调函数来实现鼠标操作的功能. 首先看一下SetMouseCallback的函数原型: c++: void ...
- opengl键盘回调函数不能获取Ctrl+c的问题
我要令窗口在按下 Ctrl+c 之后关闭. 关键代码如下: /* 这段代码位于键盘回调函数中 */ if ((glutGetModifiers() == GLUT_ACTIVE_CTRL) & ...
- Cocos Creator学习三:生命周期回调函数
1.目的:学习生命周期回调函数以及回调顺序,更有利于我们逻辑的处理把控. 2.生命周期回调函数: 节点:指cc.Node:组件:指cc.Component. ①onLoad:脚本组件绑定的节点所在场景 ...
- DELPHI语法基础学习笔记-Windows 句柄、回调函数、函数重载等(Delphi中很少需要直接使用句柄,因为句柄藏在窗体、 位图及其他Delphi 对象的内部)
函数重载重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同.实际上,编译器是通过检测参数来确定需要调用的例程.下面是从VCL 的数学单元(Math Unit)中摘录的一 ...
- JavaScript学习-5——异步同步、回调函数
----------异步同步函数 ----------回调函数 一.异步同步函数 同步:发送一个请求,等待返回,然后再发送下一个请求 异步:发送一个请求,不等待返回,随时可以再发送下一个请求 同步可以 ...
- node.js学习笔记(二)——回调函数
Node.js 异步编程的直接体现就是回调. 那什么是回调呢?回调指的是将一个函数作为参数传递给另一个函数,并且通常在第一个函数完成后被调用.需要指明的是,回调函数不是由该函数的实现方直接调用,而是在 ...
- 我们一起学习WCF 第八篇回调函数
什么是回调函数? 一个简单的例子:小明想要在京东购买一件商品.他会登陆网站选好自己的商品.然后他把这件商品放在购物车,然后开始付钱(这个表示触发,不付钱不发货(排除货到付款)).然后京东的人员收到了小 ...
- STM32 HAL库学习系列第8篇---回调函数总结
普通函数与回调函数的区别:就是ST将中断封装,给使用者的API,就是标准库的中断函数 对普通函数的调用: 调用程序发出对普通函数的调用后,程序执行立即转向被调用函数执行,直到被调用函数执行完毕后,再返 ...
随机推荐
- java内存模型6-final
与前面介绍的锁和volatile相比较,对final域的读和写更像是普通的变量访问.对于final域,编译器和处理器要遵守两个重排序规则: 在构造函数内对一个final域的写入,与随后把这个被构造对象 ...
- Ajax01 什么是ajax、获取ajax对象、ajax对象的属性和方法
1 什么是ajax ajax是一种用来改善用户体验的技术,其本质是利用浏览器提供的一个对象(XMLHttpRequest,也可称之为ajax对象) 向服务器发送异步请求;服务器返回部分数据(不是一个完 ...
- Javascript版-显示相应图片的详细信息
Hi All, 分享一个通过JS来显示相应图片的详细信息. 需求:进入页面时,动态加载图片信息:当鼠标移动到某一图片上时,则显示该图片的大图片并显示相应说明信息:当鼠标移开图片时,清除新创建的元素. ...
- js中 substr(), substring(), slice()的区别
一.作用 三者都是基于原字符串创建新字符串的方法. 接收一到两个参数,第一个参数截取字符串的开始位置(字符下标,从0开始),第二个参数因方法不同而不同,后面不同点会说到. 另外,三个方法都不会修改原字 ...
- 放弃FreeMark?
JAVA项目FreeMark 生产静态页面 a) 弊端 i. 依赖后台工程师拼装页面,不利于前端.后台工作完全解耦. ii. ...
- 源代码安装软件-MySQL
一.源码安装 1.经典的源代码安装三步曲: 1.编译前的配置 ./configure 2.编译 make 3.安装 make install 2.源代码软件安装步骤: 1.下载软件包 2.校验软件包 ...
- D. 实验室传染病
D. 实验室传染病 题意 给出 n 个人的位置,以及每个人的传染范围,当一个人患病时,他的传染范围内(包括边界上)的人全部会被感染并继续向外传播. 求以每个人为传染源最多有多少人被感染. 分析 首先二 ...
- IPv6启动五年后,距离我们究竟还有多远?
作者:RicardoIPv6拥有更好的IP拓展性,更高的安全保障以及更快的传输速度,互联网协会将2012年6月6日定为了世界IPv6启动日,距此5年后,国内外Cloudflare.又拍云等CDN服务已 ...
- CTE递归 MAXRECURSION 遇到的问题
在使用Sql Server的时候,当需要递归的时候很多时候就会想到使用CTE.但是当递归层数比较多,超过了100层,或者是一个递归死循环的时候.执行就会爆递归次数已到,最多100的错误. 当面对第一种 ...
- [转]tomcat部署(1)
阅读目录 1 目录结构 2 部署 3 发布 4 测试 本篇参考Tomcat官方文档:<First Webapp>翻译,并结合自己的开发经验介绍关于tomcat部署以及发布的相关内容. ...