#include
#include
#include
#include
using namespace std;
using namespace cv;
using namespace dnn;
//大致步骤:加载网络文件,输入到网络去,加载检测的图片,图片预处理,处理的图片送入网络,网络返回结果,再结果中找到点,点的位置再拟合到原图中,展示原图
int nPoints = 22;//21个关键带你
string protoFile = "H:\\openpose_data\\0.prototxt";//prototxt表示模型的配置文件
string weightsFile = "H:\\openpose_data\\1.caffemodel";//caffeModel表示模型的权重二进制文件
const int POSE_PAIRS[20][2] =//手指姿势数组
{//每个手指4个关键点(一点两个位置坐标参数)(5个手指每个手指4个关键点加一个手掌点等于21,故【20】【2】的数组)
{0,1}, {1,2}, {2,3}, {3,4}, // 拇指
{0,5}, {5,6}, {6,7}, {7,8}, // 食指
{0,9}, {9,10}, {10,11}, {11,12}, // 中指
{0,13}, {13,14}, {14,15}, {15,16}, // 无名指
{0,17}, {17,18}, {18,19}, {19,20} // 小指
};
int main()
{
Mat img;
VideoCapture cap;
cap.open("H:\\openpose_data\\my_hand.mp4");
if(!cap.isOpened())
{
cout << "video can not open " << endl;
}
//cap.set(CAP_PROP_FOURCC, VideoWriter::fourcc('M', 'J,', 'P', 'G'));
//cap.set(CAP_PROP_FRAME_WIDTH, 640);
//cap.set(CAP_PROP_FRAME_HEIGHT, 480);
namedWindow("pose", WINDOW_AUTOSIZE);
int a = 0;
while (true)
{
a += 30;
if (a >= cap.get(CAP_PROP_FRAME_COUNT))//得到总的帧数
{
cout << "检测完成" << endl;
break;
}
cap.set(CAP_PROP_POS_FRAMES, a); //(跳帧)优化图片,缩减尺寸和提高图片质量(直方图均衡化),设置缓冲区,处理时间小于帧差间隔(重点)
if (cap.get(CAP_PROP_POS_MSEC) != 0)//暂停几秒,让图片完成装载
{
Sleep(100);
}
cap >> img;
//resize(img, img, Size(640,480));//缩减尺寸
float aspect_ratio = img.cols / (float)img.rows;//获取图片的宽高比
int inHeight = 368;
int inWidth = (int)(aspect_ratio * inHeight);//感觉这里的*8/8没用啊
float thresh = 0.01;//定义关键点的阈值
Net net = readNetFromCaffe(protoFile, weightsFile);//加载网络模型函数
net.setPreferableBackend(DNN_BACKEND_OPENCV);//此函数是加速网络推理的设置
Mat inpBlob = blobFromImage(img, 1.0 / 255, Size(inWidth, inHeight), Scalar(0, 0, 0), false, false);//用于图片识别时图片预处理,图片缩放和减均值处理
//模型推理过程就是神经网络模型进行一次前向传播,在OpenCV中,用以下可读性非常强的两行代码即可完成
net.setInput(inpBlob);
Mat output = net.forward();// 返回预测结果(类别,置信度,2个坐标位置(x、y))
//类别的对应清单,从classification_classes_ILSVRC2012.txt获取
//forward函数返回一个Mat变量,返回值是指输入的layername首次出现的输出默认输出整个网络的运行结果
int H = output.size[2];
int W = output.size[3];
//这里的H、W是在预测的关键点位置信息
// 寻找关键点
vector<Point> points(nPoints);//赋值22给points(22->0-21就是上面手的21个关键点)
for (int n = 0; n < nPoints; n++)//遍历
{
// 相应身体部分的概率图
Mat probMap(H, W, CV_32F, output.ptr(0, n));//将关键点x作为H、y作为W构建一个矩阵,再将21个关键点输入进这个矩阵中
resize(probMap, probMap, Size(img.cols, img.rows));//裁剪相应的大小,恢复成原图的大小,也便确定点的位置
Point maxLoc; // 获取位置
double prob; //取最大的概率数据
minMaxLoc(probMap, 0, &prob, 0, &maxLoc);//在数组中找到全局最小和最大值
//参数1输入的数组若是图像需为单通道图像、参数2返回最小值的指针若无需返回此值设为 NULL、参数3返回最大值的指针若无需返回此值设为 NULL
//参数4返回最小值位置的指针(二维情况下)若无需返回此值设为 NULL、参数5返回最大值位置的指针(二维情况下)若无需返回此值设为 NULL、参数6可选的掩膜操作
points[n] = maxLoc;//把最大概率的点的位置装入动态数组内,为接下来画线做准备
}
//关键点之间画线
int nPairs = sizeof(POSE_PAIRS) / sizeof(POSE_PAIRS[0]);
for (int n = 0; n < nPairs; n++)
{
// 查找 2 个连接的身体/手部部件
Point2f partA = points[POSE_PAIRS[n][0]];
Point2f partB = points[POSE_PAIRS[n][1]];
if (partA.x <= 0 || partA.y <= 0 || partB.x <= 0 || partB.y <= 0)
continue;
line(img, partA, partB, Scalar(0, 255, 255), 2);//画线函数
circle(img, partA, 8, Scalar(0, 0, 255), -1);
circle(img, partB, 8, Scalar(0, 0, 255), -1);
}
imshow("pose", img);
waitKey(30);
}
cap.release();
return 0;
}
//对关键点之间的操作映射为鼠标事件
//就可以使用手势来实现一些电脑上的操作
识别效果:
B站(这个也是我的账号呦)