59 lines
2.5 KiB
C++
59 lines
2.5 KiB
C++
#pragma once
|
||
#include "types.hpp"
|
||
|
||
#include <opencv2/calib3d.hpp>
|
||
#include <opencv2/imgproc.hpp>
|
||
|
||
#include <cmath>
|
||
|
||
// ── align_face ────────────────────────────────────────────────────────────────
|
||
// Produces a 112×112 BGR crop using the ArcFace 5-point similarity transform.
|
||
// Returns an empty Mat if the affine fit fails (degenerate detection).
|
||
inline cv::Mat align_face(const cv::Mat& img,
|
||
const std::array<cv::Point2f, 5>& landmarks) {
|
||
std::vector<cv::Point2f> src(landmarks.begin(), landmarks.end());
|
||
std::vector<cv::Point2f> dst(5);
|
||
for (int i = 0; i < 5; ++i) dst[i] = {kArcFaceRef[i][0], kArcFaceRef[i][1]};
|
||
|
||
cv::Mat M = cv::estimateAffinePartial2D(src, dst, cv::noArray(), cv::RANSAC, 3.0);
|
||
if (M.empty()) return {};
|
||
|
||
cv::Mat crop;
|
||
cv::warpAffine(img, crop, M, {112, 112},
|
||
cv::INTER_LINEAR, cv::BORDER_CONSTANT, {0, 0, 0});
|
||
return crop;
|
||
}
|
||
|
||
// ── enhance_for_retry ────────────────────────────────────────────────────────
|
||
// Used when initial face detection finds nothing. Pads the image by 50%
|
||
// (border-replicated, so the detector doesn't see a hard edge) and applies
|
||
// CLAHE to boost local contrast, giving the detector a second try.
|
||
inline cv::Mat enhance_for_retry(const cv::Mat& img) {
|
||
cv::Mat padded;
|
||
const int pad_x = img.cols / 4;
|
||
const int pad_y = img.rows / 4;
|
||
cv::copyMakeBorder(img, padded, pad_y, pad_y, pad_x, pad_x, cv::BORDER_REPLICATE);
|
||
|
||
cv::Mat lab;
|
||
cv::cvtColor(padded, lab, cv::COLOR_BGR2Lab);
|
||
std::vector<cv::Mat> channels;
|
||
cv::split(lab, channels);
|
||
cv::createCLAHE(2.0, cv::Size(8, 8))->apply(channels[0], channels[0]);
|
||
cv::merge(channels, lab);
|
||
|
||
cv::Mat out;
|
||
cv::cvtColor(lab, out, cv::COLOR_Lab2BGR);
|
||
return out;
|
||
}
|
||
|
||
// ── l2_normalise ──────────────────────────────────────────────────────────────
|
||
inline Embedding l2_normalise(const float* row) {
|
||
float norm = 0.f;
|
||
for (int d = 0; d < 512; ++d) norm += row[d] * row[d];
|
||
norm = std::sqrt(norm);
|
||
if (norm < 1e-6f) norm = 1e-6f;
|
||
Embedding emb;
|
||
for (int d = 0; d < 512; ++d) emb[d] = row[d] / norm;
|
||
return emb;
|
||
}
|