feat(pose): add openpose

This commit is contained in:
Syd Xu
2021-11-03 17:14:14 +08:00
parent e9273158de
commit 021c6b71d0
21 changed files with 732 additions and 49 deletions

View File

@@ -33,7 +33,10 @@ cmake .. # optional -DNCNN_VULKAN=OFF -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COM
- tracker (for face IOU calculation bettween frames)
- hopenet (for head pose detection) [Google Drive](https://drive.google.com/drive/folders/1zLam-8s9ZMPDUxUEtNU2F9yFTDRM5fk-?usp=sharing)
- pose
- detector (for pose estimation)
- detector (for pose detection/estimation)
- ultralight [Google Drive](https://drive.google.com/drive/folders/15b-I5HDyGe2WLb-TO85SJYmnYONvGOKh?usp=sharing)
- openpose [Google Drive](https://drive.google.com/drive/folders/1Q2mq7dOE-eHsvu4BYpBaWVLkU5roKsm5?usp=sharing)
- estimator (for pose estimation)
- ultralight [Google Drive](https://drive.google.com/drive/folders/15b-I5HDyGe2WLb-TO85SJYmnYONvGOKh?usp=sharing)
- hand
- detector (for hand detect)

View File

@@ -49,6 +49,7 @@ func (o ObjectInfo) ToCObjectInfo(w float64, h float64) *C.ObjectInfo {
ret.rect.y = C.int(o.Rect.Y * h)
ret.rect.width = C.int(o.Rect.Width * w)
ret.rect.height = C.int(o.Rect.Height * h)
return ret
if len(o.Keypoints) > 0 {
ret.pts = (*C.KeypointVector)(C.malloc(C.sizeof_KeypointVector))
ret.pts.length = C.int(len(o.Keypoints))
@@ -57,6 +58,7 @@ func (o ObjectInfo) ToCObjectInfo(w float64, h float64) *C.ObjectInfo {
pt := C.Keypoint{
C.Point2f{C.float(p.Point.X * w), C.float(p.Point.Y * h)},
C.float(p.Score),
C.int(0),
}
C.KeypointVectorSetValue(ret.pts, C.int(idx), &pt)
}

View File

@@ -27,13 +27,20 @@ func main() {
cpuCores := common.GetBigCPUCount()
common.SetOMPThreads(cpuCores)
log.Printf("CPU big cores:%d\n", cpuCores)
d := ultralightDetector(modelPath)
defer d.Destroy()
common.SetEstimatorThreads(d, cpuCores)
m := ultralightEstimator(modelPath)
defer m.Destroy()
common.SetEstimatorThreads(d, cpuCores)
detect(d, m, imgPath, "ultralight-pose3.jpg")
for did, d := range []detecter.Detecter{
ultralightDetector(modelPath),
openposeDetector(modelPath),
} {
defer d.Destroy()
common.SetEstimatorThreads(d, cpuCores)
for mid, m := range []estimator.Estimator{
ultralightEstimator(modelPath),
} {
defer m.Destroy()
common.SetEstimatorThreads(d, cpuCores)
detect(d, m, imgPath, "ultralight-pose.jpg", did, mid)
}
}
}
func ultralightDetector(modelPath string) detecter.Detecter {
@@ -45,6 +52,15 @@ func ultralightDetector(modelPath string) detecter.Detecter {
return d
}
func openposeDetector(modelPath string) detecter.Detecter {
modelPath = filepath.Join(modelPath, "openpose")
d := detecter.NewOpenPose()
if err := d.LoadModel(modelPath); err != nil {
log.Fatalln(err)
}
return d
}
func ultralightEstimator(modelPath string) estimator.Estimator {
modelPath = filepath.Join(modelPath, "ultralight-pose/pose")
d := estimator.NewUltralight()
@@ -54,7 +70,7 @@ func ultralightEstimator(modelPath string) estimator.Estimator {
return d
}
func detect(d detecter.Detecter, m estimator.Estimator, imgPath string, filename string) {
func detect(d detecter.Detecter, m estimator.Estimator, imgPath string, filename string, did int, mid int) {
inPath := filepath.Join(imgPath, filename)
imgSrc, err := loadImage(inPath)
if err != nil {
@@ -65,20 +81,25 @@ func detect(d detecter.Detecter, m estimator.Estimator, imgPath string, filename
if err != nil {
log.Fatalln(err)
}
for idx, roi := range rois {
keypoints, err := m.ExtractKeypoints(img, roi.Rect)
if err != nil {
log.Fatalln(err)
if !d.HasKeypoints() {
for idx, roi := range rois {
keypoints, err := m.ExtractKeypoints(img, roi.Rect)
if err != nil {
log.Fatalln(err)
}
rois[idx].Keypoints = keypoints
}
rois[idx].Keypoints = keypoints
}
var useOpenPose bool
if did == 1 {
useOpenPose = true
}
outPath := filepath.Join(imgPath, "./results", fmt.Sprintf("pose-%s", filename))
outPath := filepath.Join(imgPath, "./results", fmt.Sprintf("pose-%d-%d-%s", did, mid, filename))
drawer := posedrawer.New()
out := drawer.Draw(img, rois, true)
out := drawer.Draw(img, rois, true, useOpenPose)
if err := saveImage(out, outPath); err != nil {
log.Fatalln(err)

View File

@@ -17,6 +17,7 @@ import (
// Detecter represents deteter interface
type Detecter interface {
common.Estimator
HasKeypoints() bool
Detect(img *common.Image) ([]common.ObjectInfo, error)
}

View File

@@ -1,2 +0,0 @@
// Package detecter pose detecter
package detecter

View File

@@ -0,0 +1,51 @@
package detecter
/*
#include <stdlib.h>
#include <stdbool.h>
#include "openvision/common/common.h"
#include "openvision/pose/detecter.h"
*/
import "C"
import (
"unsafe"
"github.com/bububa/openvision/go/common"
)
// OpenPose represents utralight detecter
type OpenPose struct {
d C.IPoseDetecter
}
// NewOpenPose returns a new OpenPose
func NewOpenPose() *OpenPose {
return &OpenPose{
d: C.new_openpose(),
}
}
// Destroy free detecter
func (d *OpenPose) Destroy() {
common.DestroyEstimator(d)
}
// Pointer implement Estimator interface
func (d *OpenPose) Pointer() unsafe.Pointer {
return unsafe.Pointer(d.d)
}
// HasKeypoints implement Detecter interface
func (d *OpenPose) HasKeypoints() bool {
return true
}
// LoadModel load model for detecter
func (d *OpenPose) LoadModel(modelPath string) error {
return common.EstimatorLoadModel(d, modelPath)
}
// Detect implement Detecter interface
func (d *OpenPose) Detect(img *common.Image) ([]common.ObjectInfo, error) {
return Detect(d, img)
}

View File

@@ -34,6 +34,11 @@ func (d *Ultralight) Pointer() unsafe.Pointer {
return unsafe.Pointer(d.d)
}
// HasKeypoints implement Detecter interface
func (d *Ultralight) HasKeypoints() bool {
return false
}
// LoadModel load model for detecter
func (d *Ultralight) LoadModel(modelPath string) error {
return common.EstimatorLoadModel(d, modelPath)

View File

@@ -64,12 +64,21 @@ var (
CocoPair = [16][2]CocoPart{
{0, 1}, {1, 3}, {0, 2}, {2, 4}, {5, 6}, {5, 7}, {7, 9}, {6, 8}, {8, 10}, {5, 11}, {6, 12}, {11, 12}, {11, 13}, {12, 14}, {13, 15}, {14, 16},
}
// OpenPosePair represents joints pair for openpose
OpenPosePair = [17][2]int{
{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},
}
// CocoColors represents color for coco parts
CocoColors = [17]string{
CocoColors = [19]string{
"#ff0000", "#ff5500", "#ffaa00", "#ffff00",
"#aaff00", "#55ff00", "#00ff00", "#00ff55", "#00ffaa",
"#00ffff", "#00aaff", "#0055ff",
"#0000ff", "#aa00ff", "#ff00ff",
"#ff00aa", "#ff0055",
"#ff00aa", "#ff0055", "#ff55aa", "#aa0055",
}
)

View File

@@ -35,7 +35,7 @@ func New(options ...Option) *Drawer {
}
// Draw draw rois
func (d *Drawer) Draw(img image.Image, rois []common.ObjectInfo, drawBorder bool) image.Image {
func (d *Drawer) Draw(img image.Image, rois []common.ObjectInfo, drawBorder bool, openPose bool) image.Image {
imgW := float64(img.Bounds().Dx())
imgH := float64(img.Bounds().Dy())
out := image.NewRGBA(img.Bounds())
@@ -53,13 +53,24 @@ func (d *Drawer) Draw(img image.Image, rois []common.ObjectInfo, drawBorder bool
borderColor := d.BorderColor
common.DrawRectangle(gc, rect, borderColor, "", d.BorderStrokeWidth)
}
if len(roi.Keypoints) == 0 {
return out
}
// draw joins
for idx, pair := range CocoPair {
pairs := CocoPair[:]
if openPose {
pairs = OpenPosePair[:]
}
for idx, pair := range pairs {
p0 := roi.Keypoints[pair[0]]
p1 := roi.Keypoints[pair[1]]
if p0.Score < 0.2 || p1.Score < 0.2 {
continue
}
if p0.Point.X < 0 || p0.Point.Y < 0 || p1.Point.Y < 0 || p1.Point.X < 0 {
continue
}
cocoColor := CocoColors[idx]
gc.SetLineWidth(d.BorderStrokeWidth)
gc.SetStrokeColor(common.ColorFromHex(cocoColor))
@@ -75,6 +86,9 @@ func (d *Drawer) Draw(img image.Image, rois []common.ObjectInfo, drawBorder bool
if pt.Score < 0.2 {
continue
}
if pt.Point.X < 0 || pt.Point.Y < 0 {
continue
}
cocoColor := CocoColors[idx]
common.DrawCircle(gc, common.Pt(pt.Point.X*imgW, pt.Point.Y*imgH), d.KeypointRadius, cocoColor, "", d.KeypointStrokeWidth)
}

View File

@@ -66,6 +66,7 @@ target_include_directories(openvision
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/pose>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/pose/detecter>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/pose/estimator>
)
#install(TARGETS openvision EXPORT openvision ARCHIVE DESTINATION ${LIBRARY_OUTPUT_PATH})

View File

@@ -2,6 +2,7 @@
#include <algorithm>
#include <math.h>
#include <float.h>
#include <iostream>
#include "cpu.h"
#ifdef OV_VULKAN
@@ -145,7 +146,6 @@ void Estimator::set_num_threads(int n) {
}
}
int RatioAnchors(const Rect & anchor,
const std::vector<float>& ratios,
std::vector<Rect>* anchors, int threads_num) {

View File

@@ -8,6 +8,7 @@ extern "C" {
#ifdef __cplusplus
typedef ov::Size Size;
typedef ov::Size2f Size2f;
typedef ov::Point Point;
typedef ov::Point2f Point2f;
typedef ov::Rect Rect;
@@ -19,6 +20,12 @@ typedef struct Size {
int width;
int height;
} Size;
//
// Wrapper for an individual cv::cvSize2f
typedef struct Size2f {
int width;
int height;
} Size2f;
// Wrapper for an individual cv::cvPoint
typedef struct Point {
@@ -41,12 +48,13 @@ typedef struct Rect {
int height;
} Rect;
typedef struct Keypoint {
Point2f p;
float score;
int id;
} Keypoint;
#endif
typedef void* IEstimator;

View File

@@ -3,6 +3,7 @@
#include <vector>
#include <string>
#include <iostream>
#include "config.h"
#include "net.h"
#ifdef OV_OPENMP
@@ -24,31 +25,50 @@ protected:
};
// Wrapper for an individual cv::cvSize
typedef struct Size {
struct Size {
int width;
int height;
Size(int _width = 0, int _height = 0): width(_width), height(_height) {}
} Size;
};
//
// Wrapper for an individual cv::cvSize2f
struct Size2f {
float width;
float height;
Size2f(float _width = 0, float _height = 0): width(_width), height(_height) {}
};
// Wrapper for an individual cv::cvPoint
typedef struct Point {
struct Point {
int x;
int y;
Point(int _x = 0, int _y = 0): x(_x), y(_y) {}
Point operator-(const Point &p2) {
return Point(x - p2.x, y - p2.y);
};
} Point;
};
// Wrapper for an individual cv::Point2f
typedef struct Point2f {
struct Point2f {
float x;
float y;
Point2f(float _x = 0, float _y = 0): x(_x), y(_y) {}
} Point2f;
Point2f operator*(float f) const {
return Point2f(x * f, y * f);
};
Point2f operator/(float f) const {
return Point2f(x / f, y / f);
};
Point2f operator+(const Point2f &p2) const {
return Point2f(x + p2.x, y + p2.y);
};
Point2f operator-(const Point2f &p2) const {
return Point2f(x - p2.x, y - p2.y);
};
};
// Wrapper for an individual cv::Rect
typedef struct Rect {
struct Rect {
int x;
int y;
int width;
@@ -79,23 +99,44 @@ typedef struct Rect {
}
return Rect(inter_x, inter_y, inter_width, inter_height);
};
} Rect;
struct ImageInfo {
std::string label_;
float score;
};
struct Keypoint {
Point2f p;
float score;
int id;
Keypoint(){};
Keypoint(const Point2f p_): p(p_){};
};
struct ObjectInfo {
Rect rect;
float score;
int label;
std::vector<Point2f> pts;
std::vector<Keypoint> pts;
};
struct Image {
std::vector<float> data;
int width;
int height;
int channels;
float at(const Point& p) const {
return data.at((p.x + p.y * width) * channels);
};
float atChannel(const Point& p, int channel) const {
return data.at((p.x + p.y * width) * channels + channel);
};
Image(){};
Image(const ncnn::Mat& mat): width(mat.w), height(mat.h), channels(mat.c) {
int data_size = mat.total();
float* ptr = (float*)malloc(data_size * sizeof(float));
memcpy(ptr, mat.data, data_size * sizeof(float));
data.clear();
data.resize(data_size);
data.assign(ptr, ptr + data_size);
free(ptr);
};
};
struct GridAndStride
@@ -105,6 +146,20 @@ struct GridAndStride
int stride;
};
template<typename T, std::size_t N>
constexpr std::size_t arraySize(const T (&)[N]) noexcept {
return N;
}
static inline int cvRound(double x) {
int y;
if(x >= (int)x+0.5)
y = (int)x++;
else
y = (int)x;
return y;
};
int RatioAnchors(const Rect & anchor,
const std::vector<float>& ratios, std::vector<Rect>* anchors, int threads_num);

View File

@@ -239,8 +239,8 @@ int YoloFace::DetectFace(const unsigned char* rgbdata,
for (int j = 0; j < obj.pts.size(); j++)
{
float ptx = (obj.pts[j].x - (float(wpad) / 2)) / scale;
float pty = (obj.pts[j].y - (float(hpad) / 2)) / scale;
float ptx = (obj.pts[j].p.x - (float(wpad) / 2)) / scale;
float pty = (obj.pts[j].p.y - (float(hpad) / 2)) / scale;
obj.pts[j] = ov::Point2f(ptx, pty);
}
@@ -258,8 +258,8 @@ int YoloFace::DetectFace(const unsigned char* rgbdata,
FaceInfo info;
info.rect = obj.rect;
for (int k = 0; k < 5; ++k) {
info.keypoints_[k] = obj.pts[k].x;
info.keypoints_[k + 5] = obj.pts[k].y;
info.keypoints_[k] = obj.pts[k].p.x;
info.keypoints_[k + 5] = obj.pts[k].p.y;
}
faces->push_back(info);

View File

@@ -1,5 +1,6 @@
#include "../pose.h"
#include "handpose/handpose.hpp"
#include <iostream>
IHandPoseEstimator new_handpose() {
return new ovhand::HandPose();

View File

@@ -9,6 +9,7 @@ extern "C" {
#endif
typedef void* IPoseDetecter;
IPoseDetecter new_ultralight();
IPoseDetecter new_openpose();
int detect_pose(IPoseDetecter d, const unsigned char* rgbdata,
int img_width, int img_height,
ObjectInfoVector* rois);

View File

@@ -1,10 +1,15 @@
#include "../detecter.h"
#include "ultralight/ultralight.hpp"
#include "openpose/openpose.hpp"
IPoseDetecter new_ultralight() {
return new ovpose::Ultralight();
}
IPoseDetecter new_openpose() {
return new ovpose::OpenPose();
}
int detect_pose(IPoseDetecter d, const unsigned char* rgbdata, int img_width, int img_height, ObjectInfoVector* rois) {
std::vector<ov::ObjectInfo> detected;
int ret = static_cast<ovpose::Detecter*>(d)->Detect(rgbdata, img_width, img_height, &detected);
@@ -16,12 +21,20 @@ int detect_pose(IPoseDetecter d, const unsigned char* rgbdata, int img_width, in
rois->items = (ObjectInfo*)malloc(rois->length * sizeof(ObjectInfo));
for (size_t i = 0; i < detected.size(); ++i) {
ov::ObjectInfo o = detected[i];
rois->items[i] = ObjectInfo{
o.rect,
o.score,
o.label,
NULL
};
rois->items[i].rect = o.rect;
rois->items[i].score = o.score;
rois->items[i].label = o.label;
if (o.pts.size() > 0) {
rois->items[i].pts = (KeypointVector*)malloc(sizeof(KeypointVector));
rois->items[i].pts->length = o.pts.size();
rois->items[i].pts->points = (Keypoint*)malloc(o.pts.size() * sizeof(Keypoint));
for (size_t j=0; j < o.pts.size(); ++j) {
ov::Keypoint pt = o.pts.at(j);
rois->items[i].pts->points[j] = pt;
}
} else {
rois->items[i].pts = NULL;
}
}
return 0;
}
@@ -30,4 +43,7 @@ namespace ovpose {
Detecter* UltralightFactory::CreateDetecter() {
return new Ultralight();
}
Detecter* OpenPoseFactory::CreateDetecter() {
return new OpenPose();
}
}

View File

@@ -24,5 +24,12 @@ public:
~UltralightFactory() {}
Detecter* CreateDetecter();
};
class OpenPoseFactory: public DetecterFactory {
public:
OpenPoseFactory() {}
~OpenPoseFactory() {}
Detecter* CreateDetecter();
};
}
#endif // !_POSE_DETECTER_H

View File

@@ -0,0 +1,424 @@
#include "openpose.hpp"
#include "mat.h"
#ifdef OV_VULKAN
#include "gpu.h"
#endif // OV_VULKAN
namespace ovpose {
static const std::pair<int, int> limbIdsHeatmap[] = {
{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}
};
static const std::pair<int, int> limbIdsPaf[] = {
{12, 13}, {20, 21}, {14, 15}, {16, 17}, {22, 23},
{24, 25}, {0, 1}, {2, 3}, {4, 5}, {6, 7},
{8, 9}, {10, 11}, {28, 29}, {30, 31}, {34, 35},
{32, 33}, {36, 37}, {18, 19}, {26, 27}
};
int OpenPose::Detect(const unsigned char* rgbdata,
int img_width, int img_height,
std::vector<ov::ObjectInfo>* rois) {
if (!initialized_) {
return 10000;
}
if (rgbdata == 0){
return 10001;
}
int w = img_width;
int h = img_height;
int net_w = 456;
int net_h = 456;
float scale = 1.0f;
if (w > h) {
scale = (float) net_w / w;
w = net_w;
h = h * scale;
} else {
scale = (float) net_h / h;
h = net_h;
w = w * scale;
}
ncnn::Mat in = ncnn::Mat::from_pixels_resize(rgbdata, ncnn::Mat::PIXEL_RGB, img_width, img_height, w, h);
in.substract_mean_normalize(mean_vals, norm_vals);
// forward
ncnn::Mat pafs;
ncnn::Mat heatmaps;
ncnn::Extractor ex = net_->create_extractor();
ex.input("data", in);
ex.extract("stage_1_output_1_heatmaps", heatmaps); // or stage_0_output_1_heatmaps
ex.extract("stage_1_output_0_pafs", pafs); // or stage_0_output_0_pafs
// postprocess
this->postProcess(pafs, heatmaps, *rois, img_height, img_width, h, w);
return 0;
}
void OpenPose::findPeaks(const std::vector<ov::Image> &heatMaps,
const float minPeaksDistance,
std::vector<std::vector<ov::Keypoint> > &allPeaks,
int heatMapId) {
const float threshold = 0.1f;
std::vector<ov::Point2f> peaks;
const ov::Image &heatMap = heatMaps[heatMapId];
int heatMapStep = heatMap.channels * heatMap.width;
for (int y = -1; y < heatMap.height + 1; y++) {
for (int x = -1; x < heatMap.width + 1; x++) {
float val = 0;
if (x >= 0
&& y >= 0
&& x < heatMap.width
&& y < heatMap.height) {
val = heatMap.data[y * heatMapStep + x];
val = val >= threshold ? val : 0;
}
float left_val = 0;
if (y >= 0
&& x < (heatMap.width- 1)
&& y < heatMap.height) {
left_val = heatMap.data[y * heatMapStep + x + 1];
left_val = left_val >= threshold ? left_val : 0;
}
float right_val = 0;
if (x > 0
&& y >= 0
&& y < heatMap.height) {
right_val = heatMap.data[y * heatMapStep + x - 1];
right_val = right_val >= threshold ? right_val : 0;
}
float top_val = 0;
if (x >= 0
&& x < heatMap.width
&& y < (heatMap.height- 1)) {
top_val = heatMap.data[(y + 1) * heatMapStep + x];
top_val = top_val >= threshold ? top_val : 0;
}
float bottom_val = 0;
if (x >= 0
&& y > 0
&& x < heatMap.width) {
bottom_val = heatMap.data[(y - 1) * heatMapStep + x];
bottom_val = bottom_val >= threshold ? bottom_val : 0;
}
if ((val > left_val)
&& (val > right_val)
&& (val > top_val)
&& (val > bottom_val)) {
peaks.push_back(ov::Point2f(x, y));
}
}
}
std::sort(peaks.begin(), peaks.end(), [](const ov::Point2f &a, const ov::Point2f &b) {
return a.x < b.x;
});
std::vector<bool> isActualPeak(peaks.size(), true);
int peakCounter = 0;
std::vector<ov::Keypoint> &peaksWithScoreAndID = allPeaks[heatMapId];
for (size_t i = 0; i < peaks.size(); i++) {
if (isActualPeak[i]) {
for (size_t j = i + 1; j < peaks.size(); j++) {
if (sqrt((peaks[i].x - peaks[j].x) * (peaks[i].x - peaks[j].x) +
(peaks[i].y - peaks[j].y) * (peaks[i].y - peaks[j].y)) < minPeaksDistance) {
isActualPeak[j] = false;
}
}
ov::Keypoint pt;
pt.id = peakCounter++;
pt.p = peaks[i];
pt.score = heatMap.data[peaks[i].x + peaks[i].y * heatMapStep];
peaksWithScoreAndID.push_back(pt);
}
}
}
void OpenPose::groupPeaksToPoses(const std::vector<std::vector<ov::Keypoint> > &allPeaks,
const std::vector<ov::Image> &pafs,
std::vector<ov::ObjectInfo>& poses,
const size_t keypointsNumber,
const float midPointsScoreThreshold,
const float foundMidPointsRatioThreshold,
const int minJointsNumber,
const float minSubsetScore) {
std::vector<ov::Keypoint> candidates;
for (const auto &peaks : allPeaks) {
candidates.insert(candidates.end(), peaks.begin(), peaks.end());
}
std::vector<HumanPoseByPeaksIndices> subset(0, HumanPoseByPeaksIndices(keypointsNumber));
for (size_t k = 0; k < ov::arraySize(limbIdsPaf); k++) {
std::vector<TwoJointsConnection> connections;
const int mapIdxOffset = 0; // keypointsNumber + 1;
std::pair<ov::Image, ov::Image> scoreMid = {pafs[limbIdsPaf[k].first - mapIdxOffset],
pafs[limbIdsPaf[k].second - mapIdxOffset]};
const int idxJointA = limbIdsHeatmap[k].first; // first - 1;
const int idxJointB = limbIdsHeatmap[k].second; // second - 1;
const std::vector<ov::Keypoint> &candA = allPeaks[idxJointA];
const std::vector<ov::Keypoint> &candB = allPeaks[idxJointB];
const size_t nJointsA = candA.size();
const size_t nJointsB = candB.size();
if (nJointsA == 0 && nJointsB == 0) {
continue;
} else if (nJointsA == 0) {
for (size_t i = 0; i < nJointsB; i++) {
int num = 0;
for (size_t j = 0; j < subset.size(); j++) {
if (subset[j].peaksIndices[idxJointB] == candB[i].id) {
num++;
continue;
}
}
if (num == 0) {
HumanPoseByPeaksIndices personKeypoints(keypointsNumber);
personKeypoints.peaksIndices[idxJointB] = candB[i].id;
personKeypoints.nJoints = 1;
personKeypoints.score = candB[i].score;
subset.push_back(personKeypoints);
}
}
continue;
} else if (nJointsB == 0) {
for (size_t i = 0; i < nJointsA; i++) {
int num = 0;
for (size_t j = 0; j < subset.size(); j++) {
if (subset[j].peaksIndices[idxJointA] == candA[i].id) {
num++;
continue;
}
}
if (num == 0) {
HumanPoseByPeaksIndices personKeypoints(keypointsNumber);
personKeypoints.peaksIndices[idxJointA] = candA[i].id;
personKeypoints.nJoints = 1;
personKeypoints.score = candA[i].score;
subset.push_back(personKeypoints);
}
}
continue;
}
std::vector<TwoJointsConnection> tempJointConnections;
for (size_t i = 0; i < nJointsA; i++) {
for (size_t j = 0; j < nJointsB; j++) {
ov::Point2f pt = candA[i].p * 0.5 + candB[j].p * 0.5;
ov::Point mid = ov::Point(ov::cvRound(pt.x), ov::cvRound(pt.y));
ov::Point2f vec = candB[j].p - candA[i].p;
double norm_vec = sqrt(vec.x * vec.x + vec.y * vec.y);
if (norm_vec == 0) {
continue;
}
vec = vec / norm_vec;
float score = vec.x * scoreMid.first.at(mid) + vec.y * scoreMid.second.at(mid);
int height_n = pafs[0].height/ 2;
float suc_ratio = 0.0f;
float mid_score = 0.0f;
const int mid_num = 10;
const float scoreThreshold = -100.0f;
if (score > scoreThreshold) {
float p_sum = 0;
float p_count = 0;
ov::Size2f step((candB[j].p.x - candA[i].p.x) / (mid_num - 1),
(candB[j].p.y - candA[i].p.y) / (mid_num - 1));
for (int n = 0; n < mid_num; n++) {
ov::Point midPoint(ov::cvRound(candA[i].p.x + n * step.width),
ov::cvRound(candA[i].p.y + n * step.height));
ov::Point2f pred(scoreMid.first.at(midPoint),
scoreMid.second.at(midPoint));
score = vec.x * pred.x + vec.y * pred.y;
if (score > midPointsScoreThreshold) {
p_sum += score;
p_count++;
}
}
suc_ratio = static_cast<float>(p_count / mid_num);
float ratio = p_count > 0 ? p_sum / p_count : 0.0f;
mid_score = ratio + static_cast<float>(std::min(height_n / norm_vec - 1, 0.0));
}
if (mid_score > 0
&& suc_ratio > foundMidPointsRatioThreshold) {
tempJointConnections.push_back(TwoJointsConnection(i, j, mid_score));
}
}
}
if (!tempJointConnections.empty()) {
std::sort(tempJointConnections.begin(), tempJointConnections.end(),
[](const TwoJointsConnection &a,
const TwoJointsConnection &b) {
return (a.score > b.score);
});
}
size_t num_limbs = std::min(nJointsA, nJointsB);
size_t cnt = 0;
std::vector<int> occurA(nJointsA, 0);
std::vector<int> occurB(nJointsB, 0);
for (size_t row = 0; row < tempJointConnections.size(); row++) {
if (cnt == num_limbs) {
break;
}
const int &indexA = tempJointConnections[row].firstJointIdx;
const int &indexB = tempJointConnections[row].secondJointIdx;
const float &score = tempJointConnections[row].score;
if (occurA[indexA] == 0
&& occurB[indexB] == 0) {
connections.push_back(TwoJointsConnection(candA[indexA].id, candB[indexB].id, score));
cnt++;
occurA[indexA] = 1;
occurB[indexB] = 1;
}
}
if (connections.empty()) {
continue;
}
bool extraJointConnections = (k == 17 || k == 18);
if (k == 0) {
subset = std::vector<HumanPoseByPeaksIndices>(
connections.size(), HumanPoseByPeaksIndices(keypointsNumber));
for (size_t i = 0; i < connections.size(); i++) {
const int &indexA = connections[i].firstJointIdx;
const int &indexB = connections[i].secondJointIdx;
subset[i].peaksIndices[idxJointA] = indexA;
subset[i].peaksIndices[idxJointB] = indexB;
subset[i].nJoints = 2;
subset[i].score = candidates[indexA].score + candidates[indexB].score + connections[i].score;
}
} else if (extraJointConnections) {
for (size_t i = 0; i < connections.size(); i++) {
const int &indexA = connections[i].firstJointIdx;
const int &indexB = connections[i].secondJointIdx;
for (size_t j = 0; j < subset.size(); j++) {
if (subset[j].peaksIndices[idxJointA] == indexA
&& subset[j].peaksIndices[idxJointB] == -1) {
subset[j].peaksIndices[idxJointB] = indexB;
} else if (subset[j].peaksIndices[idxJointB] == indexB
&& subset[j].peaksIndices[idxJointA] == -1) {
subset[j].peaksIndices[idxJointA] = indexA;
}
}
}
continue;
} else {
for (size_t i = 0; i < connections.size(); i++) {
const int &indexA = connections[i].firstJointIdx;
const int &indexB = connections[i].secondJointIdx;
bool num = false;
for (size_t j = 0; j < subset.size(); j++) {
if (subset[j].peaksIndices[idxJointA] == indexA) {
subset[j].peaksIndices[idxJointB] = indexB;
subset[j].nJoints++;
subset[j].score += candidates[indexB].score + connections[i].score;
num = true;
}
}
if (!num) {
HumanPoseByPeaksIndices hpWithScore(keypointsNumber);
hpWithScore.peaksIndices[idxJointA] = indexA;
hpWithScore.peaksIndices[idxJointB] = indexB;
hpWithScore.nJoints = 2;
hpWithScore.score = candidates[indexA].score + candidates[indexB].score + connections[i].score;
subset.push_back(hpWithScore);
}
}
}
}
for (const auto &subsetI : subset) {
if (subsetI.nJoints < minJointsNumber || subsetI.score / subsetI.nJoints < minSubsetScore) {
continue;
}
int position = -1;
ov::ObjectInfo pose;
pose.pts = std::vector<ov::Keypoint>(
keypointsNumber,
ov::Keypoint(ov::Point2f(-1.0f, -1.0f))
);
pose.score = subsetI.score * std::max(0, subsetI.nJoints - 1);
for (const auto &peakIdx : subsetI.peaksIndices) {
position++;
if (peakIdx >= 0) {
pose.pts[position] = candidates[peakIdx];
pose.pts[position].p.x += 0.5;
pose.pts[position].p.y += 0.5;
}
}
poses.push_back(pose);
}
}
void OpenPose::postProcess(const ncnn::Mat &pafs, const ncnn::Mat &heatmaps,
std::vector<ov::ObjectInfo>& poses,
int img_h, int img_w,
int net_h, int net_w) {
float upsample_ratio = 4;
// heatmaps
std::vector<ov::Image> cv_heatmaps_upsample(heatmaps.c);
for (int p = 0; p < heatmaps.c; p++) {
ncnn::Mat resizedMat;
int w = heatmaps.channel(p).w * upsample_ratio;
int h = heatmaps.channel(p).h * upsample_ratio;
ncnn::resize_bicubic(heatmaps.channel(p), resizedMat, w, h);
cv_heatmaps_upsample[p] = ov::Image(resizedMat);
}
// pafs
std::vector<ov::Image> cv_pafs_upsample(pafs.c);
for (int p = 0; p < pafs.c; p++) {
ncnn::Mat resizedMat;
int w = pafs.channel(p).w * upsample_ratio;
int h = pafs.channel(p).h * upsample_ratio;
ncnn::resize_bicubic(pafs.channel(p), resizedMat, w, h);
cv_pafs_upsample[p] = ov::Image(resizedMat);
}
// postprocess
std::vector<std::vector<ov::Keypoint> > peaksFromHeatMap(cv_heatmaps_upsample.size());
// #if defined(_OPENMP)
// #pragma omp parallel for num_threads(num_threads)
// #endif
for (int i = 0; i < cv_heatmaps_upsample.size(); i++) {
this->findPeaks(cv_heatmaps_upsample, minPeaksDistance, peaksFromHeatMap, i);
}
int peaksBefore = 0;
for (size_t heatmapId = 1; heatmapId < cv_heatmaps_upsample.size(); heatmapId++) {
peaksBefore += static_cast<int>(peaksFromHeatMap[heatmapId - 1].size());
for (auto &peak : peaksFromHeatMap[heatmapId]) {
peak.id += peaksBefore;
}
}
groupPeaksToPoses(
peaksFromHeatMap, cv_pafs_upsample, poses, keypointsNumber, midPointsScoreThreshold,
foundMidPointsRatioThreshold, minJointsNumber, minSubsetScore);
// scale keypoint
float scale_x = 1.0f * net_w / img_w;
float scale_y = 1.0f * net_h / img_h;
float stride = 8.0f;
float upsample = upsample_ratio;
for (int i = 0; i < poses.size(); i++) {
ov::ObjectInfo pose = poses[i];
for (int j = 0; j < keypointsNumber; j++) {
if (pose.pts[j].p.x == -1 || pose.pts[j].p.y == -1) {
continue;
}
poses[i].pts[j].p.x = stride / upsample * pose.pts[j].p.x / scale_x;
poses[i].pts[j].p.y = stride / upsample * pose.pts[j].p.y / scale_y;
}
}
}
}

View File

@@ -0,0 +1,65 @@
#ifndef _POSE_OPENPOSE_H_
#define _POSE_OPENPOSE_H_
#include "../detecter.hpp"
#include <vector>
#include "net.h"
namespace ovpose {
struct HumanPoseByPeaksIndices {
std::vector<int> peaksIndices;
int nJoints;
float score;
explicit HumanPoseByPeaksIndices(const int keypointsNumber) : peaksIndices(std::vector<int>(keypointsNumber, -1)),
nJoints(0),
score(0.0f) {};
};
struct TwoJointsConnection {
int firstJointIdx;
int secondJointIdx;
float score;
TwoJointsConnection(const int firstJointIdx_,
const int secondJointIdx_,
const float score_): firstJointIdx(firstJointIdx_), secondJointIdx(secondJointIdx_), score(score_) {};
};
class OpenPose : public Detecter {
public:
int Detect(const unsigned char* rgbadata,
int img_width, int img_height,
std::vector<ov::ObjectInfo>* rois);
private:
const float mean_vals[3] = {127.5f, 127.5f, 127.5f};
const float norm_vals[3] = {1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f};
const float minPeaksDistance = 3.0f;
const int keypointsNumber = 18;
const float midPointsScoreThreshold = 0.05f;
const float foundMidPointsRatioThreshold = 0.8f;
const int minJointsNumber = 3;
const float minSubsetScore = 0.2f;
void postProcess(const ncnn::Mat &pafs, const ncnn::Mat &heatmaps,
std::vector<ov::ObjectInfo>& poses,
int img_h, int img_w,
int net_h, int net_w);
void findPeaks(const std::vector<ov::Image> &heatMaps,
const float minPeaksDistance,
std::vector<std::vector<ov::Keypoint> > &allPeaks,
int heatMapId);
void groupPeaksToPoses(const std::vector<std::vector<ov::Keypoint> > &allPeaks,
const std::vector<ov::Image> &pafs,
std::vector<ov::ObjectInfo> &poses,
const size_t keypointsNumber,
const float midPointsScoreThreshold,
const float foundMidPointsRatioThreshold,
const int minJointsNumber,
const float minSubsetScore);
};
}
#endif // !_POSE_OPENPOSE_H_

View File

@@ -14,7 +14,8 @@ int extract_pose_keypoints(IPoseEstimator d, const unsigned char* rgbdata, int i
keypoints->length = points.size();
keypoints->points = (Keypoint*)malloc(keypoints->length * sizeof(Keypoint));
for (size_t i = 0; i < points.size(); ++i) {
keypoints->points[i] = points[i];
keypoints->points[i].p = points[i].p;
keypoints->points[i].score = points[i].score;
}
return 0;
}