提交 1ed09805 编写于 作者: G gineshidalgo99

CPU render: improved small face/hand render

上级 72a1f5f6
### Posting rules
1. **No duplicated** posts.
2. **No** posts about **questions already answered in** the **documentation** (e.g. **no more low-speed nor out-of-memory questions**).
2. **No** posts about **questions already answered / clearly explained in** the **documentation** (e.g. **no more low-speed nor out-of-memory questions**).
3. **Add** the **system configuration (all of it!), command and output** if you have some kind of error or performance question.
4. Set a **proper issue title**: add the Ubuntu/Windows word and be specific (e.g. do not simple call it: `Compile error`).
5. Only English comments.
......
......@@ -164,6 +164,8 @@ op::PoseModel gflagToPoseModel(const std::string& poseModeString)
return op::PoseModel::MPI_15;
else if (poseModeString == "MPI_4_layers")
return op::PoseModel::MPI_15_4;
else if (poseModeString == "BODY_22")
return op::PoseModel::BODY_22;
else
{
op::error("String does not correspond to any model (COCO, MPI, MPI_4_layers)", __LINE__, __FUNCTION__, __FILE__);
......
......@@ -9,8 +9,9 @@ namespace op
enum class PoseModel : unsigned char
{
COCO_18 = 0, /**< COCO model, with 18+1 components (see poseParameters.hpp for details). */
MPI_15, /**< MPI model, with 15+1 components (see poseParameters.hpp for details). */
MPI_15_4, /**< Same MPI model, but reducing the number of CNN stages to 4 (see poseModel.cpp for details). It should increase speed and reduce accuracy.*/
MPI_15 = 1, /**< MPI model, with 15+1 components (see poseParameters.hpp for details). */
MPI_15_4 = 2, /**< Variation of the MPI model, reduced number of CNN stages to 4: faster but less accurate.*/
BODY_22 = 3, /**< Experimental. Do not use. */
Size,
};
......
......@@ -8,8 +8,13 @@
namespace op
{
// Model-Dependent Parameters
// #define when needed in CUDA code
// Constant Global Parameters
const unsigned int POSE_MAX_PEOPLE = 96u;
// Model-Dependent Parameters
// COCO
const std::map<unsigned int, std::string> POSE_COCO_BODY_PARTS {
{0, "Nose"},
{1, "Neck"},
......@@ -33,9 +38,9 @@ namespace op
};
const unsigned int POSE_COCO_NUMBER_PARTS = 18u; // Equivalent to size of std::map POSE_COCO_BODY_PARTS - 1 (removing background)
const std::vector<unsigned int> POSE_COCO_MAP_IDX {31,32, 39,40, 33,34, 35,36, 41,42, 43,44, 19,20, 21,22, 23,24, 25,26, 27,28, 29,30, 47,48, 49,50, 53,54, 51,52, 55,56, 37,38, 45,46};
#define POSE_COCO_PAIRS_RENDER_GPU {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}
#define POSE_COCO_PAIRS_RENDER_GPU {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}
const std::vector<unsigned int> POSE_COCO_PAIRS_RENDER {POSE_COCO_PAIRS_RENDER_GPU};
const std::vector<unsigned int> POSE_COCO_PAIRS {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, 2,16, 5,17};
const std::vector<unsigned int> POSE_COCO_PAIRS {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, 2,16, 5,17};
#define POSE_COCO_COLORS_RENDER \
255.f, 0.f, 0.f, \
255.f, 85.f, 0.f, \
......@@ -55,7 +60,7 @@ namespace op
255.f, 0.f, 255.f, \
255.f, 0.f, 170.f, \
255.f, 0.f, 85.f
// MPI
const std::map<unsigned int, std::string> POSE_MPI_BODY_PARTS{
{0, "Head"},
{1, "Neck"},
......@@ -75,8 +80,8 @@ namespace op
{15, "Background"}
};
const unsigned int POSE_MPI_NUMBER_PARTS = 15; // Equivalent to size of std::map POSE_MPI_NUMBER_PARTS - 1 (removing background)
const std::vector<unsigned int> POSE_MPI_MAP_IDX {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43};
#define POSE_MPI_PAIRS_RENDER_GPU { 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}
const std::vector<unsigned int> POSE_MPI_MAP_IDX {16,17, 18,19, 20,21, 22,23, 24,25, 26,27, 28,29, 30,31, 32,33, 34,35, 36,37, 38,39, 40,41, 42,43};
#define POSE_MPI_PAIRS_RENDER_GPU { 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}
const std::vector<unsigned int> POSE_MPI_PAIRS POSE_MPI_PAIRS_RENDER_GPU;
// MPI colors chosen such that they are closed to COCO colors
#define POSE_MPI_COLORS_RENDER \
......@@ -95,35 +100,108 @@ namespace op
0.f, 170.f, 255.f, \
0.f, 85.f, 255.f, \
0.f, 0.f, 255.f
// Constant Global Parameters
const unsigned int POSE_MAX_PEOPLE = 96u;
// BODY_22 (experimental, do not use)
const std::map<unsigned int, std::string> POSE_BODY_22_BODY_PARTS {
{0, "Nose"},
{1, "Neck"},
{2, "RShoulder"},
{3, "RElbow"},
{4, "RWrist"},
{5, "LShoulder"},
{6, "LElbow"},
{7, "LWrist"},
{8, "RHip"},
{9, "RKnee"},
{10, "RAnkle"},
{11, "LHip"},
{12, "LKnee"},
{13, "LAnkle"},
{14, "REye"},
{15, "LEye"},
{16, "REar"},
{17, "LEar"},
{22, "Background"},
};
const unsigned int POSE_BODY_22_NUMBER_PARTS = 2u; // Equivalent to size of std::map POSE_BODY_22_BODY_PARTS - 1 (removing background)
const std::vector<unsigned int> POSE_BODY_22_MAP_IDX {35,36, 43,45};
#define POSE_BODY_22_PAIRS_RENDER_GPU {1,2, 1,5}
const std::vector<unsigned int> POSE_BODY_22_PAIRS_RENDER {POSE_BODY_22_PAIRS_RENDER_GPU};
const std::vector<unsigned int> POSE_BODY_22_PAIRS {1,2};
#define POSE_BODY_22_COLORS_RENDER \
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, \
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, \
85.f, 0.f, 255.f, \
170.f, 0.f, 255.f, \
255.f, 0.f, 255.f, \
255.f, 0.f, 170.f, \
255.f, 0.f, 85.f
// Constant Array Parameters
const std::array<float, (int)PoseModel::Size> POSE_CCN_DECREASE_FACTOR{ 8.f, 8.f, 8.f};
const std::array<unsigned int, (int)PoseModel::Size> POSE_MAX_PEAKS{ POSE_MAX_PEOPLE, POSE_MAX_PEOPLE, POSE_MAX_PEOPLE};
const std::array<unsigned int, (int)PoseModel::Size> POSE_NUMBER_BODY_PARTS{ POSE_COCO_NUMBER_PARTS, POSE_MPI_NUMBER_PARTS, POSE_MPI_NUMBER_PARTS};
const std::array<std::vector<unsigned int>, 3> POSE_BODY_PART_PAIRS{ POSE_COCO_PAIRS, POSE_MPI_PAIRS, POSE_MPI_PAIRS};
const std::array<std::vector<unsigned int>, 3> POSE_BODY_PART_PAIRS_RENDER{POSE_COCO_PAIRS_RENDER, POSE_MPI_PAIRS, POSE_MPI_PAIRS};
const std::array<std::vector<unsigned int>, 3> POSE_MAP_IDX{ POSE_COCO_MAP_IDX, POSE_MPI_MAP_IDX, POSE_MPI_MAP_IDX};
const std::array<std::string, (int)PoseModel::Size> POSE_PROTOTXT{ "pose/coco/pose_deploy_linevec.prototxt",
"pose/mpi/pose_deploy_linevec.prototxt",
"pose/mpi/pose_deploy_linevec_faster_4_stages.prototxt"};
const std::array<std::string, (int)PoseModel::Size> POSE_TRAINED_MODEL{ "pose/coco/pose_iter_440000.caffemodel",
"pose/mpi/pose_iter_160000.caffemodel",
"pose/mpi/pose_iter_160000.caffemodel"};
const std::array<float, (int)PoseModel::Size> POSE_CCN_DECREASE_FACTOR{
8.f, 8.f, 8.f, 8.f
};
const std::array<unsigned int, (int)PoseModel::Size> POSE_MAX_PEAKS{
POSE_MAX_PEOPLE, POSE_MAX_PEOPLE, POSE_MAX_PEOPLE, POSE_MAX_PEOPLE
};
const std::array<unsigned int, (int)PoseModel::Size> POSE_NUMBER_BODY_PARTS{
POSE_COCO_NUMBER_PARTS, POSE_MPI_NUMBER_PARTS, POSE_MPI_NUMBER_PARTS, POSE_BODY_22_NUMBER_PARTS
};
const std::array<std::vector<unsigned int>, (int)PoseModel::Size> POSE_BODY_PART_PAIRS{
POSE_COCO_PAIRS, POSE_MPI_PAIRS, POSE_MPI_PAIRS, POSE_BODY_22_PAIRS
};
const std::array<std::vector<unsigned int>, (int)PoseModel::Size> POSE_BODY_PART_PAIRS_RENDER{
POSE_COCO_PAIRS_RENDER, POSE_MPI_PAIRS, POSE_MPI_PAIRS, POSE_BODY_22_PAIRS_RENDER
};
const std::array<std::vector<unsigned int>, (int)PoseModel::Size> POSE_MAP_IDX{
POSE_COCO_MAP_IDX, POSE_MPI_MAP_IDX, POSE_MPI_MAP_IDX, POSE_BODY_22_MAP_IDX
};
const std::array<std::string, (int)PoseModel::Size> POSE_PROTOTXT{
"pose/coco/pose_deploy_linevec.prototxt",
"pose/mpi/pose_deploy_linevec.prototxt",
"pose/mpi/pose_deploy_linevec_faster_4_stages.prototxt",
"pose/body_22/pose_deploy.prototxt"
};
const std::array<std::string, (int)PoseModel::Size> POSE_TRAINED_MODEL{
"pose/coco/pose_iter_440000.caffemodel",
"pose/mpi/pose_iter_160000.caffemodel",
"pose/mpi/pose_iter_160000.caffemodel",
"pose/body_22/pose_iter_40000.caffemodel"
};
// POSE_BODY_PART_MAPPING crashes on Windows at dynamic initialization, to avoid this crash:
// POSE_BODY_PART_MAPPING has been moved to poseParameters.cpp and getPoseBodyPartMapping() wraps it
//const std::array<std::map<unsigned int, std::string>, 3> POSE_BODY_PART_MAPPING{ POSE_COCO_BODY_PARTS, POSE_MPI_BODY_PARTS, POSE_MPI_BODY_PARTS};
// const std::array<std::map<unsigned int, std::string>, (int)PoseModel::Size> POSE_BODY_PART_MAPPING{
// POSE_COCO_BODY_PARTS, POSE_MPI_BODY_PARTS, POSE_MPI_BODY_PARTS
// };
const std::map<unsigned int, std::string>& getPoseBodyPartMapping(const PoseModel poseModel);
// Default Model Parameters
// They might be modified on running time
const std::array<float, (int)PoseModel::Size> POSE_DEFAULT_NMS_THRESHOLD{ 0.05f, 0.6f, 0.3f};
const std::array<unsigned int, (int)PoseModel::Size> POSE_DEFAULT_CONNECT_INTER_MIN_ABOVE_THRESHOLD{ 9, 8, 8};
const std::array<float, (int)PoseModel::Size> POSE_DEFAULT_CONNECT_INTER_THRESHOLD{ 0.05f, 0.01f, 0.01f};
const std::array<unsigned int, (int)PoseModel::Size> POSE_DEFAULT_CONNECT_MIN_SUBSET_CNT{ 3, 3, 3};
const std::array<float, (int)PoseModel::Size> POSE_DEFAULT_CONNECT_MIN_SUBSET_SCORE{ 0.4f, 0.4f, 0.4f};
const std::array<float, (int)PoseModel::Size> POSE_DEFAULT_NMS_THRESHOLD{
0.05f, 0.6f, 0.3f, 0.05f
};
const std::array<unsigned int, (int)PoseModel::Size> POSE_DEFAULT_CONNECT_INTER_MIN_ABOVE_THRESHOLD{
9, 8, 8, 9
};
const std::array<float, (int)PoseModel::Size> POSE_DEFAULT_CONNECT_INTER_THRESHOLD{
0.05f, 0.01f, 0.01f, 0.05f
};
const std::array<unsigned int, (int)PoseModel::Size> POSE_DEFAULT_CONNECT_MIN_SUBSET_CNT{
3, 3, 3, 3
};
const std::array<float, (int)PoseModel::Size> POSE_DEFAULT_CONNECT_MIN_SUBSET_SCORE{
0.4f, 0.4f, 0.4f, 0.4f
};
// Rendering parameters
const auto POSE_DEFAULT_ALPHA_KEYPOINT = 0.6f;
......
......@@ -3,7 +3,9 @@
namespace op
{
const std::array<std::map<unsigned int, std::string>, 3> POSE_BODY_PART_MAPPING{ POSE_COCO_BODY_PARTS, POSE_MPI_BODY_PARTS, POSE_MPI_BODY_PARTS };
const std::array<std::map<unsigned int, std::string>, (int)PoseModel::Size> POSE_BODY_PART_MAPPING{
POSE_COCO_BODY_PARTS, POSE_MPI_BODY_PARTS, POSE_MPI_BODY_PARTS, POSE_BODY_22_BODY_PARTS
};
unsigned int poseBodyPartMapStringToKey(const PoseModel poseModel, const std::vector<std::string>& strings)
{
......
......@@ -10,8 +10,10 @@ namespace op
// PI digits: http://www.piday.org/million/
__constant__ const float PI = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745f;
__constant__ const unsigned int COCO_PAIRS_GPU[] = POSE_COCO_PAIRS_RENDER_GPU;
__constant__ const unsigned int BODY_22_PAIRS_GPU[] = POSE_BODY_22_PAIRS_RENDER_GPU;
__constant__ const unsigned int MPI_PAIRS_GPU[] = POSE_MPI_PAIRS_RENDER_GPU;
__constant__ const float COCO_COLORS[] = {POSE_COCO_COLORS_RENDER};
__constant__ const float BODY_22_COLORS[] = {POSE_BODY_22_COLORS_RENDER};
__constant__ const float MPI_COLORS[] = {POSE_MPI_COLORS_RENDER};
......@@ -93,8 +95,9 @@ namespace op
colorPtr.z *= rad;
}
__global__ void renderPoseCoco(float* targetPtr, const int targetWidth, const int targetHeight, const float* const posePtr, const int numberPeople,
const float threshold, const bool googlyEyes, const bool blendOriginalFrame, const float alphaColorToAdd)
__global__ void renderPoseCoco(float* targetPtr, const int targetWidth, const int targetHeight, const float* const posePtr,
const int numberPeople, const float threshold, const bool googlyEyes, const bool blendOriginalFrame,
const float alphaColorToAdd)
{
const auto x = (blockIdx.x * blockDim.x) + threadIdx.x;
const auto y = (blockIdx.y * blockDim.y) + threadIdx.y;
......@@ -118,6 +121,32 @@ namespace op
radius, stickwidth, threshold, alphaColorToAdd, blendOriginalFrame, (googlyEyes ? 14 : -1), (googlyEyes ? 15 : -1));
}
__global__ void renderPoseBody22(float* targetPtr, const int targetWidth, const int targetHeight, const float* const posePtr,
const int numberPeople, const float threshold, const bool googlyEyes, const bool blendOriginalFrame,
const float alphaColorToAdd)
{
const auto x = (blockIdx.x * blockDim.x) + threadIdx.x;
const auto y = (blockIdx.y * blockDim.y) + threadIdx.y;
const auto globalIdx = threadIdx.y * blockDim.x + threadIdx.x;
// Shared parameters
__shared__ float2 sharedMins[POSE_MAX_PEOPLE];
__shared__ float2 sharedMaxs[POSE_MAX_PEOPLE];
__shared__ float sharedScaleF[POSE_MAX_PEOPLE];
// Other parameters
const auto numberPartPairs = sizeof(BODY_22_PAIRS_GPU) / (2*sizeof(BODY_22_PAIRS_GPU[0]));
const auto numberColors = sizeof(BODY_22_COLORS) / (3*sizeof(BODY_22_COLORS[0]));
const auto radius = fastMin(targetWidth, targetHeight) / 100.f;
const auto stickwidth = fastMin(targetWidth, targetHeight) / 120.f;
// Render key points
renderKeypoints(targetPtr, sharedMaxs, sharedMins, sharedScaleF,
globalIdx, x, y, targetWidth, targetHeight, posePtr, BODY_22_PAIRS_GPU, numberPeople,
POSE_BODY_22_NUMBER_PARTS, numberPartPairs, BODY_22_COLORS, numberColors,
radius, stickwidth, threshold, alphaColorToAdd, blendOriginalFrame, (googlyEyes ? 14 : -1), (googlyEyes ? 15 : -1));
}
__global__ void renderPoseMpi29Parts(float* targetPtr, const int targetWidth, const int targetHeight, const float* const posePtr,
const int numberPeople, const float threshold, const bool blendOriginalFrame, const float alphaColorToAdd)
{
......@@ -306,11 +335,14 @@ namespace op
if (poseModel == PoseModel::COCO_18)
renderPoseCoco<<<threadsPerBlock, numBlocks>>>(framePtr, frameSize.x, frameSize.y, posePtr, numberPeople,
POSE_RENDER_THRESHOLD, googlyEyes, blendOriginalFrame, alphaBlending);
else if (poseModel == PoseModel::BODY_22)
renderPoseBody22<<<threadsPerBlock, numBlocks>>>(framePtr, frameSize.x, frameSize.y, posePtr, numberPeople,
POSE_RENDER_THRESHOLD, googlyEyes, blendOriginalFrame, alphaBlending);
else if (poseModel == PoseModel::MPI_15 || poseModel == PoseModel::MPI_15_4)
renderPoseMpi29Parts<<<threadsPerBlock, numBlocks>>>(framePtr, frameSize.x, frameSize.y, posePtr,
numberPeople, POSE_RENDER_THRESHOLD, blendOriginalFrame, alphaBlending);
else
error("Unvalid Model.", __LINE__, __FUNCTION__, __FILE__);
error("Invalid Model.", __LINE__, __FUNCTION__, __FILE__);
cudaCheck(__LINE__, __FUNCTION__, __FILE__);
}
}
......
......@@ -123,54 +123,59 @@ namespace op
// Parameters
const auto lineType = 8;
const auto shift = 0;
const auto thresholdRectangle = 0.1f;
const auto numberColors = colors.size();
const auto thresholdRectangle = 0.1f;
const auto numberKeypoints = keypoints.getSize(1);
const auto areaKeypoints = numberKeypoints * keypoints.getSize(2);
// Keypoints
for (auto person = 0 ; person < keypoints.getSize(0) ; person++)
{
const auto personRectangle = getKeypointsRectangle(&keypoints[person*areaKeypoints], keypoints.getSize(1), thresholdRectangle);
const auto ratioAreas = fastMin(1.f, fastMax(personRectangle.width/(float)width, personRectangle.height/(float)height));
// Size-dependent variables
const auto thicknessCircle = fastMax(intRound(std::sqrt(area)*thicknessCircleRatio * ratioAreas), 2);
const auto thicknessLine = intRound(thicknessCircle * thicknessLineRatioWRTCircle);
const auto radius = thicknessCircle / 2;
// Draw lines
for (auto pair = 0 ; pair < pairs.size() ; pair+=2)
const auto personRectangle = getKeypointsRectangle(&keypoints[person*areaKeypoints], numberKeypoints, thresholdRectangle);
if (personRectangle.area() > 0)
{
const auto index1 = (person * keypoints.getSize(1) + pairs[pair]) * keypoints.getSize(2);
const auto index2 = (person * keypoints.getSize(1) + pairs[pair+1]) * keypoints.getSize(2);
if (keypoints[index1+2] > threshold && keypoints[index2+2] > threshold)
const auto ratioAreas = fastMin(1.f, fastMax(personRectangle.width/(float)width, personRectangle.height/(float)height));
// Size-dependent variables
const auto thicknessRatio = fastMax(intRound(std::sqrt(area)*thicknessCircleRatio * ratioAreas), 2);
// Negative thickness in cv::circle means that a filled circle is to be drawn.
const auto thicknessCircle = (ratioAreas > 0.05 ? thicknessRatio : -1);
const auto thicknessLine = intRound(thicknessRatio * thicknessLineRatioWRTCircle);
const auto radius = thicknessRatio / 2;
// Draw lines
for (auto pair = 0 ; pair < pairs.size() ; pair+=2)
{
const auto colorIndex = pair/2*3;
const cv::Scalar color{colors[colorIndex % numberColors],
colors[(colorIndex+1) % numberColors],
colors[(colorIndex+2) % numberColors]};
const cv::Point keypoint1{intRound(keypoints[index1]), intRound(keypoints[index1+1])};
const cv::Point keypoint2{intRound(keypoints[index2]), intRound(keypoints[index2+1])};
cv::line(frameR, keypoint1, keypoint2, color[0], thicknessLine, lineType, shift);
cv::line(frameG, keypoint1, keypoint2, color[1], thicknessLine, lineType, shift);
cv::line(frameB, keypoint1, keypoint2, color[2], thicknessLine, lineType, shift);
const auto index1 = (person * numberKeypoints + pairs[pair]) * keypoints.getSize(2);
const auto index2 = (person * numberKeypoints + pairs[pair+1]) * keypoints.getSize(2);
if (keypoints[index1+2] > threshold && keypoints[index2+2] > threshold)
{
const auto colorIndex = pair/2*3;
const cv::Scalar color{colors[colorIndex % numberColors],
colors[(colorIndex+1) % numberColors],
colors[(colorIndex+2) % numberColors]};
const cv::Point keypoint1{intRound(keypoints[index1]), intRound(keypoints[index1+1])};
const cv::Point keypoint2{intRound(keypoints[index2]), intRound(keypoints[index2+1])};
cv::line(frameR, keypoint1, keypoint2, color[0], thicknessLine, lineType, shift);
cv::line(frameG, keypoint1, keypoint2, color[1], thicknessLine, lineType, shift);
cv::line(frameB, keypoint1, keypoint2, color[2], thicknessLine, lineType, shift);
}
}
}
// Draw circles
for (auto part = 0 ; part < keypoints.getSize(1) ; part++)
{
const auto faceIndex = (person * keypoints.getSize(1) + part) * keypoints.getSize(2);
if (keypoints[faceIndex+2] > threshold)
// Draw circles
for (auto part = 0 ; part < numberKeypoints ; part++)
{
const auto colorIndex = part*3;
const cv::Scalar color{colors[colorIndex % numberColors],
colors[(colorIndex+1) % numberColors],
colors[(colorIndex+2) % numberColors]};
const cv::Point center{intRound(keypoints[faceIndex]), intRound(keypoints[faceIndex+1])};
cv::circle(frameR, center, radius, color[0], thicknessCircle, lineType, shift);
cv::circle(frameG, center, radius, color[1], thicknessCircle, lineType, shift);
cv::circle(frameB, center, radius, color[2], thicknessCircle, lineType, shift);
const auto faceIndex = (person * numberKeypoints + part) * keypoints.getSize(2);
if (keypoints[faceIndex+2] > threshold)
{
const auto colorIndex = part*3;
const cv::Scalar color{colors[colorIndex % numberColors],
colors[(colorIndex+1) % numberColors],
colors[(colorIndex+2) % numberColors]};
const cv::Point center{intRound(keypoints[faceIndex]), intRound(keypoints[faceIndex+1])};
cv::circle(frameR, center, radius, color[0], thicknessCircle, lineType, shift);
cv::circle(frameG, center, radius, color[1], thicknessCircle, lineType, shift);
cv::circle(frameB, center, radius, color[2], thicknessCircle, lineType, shift);
}
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册