Add relocatable runtime and image rotation support
This commit is contained in:
parent
647824fd26
commit
682147e51e
5 changed files with 359 additions and 23 deletions
2
Makefile
2
Makefile
|
|
@ -13,7 +13,7 @@ CXXFLAGS := \
|
||||||
|
|
||||||
LDFLAGS := \
|
LDFLAGS := \
|
||||||
-L$(ROOT)/lib \
|
-L$(ROOT)/lib \
|
||||||
-Wl,-rpath,$(ROOT)/lib
|
-Wl,-rpath,'$$ORIGIN/../lib'
|
||||||
|
|
||||||
LDLIBS := \
|
LDLIBS := \
|
||||||
-lrknnrt \
|
-lrknnrt \
|
||||||
|
|
|
||||||
102
README1.md
Normal file
102
README1.md
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
cd /home/nvme/dockerdata/prove/face-clustering-rk3588
|
||||||
|
|
||||||
|
# Files e dir necessarie per far funzionare
|
||||||
|
|
||||||
|
prj_root/
|
||||||
|
├── bin/
|
||||||
|
│ └── face_scan
|
||||||
|
├── lib/
|
||||||
|
│ └── librknnrt.so
|
||||||
|
├── models/
|
||||||
|
│ ├── SCRFD_500M_KPS_640.rknn
|
||||||
|
│ └── face_recognition_sface_2021dec.rknn
|
||||||
|
└── test
|
||||||
|
└── test3f.jpg
|
||||||
|
|
||||||
|
### Copia
|
||||||
|
|
||||||
|
cd /home/nvme/dockerdata/prove/s121js || exit 1
|
||||||
|
|
||||||
|
mkdir -p bin lib models
|
||||||
|
|
||||||
|
cp -f \
|
||||||
|
/home/nvme/dockerdata/prove/face-clustering-rk3588/lib/librknnrt.so \
|
||||||
|
lib/
|
||||||
|
|
||||||
|
cp -f \
|
||||||
|
/home/nvme/dockerdata/prove/face-clustering-rk3588/models/SCRFD_500M_KPS_640.rknn \
|
||||||
|
models/
|
||||||
|
|
||||||
|
cp -f \
|
||||||
|
/home/nvme/dockerdata/prove/face-clustering-rk3588/models/face_recognition_sface_2021dec.rknn \
|
||||||
|
models/
|
||||||
|
|
||||||
|
|
||||||
|
### Compilazione
|
||||||
|
|
||||||
|
make clean
|
||||||
|
make -j"$(nproc)"
|
||||||
|
|
||||||
|
### Test delle quattro rotazioni
|
||||||
|
|
||||||
|
for rotation in 0 90 180 270; do
|
||||||
|
./bin/face_scan \
|
||||||
|
--rotation "$rotation" \
|
||||||
|
test/test3f.jpg \
|
||||||
|
> "/tmp/rotation_${rotation}.json" \
|
||||||
|
2> "/tmp/rotation_${rotation}.stderr"
|
||||||
|
|
||||||
|
status=$?
|
||||||
|
stderr_bytes=$(wc -c < "/tmp/rotation_${rotation}.stderr")
|
||||||
|
|
||||||
|
echo \
|
||||||
|
"rotation=$rotation" \
|
||||||
|
"status=$status" \
|
||||||
|
"stderr_bytes=$stderr_bytes"
|
||||||
|
done
|
||||||
|
|
||||||
|
### Verifica dimensioni, rotazione e volti
|
||||||
|
|
||||||
|
python3 - <<'PY'
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
for rotation in (0, 90, 180, 270):
|
||||||
|
json_path = Path(
|
||||||
|
"/tmp/rotation_"
|
||||||
|
+ str(rotation)
|
||||||
|
+ ".json"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = json.loads(
|
||||||
|
json_path.read_text(encoding="utf-8")
|
||||||
|
)
|
||||||
|
|
||||||
|
image = data.get("image")
|
||||||
|
faces = data.get("faces")
|
||||||
|
|
||||||
|
if not isinstance(image, dict):
|
||||||
|
raise SystemExit(
|
||||||
|
"Campo image non valido per rotazione "
|
||||||
|
+ str(rotation)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not isinstance(faces, list):
|
||||||
|
raise SystemExit(
|
||||||
|
"Campo faces non valido per rotazione "
|
||||||
|
+ str(rotation)
|
||||||
|
)
|
||||||
|
|
||||||
|
print(
|
||||||
|
"rotation:",
|
||||||
|
rotation,
|
||||||
|
"size:",
|
||||||
|
str(image.get("width"))
|
||||||
|
+ "x"
|
||||||
|
+ str(image.get("height")),
|
||||||
|
"json_rotation:",
|
||||||
|
image.get("rotation"),
|
||||||
|
"faces:",
|
||||||
|
len(faces),
|
||||||
|
)
|
||||||
|
PY
|
||||||
|
|
@ -38,7 +38,8 @@ public:
|
||||||
const std::string& image_path,
|
const std::string& image_path,
|
||||||
std::vector<Face>& faces,
|
std::vector<Face>& faces,
|
||||||
int& image_width,
|
int& image_width,
|
||||||
int& image_height);
|
int& image_height,
|
||||||
|
int rotation_degrees = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,108 @@ struct Image {
|
||||||
std::vector<uint8_t> rgb;
|
std::vector<uint8_t> rgb;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool normalize_rotation(
|
||||||
|
int rotation_degrees,
|
||||||
|
int& normalized_rotation)
|
||||||
|
{
|
||||||
|
int value = rotation_degrees % 360;
|
||||||
|
|
||||||
|
if (value < 0)
|
||||||
|
value += 360;
|
||||||
|
|
||||||
|
if (
|
||||||
|
value != 0
|
||||||
|
&& value != 90
|
||||||
|
&& value != 180
|
||||||
|
&& value != 270
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
normalized_rotation = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Image rotate_image(
|
||||||
|
const Image& source,
|
||||||
|
int rotation_degrees)
|
||||||
|
{
|
||||||
|
if (rotation_degrees == 0)
|
||||||
|
return source;
|
||||||
|
|
||||||
|
Image destination;
|
||||||
|
|
||||||
|
if (
|
||||||
|
rotation_degrees == 90
|
||||||
|
|| rotation_degrees == 270
|
||||||
|
) {
|
||||||
|
destination.w = source.h;
|
||||||
|
destination.h = source.w;
|
||||||
|
} else {
|
||||||
|
destination.w = source.w;
|
||||||
|
destination.h = source.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.rgb.resize(
|
||||||
|
static_cast<std::size_t>(destination.w)
|
||||||
|
* static_cast<std::size_t>(destination.h)
|
||||||
|
* 3);
|
||||||
|
|
||||||
|
for (int source_y = 0; source_y < source.h; ++source_y) {
|
||||||
|
for (int source_x = 0; source_x < source.w; ++source_x) {
|
||||||
|
int destination_x = 0;
|
||||||
|
int destination_y = 0;
|
||||||
|
|
||||||
|
switch (rotation_degrees) {
|
||||||
|
case 90:
|
||||||
|
destination_x = source.h - 1 - source_y;
|
||||||
|
destination_y = source_x;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 180:
|
||||||
|
destination_x = source.w - 1 - source_x;
|
||||||
|
destination_y = source.h - 1 - source_y;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 270:
|
||||||
|
destination_x = source_y;
|
||||||
|
destination_y = source.w - 1 - source_x;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
destination_x = source_x;
|
||||||
|
destination_y = source_y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t source_offset =
|
||||||
|
(
|
||||||
|
static_cast<std::size_t>(source_y)
|
||||||
|
* static_cast<std::size_t>(source.w)
|
||||||
|
+ static_cast<std::size_t>(source_x)
|
||||||
|
) * 3;
|
||||||
|
|
||||||
|
const std::size_t destination_offset =
|
||||||
|
(
|
||||||
|
static_cast<std::size_t>(destination_y)
|
||||||
|
* static_cast<std::size_t>(destination.w)
|
||||||
|
+ static_cast<std::size_t>(destination_x)
|
||||||
|
) * 3;
|
||||||
|
|
||||||
|
destination.rgb[destination_offset + 0] =
|
||||||
|
source.rgb[source_offset + 0];
|
||||||
|
|
||||||
|
destination.rgb[destination_offset + 1] =
|
||||||
|
source.rgb[source_offset + 1];
|
||||||
|
|
||||||
|
destination.rgb[destination_offset + 2] =
|
||||||
|
source.rgb[source_offset + 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
static bool load_image(const std::string& path, Image& img)
|
static bool load_image(const std::string& path, Image& img)
|
||||||
{
|
{
|
||||||
int c = 0;
|
int c = 0;
|
||||||
|
|
@ -314,7 +416,6 @@ public:
|
||||||
(const uint16_t*)outputs[6 + level].buf;
|
(const uint16_t*)outputs[6 + level].buf;
|
||||||
|
|
||||||
int feat_w = SCRFD_SIZE / stride;
|
int feat_w = SCRFD_SIZE / stride;
|
||||||
int feat_h = SCRFD_SIZE / stride;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
float score = fp16_to_float(scores[i]);
|
float score = fp16_to_float(scores[i]);
|
||||||
|
|
@ -692,7 +793,8 @@ bool FaceEngine::process_image(
|
||||||
const std::string& image_path,
|
const std::string& image_path,
|
||||||
std::vector<Face>& faces,
|
std::vector<Face>& faces,
|
||||||
int& image_width,
|
int& image_width,
|
||||||
int& image_height)
|
int& image_height,
|
||||||
|
int rotation_degrees)
|
||||||
{
|
{
|
||||||
faces.clear();
|
faces.clear();
|
||||||
image_width = 0;
|
image_width = 0;
|
||||||
|
|
@ -703,16 +805,33 @@ bool FaceEngine::process_image(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image image;
|
int normalized_rotation = 0;
|
||||||
|
|
||||||
if (!load_image(image_path, image))
|
if (!normalize_rotation(
|
||||||
|
rotation_degrees,
|
||||||
|
normalized_rotation)) {
|
||||||
|
std::cerr
|
||||||
|
<< "Rotazione non valida: "
|
||||||
|
<< rotation_degrees
|
||||||
|
<< ". Valori ammessi: 0, 90, 180, 270\n";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image source_image;
|
||||||
|
|
||||||
|
if (!load_image(image_path, source_image))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
image_width = image.w;
|
Image oriented_image = rotate_image(
|
||||||
image_height = image.h;
|
source_image,
|
||||||
|
normalized_rotation);
|
||||||
|
|
||||||
|
image_width = oriented_image.w;
|
||||||
|
image_height = oriented_image.h;
|
||||||
|
|
||||||
faces = ::process_image(
|
faces = ::process_image(
|
||||||
image,
|
oriented_image,
|
||||||
impl_->detector,
|
impl_->detector,
|
||||||
impl_->recognizer);
|
impl_->recognizer);
|
||||||
|
|
||||||
|
|
|
||||||
142
src/face_scan.cc
142
src/face_scan.cc
|
|
@ -1,15 +1,72 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "face_engine.h"
|
#include "face_engine.h"
|
||||||
#include "face_config.h"
|
|
||||||
|
|
||||||
static constexpr std::size_t EMBEDDING_SIZE = 128;
|
static constexpr std::size_t EMBEDDING_SIZE = 128;
|
||||||
|
|
||||||
|
static std::filesystem::path executable_path()
|
||||||
|
{
|
||||||
|
std::vector<char> buffer(4096);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const ssize_t length = readlink(
|
||||||
|
"/proc/self/exe",
|
||||||
|
buffer.data(),
|
||||||
|
buffer.size() - 1);
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Impossibile leggere /proc/self/exe");
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t path_length =
|
||||||
|
static_cast<std::size_t>(length);
|
||||||
|
|
||||||
|
if (path_length < buffer.size() - 1) {
|
||||||
|
buffer[path_length] = '\0';
|
||||||
|
|
||||||
|
return std::filesystem::path(
|
||||||
|
buffer.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.resize(buffer.size() * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::filesystem::path application_root()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Struttura prevista:
|
||||||
|
*
|
||||||
|
* root/
|
||||||
|
* bin/face_scan
|
||||||
|
* lib/librknnrt.so
|
||||||
|
* models/<model>.rknn
|
||||||
|
*
|
||||||
|
* executable_path():
|
||||||
|
* root/bin/face_scan
|
||||||
|
*
|
||||||
|
* parent_path():
|
||||||
|
* root/bin
|
||||||
|
*
|
||||||
|
* parent_path().parent_path():
|
||||||
|
* root
|
||||||
|
*/
|
||||||
|
return executable_path()
|
||||||
|
.parent_path()
|
||||||
|
.parent_path();
|
||||||
|
}
|
||||||
|
|
||||||
static void write_json_string(const std::string& value)
|
static void write_json_string(const std::string& value)
|
||||||
{
|
{
|
||||||
std::cout << '"';
|
std::cout << '"';
|
||||||
|
|
@ -165,23 +222,77 @@ static void write_face_json(
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc != 2) {
|
int rotation_degrees = 0;
|
||||||
|
const char* image_path = nullptr;
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
image_path = argv[1];
|
||||||
|
} else if (
|
||||||
|
argc == 4
|
||||||
|
&& std::string(argv[1]) == "--rotation"
|
||||||
|
) {
|
||||||
|
char* parse_end = nullptr;
|
||||||
|
|
||||||
|
const long parsed_rotation = std::strtol(
|
||||||
|
argv[2],
|
||||||
|
&parse_end,
|
||||||
|
10);
|
||||||
|
|
||||||
|
if (
|
||||||
|
parse_end == argv[2]
|
||||||
|
|| *parse_end != '\0'
|
||||||
|
|| parsed_rotation < -360000
|
||||||
|
|| parsed_rotation > 360000
|
||||||
|
) {
|
||||||
std::cerr
|
std::cerr
|
||||||
<< "Uso: "
|
<< "Rotazione non valida: "
|
||||||
<< argv[0]
|
<< argv[2]
|
||||||
<< " image.jpg\n";
|
<< "\n";
|
||||||
|
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string root =
|
rotation_degrees =
|
||||||
FACE_CLUSTERING_ROOT;
|
static_cast<int>(parsed_rotation);
|
||||||
|
|
||||||
|
image_path = argv[3];
|
||||||
|
} else {
|
||||||
|
std::cerr
|
||||||
|
<< "Uso:\n"
|
||||||
|
<< " " << argv[0]
|
||||||
|
<< " image.jpg\n"
|
||||||
|
<< " " << argv[0]
|
||||||
|
<< " --rotation 90 image.jpg\n";
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path root;
|
||||||
|
|
||||||
|
try {
|
||||||
|
root = application_root();
|
||||||
|
} catch (const std::exception& error) {
|
||||||
|
std::cerr
|
||||||
|
<< "Impossibile determinare la root applicativa: "
|
||||||
|
<< error.what()
|
||||||
|
<< "\n";
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string scrfd_model =
|
const std::string scrfd_model =
|
||||||
root + "/models/SCRFD_500M_KPS_640.rknn";
|
(
|
||||||
|
root
|
||||||
|
/ "models"
|
||||||
|
/ "SCRFD_500M_KPS_640.rknn"
|
||||||
|
).string();
|
||||||
|
|
||||||
const std::string sface_model =
|
const std::string sface_model =
|
||||||
root + "/models/face_recognition_sface_2021dec.rknn";
|
(
|
||||||
|
root
|
||||||
|
/ "models"
|
||||||
|
/ "face_recognition_sface_2021dec.rknn"
|
||||||
|
).string();
|
||||||
|
|
||||||
FaceEngine engine;
|
FaceEngine engine;
|
||||||
|
|
||||||
|
|
@ -197,13 +308,14 @@ int main(int argc, char** argv)
|
||||||
int image_height = 0;
|
int image_height = 0;
|
||||||
|
|
||||||
if (!engine.process_image(
|
if (!engine.process_image(
|
||||||
argv[1],
|
image_path,
|
||||||
faces,
|
faces,
|
||||||
image_width,
|
image_width,
|
||||||
image_height)) {
|
image_height,
|
||||||
|
rotation_degrees)) {
|
||||||
std::cerr
|
std::cerr
|
||||||
<< "Scansione fallita: "
|
<< "Scansione fallita: "
|
||||||
<< argv[1]
|
<< image_path
|
||||||
<< "\n";
|
<< "\n";
|
||||||
|
|
||||||
return 4;
|
return 4;
|
||||||
|
|
@ -227,13 +339,15 @@ int main(int argc, char** argv)
|
||||||
std::cout << "\"image\":{";
|
std::cout << "\"image\":{";
|
||||||
|
|
||||||
std::cout << "\"path\":";
|
std::cout << "\"path\":";
|
||||||
write_json_string(argv[1]);
|
write_json_string(image_path);
|
||||||
|
|
||||||
std::cout
|
std::cout
|
||||||
<< ",\"width\":"
|
<< ",\"width\":"
|
||||||
<< image_width
|
<< image_width
|
||||||
<< ",\"height\":"
|
<< ",\"height\":"
|
||||||
<< image_height;
|
<< image_height
|
||||||
|
<< ",\"rotation\":"
|
||||||
|
<< rotation_degrees;
|
||||||
|
|
||||||
std::cout << "},";
|
std::cout << "},";
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue