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 := \
|
||||
-L$(ROOT)/lib \
|
||||
-Wl,-rpath,$(ROOT)/lib
|
||||
-Wl,-rpath,'$$ORIGIN/../lib'
|
||||
|
||||
LDLIBS := \
|
||||
-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,
|
||||
std::vector<Face>& faces,
|
||||
int& image_width,
|
||||
int& image_height);
|
||||
int& image_height,
|
||||
int rotation_degrees = 0);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
|
|
|
|||
|
|
@ -132,6 +132,108 @@ struct Image {
|
|||
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)
|
||||
{
|
||||
int c = 0;
|
||||
|
|
@ -314,7 +416,6 @@ public:
|
|||
(const uint16_t*)outputs[6 + level].buf;
|
||||
|
||||
int feat_w = SCRFD_SIZE / stride;
|
||||
int feat_h = SCRFD_SIZE / stride;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
float score = fp16_to_float(scores[i]);
|
||||
|
|
@ -692,7 +793,8 @@ bool FaceEngine::process_image(
|
|||
const std::string& image_path,
|
||||
std::vector<Face>& faces,
|
||||
int& image_width,
|
||||
int& image_height)
|
||||
int& image_height,
|
||||
int rotation_degrees)
|
||||
{
|
||||
faces.clear();
|
||||
image_width = 0;
|
||||
|
|
@ -703,16 +805,33 @@ bool FaceEngine::process_image(
|
|||
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;
|
||||
|
||||
image_width = image.w;
|
||||
image_height = image.h;
|
||||
Image oriented_image = rotate_image(
|
||||
source_image,
|
||||
normalized_rotation);
|
||||
|
||||
image_width = oriented_image.w;
|
||||
image_height = oriented_image.h;
|
||||
|
||||
faces = ::process_image(
|
||||
image,
|
||||
oriented_image,
|
||||
impl_->detector,
|
||||
impl_->recognizer);
|
||||
|
||||
|
|
|
|||
142
src/face_scan.cc
142
src/face_scan.cc
|
|
@ -1,15 +1,72 @@
|
|||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "face_engine.h"
|
||||
#include "face_config.h"
|
||||
|
||||
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)
|
||||
{
|
||||
std::cout << '"';
|
||||
|
|
@ -165,23 +222,77 @@ static void write_face_json(
|
|||
|
||||
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
|
||||
<< "Rotazione non valida: "
|
||||
<< argv[2]
|
||||
<< "\n";
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
rotation_degrees =
|
||||
static_cast<int>(parsed_rotation);
|
||||
|
||||
image_path = argv[3];
|
||||
} else {
|
||||
std::cerr
|
||||
<< "Uso: "
|
||||
<< argv[0]
|
||||
<< " image.jpg\n";
|
||||
<< "Uso:\n"
|
||||
<< " " << argv[0]
|
||||
<< " image.jpg\n"
|
||||
<< " " << argv[0]
|
||||
<< " --rotation 90 image.jpg\n";
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
const std::string root =
|
||||
FACE_CLUSTERING_ROOT;
|
||||
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 =
|
||||
root + "/models/SCRFD_500M_KPS_640.rknn";
|
||||
(
|
||||
root
|
||||
/ "models"
|
||||
/ "SCRFD_500M_KPS_640.rknn"
|
||||
).string();
|
||||
|
||||
const std::string sface_model =
|
||||
root + "/models/face_recognition_sface_2021dec.rknn";
|
||||
(
|
||||
root
|
||||
/ "models"
|
||||
/ "face_recognition_sface_2021dec.rknn"
|
||||
).string();
|
||||
|
||||
FaceEngine engine;
|
||||
|
||||
|
|
@ -197,13 +308,14 @@ int main(int argc, char** argv)
|
|||
int image_height = 0;
|
||||
|
||||
if (!engine.process_image(
|
||||
argv[1],
|
||||
image_path,
|
||||
faces,
|
||||
image_width,
|
||||
image_height)) {
|
||||
image_height,
|
||||
rotation_degrees)) {
|
||||
std::cerr
|
||||
<< "Scansione fallita: "
|
||||
<< argv[1]
|
||||
<< image_path
|
||||
<< "\n";
|
||||
|
||||
return 4;
|
||||
|
|
@ -227,13 +339,15 @@ int main(int argc, char** argv)
|
|||
std::cout << "\"image\":{";
|
||||
|
||||
std::cout << "\"path\":";
|
||||
write_json_string(argv[1]);
|
||||
write_json_string(image_path);
|
||||
|
||||
std::cout
|
||||
<< ",\"width\":"
|
||||
<< image_width
|
||||
<< ",\"height\":"
|
||||
<< image_height;
|
||||
<< image_height
|
||||
<< ",\"rotation\":"
|
||||
<< rotation_degrees;
|
||||
|
||||
std::cout << "},";
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue