增加模糊图片增强功能

This commit is contained in:
guanyuankai 2025-11-17 16:39:05 +08:00
parent aafd81fc5f
commit 54d5deb832
2 changed files with 99 additions and 153 deletions

View File

@ -2,12 +2,11 @@
#include <string> #include <string>
#include <vector> #include <vector>
FacePipeline::FacePipeline(const std::string &model_dir) FacePipeline::FacePipeline(const std::string &model_dir)
: m_env(ORT_LOGGING_LEVEL_WARNING, "FaceSDK"), : m_env(ORT_LOGGING_LEVEL_WARNING, "FaceSDK"),
m_memory_info( m_memory_info(
Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault)) { Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault)) {
m_session_options.SetIntraOpNumThreads(4); m_session_options.SetIntraOpNumThreads(4);
m_session_options.SetGraphOptimizationLevel( m_session_options.SetGraphOptimizationLevel(
GraphOptimizationLevel::ORT_ENABLE_ALL); GraphOptimizationLevel::ORT_ENABLE_ALL);
@ -22,7 +21,6 @@ FacePipeline::FacePipeline(const std::string &model_dir)
FacePipeline::~FacePipeline() {} FacePipeline::~FacePipeline() {}
bool FacePipeline::LoadModels(const std::string &model_dir) { bool FacePipeline::LoadModels(const std::string &model_dir) {
auto load_session = [&](std::unique_ptr<Ort::Session> &session, auto load_session = [&](std::unique_ptr<Ort::Session> &session,
const std::string &model_name) { const std::string &model_name) {
@ -57,9 +55,8 @@ bool FacePipeline::LoadModels(const std::string &model_dir) {
return true; return true;
} }
void FacePipeline::InitMemoryAllocators() { void FacePipeline::InitMemoryAllocators() {
auto get_io_names = [&](Ort::Session *session, auto get_io_names = [&](Ort::Session *session,
std::vector<const char *> &input_names, std::vector<const char *> &input_names,
std::vector<const char *> &output_names, std::vector<const char *> &output_names,
@ -99,7 +96,6 @@ void FacePipeline::InitMemoryAllocators() {
throw std::runtime_error("Model input shape is empty"); throw std::runtime_error("Model input shape is empty");
} }
std::string shape_str = "["; std::string shape_str = "[";
for (long long dim : input_shape) for (long long dim : input_shape)
shape_str += std::to_string(dim) + ", "; shape_str += std::to_string(dim) + ", ";
@ -107,13 +103,12 @@ void FacePipeline::InitMemoryAllocators() {
LOGI("Model %s input shape: %s", model_name, shape_str.c_str()); LOGI("Model %s input shape: %s", model_name, shape_str.c_str());
if (input_shape[0] < 1) if (input_shape[0] < 1)
input_shape[0] = 1; input_shape[0] = 1;
} else { } else {
LOGE("Model %s has no inputs!", model_name); LOGE("Model %s has no inputs!", model_name);
} }
}; };
get_io_names(m_session_rotator.get(), m_rot_input_names, m_rot_output_names, get_io_names(m_session_rotator.get(), m_rot_input_names, m_rot_output_names,
m_rot_input_shape, "Rotator"); m_rot_input_shape, "Rotator");
get_io_names(m_session_detector.get(), m_det_input_names, m_det_output_names, get_io_names(m_session_detector.get(), m_det_input_names, m_det_output_names,
@ -129,41 +124,38 @@ void FacePipeline::InitMemoryAllocators() {
get_io_names(m_session_recognizer.get(), m_rec_input_names, get_io_names(m_session_recognizer.get(), m_rec_input_names,
m_rec_output_names, m_rec_input_shape, "Recognizer"); m_rec_output_names, m_rec_input_shape, "Recognizer");
if (m_det_input_shape.size() < 4) { if (m_det_input_shape.size() < 4) {
LOGE("Detector input shape has < 4 dimensions! Cannot generate anchors."); LOGE("Detector input shape has < 4 dimensions! Cannot generate anchors.");
throw std::runtime_error("Detector input shape invalid"); throw std::runtime_error("Detector input shape invalid");
} }
if (m_det_input_shape[2] < 0 || m_det_input_shape[3] < 0) { if (m_det_input_shape[2] < 0 || m_det_input_shape[3] < 0) {
LOGE("Detector input shape is dynamic (H/W is -1). This is not supported " LOGE("Detector input shape is dynamic (H/W is -1). This is not supported "
"by the Python logic."); "by the Python logic.");
LOGI("Forcing detector H/W to 640x640."); LOGI("Forcing detector H/W to 640x640.");
m_det_input_shape[2] = 640; m_det_input_shape[2] = 640;
m_det_input_shape[3] = 640; m_det_input_shape[3] = 640;
} }
generate_anchors_faceboxes(m_det_input_shape[2], m_det_input_shape[3]); generate_anchors_faceboxes(m_det_input_shape[2], m_det_input_shape[3]);
size_t max_blob_size = 0; size_t max_blob_size = 0;
auto update_max = [&](const std::vector<int64_t> &shape, auto update_max = [&](const std::vector<int64_t> &shape,
const char *model_name) { const char *model_name) {
if (shape.size() <= 1) { if (shape.size() <= 1) {
return; return;
} }
size_t s = 1; size_t s = 1;
for (size_t i = 1; i < shape.size(); ++i) { for (size_t i = 1; i < shape.size(); ++i) {
if (shape[i] < 0) { if (shape[i] < 0) {
LOGE("Model %s has dynamic dimension at index %zu. Skipping for " LOGE("Model %s has dynamic dimension at index %zu. Skipping for "
"max_blob_size calculation.", "max_blob_size calculation.",
model_name, i); model_name, i);
return; return;
} }
s *= static_cast<size_t>(shape[i]); s *= static_cast<size_t>(shape[i]);
} }
@ -178,7 +170,6 @@ void FacePipeline::InitMemoryAllocators() {
update_max(m_pose_var_input_shape, "PoseVar"); update_max(m_pose_var_input_shape, "PoseVar");
update_max(m_lm1_input_shape, "Landmarker1"); update_max(m_lm1_input_shape, "Landmarker1");
update_max(m_rec_input_shape, "Recognizer"); update_max(m_rec_input_shape, "Recognizer");
if (max_blob_size == 0) { if (max_blob_size == 0) {
LOGE( LOGE(
@ -191,7 +182,6 @@ void FacePipeline::InitMemoryAllocators() {
LOGI("m_blob_buffer resized successfully."); LOGI("m_blob_buffer resized successfully.");
} }
void FacePipeline::image_to_blob(const cv::Mat &img, std::vector<float> &blob, void FacePipeline::image_to_blob(const cv::Mat &img, std::vector<float> &blob,
const float *mean, const float *std) { const float *mean, const float *std) {
int channels = img.channels(); int channels = img.channels();
@ -221,8 +211,6 @@ FacePipeline::create_tensor(const std::vector<float> &blob_data,
input_shape.data(), input_shape.size()); input_shape.data(), input_shape.size());
} }
bool FacePipeline::Extract(const cv::Mat &image, std::vector<float> &feature) { bool FacePipeline::Extract(const cv::Mat &image, std::vector<float> &feature) {
if (!m_initialized) { if (!m_initialized) {
LOGE("Extract failed: Pipeline is not initialized."); LOGE("Extract failed: Pipeline is not initialized.");
@ -233,8 +221,6 @@ bool FacePipeline::Extract(const cv::Mat &image, std::vector<float> &feature) {
return false; return false;
} }
int rot_angle_code = RunRotation(image); int rot_angle_code = RunRotation(image);
cv::Mat upright_image; cv::Mat upright_image;
if (rot_angle_code >= 0) { if (rot_angle_code >= 0) {
@ -243,8 +229,6 @@ bool FacePipeline::Extract(const cv::Mat &image, std::vector<float> &feature) {
upright_image = image; upright_image = image;
} }
std::vector<FaceBox> boxes; std::vector<FaceBox> boxes;
if (!RunDetection(upright_image, boxes)) { if (!RunDetection(upright_image, boxes)) {
LOGI("Extract failed: No face detected."); LOGI("Extract failed: No face detected.");
@ -252,9 +236,6 @@ bool FacePipeline::Extract(const cv::Mat &image, std::vector<float> &feature) {
} }
FaceBox best_box = boxes[0]; FaceBox best_box = boxes[0];
cv::Rect face_rect_raw(best_box.x1, best_box.y1, best_box.x2 - best_box.x1, cv::Rect face_rect_raw(best_box.x1, best_box.y1, best_box.x2 - best_box.x1,
best_box.y2 - best_box.y1); best_box.y2 - best_box.y1);
int pad_top = std::max(0, -face_rect_raw.y); int pad_top = std::max(0, -face_rect_raw.y);
@ -273,7 +254,6 @@ bool FacePipeline::Extract(const cv::Mat &image, std::vector<float> &feature) {
face_rect_raw.y + pad_top, face_rect_raw.width, face_rect_raw.y + pad_top, face_rect_raw.width,
face_rect_raw.height); face_rect_raw.height);
if (face_rect_padded.width <= 0 || face_rect_padded.height <= 0 || if (face_rect_padded.width <= 0 || face_rect_padded.height <= 0 ||
face_rect_padded.x < 0 || face_rect_padded.y < 0 || face_rect_padded.x < 0 || face_rect_padded.y < 0 ||
face_rect_padded.x + face_rect_padded.width > face_crop_padded.cols || face_rect_padded.x + face_rect_padded.width > face_crop_padded.cols ||
@ -288,33 +268,24 @@ bool FacePipeline::Extract(const cv::Mat &image, std::vector<float> &feature) {
return false; return false;
} }
FaceLandmark landmark; FaceLandmark landmark;
if (!RunLandmark(upright_image, best_box, landmark)) { if (!RunLandmark(upright_image, best_box, landmark)) {
LOGI("Extract failed: Landmark detection failed."); LOGI("Extract failed: Landmark detection failed.");
return false; return false;
} }
cv::Mat aligned_face = RunAlignment(upright_image, landmark); cv::Mat aligned_face = RunAlignment(upright_image, landmark);
if (aligned_face.empty()) { if (aligned_face.empty()) {
LOGI("Extract failed: Alignment produced an empty image."); LOGI("Extract failed: Alignment produced an empty image.");
return false; return false;
} }
FacePose pose; FacePose pose;
if (!RunPose(aligned_face, pose)) if (!RunPose(aligned_face, pose)) {
{
LOGI("Extract failed: Pose estimation failed."); LOGI("Extract failed: Pose estimation failed.");
return false; return false;
} }
if (std::abs(pose.yaw) > m_pose_yaw_threshold || if (std::abs(pose.yaw) > m_pose_yaw_threshold ||
std::abs(pose.pitch) > m_pose_pitch_threshold) { std::abs(pose.pitch) > m_pose_pitch_threshold) {
LOGI("Extract failed: Face pose (Y:%.1f, P:%.1f) exceeds threshold " LOGI("Extract failed: Face pose (Y:%.1f, P:%.1f) exceeds threshold "
@ -322,45 +293,36 @@ bool FacePipeline::Extract(const cv::Mat &image, std::vector<float> &feature) {
pose.yaw, pose.pitch, m_pose_yaw_threshold, m_pose_pitch_threshold); pose.yaw, pose.pitch, m_pose_yaw_threshold, m_pose_pitch_threshold);
return false; return false;
} }
cv::Mat enhanced_face_region = PreprocessSmallFace(face_region);
if (!CheckResolution(enhanced_face_region)) {
if (!CheckResolution(face_region)) {
LOGI("Extract failed: Resolution (H:%d, W:%d) below threshold (%d, %d)", LOGI("Extract failed: Resolution (H:%d, W:%d) below threshold (%d, %d)",
face_region.rows, face_region.cols, m_quality_min_resolution.height, enhanced_face_region.rows, enhanced_face_region.cols,
m_quality_min_resolution.width); m_quality_min_resolution.height, m_quality_min_resolution.width);
return false; return false;
} }
if (!CheckBrightness(enhanced_face_region)) {
if (!CheckBrightness(face_region)) {
LOGI("Extract failed: Brightness check failed (thresholds [%.1f, %.1f]).", LOGI("Extract failed: Brightness check failed (thresholds [%.1f, %.1f]).",
m_quality_bright_v1, m_quality_bright_v2); m_quality_bright_v1, m_quality_bright_v2);
return false; return false;
} }
if (!CheckClarity(enhanced_face_region)) {
if (!CheckClarity(face_region)) {
LOGI("Extract failed: Clarity check failed (threshold [%.2f]).", LOGI("Extract failed: Clarity check failed (threshold [%.2f]).",
m_quality_clarity_low_thresh); m_quality_clarity_low_thresh);
return false; return false;
} }
if (!RunRecognition(aligned_face, feature)) { if (!RunRecognition(aligned_face, feature)) {
LOGI("Extract failed: Feature recognition failed."); LOGI("Extract failed: Feature recognition failed.");
return false; return false;
} }
LOGI("Extract success."); LOGI("Extract success.");
return true; return true;
} }
void FacePipeline::preprocess_rotation(const cv::Mat &image, void FacePipeline::preprocess_rotation(const cv::Mat &image,
std::vector<float> &blob_data) { std::vector<float> &blob_data) {
cv::Mat gray_img, resized, cropped, gray_3d; cv::Mat gray_img, resized, cropped, gray_3d;
@ -369,12 +331,10 @@ void FacePipeline::preprocess_rotation(const cv::Mat &image,
int start = (256 - 224) / 2; int start = (256 - 224) / 2;
cv::Rect crop_rect(start, start, 224, 224); cv::Rect crop_rect(start, start, 224, 224);
cropped = resized(crop_rect); cropped = resized(crop_rect);
cv::cvtColor(cropped, gray_3d, cv::COLOR_GRAY2BGR); cv::cvtColor(cropped, gray_3d, cv::COLOR_GRAY2BGR);
const float mean[3] = {0.0f, 0.0f, 0.0f}; const float mean[3] = {0.0f, 0.0f, 0.0f};
const float std[3] = {1.0f / 255.0f, 1.0f / 255.0f, const float std[3] = {1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f};
1.0f / 255.0f};
image_to_blob(gray_3d, blob_data, mean, std); image_to_blob(gray_3d, blob_data, mean, std);
} }
@ -390,7 +350,6 @@ int FacePipeline::RunRotation(const cv::Mat &image) {
int max_index = std::distance(output_data, int max_index = std::distance(output_data,
std::max_element(output_data, output_data + 4)); std::max_element(output_data, output_data + 4));
if (max_index == 1) if (max_index == 1)
return cv::ROTATE_90_CLOCKWISE; return cv::ROTATE_90_CLOCKWISE;
if (max_index == 2) if (max_index == 2)
@ -400,15 +359,13 @@ int FacePipeline::RunRotation(const cv::Mat &image) {
return -1; return -1;
} }
void FacePipeline::preprocess_detection(const cv::Mat &img, void FacePipeline::preprocess_detection(const cv::Mat &img,
std::vector<float> &blob_data) { std::vector<float> &blob_data) {
cv::Mat resized; cv::Mat resized;
cv::resize(img, resized, cv::resize(img, resized,
cv::Size(m_det_input_shape[3], m_det_input_shape[2])); cv::Size(m_det_input_shape[3], m_det_input_shape[2]));
const float mean[3] = {104.0f, 117.0f, 123.0f};
const float mean[3] = {104.0f, 117.0f, 123.0f};
const float std[3] = {1.0f, 1.0f, 1.0f}; const float std[3] = {1.0f, 1.0f, 1.0f};
image_to_blob(resized, blob_data, mean, std); image_to_blob(resized, blob_data, mean, std);
} }
@ -423,12 +380,10 @@ bool FacePipeline::RunDetection(const cv::Mat &image,
auto output_tensors = m_session_detector->Run( auto output_tensors = m_session_detector->Run(
Ort::RunOptions{nullptr}, m_det_input_names.data(), &input_tensor, 1, Ort::RunOptions{nullptr}, m_det_input_names.data(), &input_tensor, 1,
m_det_output_names.data(), 2); m_det_output_names.data(), 2);
const float *bboxes_data = const float *bboxes_data = output_tensors[0].GetTensorData<float>();
output_tensors[0].GetTensorData<float>(); const float *probs_data = output_tensors[1].GetTensorData<float>();
const float *probs_data =
output_tensors[1].GetTensorData<float>();
long num_anchors = long num_anchors =
output_tensors[0].GetTensorTypeAndShapeInfo().GetShape()[1]; output_tensors[0].GetTensorTypeAndShapeInfo().GetShape()[1];
@ -439,10 +394,10 @@ bool FacePipeline::RunDetection(const cv::Mat &image,
} }
std::vector<FaceBox> bbox_collection; std::vector<FaceBox> bbox_collection;
const float variance[2] = {0.1f, 0.2f}; const float variance[2] = {0.1f, 0.2f};
for (long i = 0; i < num_anchors; ++i) { for (long i = 0; i < num_anchors; ++i) {
float conf = probs_data[i * 2 + 1]; float conf = probs_data[i * 2 + 1];
if (conf < m_det_threshold) if (conf < m_det_threshold)
continue; continue;
@ -452,24 +407,23 @@ bool FacePipeline::RunDetection(const cv::Mat &image,
float dw = bboxes_data[i * 4 + 2]; float dw = bboxes_data[i * 4 + 2];
float dh = bboxes_data[i * 4 + 3]; float dh = bboxes_data[i * 4 + 3];
float cx = anchor.cx + dx * variance[0] * anchor.s_kx; float cx = anchor.cx + dx * variance[0] * anchor.s_kx;
float cy = anchor.cy + dy * variance[0] * anchor.s_ky; float cy = anchor.cy + dy * variance[0] * anchor.s_ky;
float w = anchor.s_kx * std::exp(dw * variance[1]); float w = anchor.s_kx * std::exp(dw * variance[1]);
float h = anchor.s_ky * std::exp(dh * variance[1]); float h = anchor.s_ky * std::exp(dh * variance[1]);
bbox_collection.push_back( bbox_collection.push_back(
{(cx - w / 2.0f) * img_width, (cy - h / 2.0f) * img_height, {(cx - w / 2.0f) * img_width, (cy - h / 2.0f) * img_height,
(cx + w / 2.0f) * img_width, (cy + h / 2.0f) * img_height, conf}); (cx + w / 2.0f) * img_width, (cy + h / 2.0f) * img_height, conf});
} }
boxes = hard_nms(bbox_collection, m_det_iou_threshold, boxes = hard_nms(bbox_collection, m_det_iou_threshold, m_det_topk);
m_det_topk);
return !boxes.empty(); return !boxes.empty();
} }
void FacePipeline::generate_anchors_faceboxes(int target_height, void FacePipeline::generate_anchors_faceboxes(int target_height,
int target_width) { int target_width) {
m_anchors.clear(); m_anchors.clear();
std::vector<int> steps = {32, 64, 128}; std::vector<int> steps = {32, 64, 128};
std::vector<std::vector<int>> min_sizes = {{32, 64, 128}, {256}, {512}}; std::vector<std::vector<int>> min_sizes = {{32, 64, 128}, {256}, {512}};
@ -516,13 +470,9 @@ void FacePipeline::generate_anchors_faceboxes(int target_height,
} }
} }
void FacePipeline::preprocess_pose(const cv::Mat &img, void FacePipeline::preprocess_pose(const cv::Mat &img,
std::vector<float> &blob_data) { std::vector<float> &blob_data) {
float pad = 0.3f; float pad = 0.3f;
int h = img.rows; int h = img.rows;
int w = img.cols; int w = img.cols;
@ -535,27 +485,23 @@ void FacePipeline::preprocess_pose(const cv::Mat &img,
img.copyTo(canvas(cv::Rect(nx1, ny1, w, h))); img.copyTo(canvas(cv::Rect(nx1, ny1, w, h)));
cv::Mat resized; cv::Mat resized;
cv::resize( cv::resize(canvas, resized,
canvas, resized, cv::Size(m_pose_var_input_shape[3], m_pose_var_input_shape[2]));
cv::Size(m_pose_var_input_shape[3], m_pose_var_input_shape[2]));
const float mean[3] = {127.5f, 127.5f, 127.5f}; const float mean[3] = {127.5f, 127.5f, 127.5f};
const float std[3] = {1.0f / 127.5f, 1.0f / 127.5f, 1.0f / 127.5f}; const float std[3] = {1.0f / 127.5f, 1.0f / 127.5f, 1.0f / 127.5f};
image_to_blob(resized, blob_data, mean, std); image_to_blob(resized, blob_data, mean, std);
} }
bool FacePipeline::RunPose(const cv::Mat &face_input, FacePose &pose) { bool FacePipeline::RunPose(const cv::Mat &face_input, FacePose &pose) {
preprocess_pose(face_input, m_blob_buffer); preprocess_pose(face_input, m_blob_buffer);
auto input_tensor_var = create_tensor(m_blob_buffer, m_pose_var_input_shape); auto input_tensor_var = create_tensor(m_blob_buffer, m_pose_var_input_shape);
auto output_var = m_session_pose_var->Run( auto output_var = m_session_pose_var->Run(
Ort::RunOptions{nullptr}, m_pose_var_input_names.data(), Ort::RunOptions{nullptr}, m_pose_var_input_names.data(),
&input_tensor_var, 1, m_pose_var_output_names.data(), 1); &input_tensor_var, 1, m_pose_var_output_names.data(), 1);
auto input_tensor_conv = auto input_tensor_conv =
create_tensor(m_blob_buffer, m_pose_conv_input_shape); create_tensor(m_blob_buffer, m_pose_conv_input_shape);
auto output_conv = m_session_pose_conv->Run( auto output_conv = m_session_pose_conv->Run(
@ -565,28 +511,24 @@ bool FacePipeline::RunPose(const cv::Mat &face_input, FacePose &pose) {
const float *data_var = output_var[0].GetTensorData<float>(); const float *data_var = output_var[0].GetTensorData<float>();
const float *data_conv = output_conv[0].GetTensorData<float>(); const float *data_conv = output_conv[0].GetTensorData<float>();
pose.yaw = (data_var[0] + data_conv[0]) / 2.0f; pose.yaw = (data_var[0] + data_conv[0]) / 2.0f;
pose.pitch = (data_var[1] + data_conv[1]) / 2.0f; pose.pitch = (data_var[1] + data_conv[1]) / 2.0f;
pose.roll = (data_var[2] + data_conv[2]) / 2.0f; pose.roll = (data_var[2] + data_conv[2]) / 2.0f;
return true; return true;
} }
void FacePipeline::preprocess_landmark_net1(const cv::Mat &img, void FacePipeline::preprocess_landmark_net1(const cv::Mat &img,
std::vector<float> &blob_data) { std::vector<float> &blob_data) {
cv::Mat resized, gray_img; cv::Mat resized, gray_img;
cv::resize(img, resized, cv::resize(img, resized,
cv::Size(m_lm1_input_shape[3], m_lm1_input_shape[2])); cv::Size(m_lm1_input_shape[3], m_lm1_input_shape[2]));
cv::cvtColor(resized, gray_img, cv::COLOR_BGR2GRAY); cv::cvtColor(resized, gray_img, cv::COLOR_BGR2GRAY);
const float mean[1] = {0.0f}; const float mean[1] = {0.0f};
const float std[1] = {1.0f}; const float std[1] = {1.0f};
image_to_blob(gray_img, blob_data, mean, std); image_to_blob(gray_img, blob_data, mean, std);
} }
std::vector<float> std::vector<float>
FacePipeline::shape_index_process(const Ort::Value &feat_val, FacePipeline::shape_index_process(const Ort::Value &feat_val,
const Ort::Value &pos_val) { const Ort::Value &pos_val) {
@ -595,13 +537,13 @@ FacePipeline::shape_index_process(const Ort::Value &feat_val,
const float *feat_data = feat_val.GetTensorData<float>(); const float *feat_data = feat_val.GetTensorData<float>();
const float *pos_data = pos_val.GetTensorData<float>(); const float *pos_data = pos_val.GetTensorData<float>();
long feat_n = feat_shape[0]; long feat_n = feat_shape[0];
long feat_c = feat_shape[1]; long feat_c = feat_shape[1];
long feat_h = feat_shape[2]; long feat_h = feat_shape[2];
long feat_w = feat_shape[3]; long feat_w = feat_shape[3];
long pos_n = pos_shape[0]; long pos_n = pos_shape[0];
long landmark_x2 = pos_shape[1]; long landmark_x2 = pos_shape[1];
int landmark_num = landmark_x2 / 2; int landmark_num = landmark_x2 / 2;
float m_origin[] = {112.0f, 112.0f}; float m_origin[] = {112.0f, 112.0f};
float m_origin_patch[] = {15.0f, 15.0f}; float m_origin_patch[] = {15.0f, 15.0f};
@ -655,7 +597,7 @@ FacePipeline::shape_index_process(const Ort::Value &feat_val,
bool FacePipeline::RunLandmark(const cv::Mat &image, const FaceBox &box, bool FacePipeline::RunLandmark(const cv::Mat &image, const FaceBox &box,
FaceLandmark &landmark) { FaceLandmark &landmark) {
cv::Rect face_rect_raw(box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1); cv::Rect face_rect_raw(box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1);
int pad_top = std::max(0, -face_rect_raw.y); int pad_top = std::max(0, -face_rect_raw.y);
int pad_bottom = int pad_bottom =
@ -671,41 +613,34 @@ bool FacePipeline::RunLandmark(const cv::Mat &image, const FaceBox &box,
face_rect_raw.height); face_rect_raw.height);
cv::Mat face_crop = face_crop_padded(face_rect_padded); cv::Mat face_crop = face_crop_padded(face_rect_padded);
preprocess_landmark_net1(face_crop, m_blob_buffer); preprocess_landmark_net1(face_crop, m_blob_buffer);
auto input_tensor_net1 = create_tensor(m_blob_buffer, m_lm1_input_shape); auto input_tensor_net1 = create_tensor(m_blob_buffer, m_lm1_input_shape);
auto output_net1 = m_session_landmarker1->Run( auto output_net1 = m_session_landmarker1->Run(
Ort::RunOptions{nullptr}, m_lm1_input_names.data(), &input_tensor_net1, 1, Ort::RunOptions{nullptr}, m_lm1_input_names.data(), &input_tensor_net1, 1,
m_lm1_output_names.data(), 2); m_lm1_output_names.data(), 2);
std::vector<float> shape_index_blob = std::vector<float> shape_index_blob =
shape_index_process(output_net1[0], output_net1[1]); shape_index_process(output_net1[0], output_net1[1]);
auto input_tensor_net2 = Ort::Value::CreateTensor<float>( auto input_tensor_net2 = Ort::Value::CreateTensor<float>(
m_memory_info, shape_index_blob.data(), shape_index_blob.size(), m_memory_info, shape_index_blob.data(), shape_index_blob.size(),
m_lm2_input_shape.data(), m_lm2_input_shape.size()); m_lm2_input_shape.data(), m_lm2_input_shape.size());
auto output_net2 = m_session_landmarker2->Run( auto output_net2 = m_session_landmarker2->Run(
Ort::RunOptions{nullptr}, m_lm2_input_names.data(), &input_tensor_net2, 1, Ort::RunOptions{nullptr}, m_lm2_input_names.data(), &input_tensor_net2, 1,
m_lm2_output_names.data(), 1); m_lm2_output_names.data(), 1);
const float *data_net1_pos = output_net1[1].GetTensorData<float>(); const float *data_net1_pos = output_net1[1].GetTensorData<float>();
const float *data_net2 = output_net2[0].GetTensorData<float>(); const float *data_net2 = output_net2[0].GetTensorData<float>();
auto shape_net1_pos = auto shape_net1_pos = output_net1[1].GetTensorTypeAndShapeInfo().GetShape();
output_net1[1].GetTensorTypeAndShapeInfo().GetShape();
int landmark_x2 = shape_net1_pos[1]; int landmark_x2 = shape_net1_pos[1];
float scale_x = (box.x2 - box.x1) / 112.0f; float scale_x = (box.x2 - box.x1) / 112.0f;
float scale_y = (box.y2 - box.y1) / 112.0f; float scale_y = (box.y2 - box.y1) / 112.0f;
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 5; ++i) {
float x_norm = (data_net2[i * 2 + 0] + data_net1_pos[i * 2 + 0]) * 112.0f; float x_norm = (data_net2[i * 2 + 0] + data_net1_pos[i * 2 + 0]) * 112.0f;
float y_norm = (data_net2[i * 2 + 1] + data_net1_pos[i * 2 + 1]) * 112.0f; float y_norm = (data_net2[i * 2 + 1] + data_net1_pos[i * 2 + 1]) * 112.0f;
@ -719,10 +654,9 @@ bool FacePipeline::RunLandmark(const cv::Mat &image, const FaceBox &box,
return true; return true;
} }
cv::Mat FacePipeline::RunAlignment(const cv::Mat &image, cv::Mat FacePipeline::RunAlignment(const cv::Mat &image,
const FaceLandmark &landmark) { const FaceLandmark &landmark) {
std::vector<cv::Point2f> src_points; std::vector<cv::Point2f> src_points;
std::vector<cv::Point2f> dst_points; std::vector<cv::Point2f> dst_points;
@ -732,49 +666,40 @@ cv::Mat FacePipeline::RunAlignment(const cv::Mat &image,
m_landmark_template.at<float>(i, 1))); m_landmark_template.at<float>(i, 1)));
} }
cv::Mat transform_matrix = cv::Mat transform_matrix =
cv::estimateAffinePartial2D(src_points, dst_points); cv::estimateAffinePartial2D(src_points, dst_points);
cv::Mat aligned_face; cv::Mat aligned_face;
cv::warpAffine(image, aligned_face, transform_matrix, m_align_output_size, cv::warpAffine(image, aligned_face, transform_matrix, m_align_output_size,
cv::INTER_LINEAR); cv::INTER_LINEAR);
return aligned_face; return aligned_face;
} }
void FacePipeline::preprocess_recognition(const cv::Mat &img, void FacePipeline::preprocess_recognition(const cv::Mat &img,
std::vector<float> &blob_data) { std::vector<float> &blob_data) {
cv::Mat resized, rgb_img; cv::Mat resized, rgb_img;
const cv::Size target_size(248, 248); const cv::Size target_size(248, 248);
cv::resize(img, resized, target_size); cv::resize(img, resized, target_size);
cv::cvtColor(resized, rgb_img, cv::COLOR_BGR2RGB); cv::cvtColor(resized, rgb_img, cv::COLOR_BGR2RGB);
const float mean[3] = {0.0f, 0.0f, 0.0f}; const float mean[3] = {0.0f, 0.0f, 0.0f};
const float std[3] = {1.0f, 1.0f, 1.0f}; const float std[3] = {1.0f, 1.0f, 1.0f};
image_to_blob(rgb_img, blob_data, mean, std); image_to_blob(rgb_img, blob_data, mean, std);
} }
void FacePipeline::normalize_sqrt_l2(std::vector<float> &v) { void FacePipeline::normalize_sqrt_l2(std::vector<float> &v) {
double norm = 0.0; double norm = 0.0;
for (float &val : v) { for (float &val : v) {
val = std::sqrt(std::max(0.0f, val)); val = std::sqrt(std::max(0.0f, val));
norm += val * val; norm += val * val;
} }
if (norm > 1e-6) { if (norm > 1e-6) {
norm = std::sqrt(norm); norm = std::sqrt(norm);
for (float &val : v) { for (float &val : v) {
@ -785,19 +710,13 @@ void FacePipeline::normalize_sqrt_l2(std::vector<float> &v) {
bool FacePipeline::RunRecognition(const cv::Mat &aligned_face, bool FacePipeline::RunRecognition(const cv::Mat &aligned_face,
std::vector<float> &feature) { std::vector<float> &feature) {
preprocess_recognition(aligned_face, m_blob_buffer); preprocess_recognition(aligned_face, m_blob_buffer);
const std::vector<int64_t> hardcoded_shape = {1, 3, 248, 248};
const std::vector<int64_t> hardcoded_shape = {1, 3, 248, 248};
auto input_tensor = create_tensor(m_blob_buffer, hardcoded_shape); auto input_tensor = create_tensor(m_blob_buffer, hardcoded_shape);
auto output_tensors = m_session_recognizer->Run( auto output_tensors = m_session_recognizer->Run(
Ort::RunOptions{nullptr}, m_rec_input_names.data(), &input_tensor, 1, Ort::RunOptions{nullptr}, m_rec_input_names.data(), &input_tensor, 1,
m_rec_output_names.data(), 1); m_rec_output_names.data(), 1);
@ -809,15 +728,11 @@ bool FacePipeline::RunRecognition(const cv::Mat &aligned_face,
feature.resize(feature_dim); feature.resize(feature_dim);
memcpy(feature.data(), output_data, feature_dim * sizeof(float)); memcpy(feature.data(), output_data, feature_dim * sizeof(float));
normalize_sqrt_l2(feature); normalize_sqrt_l2(feature);
return true; return true;
} }
bool FacePipeline::CheckResolution(const cv::Mat &face_region) { bool FacePipeline::CheckResolution(const cv::Mat &face_region) {
if (face_region.rows < m_quality_min_resolution.height || if (face_region.rows < m_quality_min_resolution.height ||
face_region.cols < m_quality_min_resolution.width) { face_region.cols < m_quality_min_resolution.width) {
@ -826,7 +741,6 @@ bool FacePipeline::CheckResolution(const cv::Mat &face_region) {
return true; return true;
} }
bool FacePipeline::CheckBrightness(const cv::Mat &face_region) { bool FacePipeline::CheckBrightness(const cv::Mat &face_region) {
cv::Mat gray; cv::Mat gray;
if (face_region.channels() == 3) if (face_region.channels() == 3)
@ -836,17 +750,14 @@ bool FacePipeline::CheckBrightness(const cv::Mat &face_region) {
float bright_value = grid_max_bright(gray, 3, 3); float bright_value = grid_max_bright(gray, 3, 3);
return (bright_value >= m_quality_bright_v1 && return (bright_value >= m_quality_bright_v1 &&
bright_value <= m_quality_bright_v2); bright_value <= m_quality_bright_v2);
} }
float FacePipeline::grid_max_bright(const cv::Mat &gray_img, int rows, float FacePipeline::grid_max_bright(const cv::Mat &gray_img, int rows,
int cols) { int cols) {
float max_bright = 0.0f; float max_bright = 0.0f;
if (rows == 0 || cols == 0) if (rows == 0 || cols == 0)
return 0.0f; return 0.0f;
int row_height = gray_img.rows / rows; int row_height = gray_img.rows / rows;
@ -867,14 +778,12 @@ float FacePipeline::grid_max_bright(const cv::Mat &gray_img, int rows,
return max_bright; return max_bright;
} }
bool FacePipeline::CheckClarity(const cv::Mat &face_region) { bool FacePipeline::CheckClarity(const cv::Mat &face_region) {
float clarity = clarity_estimate(face_region); float clarity = clarity_estimate(face_region);
return (clarity >= m_quality_clarity_low_thresh); return (clarity >= m_quality_clarity_low_thresh);
} }
float FacePipeline::clarity_estimate(const cv::Mat &image) { float FacePipeline::clarity_estimate(const cv::Mat &image) {
cv::Mat gray; cv::Mat gray;
if (image.channels() == 3) if (image.channels() == 3)
@ -885,21 +794,19 @@ float FacePipeline::clarity_estimate(const cv::Mat &image) {
float blur_val = grid_max_reblur(gray, 2, 2); float blur_val = grid_max_reblur(gray, 2, 2);
float clarity = 1.0f - blur_val; float clarity = 1.0f - blur_val;
return std::max(0.0f, std::min(1.0f, clarity)); return std::max(0.0f, std::min(1.0f, clarity));
} }
float FacePipeline::grid_max_reblur(const cv::Mat &img, int rows, int cols) { float FacePipeline::grid_max_reblur(const cv::Mat &img, int rows, int cols) {
int row_height = img.rows / rows; int row_height = img.rows / rows;
int col_width = img.cols / cols; int col_width = img.cols / cols;
if (row_height == 0 || col_width == 0) if (row_height == 0 || col_width == 0)
return 1.0f; return 1.0f;
float max_blur_val = -FLT_MAX; float max_blur_val = -FLT_MAX;
cv::Mat data_float; cv::Mat data_float;
img.convertTo(data_float, CV_32F); img.convertTo(data_float, CV_32F);
for (int y = 0; y < rows; ++y) { for (int y = 0; y < rows; ++y) {
for (int x = 0; x < cols; ++x) { for (int x = 0; x < cols; ++x) {
@ -916,14 +823,13 @@ float FacePipeline::grid_max_reblur(const cv::Mat &img, int rows, int cols) {
return std::max(max_blur_val, 0.0f); return std::max(max_blur_val, 0.0f);
} }
float FacePipeline::reblur(const cv::Mat &data) { float FacePipeline::reblur(const cv::Mat &data) {
if (data.rows <= 1 || data.cols <= 1)
return 1.0f;
cv::Mat kernel_v = cv::Mat::ones(9, 1, CV_32F) / 9.0f; if (data.rows <= 1 || data.cols <= 1)
cv::Mat kernel_h = cv::Mat::ones(1, 9, CV_32F) / 9.0f; return 1.0f;
cv::Mat kernel_v = cv::Mat::ones(9, 1, CV_32F) / 9.0f;
cv::Mat kernel_h = cv::Mat::ones(1, 9, CV_32F) / 9.0f;
cv::Mat BVer, BHor; cv::Mat BVer, BHor;
cv::filter2D(data, BVer, CV_32F, kernel_v, cv::Point(-1, -1), 0, cv::filter2D(data, BVer, CV_32F, kernel_v, cv::Point(-1, -1), 0,
@ -952,4 +858,42 @@ float FacePipeline::reblur(const cv::Mat &data) {
(s_FHor > 1e-6) ? static_cast<float>((s_FHor - s_VHor) / s_FHor) : 0.0f; (s_FHor > 1e-6) ? static_cast<float>((s_FHor - s_VHor) / s_FHor) : 0.0f;
return std::max(b_FVer, b_FHor); return std::max(b_FVer, b_FHor);
}
cv::Mat FacePipeline::PreprocessSmallFace(const cv::Mat &face_region) {
int h = face_region.rows;
int w = face_region.cols;
if (h >= m_quality_min_resolution.height &&
w >= m_quality_min_resolution.width) {
return face_region;
}
LOGI("PreprocessSmallFace: Input (H:%d, W:%d) is small. Enhancing...", h, w);
float scale_w = (w < m_quality_min_resolution.width)
? (float)m_quality_min_resolution.width / w
: 1.0f;
float scale_h = (h < m_quality_min_resolution.height)
? (float)m_quality_min_resolution.height / h
: 1.0f;
float scale = std::max(scale_w, scale_h);
int new_width = static_cast<int>(w * scale);
int new_height = static_cast<int>(h * scale);
cv::Mat resized;
cv::resize(face_region, resized, cv::Size(new_width, new_height), 0, 0,
cv::INTER_CUBIC);
cv::Mat blurred;
cv::GaussianBlur(resized, blurred, cv::Size(0, 0), 2.0);
cv::Mat sharpened;
cv::addWeighted(resized, 1.5, blurred, -0.5, 0, sharpened);
return sharpened;
} }

View File

@ -197,4 +197,6 @@ private:
const float m_quality_bright_v2 = 230.0f; const float m_quality_bright_v2 = 230.0f;
const float m_quality_clarity_low_thresh = 0.10f; const float m_quality_clarity_low_thresh = 0.10f;
cv::Mat PreprocessSmallFace(const cv::Mat &face_region);
}; };