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() - 1);
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; 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 = 0; i < m.rows;i++)
    {
        for (int j = 0; 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 = 1;
    int lineType = 8;
    Scalar sca(0, 255, 0);
    char index[20];

    int vy = 55;
    int vx = 10;
    putText(img, "frame:"+to_string(frame), Point(vx, vy += 15), 1, 1, Scalar(0, 0, 0));
    putText(img, "s--save", Point(vx, vy += 15), 1, 1, Scalar(0, 0, 0));
    putText(img, "n--next", Point(vx, vy += 15), 1, 1, Scalar(0, 0, 0));
    putText(img, "p--previous", Point(vx, vy += 15), 1, 1, Scalar(0, 0, 0));

    for (int i = 0; i < JOINT_NUM; i++)
    {
        ///校验 x y坐标 可能在画面外
        x[i] = max((float)20.0, x[i]);
        x[i] = min((float)WIDTH - 5, x[i]);
        y[i] = max((float)25.0, y[i]);
        y[i] = min((float)HEIGHT - 5, y[i]);
    }

    for (int i = 0; i < JOINT_NUM; i++)
    {
        sprintf_s(index, "%d", i);
        circle(img, Point(x[i], y[i]), 2, Scalar(0, 255, 0), 2, 8);
        putText(img, JOINTS[i], Point(x[i], y[i]), 1, 1, Scalar(0, 0, 255));
        putText(img, index, Point(x[i] + 5, y[i] - 10), 1, 1, Scalar(0, 0, 255));

        if (i == 2 || i == 5 || i == 8 || i == 11 || i == 14)continue;

        line(img, Point(x[i], y[i]), Point(x[i + 1], y[i + 1]), sca, thickness, lineType);
    }
    line(img, Point(x[0], y[0]), Point(x[9], y[9]), sca, thickness, lineType);//hip--lthigh
    line(img, Point(x[0], y[0]), Point(x[12], y[12]), sca, thickness, lineType);//hip--rthigh
    line(img, Point(x[2], y[2]), Point(x[3], y[3]), sca, thickness, lineType);//neck--lshldr
    line(img, Point(x[2], y[2]), Point(x[6], y[6]), 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 = 0;

//读取关节点坐标存入 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 = 0; 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 = 0; i < JOINT_NUM;i++)//选中某个关节
        {
            if (abs(jointsX[i] - x) < 10 && abs(jointsY[i] - y) < 10)
            {
                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", 32, 30);
    char  featurePath[128];
    ofstream errlog("output/errlog.txt", ios_base::app);
    for (int model = 1; model <= 6;model++)
    {
        for (int action = 1; action <= 14;action++)
        {
            for (int frame = 0; frame < 125;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, 1, 32);
                if (featrueMat.empty())//说明为空
                {
                    errlog << featurePath << " 不存在"<<endl;
                    cout << featurePath << " 不存在" << endl;
                    errlog.close();
                    continue;
                }
                Mat guessJoints = featrueMat * projectMat;
                char temp[128];
                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", 32, 30);
    char  pointCloud[128];
    char joints[128];
    for (int action = 1; action <= 14; action++)
    {
        for (int model = 1; model <= 6; model++)
        {
            for (int frame = 0; frame < 125; 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, 1);
                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 = 0;
                bool processFlag = true;
                while (processFlag)
                {
                    keyValue = waitKey(0);//没有这句话会卡住不动。。。

                    switch (keyValue)
                    {
                    case ‘p‘:    //重新加载上一帧
                        if (frame >= 0)
                        {
                            if (frame == 0)frame = -1;
                            else frame -= 2;
                            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 2424832:////left

                        jointsX[selectIndex] -= 1;
                        break;

                    case 2490368://38://up
                        jointsY[selectIndex] -= 1;
                        break;

                    case 2555904://39://right
                        jointsX[selectIndex] += 1;
                        break;

                    case 2621440://40://down
                        jointsY[selectIndex] += 1;
                        break;
                    default:
                        break;
                    }

                    tools.DrawSkeleton(photo, jointsX, jointsY);
                }
            }
        }
    }
}

int main()
{

    //generateJoints();

    calibration();

    getchar();
    return 0;
}
时间: 2024-10-10 21:58:39

OpenCV学习3-----利用鼠标、键盘回调函数实现标定人体关节点的相关文章

OpenCV 学习(利用滤波器进行边缘提取)

OpenCV 学习(利用滤波器进行边缘提取) 通过低通滤波器,我们可以将图像平滑,相反的,利用高通滤波器可以提取出图像的边缘. Sobel 滤波器 Sobel 滤波器是一种有方向性的滤波器,可以作用在 X 方向或 Y 方向. 关于这种滤波器的理论介绍可以参考: https://en.wikipedia.org/wiki/Sobel_operator 函数原型如下: void Sobel( InputArray src, OutputArray dst, int ddepth, int dx, i

node.js学习笔记(二)——回调函数

Node.js 异步编程的直接体现就是回调. 那什么是回调呢?回调指的是将一个函数作为参数传递给另一个函数,并且通常在第一个函数完成后被调用.需要指明的是,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应.回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数.例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回.这样在执行代码时就没有阻

【Node.js学习四】 Node.js回调函数

Node.js 回调函数 Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了. 回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数. 例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回.这样在执行代码时就没有阻塞或等待文件 I/O 操作.这就大大提高了 Node.js 的性能,可以处理大量的并发请求. 下面对阻塞代码和非阻塞代码分别举出一例

OpenCV学习 5:关于平滑滤波器 cvSmooth()函数

原创文章,欢迎转载,转载请注明出处 本节主要了解下cvSmooth函数的一些参数对结果的影响.从opencv tutorial中可以看到这样一段话: 像我这样的数学渣,还是看下图来得形象: 高斯滤波器的说明如下: 反正看得我晕,我只知道一点,高斯滤波对于去除服从正态分布的噪声很有效.看来学好数学还是很重要...还有英语....英语渣一个.. 平滑方法使用 CV_GAUSSIAN,平滑窗口一次为3*3,5*5,9*9和11*11.下面直接看效果.1:原图 2:核为3*3 3:核为5*5 4:9*9

GTK入门学习:信号与回调函数

前面我们学习的GTK界面都是静态的,我们按下按钮它是没有响应的,如何让它有响应呢?接下来我们一起学习GTK的信号与回调函数. GTK采用了信号与回调函数来处理窗口外部传来的事件.消息或信号.当信号发生时,程序自动调用为信号连接的回调函数. 学习应用编程,我们会经常接触到"信号"这个名词.GTK中的"信号"实际上是一种软件中断."中断"在我们生活中经常遇到,譬如,我正在房间里打游戏,突然送快递的来了,把正在玩游戏的我给"中断"了

OpenGL的GLUT注册回调函数[转]

OpenGL的注册回调函数 void glutDisplayFunc(void (*func)(void) ); 为当前窗口设置显示回调函数 void glutOverlayDisplayFunc(void (*func)(void) ); 注册当前窗口的重叠层的显示回调函数 void glutReshapeFunc(void (*Func)(int width, int height) ); 指定当窗口的大小改变时调用的函数 void glutKeyboardFunc(void (*func)

Java回调函数详解

为了了解什么是回调函数,在网上查阅了如下资料,整理如下: 资料一: 首先说说什么叫回调函数? 在WINDOWS中,程序员想让系统DLL调用自己编写的一个方法,于是利用DLL当中回调函数(CALLBACK)的接口来编写程序,使它调用,这个就 称为回调.在调用接口时,需要严格的按照定义的参数和方法调用,并且需要处理函数的异步,否则会导致程序的崩溃. 这样的解释似乎还是比较难懂,这里举个简 单的例子: 程序员A写了一段程序(程序a),其中预留有回调函数接口,并封装好了该程序.程序员B要让a调用自己的程

JS之——回调函数的简单应用(插曲)

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46274893 JS无疑是前端页面与服务端交互数据的强大语言,那么如何利用JS的回调函数来封装JS的网络请求呢,本文就来简单说明这一问题 一.创建一个index.html文件 页面内容随便定义,重点是要引入如下自定义的两个js文件,一个是constants.js一个是validate.js 二.创建constants.js文件 /** * Created by liuyazhuan

OpenCV for Python 学习 (二 事件与回调函数)

今天主要看了OpenCV中的事件以及回调函数,这么说可能不准确,主要是下面这两个函数(OpenCV中还有很多这些函数,可以在 http://docs.opencv.org/trunk/modules/highgui/doc/user_interface.html 找到,就不一一列举了),然后自己做了一个简单的绘图程序 函数如下: cv2.setMouseCallback(windowName, onMouse[, param]) cv2.createTrackbar(trackbarName,