基于openpose的引体向上的识别计数统计项目(3)CPoseRender类设计与实现

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aworkholic

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值