提交 9e6849fe 编写于 作者: G gineshidalgo99

Fixed hand & face issues with non-default output_resolution

上级 2bfdc5e0
......@@ -22,7 +22,7 @@ There are 2 alternatives to save the OpenPose output.
1. The `write_json` flag saves the people pose data using a custom JSON writer. Each JSON file has a `people` array of objects, where each object has:
1. An array `pose_keypoints_2d` containing the body part locations and detection confidence formatted as `x1,y1,c1,x2,y2,c2,...`. The coordinates `x` and `y` can be normalized to the range [0,1], [-1,1], [0, source size], [0, output size], etc., depending on the flag `keypoint_scale`, while `c` is the confidence score in the range [0,1].
2. The arrays `face_keypoints_2d`, `hand_left_keypoints_2d`, and `hand_right_keypoints_2d`, analogous to `pose_keypoints_2d`.
3. The analogous 3-D arrays `body_keypoints_3d`, `face_keypoints_3d`, `hand_left_keypoints_2d`, and `hand_right_keypoints_2d` (if `--3d` is enabled, otherwise they will be empty).
3. The analogous 3-D arrays `body_keypoints_3d`, `face_keypoints_3d`, `hand_left_keypoints_2d`, and `hand_right_keypoints_2d` (if `--3d` is enabled, otherwise they will be empty). Instead of `x1,y1,c1,x2,y2,c2,...`, their format is `x1,y1,z1,c1,x2,y2,z2,c2,...`, where `c` is simply 1 or 0 depending on whether the 3-D reconstruction was successful or not.
4. The body part candidates before being assembled into people (if `--part_candidates` is enabled).
```
{
......
......@@ -219,7 +219,7 @@ OpenPose Library - Release Notes
2. Functions or parameters renamed:
1. Removed scale parameter from hand and face rectangle extractor (causing wrong results if custom `--output_resolution`).
3. Main bugs fixed:
1. Hand and face work properly again with any `--output_resolution`.
1. Fixed hand and face extraction and rendering scaling issues when `--output_resolution` is not the default one.
2. Part candidates (`--part_candidates`) are saved with the same scale than the final keypoints itself.
......
......@@ -14,7 +14,7 @@ namespace op
FaceCpuRenderer(const float renderThreshold, const float alphaKeypoint = FACE_DEFAULT_ALPHA_KEYPOINT,
const float alphaHeatMap = FACE_DEFAULT_ALPHA_HEAT_MAP);
void renderFace(Array<float>& outputData, const Array<float>& faceKeypoints);
void renderFaceInherited(Array<float>& outputData, const Array<float>& faceKeypoints);
DELETE_COPY(FaceCpuRenderer);
};
......
......@@ -42,11 +42,8 @@ namespace op
* (similar to cv::Rect for floating values) with the position of that face (or 0,0,0,0 if
* some face is missing, e.g. if a specific person has only half of the body inside the image).
* @param cvInputData Original image in cv::Mat format and BGR format.
* @param scaleInputToOutput Desired scale of the final keypoints. Set to 1 if the desired size is the
* cvInputData size.
*/
virtual void forwardPass(const std::vector<Rectangle<float>>& faceRectangles, const cv::Mat& cvInputData,
const double scaleInputToOutput) = 0;
virtual void forwardPass(const std::vector<Rectangle<float>>& faceRectangles, const cv::Mat& cvInputData) = 0;
Array<float> getHeatMaps() const;
......
......@@ -40,11 +40,8 @@ namespace op
* (similar to cv::Rect for floating values) with the position of that face (or 0,0,0,0 if
* some face is missing, e.g. if a specific person has only half of the body inside the image).
* @param cvInputData Original image in cv::Mat format and BGR format.
* @param scaleInputToOutput Desired scale of the final keypoints. Set to 1 if the desired size is the
* cvInputData size.
*/
void forwardPass(const std::vector<Rectangle<float>>& faceRectangles, const cv::Mat& cvInputData,
const double scaleInputToOutput);
void forwardPass(const std::vector<Rectangle<float>>& faceRectangles, const cv::Mat& cvInputData);
private:
// PIMPL idiom
......
......@@ -19,7 +19,7 @@ namespace op
void initializationOnThread();
void renderFace(Array<float>& outputData, const Array<float>& faceKeypoints);
void renderFaceInherited(Array<float>& outputData, const Array<float>& faceKeypoints);
private:
float* pGpuFace; // GPU aux memory
......
......@@ -10,7 +10,11 @@ namespace op
public:
virtual void initializationOnThread(){};
virtual void renderFace(Array<float>& outputData, const Array<float>& faceKeypoints) = 0;
void renderFace(Array<float>& outputData, const Array<float>& faceKeypoints,
const float scaleInputToOutput);
private:
virtual void renderFaceInherited(Array<float>& outputData, const Array<float>& faceKeypoints) = 0;
};
}
......
......@@ -58,7 +58,7 @@ namespace op
// Extract people face
for (auto& tDatum : *tDatums)
{
spFaceExtractor->forwardPass(tDatum.faceRectangles, tDatum.cvInputData, tDatum.scaleInputToOutput);
spFaceExtractor->forwardPass(tDatum.faceRectangles, tDatum.cvInputData);
tDatum.faceHeatMaps = spFaceExtractor->getHeatMaps().clone();
tDatum.faceKeypoints = spFaceExtractor->getFaceKeypoints().clone();
}
......
......@@ -57,7 +57,8 @@ namespace op
const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__);
// Render people face
for (auto& tDatum : *tDatums)
spFaceRenderer->renderFace(tDatum.outputData, tDatum.faceKeypoints);
spFaceRenderer->renderFace(tDatum.outputData, tDatum.faceKeypoints,
(float)tDatum.scaleInputToOutput);
// Profiling speed
Profiler::timerEnd(profilerKey);
Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__);
......
......@@ -14,7 +14,7 @@ namespace op
HandCpuRenderer(const float renderThreshold, const float alphaKeypoint = HAND_DEFAULT_ALPHA_KEYPOINT,
const float alphaHeatMap = HAND_DEFAULT_ALPHA_HEAT_MAP);
void renderHand(Array<float>& outputData, const std::array<Array<float>, 2>& handKeypoints);
void renderHandInherited(Array<float>& outputData, const std::array<Array<float>, 2>& handKeypoints);
DELETE_COPY(HandCpuRenderer);
};
......
......@@ -47,12 +47,9 @@ namespace op
* op::Rectangle<float> (similar to cv::Rect for floating values) with the position of that hand (or 0,0,0,0 if
* some hand is missing, e.g. if a specific person has only half of the body inside the image).
* @param cvInputData Original image in cv::Mat format and BGR format.
* @param scaleInputToOutput Desired scale of the final keypoints. Set to 1 if the desired size is the
* cvInputData size.
*/
virtual void forwardPass(const std::vector<std::array<Rectangle<float>, 2>> handRectangles,
const cv::Mat& cvInputData,
const double scaleInputToOutput) = 0;
const cv::Mat& cvInputData) = 0;
std::array<Array<float>, 2> getHeatMaps() const;
......
......@@ -51,11 +51,8 @@ namespace op
* op::Rectangle<float> (similar to cv::Rect for floating values) with the position of that hand (or 0,0,0,0 if
* some hand is missing, e.g. if a specific person has only half of the body inside the image).
* @param cvInputData Original image in cv::Mat format and BGR format.
* @param scaleInputToOutput Desired scale of the final keypoints. Set to 1 if the desired size is the
* cvInputData size.
*/
void forwardPass(const std::vector<std::array<Rectangle<float>, 2>> handRectangles, const cv::Mat& cvInputData,
const double scaleInputToOutput);
void forwardPass(const std::vector<std::array<Rectangle<float>, 2>> handRectangles, const cv::Mat& cvInputData);
private:
// PIMPL idiom
......@@ -63,7 +60,7 @@ namespace op
struct ImplHandExtractorCaffe;
std::unique_ptr<ImplHandExtractorCaffe> upImpl;
void detectHandKeypoints(Array<float>& handCurrent, const double scaleInputToOutput, const int person,
void detectHandKeypoints(Array<float>& handCurrent, const int person,
const cv::Mat& affineMatrix);
Array<float> getHeatMapsFromLastPass() const;
......
......@@ -19,7 +19,7 @@ namespace op
void initializationOnThread();
void renderHand(Array<float>& outputData, const std::array<Array<float>, 2>& handKeypoints);
void renderHandInherited(Array<float>& outputData, const std::array<Array<float>, 2>& handKeypoints);
private:
float* pGpuHand; // GPU aux memory
......
......@@ -10,7 +10,12 @@ namespace op
public:
virtual void initializationOnThread(){};
virtual void renderHand(Array<float>& outputData, const std::array<Array<float>, 2>& handKeypoints) = 0;
void renderHand(Array<float>& outputData, const std::array<Array<float>, 2>& handKeypoints,
const float scaleInputToOutput);
private:
virtual void renderHandInherited(Array<float>& outputData,
const std::array<Array<float>, 2>& handKeypoints) = 0;
};
}
......
......@@ -58,7 +58,7 @@ namespace op
// Extract people hands
for (auto& tDatum : *tDatums)
{
spHandExtractor->forwardPass(tDatum.handRectangles, tDatum.cvInputData, tDatum.scaleInputToOutput);
spHandExtractor->forwardPass(tDatum.handRectangles, tDatum.cvInputData);
for (auto hand = 0 ; hand < 2 ; hand++)
{
tDatum.handHeatMaps[hand] = spHandExtractor->getHeatMaps()[hand].clone();
......
......@@ -57,7 +57,8 @@ namespace op
const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__);
// Render people hands
for (auto& tDatum : *tDatums)
spHandRenderer->renderHand(tDatum.outputData, tDatum.handKeypoints);
spHandRenderer->renderHand(tDatum.outputData, tDatum.handKeypoints,
(float)tDatum.scaleInputToOutput);
// Profiling speed
Profiler::timerEnd(profilerKey);
Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__);
......
......@@ -9,13 +9,10 @@ namespace op
{
}
void FaceCpuRenderer::renderFace(Array<float>& outputData, const Array<float>& faceKeypoints)
void FaceCpuRenderer::renderFaceInherited(Array<float>& outputData, const Array<float>& faceKeypoints)
{
try
{
// Security checks
if (outputData.empty())
error("Empty Array<float> outputData.", __LINE__, __FUNCTION__, __FILE__);
// CPU rendering
renderFaceKeypointsCpu(outputData, faceKeypoints, mRenderThreshold);
}
......
......@@ -165,8 +165,7 @@ namespace op
}
void FaceExtractorCaffe::forwardPass(const std::vector<Rectangle<float>>& faceRectangles,
const cv::Mat& cvInputData,
const double scaleInputToOutput)
const cv::Mat& cvInputData)
{
try
{
......@@ -282,14 +281,12 @@ namespace op
const auto score = facePeaksPtr[xyIndex + 2];
const auto baseIndex = mFaceKeypoints.getSize(2)
* (part + person * mFaceKeypoints.getSize(1));
mFaceKeypoints[baseIndex] = (float)(scaleInputToOutput
* (Mscaling.at<double>(0,0) * x
+ Mscaling.at<double>(0,1) * y
+ Mscaling.at<double>(0,2)));
mFaceKeypoints[baseIndex+1] = (float)(scaleInputToOutput
* (Mscaling.at<double>(1,0) * x
+ Mscaling.at<double>(1,1) * y
+ Mscaling.at<double>(1,2)));
mFaceKeypoints[baseIndex] = (float)(Mscaling.at<double>(0,0) * x
+ Mscaling.at<double>(0,1) * y
+ Mscaling.at<double>(0,2));
mFaceKeypoints[baseIndex+1] = (float)(Mscaling.at<double>(1,0) * x
+ Mscaling.at<double>(1,1) * y
+ Mscaling.at<double>(1,2));
mFaceKeypoints[baseIndex+2] = score;
}
// HeatMaps: storing
......@@ -312,7 +309,6 @@ namespace op
#else
UNUSED(faceRectangles);
UNUSED(cvInputData);
UNUSED(scaleInputToOutput);
#endif
}
catch (const std::exception& e)
......
......@@ -46,22 +46,20 @@ namespace op
}
}
void FaceGpuRenderer::renderFace(Array<float>& outputData, const Array<float>& faceKeypoints)
void FaceGpuRenderer::renderFaceInherited(Array<float>& outputData, const Array<float>& faceKeypoints)
{
try
{
// Security checks
if (outputData.empty())
error("Empty Array<float> outputData.", __LINE__, __FUNCTION__, __FILE__);
// GPU rendering
#ifdef USE_CUDA
const auto elementRendered = spElementToRender->load(); // I prefer std::round(T&) over intRound(T) for std::atomic
// I prefer std::round(T&) over intRound(T) for std::atomic
const auto elementRendered = spElementToRender->load();
const auto numberPeople = faceKeypoints.getSize(0);
const Point<int> frameSize{outputData.getSize(2), outputData.getSize(1)};
if (numberPeople > 0 && elementRendered == 0)
{
cpuToGpuMemoryIfNotCopiedYet(outputData.getPtr(), outputData.getVolume());
// Draw faceKeypoints
cpuToGpuMemoryIfNotCopiedYet(outputData.getPtr(), outputData.getVolume());
cudaMemcpy(pGpuFace, faceKeypoints.getConstPtr(),
faceKeypoints.getSize(0) * FACE_NUMBER_PARTS * 3 * sizeof(float),
cudaMemcpyHostToDevice);
......
#include <openpose/face/renderFace.hpp>
#include <openpose/utilities/keypoint.hpp>
#include <openpose/face/faceRenderer.hpp>
namespace op
{
void FaceRenderer::renderFace(Array<float>& outputData, const Array<float>& faceKeypoints,
const float scaleInputToOutput)
{
try
{
// Security checks
if (outputData.empty())
error("Empty Array<float> outputData.", __LINE__, __FUNCTION__, __FILE__);
// Rescale keypoints to output size
auto faceKeypointsRescaled = faceKeypoints.clone();
scaleKeypoints(faceKeypointsRescaled, scaleInputToOutput);
// CPU/GPU rendering
renderFaceInherited(outputData, faceKeypointsRescaled);
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
}
}
}
......@@ -9,18 +9,17 @@ namespace op
{
}
void HandCpuRenderer::renderHand(Array<float>& outputData, const std::array<Array<float>, 2>& handKeypoints)
void HandCpuRenderer::renderHandInherited(Array<float>& outputData,
const std::array<Array<float>, 2>& handKeypoints)
{
try
{
// Security checks
if (outputData.empty())
error("Empty Array<float> outputData.", __LINE__, __FUNCTION__, __FILE__);
if (handKeypoints[0].getSize(0) != handKeypoints[1].getSize(0))
error("Wrong hand format: handKeypoints.getSize(0) != handKeypoints.getSize(1).",
__LINE__, __FUNCTION__, __FILE__);
// CPU rendering
renderHandKeypointsCpu(outputData, handKeypoints, mRenderThreshold);
renderHandKeypointsCpu(
outputData,
handKeypoints,
mRenderThreshold
);
}
catch (const std::exception& e)
{
......
......@@ -73,7 +73,7 @@ namespace op
}
}
void connectKeypoints(Array<float>& handCurrent, const double scaleInputToOutput, const int person,
void connectKeypoints(Array<float>& handCurrent, const int person,
const cv::Mat& affineMatrix, const float* handPeaks)
{
try
......@@ -86,12 +86,10 @@ namespace op
const auto y = handPeaks[xyIndex + 1];
const auto score = handPeaks[xyIndex + 2];
const auto baseIndex = handCurrent.getSize(2) * (part + person * handCurrent.getSize(1));
handCurrent[baseIndex] = (float)(scaleInputToOutput
* (affineMatrix.at<double>(0,0)*x + affineMatrix.at<double>(0,1)*y
+ affineMatrix.at<double>(0,2)));
handCurrent[baseIndex+1] = (float)(scaleInputToOutput
* (affineMatrix.at<double>(1,0)*x + affineMatrix.at<double>(1,1)*y
+ affineMatrix.at<double>(1,2)));
handCurrent[baseIndex] = (float)(affineMatrix.at<double>(0,0)*x + affineMatrix.at<double>(0,1)*y
+ affineMatrix.at<double>(0,2));
handCurrent[baseIndex+1] = (float)(affineMatrix.at<double>(1,0)*x + affineMatrix.at<double>(1,1)*y
+ affineMatrix.at<double>(1,2));
handCurrent[baseIndex+2] = score;
}
}
......@@ -255,8 +253,7 @@ namespace op
}
void HandExtractorCaffe::forwardPass(const std::vector<std::array<Rectangle<float>, 2>> handRectangles,
const cv::Mat& cvInputData,
const double scaleInputToOutput)
const cv::Mat& cvInputData)
{
try
{
......@@ -322,7 +319,7 @@ namespace op
cropFrame(mHandImageCrop, affineMatrix, cvInputData, handRectangle, netInputSide,
mNetOutputSize, mirrorImage);
// Deep net + Estimate keypoint locations
detectHandKeypoints(handCurrent, scaleInputToOutput, person, affineMatrix);
detectHandKeypoints(handCurrent, person, affineMatrix);
}
// Multi-scale detection
else
......@@ -359,7 +356,7 @@ namespace op
cropFrame(mHandImageCrop, affineMatrix, cvInputData, handRectangleScale,
netInputSide, mNetOutputSize, mirrorImage);
// Deep net + Estimate keypoint locations
detectHandKeypoints(handEstimated, scaleInputToOutput, 0, affineMatrix);
detectHandKeypoints(handEstimated, 0, affineMatrix);
if (i == 0
|| getAverageScore(handEstimated,0) > getAverageScore(handCurrent,person))
std::copy(handEstimated.getConstPtr(),
......@@ -390,7 +387,6 @@ namespace op
#else
UNUSED(handRectangles);
UNUSED(cvInputData);
UNUSED(scaleInputToOutput);
#endif
}
catch (const std::exception& e)
......@@ -399,8 +395,8 @@ namespace op
}
}
void HandExtractorCaffe::detectHandKeypoints(Array<float>& handCurrent, const double scaleInputToOutput,
const int person, const cv::Mat& affineMatrix)
void HandExtractorCaffe::detectHandKeypoints(Array<float>& handCurrent, const int person,
const cv::Mat& affineMatrix)
{
try
{
......@@ -442,11 +438,10 @@ namespace op
#endif
// Estimate keypoint locations
connectKeypoints(handCurrent, scaleInputToOutput, person, affineMatrix,
connectKeypoints(handCurrent, person, affineMatrix,
upImpl->spPeaksBlob->mutable_cpu_data());
#else
UNUSED(handCurrent);
UNUSED(scaleInputToOutput);
UNUSED(person);
UNUSED(affineMatrix);
#endif
......
......@@ -46,29 +46,28 @@ namespace op
}
}
void HandGpuRenderer::renderHand(Array<float>& outputData, const std::array<Array<float>, 2>& handKeypoints)
void HandGpuRenderer::renderHandInherited(Array<float>& outputData,
const std::array<Array<float>, 2>& handKeypoints)
{
try
{
// Security checks
if (outputData.empty())
error("Empty Array<float> outputData.", __LINE__, __FUNCTION__, __FILE__);
if (handKeypoints[0].getSize(0) != handKeypoints[1].getSize(0))
error("Wrong hand format: handKeypoints.getSize(0) != handKeypoints.getSize(1).", __LINE__, __FUNCTION__, __FILE__);
// GPU rendering
#ifdef USE_CUDA
const auto elementRendered = spElementToRender->load(); // I prefer std::round(T&) over intRound(T) for std::atomic
// I prefer std::round(T&) over intRound(T) for std::atomic
const auto elementRendered = spElementToRender->load();
const auto numberPeople = handKeypoints[0].getSize(0);
const Point<int> frameSize{outputData.getSize(2), outputData.getSize(1)};
// GPU rendering
if (numberPeople > 0 && elementRendered == 0)
{
cpuToGpuMemoryIfNotCopiedYet(outputData.getPtr(), outputData.getVolume());
// Draw handKeypoints
cpuToGpuMemoryIfNotCopiedYet(outputData.getPtr(), outputData.getVolume());
const auto handArea = handKeypoints[0].getSize(1)*handKeypoints[0].getSize(2);
const auto handVolume = numberPeople * handArea;
cudaMemcpy(pGpuHand, handKeypoints[0].getConstPtr(), handVolume * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(pGpuHand + handVolume, handKeypoints[1].getConstPtr(), handVolume * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(pGpuHand, handKeypoints[0].getConstPtr(), handVolume * sizeof(float),
cudaMemcpyHostToDevice);
cudaMemcpy(pGpuHand + handVolume, handKeypoints[1].getConstPtr(),
handVolume * sizeof(float), cudaMemcpyHostToDevice);
renderHandKeypointsGpu(*spGpuMemory, frameSize, pGpuHand, 2 * numberPeople, mRenderThreshold);
// CUDA check
cudaCheck(__LINE__, __FUNCTION__, __FILE__);
......
#include <openpose/utilities/keypoint.hpp>
#include <openpose/hand/handRenderer.hpp>
namespace op
{
void HandRenderer::renderHand(Array<float>& outputData,
const std::array<Array<float>, 2>& handKeypoints,
const float scaleInputToOutput)
{
try
{
// Security checks
if (outputData.empty())
error("Empty Array<float> outputData.", __LINE__, __FUNCTION__, __FILE__);
if (handKeypoints[0].getSize(0) != handKeypoints[1].getSize(0))
error("Wrong hand format: handKeypoints.getSize(0) != handKeypoints.getSize(1).",
__LINE__, __FUNCTION__, __FILE__);
// Rescale keypoints to output size
auto leftHandKeypointsRescaled = handKeypoints[0].clone();
scaleKeypoints(leftHandKeypointsRescaled, scaleInputToOutput);
auto rightHandKeypointsRescaled = handKeypoints[1].clone();
scaleKeypoints(rightHandKeypointsRescaled, scaleInputToOutput);
// CPU/GPU rendering
renderHandInherited(
outputData,
std::array<Array<float>, 2>{leftHandKeypointsRescaled, rightHandKeypointsRescaled}
);
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册