CPoseRender 主要是为了简化openpose中的调用方式进行简化重写,只需要opencv的参数即可使用。
1、CPoseRender 声明
#pragma once
#include "opencv2/core.hpp"
#include "CPoseClassify.h"
#include "Keypoints.h"
namespace Utils {
// fastmath, taken from op
//
// Use op::round/max/min for basic types (int, char, long, float, double, etc). Never with classes!
// `std::` alternatives uses 'const T&' instead of 'const T' as argument.
// E.g., std::round is really slow (~300 ms vs ~10 ms when I individually apply it to each element of a whole
// image array
// VERY IMPORTANT: These fast functions does NOT work for negative integer numbers.
// E.g., positiveIntRound(-180.f) = -179.
// Round functions
// Signed
template<typename T>
inline char positiveCharRound(const T a)
{
return char(a + 0.5f);
}
template<typename T>
inline signed char positiveSCharRound(const T a)
{
return (signed char)(a + 0.5f);
}
template<typename T>
inline int positiveIntRound(const T a)
{
return int(a + 0.5f);
}
template<typename T>
inline long positiveLongRound(const T a)
{
return long(a + 0.5f);
}
template<typename T>
inline long long positiveLongLongRound(const T a)
{
return (long long)(a + 0.5f);
}
// Unsigned
template<typename T>
inline unsigned char uCharRound(const T a)
{
return (unsigned char)(a + 0.5f);
}
template<typename T>
inline unsigned int uIntRound(const T a)
{
return (unsigned int)(a + 0.5f);
}
template<typename T>
inline unsigned long ulongRound(const T a)
{
return (unsigned long)(a + 0.5f);
}
template<typename T>
inline unsigned long long uLongLongRound(const T a)
{
return (unsigned long long)(a + 0.5f);
}
// Max/min functions
template<typename T>
inline T fastMax(const T a, const T b)
{
return (a > b ? a : b);
}
template<typename T>
inline T fastMin(const T a, const T b)
{
return (a < b ? a : b);
}
template<class T>
inline T fastTruncate(T value, T min = 0, T max = 1)
{
return fastMin(max, fastMax(min, value));
}
//
} // namespace Utils
class CPoseRender
{
public:
CPoseRender(PoseModel poseModel);
~CPoseRender();
void rendPose(cv::Mat& frame, const cv::Mat& poseData, float renderThreshold = 0.05);
private:
const PoseModel poseModel;
};
2、CPoseRender 实现
#include "CPoseRender.h"
#include <vector>
#include <array>
//#include "openpose/utilities/fastMath.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
namespace {
#define POSE_BODY_25_PAIRS_RENDER \
1,8, 1,2, 1,5, 2,3, 3,4, 5,6, 6,7, 8,9, 9,10, 10,11, 8,12, 12,13, 13,14, 1,0, 0,15, 15,17, 0,16, 16,18, 14,19,19,20,14,21, 11,22,22,23,11,24
#define POSE_BODY_25_SCALES_RENDER 1
#define POSE_BODY_25_COLORS_RENDER \
255.f, 0.f, 85.f, \
255.f, 0.f, 0.f, \
255.f, 85.f, 0.f, \
255.f, 170.f, 0.f, \
255.f, 255.f, 0.f, \
170.f, 255.f, 0.f, \
85.f, 255.f, 0.f, \
0.f, 255.f, 0.f, \
255.f, 0.f, 0.f, \
0.f, 255.f, 85.f, \
0.f, 255.f, 170.f, \
0.f, 255.f, 255.f, \
0.f, 170.f, 255.f, \
0.f, 85.f, 255.f, \
0.f, 0.f, 255.f, \
255.f, 0.f, 170.f, \
170.f, 0.f, 255.f, \
255.f, 0.f, 255.f, \
85.f, 0.f, 255.f, \
0.f, 0.f, 255.f, \
0.f, 0.f, 255.f, \
0.f, 0.f, 255.f, \
0.f, 255.f, 255.f, \
0.f, 255.f, 255.f, \
0.f, 255.f, 255.f
const std::array<std::vector<unsigned int>, (int)PoseModel::Size> POSE_BODY_PART_PAIRS_RENDER{
std::vector<unsigned int>{POSE_BODY_25_PAIRS_RENDER}, // BODY_25
};
const std::array<std::vector<unsigned int>, (int)PoseModel::Size> POSE_BODY_PART_PAIRS{
// BODY_25
std::vector<unsigned int>{
1,8, 1,2, 1,5, 2,3, 3,4, 5,6, 6,7, 8,9, 9,10, 10,11, 8,12, 12,13, 13,14, 1,0, 0,15, 15,17, 0,16, 16,18, 2,17, 5,18, 14,19,19,20,14,21, 11,22,22,23,11,24
}
};
const std::array<std::vector<float>, (int)PoseModel::Size> POSE_SCALES{
std::vector<float>{POSE_BODY_25_SCALES_RENDER}, // BODY_25
};
const std::array<std::vector<float>, (int)PoseModel::Size> POSE_COLORS{
std::vector<float>{POSE_BODY_25_COLORS_RENDER}, // BODY_25
};
void renderOpenPoseKeypoints(cv::Mat & frame, const cv::Mat & poseData, PoseModel poseModel, const float threshold)
{
const auto thicknessCircleRatio = 1.f / 75.f;
const auto thicknessLineRatioWRTCircle = 0.75f;
const auto& pairs = POSE_BODY_PART_PAIRS_RENDER.at((int)poseModel);
const auto& poseScales = POSE_SCALES.at((int)poseModel);
const auto& colors = POSE_COLORS.at((int)(int)poseModel);
//-------------------------------------------------------------------------
const auto width = frame.size[1];
const auto height = frame.size[0];
const auto area = width * height;
// Parameters
const auto lineType = 8;
const auto shift = 0;
const auto numberColors = colors.size();
const auto numberScales = poseScales.size();
const auto thresholdRectangle = float(0.1);
const auto numberKeypoints = poseData.cols; //keypoints.getSize(1);
// Keypoints
for(auto person = 0; person < poseData.rows; person++) {
const auto personRectangle = Utils::getKeypointsRectangle(poseData, person, thresholdRectangle);
if(personRectangle.area() > 0) {
const auto ratioAreas = Utils::fastMin(
float(1), Utils::fastMax(
personRectangle.width / (float)width, personRectangle.height / (float)height));
// Size-dependent variables
const auto thicknessRatio = Utils::fastMax(
Utils::positiveIntRound(std::sqrt(area)* thicknessCircleRatio * ratioAreas), 2);
// Negative thickness in cv::circle means that a filled circle is to be drawn.
const auto thicknessCircle = Utils::fastMax(1, (ratioAreas > float(0.05) ? thicknessRatio : -1));
const auto thicknessLine = Utils::fastMax(
1, Utils::positiveIntRound(thicknessRatio * thicknessLineRatioWRTCircle));
const auto radius = thicknessRatio / 2;
// Draw lines
for(auto pair = 0u; pair < pairs.size(); pair += 2) {
const auto index1 = (person * numberKeypoints + pairs[pair]) * poseData.channels();
const auto index2 = (person * numberKeypoints + pairs[pair + 1]) * poseData.channels();
if(poseData.at<float>(index1 + 2) > threshold && poseData.at<float>(index2 + 2) > threshold) {
const auto thicknessLineScaled = Utils::positiveIntRound(
thicknessLine * poseScales[pairs[pair + 1] % numberScales]);
const auto colorIndex = pairs[pair + 1] * 3; // Before: colorIndex = pair/2*3;
const cv::Scalar color{
colors[(colorIndex + 2) % numberColors],
colors[(colorIndex + 1) % numberColors],
colors[colorIndex % numberColors]
};
const cv::Point keypoint1{
Utils::positiveIntRound(poseData.at<float>(index1)), Utils::positiveIntRound(poseData.at<float>(index1+1))};
const cv::Point keypoint2{
Utils::positiveIntRound(poseData.at<float>(index2)), Utils::positiveIntRound(poseData.at<float>(index2 + 1))};
cv::line(frame, keypoint1, keypoint2, color, thicknessLineScaled, lineType, shift);
}
}
// Draw circles
for(auto part = 0; part < numberKeypoints; part++) {
const auto faceIndex = (person * numberKeypoints + part) * frame.channels();
if(poseData.at<float>(faceIndex + 2) > threshold) {
const auto radiusScaled = Utils::positiveIntRound(radius * poseScales[part % numberScales]);
const auto thicknessCircleScaled = Utils::positiveIntRound(
thicknessCircle * poseScales[part % numberScales]);
const auto colorIndex = part * 3;
const cv::Scalar color{
colors[(colorIndex + 2) % numberColors],
colors[(colorIndex + 1) % numberColors],
colors[colorIndex % numberColors]
};
const cv::Point center{Utils::positiveIntRound(poseData.at<float>(faceIndex)),
Utils::positiveIntRound(poseData.at<float>(faceIndex + 1))};
cv::circle(frame, center, radiusScaled, color, thicknessCircleScaled, lineType, shift);
}
}
}
}
}
} // namespace
CPoseRender::CPoseRender(PoseModel poseModel):
poseModel(poseModel)
{
}
CPoseRender::~CPoseRender()
{
}
void CPoseRender::rendPose(cv::Mat & frame, const cv::Mat & poseData, float renderThreshold)
{
if(frame.empty() || poseData.empty())
return;
renderOpenPoseKeypoints(frame, poseData, poseModel, renderThreshold);
}