Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions demos/face_recognition_demo/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ add_demo(NAME face_recognition_demo
HEADERS ${HEADERS}
INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include"
DEPENDENCIES monitors utils)

target_compile_features(face_recognition_demo PRIVATE cxx_std_17)
24 changes: 13 additions & 11 deletions demos/face_recognition_demo/cpp/include/reid_gallery.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,27 @@ class EmbeddingsGallery {
static constexpr char unknownLabel[] = "Unknown";
static constexpr int unknownId = -1;
static constexpr float unknownDistance = 1.0;
EmbeddingsGallery(const std::string& ids_list, double threshold,
bool crop_gallery, const DetectorConfig& detector_config,
AsyncModel& landmarks_det,
AsyncModel& image_reid,
bool use_greedy_matcher=false);
EmbeddingsGallery(const std::string& fgPath,
double threshold,
bool crop,
const DetectorConfig& detectorConfig,
AsyncModel& landmarksDet,
AsyncModel& imageReid,
bool useGreedyMatcher = false);
size_t size() const;
std::vector<std::pair<int, float>> getIDsByEmbeddings(const std::vector<cv::Mat>& embeddings) const;
std::string getLabelByID(int id) const;
bool labelExists(const std::string& label) const;
std::string tryToSave(cv::Mat new_face);
void addFace(cv::Mat new_face, cv::Mat embedding, std::string label);
std::string tryToSave(cv::Mat newFace);
void addFace(cv::Mat newFace, cv::Mat embedding, std::string label);

private:
RegistrationStatus registerIdentity(const std::string& identity_label,
RegistrationStatus registerIdentity(const std::string& identityLabel,
const cv::Mat& image,
const bool crop_gallery,
const bool crop,
FaceDetector& detector,
AsyncModel& landmarks_det,
AsyncModel& image_reid,
AsyncModel& landmarksDet,
AsyncModel& imageReid,
cv::Mat& embedding);
std::vector<int> idxToId;
double reidThreshold;
Expand Down
82 changes: 42 additions & 40 deletions demos/face_recognition_demo/cpp/src/align_transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,65 +6,67 @@
#include <opencv2/imgproc.hpp>
#include <vector>

static const float h = 112.;
static const float w = 96.;
// reference landmarks points in the unit square [0,1]x[0,1]
static const float ref_landmarks_normalized[] = {
30.2946f / w, 51.6963f / h, 65.5318f / w, 51.5014f / h, 48.0252f / w,
71.7366f / h, 33.5493f / w, 92.3655f / h, 62.7299f / w, 92.2041f / h
};
namespace {
static const float h = 112.;
static const float w = 96.;
// reference landmarks points in the unit square [0,1]x[0,1]
static const float REF_LANDMARKS_NORMED[] = {
30.2946f / w, 51.6963f / h, 65.5318f / w, 51.5014f / h, 48.0252f / w,
71.7366f / h, 33.5493f / w, 92.3655f / h, 62.7299f / w, 92.2041f / h
};
}

cv::Mat getTransform(cv::Mat* src, cv::Mat* dst) {
cv::Mat col_mean_src;
reduce(*src, col_mean_src, 0, cv::REDUCE_AVG);
cv::Mat colMeanSrc;
reduce(*src, colMeanSrc, 0, cv::REDUCE_AVG);
for (int i = 0; i < src->rows; i++) {
src->row(i) -= col_mean_src;
src->row(i) -= colMeanSrc;
}

cv::Mat col_mean_dst;
reduce(*dst, col_mean_dst, 0, cv::REDUCE_AVG);
cv::Mat colMeanDst;
reduce(*dst, colMeanDst, 0, cv::REDUCE_AVG);
for (int i = 0; i < dst->rows; i++) {
dst->row(i) -= col_mean_dst;
dst->row(i) -= colMeanDst;
}

cv::Scalar mean, dev_src, dev_dst;
cv::meanStdDev(*src, mean, dev_src);
dev_src(0) =
std::max(static_cast<double>(std::numeric_limits<float>::epsilon()), dev_src(0));
*src /= dev_src(0);
cv::meanStdDev(*dst, mean, dev_dst);
dev_dst(0) =
std::max(static_cast<double>(std::numeric_limits<float>::epsilon()), dev_dst(0));
*dst /= dev_dst(0);
cv::Scalar mean, devSrc, devDst;
cv::meanStdDev(*src, mean, devSrc);
devSrc(0) =
std::max(static_cast<double>(std::numeric_limits<float>::epsilon()), devSrc(0));
*src /= devSrc(0);
cv::meanStdDev(*dst, mean, devDst);
devDst(0) =
std::max(static_cast<double>(std::numeric_limits<float>::epsilon()), devDst(0));
*dst /= devDst(0);

cv::Mat w, u, vt;
cv::SVD::compute((*src).t() * (*dst), w, u, vt);
cv::Mat r = (u * vt).t();
cv::Mat m(2, 3, CV_32F);
m.colRange(0, 2) = r * (dev_dst(0) / dev_src(0));
m.col(2) = (col_mean_dst.t() - m.colRange(0, 2) * col_mean_src.t());
m.colRange(0, 2) = r * (devDst(0) / devSrc(0));
m.col(2) = (colMeanDst.t() - m.colRange(0, 2) * colMeanSrc.t());
return m;
}

void alignFaces(std::vector<cv::Mat>& face_images, std::vector<cv::Mat>& landmarks_vec) {
if (landmarks_vec.size() == 0) {
void alignFaces(std::vector<cv::Mat>& faceImages, std::vector<cv::Mat>& landmarksVec) {
if (landmarksVec.size() == 0) {
return;
}
CV_Assert(face_images.size() == landmarks_vec.size());
cv::Mat ref_landmarks = cv::Mat(5, 2, CV_32F);
CV_Assert(faceImages.size() == landmarksVec.size());
cv::Mat refLandmarks = cv::Mat(5, 2, CV_32F);

for (size_t j = 0; j < face_images.size(); j++) {
for (int i = 0; i < ref_landmarks.rows; i++) {
ref_landmarks.at<float>(i, 0) =
ref_landmarks_normalized[2 * i] * face_images.at(j).cols;
ref_landmarks.at<float>(i, 1) =
ref_landmarks_normalized[2 * i + 1] * face_images.at(j).rows;
landmarks_vec.at(j) = landmarks_vec.at(j).reshape(1, 5);
landmarks_vec.at(j).at<float>(i, 0) *= face_images.at(j).cols;
landmarks_vec.at(j).at<float>(i, 1) *= face_images.at(j).rows;
for (size_t j = 0; j < faceImages.size(); j++) {
for (int i = 0; i < refLandmarks.rows; i++) {
refLandmarks.at<float>(i, 0) =
REF_LANDMARKS_NORMED[2 * i] * faceImages.at(j).cols;
refLandmarks.at<float>(i, 1) =
REF_LANDMARKS_NORMED[2 * i + 1] * faceImages.at(j).rows;
landmarksVec.at(j) = landmarksVec.at(j).reshape(1, 5);
landmarksVec.at(j).at<float>(i, 0) *= faceImages.at(j).cols;
landmarksVec.at(j).at<float>(i, 1) *= faceImages.at(j).rows;
}
cv::Mat m = getTransform(&ref_landmarks, &landmarks_vec.at(j));
cv::warpAffine(face_images.at(j), face_images.at(j), m,
face_images.at(j).size(), cv::WARP_INVERSE_MAP);
cv::Mat m = getTransform(&refLandmarks, &landmarksVec.at(j));
cv::warpAffine(faceImages.at(j), faceImages.at(j), m,
faceImages.at(j).size(), cv::WARP_INVERSE_MAP);
}
}
26 changes: 13 additions & 13 deletions demos/face_recognition_demo/cpp/src/async_queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
#include "utils/ocv_common.hpp"
#include <vector>

AsyncInferQueue::AsyncInferQueue(ov::CompiledModel& compiled_model, size_t size) {
AsyncInferQueue::AsyncInferQueue(ov::CompiledModel& compiledModel, size_t size) {
requests.resize(size);
for (size_t request_id = 0; request_id < size; ++request_id) {
requests[request_id] = compiled_model.create_infer_request();
idsOfFreeRequests.push(request_id);
for (size_t requestId = 0; requestId < size; ++requestId) {
requests[requestId] = compiledModel.create_infer_request();
idsOfFreeRequests.push(requestId);
}

for (const auto& output: compiled_model.outputs()) {
for (const auto& output: compiledModel.outputs()) {
outputNames.push_back(output.get_any_name());
}

Expand All @@ -23,7 +23,7 @@ AsyncInferQueue::AsyncInferQueue(ov::CompiledModel& compiled_model, size_t size)

void AsyncInferQueue::setCallback() {
for (size_t requestId = 0; requestId < requests.size(); ++requestId) {
requests[requestId].set_callback([this, requestId /* ... */](std::exception_ptr exception_ptr) {
requests[requestId].set_callback([this, requestId /* ... */](std::exception_ptr exceptionPtr) {
{
// acquire the mutex to access m_idle_handles
std::lock_guard<std::mutex> lock(mutex);
Expand All @@ -38,8 +38,8 @@ void AsyncInferQueue::setCallback() {
// Notify locks in getIdleRequestId()
cv.notify_one();
try {
if (exception_ptr) {
std::rethrow_exception(exception_ptr);
if (exceptionPtr) {
std::rethrow_exception(exceptionPtr);
}
} catch (const std::exception& e) {
throw ov::Exception(e.what());
Expand Down Expand Up @@ -71,29 +71,29 @@ void AsyncInferQueue::waitAll() {
}
}

void AsyncInferQueue::submitData(std::unordered_map<std::string, cv::Mat> inputs, size_t input_id) {
void AsyncInferQueue::submitData(std::unordered_map<std::string, cv::Mat> inputs, size_t inputId) {
size_t id = getIdleRequestId();

{
std::lock_guard<std::mutex> lock(mutex);
idsOfFreeRequests.pop();
}
requests[id].set_callback([this, id, input_id /* ... */](std::exception_ptr exception_ptr) {
requests[id].set_callback([this, id, inputId /* ... */](std::exception_ptr exceptionPtr) {
{
// acquire the mutex to access m_idle_handles
std::lock_guard<std::mutex> lock(mutex);
for (const auto& outName : outputNames) {
auto tensor = requests[id].get_tensor(outName);
results[input_id][outName] = tensor;
results[inputId][outName] = tensor;
}
// Add idle handle to queue
idsOfFreeRequests.push(id);
}
// Notify locks in getIdleRequestId()
cv.notify_one();
try {
if (exception_ptr) {
std::rethrow_exception(exception_ptr);
if (exceptionPtr) {
std::rethrow_exception(exceptionPtr);
}
} catch (const std::exception& e) {
throw ov::Exception(e.what());
Expand Down
89 changes: 50 additions & 39 deletions demos/face_recognition_demo/cpp/src/reid_gallery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@

#include "reid_gallery.hpp"

#include <filesystem>
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>

#ifdef _WIN32
# include "w_dirent.hpp"
#else
# include <dirent.h> // for closedir, dirent, opendir, readdir, DIR
#endif

#include "utils/kuhn_munkres.hpp"

namespace fs = std::filesystem;
// namespace fs = std::filesystem;
namespace {
float computeReidDistance(const cv::Mat& descr1, const cv::Mat& descr2) {
float xy = static_cast<float>(descr1.dot(descr2));
Expand All @@ -25,38 +30,44 @@ namespace {

} // namespace

EmbeddingsGallery::EmbeddingsGallery(const std::string& face_gallery_path,
EmbeddingsGallery::EmbeddingsGallery(const std::string& fgPath,
double threshold,
bool crop_gallery,
const DetectorConfig& detector_config,
AsyncModel& landmarks_det,
AsyncModel& image_reid,
bool crop,
const DetectorConfig& detectorConfig,
AsyncModel& landmarksDet,
AsyncModel& imageReid,
bool useGreedyMatcher) :
reidThreshold(threshold), useGreedyMatcher(useGreedyMatcher), faceGalleryPath(face_gallery_path) {
reidThreshold(threshold), useGreedyMatcher(useGreedyMatcher), faceGalleryPath(fgPath) {
if (faceGalleryPath.empty()) {
return;
}

FaceDetector detector(detector_config);
FaceDetector detector(detectorConfig);

int id = 0;
for (const auto & entry : fs::directory_iterator(faceGalleryPath)) {
if (entry.is_regular_file() &&
std::find(file_extensions.begin(), file_extensions.end(), entry.path().extension()) != file_extensions.end()) {
std::string label = entry.path().stem();
DIR* dir = opendir(fgPath.c_str());
if (!dir) {
throw std::runtime_error("Can't find the directory " + fgPath);
}
while (struct dirent* ent = readdir(dir)) {
if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) {
std::string name = ent->d_name;
cv::Mat image = cv::imread(fgPath + '/' + name);
if (image.empty()) {
throw std::runtime_error("Image is empty");
}
std::vector<cv::Mat> embeddings;
cv::Mat image = cv::imread(entry.path());
assert(!image.empty());
cv::Mat emb;
RegistrationStatus status = registerIdentity(label, image, crop_gallery, detector, landmarks_det, image_reid, emb);
RegistrationStatus status = registerIdentity(name, image, crop, detector, landmarksDet, imageReid, emb);
if (status == RegistrationStatus::SUCCESS) {
embeddings.push_back(emb);
idxToId.push_back(id);
identities.emplace_back(embeddings, label, id);
identities.emplace_back(embeddings, name, id);
++id;
}
}
}
closedir(dir);
slog::info << identities.size() << " persons to recognize were added from the gallery" << slog::endl;
}

Expand Down Expand Up @@ -111,24 +122,24 @@ bool EmbeddingsGallery::labelExists(const std::string& label) const {
[label](const GalleryObject& o) {return o.label == label;});
}

std::string EmbeddingsGallery::tryToSave(cv::Mat new_face){
std::string EmbeddingsGallery::tryToSave(cv::Mat newFace){
std::string winname = "Unknown face";
size_t height = int(400 * new_face.rows / new_face.cols);
size_t height = int(400 * newFace.rows / newFace.cols);
cv::Mat resized;
cv::resize(new_face, resized, cv::Size(400, height), 0.0, 0.0, cv::INTER_AREA);
cv::resize(newFace, resized, cv::Size(400, height), 0.0, 0.0, cv::INTER_AREA);
size_t font = cv::FONT_HERSHEY_PLAIN;
size_t fontScale = 1;
cv::Scalar fontColor(255, 255, 255);
size_t lineType = 1;
cv::copyMakeBorder(resized, new_face, 5, 5, 5, 5, cv::BORDER_CONSTANT, cv::Scalar(255, 255, 255));
cv::putText(new_face, "Please, enter person's name and", cv::Point2d(30, 80), font, fontScale, fontColor, lineType);
cv::putText(new_face, "press \"Enter\" to accept and continue.", cv::Point2d(30, 110), font, fontScale, fontColor, lineType);
cv::putText(new_face, "Press \"Escape\" to discard.", cv::Point2d(30, 140), font, fontScale, fontColor, lineType);
cv::putText(new_face, "Name: ", cv::Point2d(30, 170), font, fontScale, fontColor, lineType);
cv::copyMakeBorder(resized, newFace, 5, 5, 5, 5, cv::BORDER_CONSTANT, cv::Scalar(255, 255, 255));
cv::putText(newFace, "Please, enter person's name and", cv::Point2d(30, 80), font, fontScale, fontColor, lineType);
cv::putText(newFace, "press \"Enter\" to accept and continue.", cv::Point2d(30, 110), font, fontScale, fontColor, lineType);
cv::putText(newFace, "Press \"Escape\" to discard.", cv::Point2d(30, 140), font, fontScale, fontColor, lineType);
cv::putText(newFace, "Name: ", cv::Point2d(30, 170), font, fontScale, fontColor, lineType);
std::string name;
bool save = false;
while (true) {
cv::Mat cc = new_face.clone();
cv::Mat cc = newFace.clone();
cv::putText(cc, name, cv::Point2d(30, 200), font, fontScale, fontColor, lineType);
cv::imshow(winname, cc);
int key = (cv::waitKey(0) & 0xFF);
Expand Down Expand Up @@ -164,42 +175,42 @@ std::string EmbeddingsGallery::tryToSave(cv::Mat new_face){
continue;
}
}

cv::destroyWindow(winname);
return (save ? name : "");
}

void EmbeddingsGallery::addFace(const cv::Mat new_face, const cv::Mat embedding, std::string label) {
void EmbeddingsGallery::addFace(const cv::Mat newFace, const cv::Mat embedding, std::string label) {
identities.emplace_back(std::vector<cv::Mat>{embedding}, label, idxToId.size());
idxToId.push_back(idxToId.size());
label += ".jpg";
fs::path filename = fs::path(faceGalleryPath) / label;

cv::imwrite(filename, new_face);
cv::imwrite(faceGalleryPath + "/" + label, newFace);
}

RegistrationStatus EmbeddingsGallery::registerIdentity(const std::string& identity_label,
RegistrationStatus EmbeddingsGallery::registerIdentity(const std::string& identityLabel,
const cv::Mat& image,
bool crop_gallery,
bool crop,
FaceDetector& detector,
AsyncModel& landmarks_det,
AsyncModel& image_reid,
AsyncModel& landmarksDet,
AsyncModel& imageReid,
cv::Mat& embedding) {
cv::Mat target = image;
if (crop_gallery) {
if (crop) {
detector.submitData(image);
std::vector<FaceBox> faces = detector.getResults();
if (faces.size() == 0) {
return RegistrationStatus::FAILURE_NOT_DETECTED;
}
CV_Assert(faces.size() == 1);
if (faces.size() != 1) {
throw std::runtime_error("More than 1 face on the image provided for face gallery");
}
cv::Mat faceRoi = image(faces[0].face);
target = faceRoi;
}

cv::Mat landmarks;
std::vector<cv::Mat> images = { target };
std::vector<cv::Mat> landmarksVec = landmarks_det.infer(images);
std::vector<cv::Mat> landmarksVec = landmarksDet.infer(images);
alignFaces(images, landmarksVec);
embedding = image_reid.infer(images)[0];
embedding = imageReid.infer(images)[0];
return RegistrationStatus::SUCCESS;
}