Skip to content

Commit

Permalink
基于opencv的人脸检测onnx已转好,直接调用.
Browse files Browse the repository at this point in the history
Signed-off-by: zhd5120153951 <[email protected]>
  • Loading branch information
zhd5120153951 committed Oct 19, 2023
1 parent 89c5df1 commit 7c7735e
Show file tree
Hide file tree
Showing 16 changed files with 375 additions and 454 deletions.
6 changes: 6 additions & 0 deletions Face-Detect/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# scrfd-opencv
使用OpenCV部署SCRFD人脸检测,包含C++和Python两种版本的程序实现,本套程序只依赖opencv库就可以运行, 从而彻底摆脱对任何深度学习框架的依赖。

SCRFD是一个FCOS式的人脸检测器,2021年5月刚发出来的,SCRFD 是高效率高精度人脸检测算法,速度和精度相比其他算法都有提升。
你的机器里只要安装里OpenCV库,就能运行本套程序。C++版本的主程序是main.cpp,Python版本的主程序是main.py。
程序输出检测到的人脸矩形框和5个关键点
167 changes: 167 additions & 0 deletions Face-Detect/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#include <fstream>
#include <sstream>
#include <iostream>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;
using namespace dnn;
using namespace std;

struct Net_config
{
float confThreshold; // class Confidence threshold
float nmsThreshold; // Non-maximum suppression threshold
string modelfile;
};

class SCRFD
{
public:
SCRFD(Net_config config);
void detect(Mat& frame);
private:

const float stride[3] = { 8.0, 16.0, 32.0 };
const int inpWidth = 640;
const int inpHeight = 640;
float confThreshold;
float nmsThreshold;
const bool keep_ratio = true;
Net net;
Mat resize_image(Mat srcimg, int* newh, int* neww, int* top, int* left);
};

SCRFD::SCRFD(Net_config config)
{
this->confThreshold = config.confThreshold;
this->nmsThreshold = config.nmsThreshold;
this->net = readNet(config.modelfile);
}

Mat SCRFD::resize_image(Mat srcimg, int* newh, int* neww, int* top, int* left)
{
int srch = srcimg.rows, srcw = srcimg.cols;
*newh = this->inpHeight;
*neww = this->inpWidth;
Mat dstimg;
if (this->keep_ratio && srch != srcw)
{
float hw_scale = (float)srch / srcw;
if (hw_scale > 1)
{
*newh = this->inpHeight;
*neww = int(this->inpWidth / hw_scale);
resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
*left = int((this->inpWidth - *neww) * 0.5);
copyMakeBorder(dstimg, dstimg, 0, 0, *left, this->inpWidth - *neww - *left, BORDER_CONSTANT, 0);
}
else
{
*newh = (int)this->inpHeight * hw_scale;
*neww = this->inpWidth;
resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
*top = (int)(this->inpHeight - *newh) * 0.5;
copyMakeBorder(dstimg, dstimg, *top, this->inpHeight - *newh - *top, 0, 0, BORDER_CONSTANT, 0);
}
}
else
{
resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
}
return dstimg;
}

void SCRFD::detect(Mat& frame)
{
int newh = 0, neww = 0, padh = 0, padw = 0;
Mat img = this->resize_image(frame, &newh, &neww, &padh, &padw);
Mat blob;
blobFromImage(img, blob, 1 / 128.0, Size(this->inpWidth, this->inpHeight), Scalar(127.5, 127.5, 127.5), true, false);
this->net.setInput(blob);
vector<Mat> outs;
this->net.forward(outs, this->net.getUnconnectedOutLayersNames());

/////generate proposals
vector<float> confidences;
vector<Rect> boxes;
vector< vector<int>> landmarks;
float ratioh = (float)frame.rows / newh, ratiow = (float)frame.cols / neww;
int n = 0, i = 0, j = 0, k = 0, l = 0;
for (n = 0; n < 3; n++) ///�߶�
{
int num_grid_x = (int)(this->inpWidth / this->stride[n]);
int num_grid_y = (int)(this->inpHeight / this->stride[n]);
float* pdata_score = (float*)outs[n * 3].data; ///score
float* pdata_bbox = (float*)outs[n * 3 + 1].data; ///bounding box
float* pdata_kps = (float*)outs[n * 3 + 2].data; ///face landmark
for (i = 0; i < num_grid_y; i++)
{
for (j = 0; j < num_grid_x; j++)
{
for (k = 0; k < 2; k++)
{
if (pdata_score[0] > this->confThreshold)
{
const int xmin = (int)(((j - pdata_bbox[0]) * this->stride[n] - padw) * ratiow);
const int ymin = (int)(((i - pdata_bbox[1]) * this->stride[n] - padh) * ratioh);
const int width = (int)((pdata_bbox[2] + pdata_bbox[0])*this->stride[n] * ratiow);
const int height = (int)((pdata_bbox[3] + pdata_bbox[1])*this->stride[n] * ratioh);
confidences.push_back(pdata_score[0]);
boxes.push_back(Rect(xmin, ymin, width, height));
vector<int> landmark(10, 0);
for (l = 0; l < 10; l+=2)
{
landmark[l] = (int)(((j + pdata_kps[l]) * this->stride[n] - padw) * ratiow);
landmark[l + 1] = (int)(((i + pdata_kps[l + 1]) * this->stride[n] - padh) * ratioh);
}
landmarks.push_back(landmark);
}
pdata_score++;
pdata_bbox += 4;
pdata_kps += 10;
}
}
}
}

// Perform non maximum suppression to eliminate redundant overlapping boxes with
// lower confidences
vector<int> indices;
dnn::NMSBoxes(boxes, confidences, this->confThreshold, this->nmsThreshold, indices);
for (i = 0; i < indices.size(); ++i)
{
int idx = indices[i];
Rect box = boxes[idx];
rectangle(frame, Point(box.x, box.y), Point(box.x + box.width, box.y + box.height), Scalar(0, 0, 255), 2);
for (k = 0; k < 10; k+=2)
{
circle(frame, Point(landmarks[idx][k], landmarks[idx][k + 1]), 1, Scalar(0, 255, 0), -1);
}

//Get the label for the class name and its confidence
string label = format("%.2f", confidences[idx]);
//Display the label at the top of the bounding box
int baseLine;
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
int top = max(box.y, labelSize.height);
//rectangle(frame, Point(left, top - int(1.5 * labelSize.height)), Point(left + int(1.5 * labelSize.width), top + baseLine), Scalar(0, 255, 0), FILLED);
putText(frame, label, Point(box.x, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 255, 0), 1);
}
}

int main()
{
Net_config cfg = { 0.5, 0.5, "weights/scrfd_2.5g_kps.onnx" }; ///choices = ["weights/scrfd_500m_kps.onnx", "weights/scrfd_2.5g_kps.onnx", "weights/scrfd_10g_kps.onnx"]
SCRFD mynet(cfg);
string imgpath = "selfie.jpg";
Mat srcimg = imread(imgpath);
mynet.detect(srcimg);

static const string kWinName = "Deep learning object detection in OpenCV";
namedWindow(kWinName, WINDOW_NORMAL);
imshow(kWinName, srcimg);
waitKey(0);
destroyAllWindows();
}
129 changes: 129 additions & 0 deletions Face-Detect/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import cv2
import argparse
import numpy as np

class SCRFD():
def __init__(self, onnxmodel, confThreshold=0.5, nmsThreshold=0.5):
self.inpWidth = 640
self.inpHeight = 640
self.confThreshold = confThreshold
self.nmsThreshold = nmsThreshold
self.net = cv2.dnn.readNet(onnxmodel)
self.keep_ratio = True
self.fmc = 3
self._feat_stride_fpn = [8, 16, 32]
self._num_anchors = 2
def resize_image(self, srcimg):
padh, padw, newh, neww = 0, 0, self.inpHeight, self.inpWidth
if self.keep_ratio and srcimg.shape[0] != srcimg.shape[1]:
hw_scale = srcimg.shape[0] / srcimg.shape[1]
if hw_scale > 1:
newh, neww = self.inpHeight, int(self.inpWidth / hw_scale)
img = cv2.resize(srcimg, (neww, newh), interpolation=cv2.INTER_AREA)
padw = int((self.inpWidth - neww) * 0.5)
img = cv2.copyMakeBorder(img, 0, 0, padw, self.inpWidth - neww - padw, cv2.BORDER_CONSTANT,
value=0) # add border
else:
newh, neww = int(self.inpHeight * hw_scale) + 1, self.inpWidth
img = cv2.resize(srcimg, (neww, newh), interpolation=cv2.INTER_AREA)
padh = int((self.inpHeight - newh) * 0.5)
img = cv2.copyMakeBorder(img, padh, self.inpHeight - newh - padh, 0, 0, cv2.BORDER_CONSTANT, value=0)
else:
img = cv2.resize(srcimg, (self.inpWidth, self.inpHeight), interpolation=cv2.INTER_AREA)
return img, newh, neww, padh, padw
def distance2bbox(self, points, distance, max_shape=None):
x1 = points[:, 0] - distance[:, 0]
y1 = points[:, 1] - distance[:, 1]
x2 = points[:, 0] + distance[:, 2]
y2 = points[:, 1] + distance[:, 3]
if max_shape is not None:
x1 = x1.clamp(min=0, max=max_shape[1])
y1 = y1.clamp(min=0, max=max_shape[0])
x2 = x2.clamp(min=0, max=max_shape[1])
y2 = y2.clamp(min=0, max=max_shape[0])
return np.stack([x1, y1, x2, y2], axis=-1)
def distance2kps(self, points, distance, max_shape=None):
preds = []
for i in range(0, distance.shape[1], 2):
px = points[:, i % 2] + distance[:, i]
py = points[:, i % 2 + 1] + distance[:, i + 1]
if max_shape is not None:
px = px.clamp(min=0, max=max_shape[1])
py = py.clamp(min=0, max=max_shape[0])
preds.append(px)
preds.append(py)
return np.stack(preds, axis=-1)
def detect(self, srcimg):
img, newh, neww, padh, padw = self.resize_image(srcimg)
blob = cv2.dnn.blobFromImage(img, 1.0 / 128, (self.inpWidth, self.inpHeight), (127.5, 127.5, 127.5), swapRB=True)
# Sets the input to the network
self.net.setInput(blob)

# Runs the forward pass to get output of the output layers
outs = self.net.forward(self.net.getUnconnectedOutLayersNames())
# inference output
scores_list, bboxes_list, kpss_list = [], [], []
for idx, stride in enumerate(self._feat_stride_fpn):
scores = outs[idx * self.fmc][0]
bbox_preds = outs[idx * self.fmc + 1][0] * stride
kps_preds = outs[idx * self.fmc + 2][0] * stride
height = blob.shape[2] // stride
width = blob.shape[3] // stride
anchor_centers = np.stack(np.mgrid[:height, :width][::-1], axis=-1).astype(np.float32)
anchor_centers = (anchor_centers * stride).reshape((-1, 2))
if self._num_anchors > 1:
anchor_centers = np.stack([anchor_centers] * self._num_anchors, axis=1).reshape((-1, 2))

pos_inds = np.where(scores >= self.confThreshold)[0]
bboxes = self.distance2bbox(anchor_centers, bbox_preds)
pos_scores = scores[pos_inds]
pos_bboxes = bboxes[pos_inds]
scores_list.append(pos_scores)
bboxes_list.append(pos_bboxes)

kpss = self.distance2kps(anchor_centers, kps_preds)
# kpss = kps_preds
kpss = kpss.reshape((kpss.shape[0], -1, 2))
pos_kpss = kpss[pos_inds]
kpss_list.append(pos_kpss)

scores = np.vstack(scores_list).ravel()
# bboxes = np.vstack(bboxes_list) / det_scale
# kpss = np.vstack(kpss_list) / det_scale
bboxes = np.vstack(bboxes_list)
kpss = np.vstack(kpss_list)
bboxes[:, 2:4] = bboxes[:, 2:4] - bboxes[:, 0:2]
ratioh, ratiow = srcimg.shape[0] / newh, srcimg.shape[1] / neww
bboxes[:, 0] = (bboxes[:, 0] - padw) * ratiow
bboxes[:, 1] = (bboxes[:, 1] - padh) * ratioh
bboxes[:, 2] = bboxes[:, 2] * ratiow
bboxes[:, 3] = bboxes[:, 3] * ratioh
kpss[:, :, 0] = (kpss[:, :, 0] - padw) * ratiow
kpss[:, :, 1] = (kpss[:, :, 1] - padh) * ratioh
indices = cv2.dnn.NMSBoxes(bboxes.tolist(), scores.tolist(), self.confThreshold, self.nmsThreshold)
for i in indices:
i = i[0]
xmin, ymin, xamx, ymax = int(bboxes[i, 0]), int(bboxes[i, 1]), int(bboxes[i, 0] + bboxes[i, 2]), int(bboxes[i, 1] + bboxes[i, 3])
cv2.rectangle(srcimg, (xmin, ymin), (xamx, ymax), (0, 0, 255), thickness=2)
for j in range(5):
cv2.circle(srcimg, (int(kpss[i, j, 0]), int(kpss[i, j, 1])), 1, (0,255,0), thickness=-1)
cv2.putText(srcimg, str(round(scores[i], 3)), (xmin, ymin - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), thickness=1)
return srcimg

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--imgpath', type=str, default='s_l.jpg', help='image path')
parser.add_argument('--onnxmodel', default='weights/scrfd_500m_kps.onnx', type=str, choices=['weights/scrfd_500m_kps.onnx', 'weights/scrfd_2.5g_kps.onnx', 'weights/scrfd_10g_kps.onnx'], help='onnx model')
parser.add_argument('--confThreshold', default=0.5, type=float, help='class confidence')
parser.add_argument('--nmsThreshold', default=0.5, type=float, help='nms iou thresh')
args = parser.parse_args()

mynet = SCRFD(args.onnxmodel, confThreshold=args.confThreshold, nmsThreshold=args.nmsThreshold)
srcimg = cv2.imread(args.imgpath)
outimg = mynet.detect(srcimg)

winName = 'Deep learning object detection in OpenCV'
cv2.namedWindow(winName, 0)
cv2.imshow(winName, outimg)
cv2.waitKey(0)
cv2.destroyAllWindows()
Binary file added Face-Detect/s_l.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Face-Detect/selfie.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Face-Detect/weights/scrfd_10g_kps.onnx
Binary file not shown.
Binary file added Face-Detect/weights/scrfd_2.5g_kps.onnx
Binary file not shown.
Binary file added Face-Detect/weights/scrfd_500m_kps.onnx
Binary file not shown.
61 changes: 0 additions & 61 deletions Flask_Yolov5_Seg/client.py

This file was deleted.

1 change: 0 additions & 1 deletion Flask_Yolov5_Seg/readme.md

This file was deleted.

Loading

0 comments on commit 7c7735e

Please sign in to comment.