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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM