最近做實驗,需要一些人體關節點的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; }