opencv通过dnn模块调用openpose 网络实现人体姿态预测

下载openpose对应数据集的与训练模型(caffe model)
本文采用的是coco数据集,拥有18个点,openpose 网络的caffe模型权重文件。
下载链接如下:
caffemodel
prototxt
定义连接对,18个点关联的两个点为一对,比如鼻子跟眼睛是关联对。为了方便预测后直接连接两点而预先确定那两个点作为连接对。
通过dnn的net加载模型,并且进行预测。
然后需要注意的是,特征识别网络到底需要什么输入输出数据。这里你可以通过模型配置文件prototxt读取获得,例如本文用到的配置文件打开后,前面几行定义了输入数据的dim(dimension维度)如下:

input: "image"
input_dim: 1
input_dim: 3
input_dim: 1 # This value will be defined at runtime
input_dim: 1 # This value will be defined at runtime

表示输入的是一个size为13n*n的数据,为什么h,w(高,宽)为n呢?因为值为1就表示动态决定输入数据的宽度和高度,但是就使用的时候,最好还是定义为368比较合适,(具体为什么为368我也不知道),然后输出的数据size可以从最后的一层得到,查看最后一层得到这样的信息:

layer {
  name: "Mconv7_stage6_L1"
  type: "Convolution"
  bottom: "Mconv6_stage6_L1"
  top: "Mconv7_stage6_L1"
  param {
    lr_mult: 4.0
    decay_mult: 1
  }
  param {
    lr_mult: 8.0
    decay_mult: 0
  }
  convolution_param {
    num_output: 38
    pad: 0
    kernel_size: 1
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "Mconv7_stage6_L2"
  type: "Convolution"
  bottom: "Mconv6_stage6_L2"
  top: "Mconv7_stage6_L2"
  param {
    lr_mult: 4.0
    decay_mult: 1
  }
  param {
    lr_mult: 8.0
    decay_mult: 0
  }
  convolution_param {
    num_output: 19
    pad: 0
    kernel_size: 1
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "concat_stage7"
  type: "Concat"
  bottom: "Mconv7_stage6_L2"
  bottom: "Mconv7_stage6_L1"
  # top: "concat_stage7"
  top: "net_output"
  concat_param {
    axis: 1
  }
}

得到它其实是一个连接合并层concat,查询caffe对层的定义可以得到concat就是连接了两个层的结果合并成一个结果。然后我们可以查看它合并的具体哪两个层,由bottom属性可以得到,它其实合并了两个层,stage6_L2跟stage_L1,然后合并是按照c(channel)合并的(不明白的可以查询合并相关的例子,例如axis:1就表示按照channel合并,0就表示按照数量合并,本例中是1,nchw 分别表示个数,通道,高度,宽度)
然后计算两个的输出相加可以算出来最后输出的channel是19+38=57。然后就是我们只需要前面18个数据就好了,后面的我也不知道是什么。
明白了这些内容,然后就对你建立net然后输入,输出很有帮助。
这些做完了,就是初始化net然后传入数据,取出数据了。
输出的数据其实就是57个热力图数据,我们只要前面18个keypoint的热力图数据,然后把热力图数据转换成64
64的矩阵,然后回调minMaxLoc取出来最大的confidence(置信度)的位置。然后把这个点保存起来。
然后遍历我们构造的连接对,从连接对里面取出每个连接点,然后查看刚刚取出的这个链接点的位置坐标(注意坐标位置要乘以缩放系数,因为我们把输出图片定义成了64*64,所以缩放系数就是原图片的宽高分别除以64),如果小于等于0,就忽略这个连接对。然后画直线,展示。
代码如下(已经做了详细的解释):


#include 
#include 
#include 
using namespace cv;
using namespace cv::dnn;

#include 
using namespace std;
const int POSE_PAIRS[3][20][2] = {
{   // COCO body
	{1,2}, {1,5}, {2,3},
	{3,4}, {5,6}, {6,7},
	{1,8}, {8,9}, {9,10},
	{1,11}, {11,12}, {12,13},
	{1,0}, {0,14},
	{14,16}, {0,15}, {15,17}
},
{   // MPI body
	{0,1}, {1,2}, {2,3},
	{3,4}, {1,5}, {5,6},
	{6,7}, {1,14}, {14,8}, {8,9},
	{9,10}, {14,11}, {11,12}, {12,13}
},
{   // hand
	{0,1}, {1,2}, {2,3}, {3,4},         // thumb
	{0,5}, {5,6}, {6,7}, {7,8},         // pinkie
	{0,9}, {9,10}, {10,11}, {11,12},    // middle
	{0,13}, {13,14}, {14,15}, {15,16},  // ring
	{0,17}, {17,18}, {18,19}, {19,20}   // small
} };

int main(int argc, char **argv)
{


	String modelTxt = "pose_deploy_linevec.prototxt";
	String modelBin = "pose_iter_440000.caffemodel";
	String imageFile = "pose.jpg";
	String dataset = "COCO";
	int W_in = 368;
	int H_in = 368;
	float thresh = 0.1;//过滤掉小于0.1的那些点
	float scale = 0.003922;//1/255 意思就是把每个数据重新置于[0-1]区间,原图片的数据区间是0-255

	int midx, npairs, nparts;
	if (!dataset.compare("COCO")) { midx = 0; npairs = 17; nparts = 18; }
	else if (!dataset.compare("MPI")) { midx = 1; npairs = 14; nparts = 16; }
	else if (!dataset.compare("HAND")) { midx = 2; npairs = 20; nparts = 22; }
	else
	{
		std::cerr << "Can't interpret dataset parameter: " << dataset << std::endl;
		exit(-1);
	}
	// read the network model
	cout << modelBin << endl;
	Net net = readNet(modelBin, modelTxt);
	net.setPreferableTarget(0);
	net.setPreferableBackend(0);
	// and the image
	Mat img = imread(imageFile);
	if (img.empty())
	{
		std::cerr << "Can't read image from the file: " << imageFile << std::endl;
		exit(-1);
	}

	// send it through the network
	//W_in = img.cols*H_in / img.rows;
	Mat inputBlob = blobFromImage(img, scale, Size(W_in, H_in), Scalar(0, 0, 0), false, false);
	cout << inputBlob.size << endl;//打印出输入的维度信息(n*c*h*w)因为我们定义了W_in,H_in为368,所以这里应该是1*3*368*368

	net.setInput(inputBlob);
	Mat result = net.forward();
	// the result is an array of "heatmaps", the probability of a body part being in location x,y
	cout << result.size << endl;//打印出输出的维度信息
	int H = result.size[2];
	int W = result.size[3];

	// find the position of the body parts
	vector points(22);
	for (int n = 0; n < nparts; n++)
	{
		// Slice heatmap of corresponding body's part.
		Mat heatMap(H, W, CV_32F, result.ptr(0, n));
		// 1 maximum per heatmap
		Point p(-1, -1), pm;
		double conf;
		minMaxLoc(heatMap, 0, &conf, 0, &pm);
		if (conf > thresh)
			p = pm;
		points[n] = p;
	}

	// connect body parts and draw it !
	float SX = float(img.cols) / W;
	float SY = float(img.rows) / H;
	for (int n = 0; n < npairs; n++)
	{
		// lookup 2 connected body/hand parts
		Point2f a = points[POSE_PAIRS[midx][n][0]];
		Point2f b = points[POSE_PAIRS[midx][n][1]];

		// we did not find enough confidence before
		if (a.x <= 0 || a.y <= 0 || b.x <= 0 || b.y <= 0)
			continue;

		// scale to image size
		a.x *= SX; a.y *= SY;
		b.x *= SX; b.y *= SY;

		line(img, a, b, Scalar(0, 200, 0), 2);
		circle(img, a, 3, Scalar(0, 0, 200), -1);
		circle(img, b, 3, Scalar(0, 0, 200), -1);
	}

	imshow("OpenPose", img);
	waitKey();

	return 0;
}

输出效果如下
opencv通过dnn模块调用openpose 网络实现人体姿态预测_第1张图片
opencv通过dnn模块调用openpose 网络实现人体姿态预测_第2张图片

你可能感兴趣的:(python,深度学习,人工智能)