add python demos
This commit is contained in:
parent
3bdf2003ec
commit
c91356fc38
97 changed files with 3250 additions and 290 deletions
0
examples/retinaface/README.md
Normal file → Executable file
0
examples/retinaface/README.md
Normal file → Executable file
0
examples/retinaface/cpp/.gitkeep
Normal file → Executable file
0
examples/retinaface/cpp/.gitkeep
Normal file → Executable file
|
|
@ -1,63 +1,19 @@
|
|||
#TODO
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [-a <target_arch>]"
|
||||
echo " -a <target_arch> : Target architecture (default: aarch64)"
|
||||
echo " -h : Show this help message"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Default values
|
||||
TARGET_ARCH=aarch64
|
||||
|
||||
# Parse arguments
|
||||
while getopts 'a:h' opt; do
|
||||
case "$opt" in
|
||||
a)
|
||||
TARGET_ARCH=$OPTARG
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Default to aarch64-linux-gnu if GCC_COMPILER is not set
|
||||
GCC_COMPILER=${GCC_COMPILER:-aarch64-linux-gnu}
|
||||
|
||||
# Set compilers
|
||||
export CC=${GCC_COMPILER}-gcc
|
||||
export CXX=${GCC_COMPILER}-g++
|
||||
|
||||
# Validate compiler
|
||||
if ! command -v ${CC} &> /dev/null; then
|
||||
echo "Error: Compiler ${CC} not found."
|
||||
echo "Please set GCC_COMPILER environment variable to your cross-compiler path prefix."
|
||||
echo "Example: export GCC_COMPILER=/path/to/toolchain/bin/aarch64-linux-gnu"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ROOT_PWD=$(cd "$(dirname $0)" && pwd)
|
||||
BUILD_DIR=${ROOT_PWD}/build/linux
|
||||
|
||||
echo "Building for Linux..."
|
||||
echo "COMPILER: ${CC}"
|
||||
echo "TARGET_ARCH: ${TARGET_ARCH}"
|
||||
echo "BUILD_DIR: ${BUILD_DIR}"
|
||||
|
||||
mkdir -p ${BUILD_DIR}
|
||||
cd ${BUILD_DIR}
|
||||
|
||||
cmake ../../src \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DCMAKE_SYSTEM_PROCESSOR=${TARGET_ARCH} \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
make -j4
|
||||
|
||||
echo "Build complete. Executable in ${BUILD_DIR}/retinaface_demo"
|
||||
#
|
||||
# Copyright (C) 2024–2025 Amlogic, Inc. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
### TO DO
|
||||
|
|
|
|||
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2024–2025 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
|
@ -19,43 +35,87 @@ static void hwc_to_chw(const cv::Mat& src, float* dst) {
|
|||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 3) { std::cout << "Usage: " << argv[0] << " <model.adla> <image_dir>\n"; return 0; }
|
||||
if (argc < 3) {
|
||||
std::cout << "Usage: " << argv[0] << " <model.adla> <image_dir>\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
aml_config cfg{};
|
||||
cfg.typeSize = sizeof(cfg); cfg.modelType = ADLA_LOADABLE; cfg.nbgType = NN_ADLA_FILE; cfg.path = argv[1];
|
||||
cfg.typeSize = sizeof(cfg);
|
||||
cfg.modelType = ADLA_LOADABLE;
|
||||
cfg.nbgType = NN_ADLA_FILE;
|
||||
cfg.path = argv[1];
|
||||
void* ctx = aml_module_create(&cfg);
|
||||
if (!ctx) {
|
||||
std::cerr << "Failed to create aml_module\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto priors = generate_priors();
|
||||
size_t num_priors = priors.size();
|
||||
std::vector<float> chw_buffer(kInputW * kInputH * 3);
|
||||
|
||||
fs::create_directory("retinaface_result");
|
||||
const std::string out_dir = "retinaface_result";
|
||||
fs::create_directory(out_dir);
|
||||
|
||||
std::vector<fs::path> image_paths;
|
||||
for (auto& it : fs::directory_iterator(argv[2])) {
|
||||
cv::Mat img = cv::imread(it.path().string());
|
||||
std::string ext = it.path().extension().string();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
||||
if (ext == ".jpg" || ext == ".png" || ext == ".jpeg" || ext == ".bmp") {
|
||||
image_paths.push_back(it.path());
|
||||
}
|
||||
}
|
||||
std::sort(image_paths.begin(), image_paths.end());
|
||||
|
||||
int total = image_paths.size();
|
||||
if (total == 0) {
|
||||
std::cout << "No images found in " << argv[2] << "\n";
|
||||
aml_module_destroy(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < total; ++i) {
|
||||
const auto& path = image_paths[i];
|
||||
const std::string filename = path.filename().string();
|
||||
|
||||
std::cout << "============================================================\n";
|
||||
std::cout << "Processing image " << (i + 1) << "/" << total << ": " << filename << "\n";
|
||||
std::cout << "============================================================\n";
|
||||
|
||||
cv::Mat img = cv::imread(path.string());
|
||||
if (img.empty()) continue;
|
||||
|
||||
float scale = std::min((float)kInputW / img.cols, (float)kInputH / img.rows);
|
||||
int nw = img.cols * scale, nh = img.rows * scale;
|
||||
int px = (kInputW - nw) / 2, py = (kInputH - nh) / 2;
|
||||
cv::Mat res, canvas = cv::Mat::zeros(kInputH, kInputW, CV_32FC3);
|
||||
cv::resize(img, res, {nw, nh}); res.convertTo(res, CV_32FC3);
|
||||
cv::resize(img, res, {nw, nh});
|
||||
res.convertTo(res, CV_32FC3);
|
||||
res.copyTo(canvas(cv::Rect(px, py, nw, nh)));
|
||||
hwc_to_chw(canvas, chw_buffer.data());
|
||||
|
||||
nn_input in{}; in.typeSize = sizeof(in); in.input_type = BINARY_RAW_DATA;
|
||||
in.input = (unsigned char*)chw_buffer.data(); in.size = chw_buffer.size() * 4;
|
||||
in.info.valid = 1; in.info.input_format = AML_INPUT_MODEL_NCHW; in.info.input_data_type = AML_INPUT_FP32;
|
||||
nn_input in{};
|
||||
in.typeSize = sizeof(in);
|
||||
in.input_type = BINARY_RAW_DATA;
|
||||
in.input = (unsigned char*)chw_buffer.data();
|
||||
in.size = chw_buffer.size() * 4;
|
||||
in.info.valid = 1;
|
||||
in.info.input_format = AML_INPUT_MODEL_NCHW;
|
||||
in.info.input_data_type = AML_INPUT_FP32;
|
||||
aml_module_input_set(ctx, &in);
|
||||
|
||||
aml_output_config_t outcfg{}; outcfg.typeSize = sizeof(outcfg); outcfg.format = AML_OUTDATA_FLOAT32;
|
||||
aml_output_config_t outcfg{};
|
||||
outcfg.typeSize = sizeof(outcfg);
|
||||
outcfg.format = AML_OUTDATA_FLOAT32;
|
||||
nn_output* out = (nn_output*)aml_module_output_get(ctx, outcfg);
|
||||
if (!out) continue;
|
||||
|
||||
float *loc = nullptr, *conf = nullptr, *landm = nullptr;
|
||||
for (int i = 0; i < out->num; i++) {
|
||||
if (out->out[i].size == num_priors * 4 * 4) loc = (float*)out->out[i].buf;
|
||||
else if (out->out[i].size == num_priors * 2 * 4) conf = (float*)out->out[i].buf;
|
||||
else if (out->out[i].size == num_priors * 10 * 4) landm = (float*)out->out[i].buf;
|
||||
for (int j = 0; j < out->num; j++) {
|
||||
if (out->out[j].size == num_priors * 4 * 4) loc = (float*)out->out[j].buf;
|
||||
else if (out->out[j].size == num_priors * 2 * 4) conf = (float*)out->out[j].buf;
|
||||
else if (out->out[j].size == num_priors * 10 * 4) landm = (float*)out->out[j].buf;
|
||||
}
|
||||
if (!loc || !conf || !landm) continue;
|
||||
|
||||
|
|
@ -64,11 +124,11 @@ int main(int argc, char** argv) {
|
|||
std::vector<std::array<float, 10>> lms;
|
||||
std::vector<float> scores_vec;
|
||||
|
||||
for (size_t i = 0; i < num_priors; i++) {
|
||||
float sc = is_planar ? conf[num_priors + i] : conf[i * 2 + 1];
|
||||
for (size_t j = 0; j < num_priors; j++) {
|
||||
float sc = is_planar ? conf[num_priors + j] : conf[j * 2 + 1];
|
||||
if (sc > 0.5f) {
|
||||
boxes.push_back(decode_box(loc, i, num_priors, is_planar, priors[i]));
|
||||
lms.push_back(decode_landm(landm, i, num_priors, is_planar, priors[i]));
|
||||
boxes.push_back(decode_box(loc, j, num_priors, is_planar, priors[j]));
|
||||
lms.push_back(decode_landm(landm, j, num_priors, is_planar, priors[j]));
|
||||
scores_vec.push_back(sc);
|
||||
}
|
||||
}
|
||||
|
|
@ -83,7 +143,8 @@ int main(int argc, char** argv) {
|
|||
|
||||
char score_text[16];
|
||||
std::snprintf(score_text, sizeof(score_text), "%.2f", scores_vec[k]);
|
||||
cv::putText(img, score_text, {x1, std::max(y1 - 5, 5)}, cv::FONT_HERSHEY_SIMPLEX, 0.5, {0, 255, 0}, 1, cv::LINE_AA);
|
||||
cv::putText(img, score_text, {x1, std::max(y1 - 5, 5)},
|
||||
cv::FONT_HERSHEY_SIMPLEX, 0.5, {0, 255, 0}, 1, cv::LINE_AA);
|
||||
|
||||
auto& lm = lms[k];
|
||||
for (int j = 0; j < 5; j++) {
|
||||
|
|
@ -92,8 +153,14 @@ int main(int argc, char** argv) {
|
|||
cv::circle(img, {lx, ly}, 2, {0, 0, 255}, -1);
|
||||
}
|
||||
}
|
||||
cv::imwrite("retinaface_result/" + it.path().filename().string(), img);
|
||||
std::cout << "Detected: " << it.path().filename() << " (" << keep.size() << " faces)\n";
|
||||
|
||||
std::string save_path = out_dir + "/" + filename;
|
||||
cv::imwrite(save_path, img);
|
||||
|
||||
std::cout << " Detected " << keep.size() << " faces\n";
|
||||
std::cout << " Result saved to: " << save_path << "\n\n";
|
||||
}
|
||||
aml_module_destroy(ctx); return 0;
|
||||
|
||||
aml_module_destroy(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2024–2025 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "postprocess.h"
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2024–2025 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef RETINAFACE_POSTPROCESS_H
|
||||
#define RETINAFACE_POSTPROCESS_H
|
||||
|
||||
|
|
|
|||
0
examples/retinaface/model/.gitkeep
Normal file → Executable file
0
examples/retinaface/model/.gitkeep
Normal file → Executable file
0
examples/retinaface/py/.gitkeep
Normal file → Executable file
0
examples/retinaface/py/.gitkeep
Normal file → Executable file
173
examples/retinaface/py/RetinaFace.py
Executable file
173
examples/retinaface/py/RetinaFace.py
Executable file
|
|
@ -0,0 +1,173 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Copyright (C) 2024–2025 Amlogic, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import os
|
||||
import cv2
|
||||
import glob
|
||||
import argparse
|
||||
import time
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
from amlnnlite.api import AMLNNLite
|
||||
|
||||
class PriorBox:
|
||||
def __init__(self, image_size=(320, 320)):
|
||||
self.image_size = image_size
|
||||
self.steps = [8, 16, 32]
|
||||
self.min_sizes = [[16, 32], [64, 128], [256, 512]]
|
||||
|
||||
def forward(self):
|
||||
priors = []
|
||||
h, w = self.image_size
|
||||
for idx, step in enumerate(self.steps):
|
||||
fm_h, fm_w = int(np.ceil(h / step)), int(np.ceil(w / step))
|
||||
for i in range(fm_h):
|
||||
for j in range(fm_w):
|
||||
for min_size in self.min_sizes[idx]:
|
||||
cx, cy = (j + 0.5) * step / w, (i + 0.5) * step / h
|
||||
s_kx, s_ky = min_size / w, min_size / h
|
||||
priors.append([cx, cy, s_kx, s_ky])
|
||||
return np.array(priors, dtype=np.float32)
|
||||
|
||||
def decode_boxes(loc, priors, variances=(0.1, 0.2)):
|
||||
boxes = np.concatenate((
|
||||
priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:],
|
||||
priors[:, 2:] * np.exp(loc[:, 2:] * variances[1])
|
||||
), axis=1)
|
||||
boxes[:, :2] -= boxes[:, 2:] / 2
|
||||
boxes[:, 2:] += boxes[:, :2]
|
||||
return boxes
|
||||
|
||||
def decode_landmarks(pre, priors, variances=(0.1, 0.2)):
|
||||
landms = np.concatenate([
|
||||
priors[:, :2] + pre[:, i:i+2] * variances[0] * priors[:, 2:] for i in range(0, 10, 2)
|
||||
], axis=1)
|
||||
return landms
|
||||
|
||||
def nms(dets, thresh=0.4):
|
||||
x1, y1, x2, y2, scores = dets.T
|
||||
areas = (x2 - x1) * (y2 - y1)
|
||||
order = scores.argsort()[::-1]
|
||||
keep = []
|
||||
while order.size > 0:
|
||||
i = order[0]; keep.append(i)
|
||||
xx1, yy1 = np.maximum(x1[i], x1[order[1:]]), np.maximum(y1[i], y1[order[1:]])
|
||||
xx2, yy2 = np.maximum(y1[i], y1[order[1:]]), np.maximum(y1[i], y1[order[1:]]) # fix
|
||||
xx2, yy2 = np.minimum(x2[i], x2[order[1:]]), np.minimum(y2[i], y2[order[1:]])
|
||||
w, h = np.maximum(0.0, xx2 - xx1), np.maximum(0.0, yy2 - yy1)
|
||||
ovr = (w * h) / (areas[i] + areas[order[1:]] - (w * h))
|
||||
order = order[np.where(ovr <= thresh)[0] + 1]
|
||||
return keep
|
||||
|
||||
def postprocess_retinaface(outputs, priors, conf_thresh=0.5, nms_thresh=0.4):
|
||||
loc = conf = landms = None
|
||||
for out in outputs:
|
||||
out = np.squeeze(np.asarray(out))
|
||||
if out.shape[-1] == 4: loc = out
|
||||
elif out.shape[-1] == 2: conf = out
|
||||
elif out.shape[-1] == 10: landms = out
|
||||
|
||||
if loc is None or conf is None or landms is None: return [], [], []
|
||||
scores = conf[:, 1]
|
||||
mask = scores > conf_thresh
|
||||
if not np.any(mask): return [], [], []
|
||||
|
||||
boxes = decode_boxes(loc[mask], priors[mask])
|
||||
landms = decode_landmarks(landms[mask], priors[mask])
|
||||
scores = scores[mask]
|
||||
keep = nms(np.hstack((boxes, scores[:, None])), nms_thresh)
|
||||
return boxes[keep], landms[keep], scores[keep]
|
||||
|
||||
def preprocess(img_path, input_size=(320, 320)):
|
||||
img = cv2.imread(img_path)
|
||||
if img is None: return None, None, 0, 0, 0
|
||||
h0, w0 = img.shape[:2]
|
||||
scale = min(input_size[0] / w0, input_size[1] / h0)
|
||||
nw, nh = int(w0 * scale), int(h0 * scale)
|
||||
resized = cv2.resize(img, (nw, nh))
|
||||
canvas = np.full((input_size[1], input_size[0], 3), 128, dtype=np.uint8)
|
||||
pad_x, pad_y = (input_size[0] - nw) // 2, (input_size[1] - nh) // 2
|
||||
canvas[pad_y:pad_y + nh, pad_x:pad_x + nw] = resized
|
||||
return np.expand_dims(canvas.astype(np.float32), axis=0), img, scale, pad_x, pad_y
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="RetinaFace AMLNNLite Demo")
|
||||
parser.add_argument('--board-work-path', type=str, default='/data/nn')
|
||||
parser.add_argument('--model-path', required=True, help='Path to .adla model')
|
||||
parser.add_argument('--image-dir', required=True, help='Directory of test images')
|
||||
parser.add_argument('--run-cycles', type=int, default=1, help='Inference cycles')
|
||||
parser.add_argument('--loglevel', type=str, default='WARNING', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'])
|
||||
args = parser.parse_args()
|
||||
|
||||
amlnn = AMLNNLite()
|
||||
amlnn.config(board_work_path=args.board_work_path,
|
||||
model_path=args.model_path,
|
||||
run_cycles=args.run_cycles,
|
||||
loglevel=args.loglevel)
|
||||
amlnn.init()
|
||||
|
||||
priors = PriorBox((320, 320)).forward()
|
||||
image_files = sorted(glob.glob(os.path.join(args.image_dir, "*.[jp][pn][g]")))
|
||||
|
||||
if not image_files:
|
||||
print(f"No images found in {args.image_dir}")
|
||||
amlnn.uninit(); return
|
||||
|
||||
res_dir = "retinaface_result"
|
||||
os.makedirs(res_dir, exist_ok=True)
|
||||
|
||||
for idx, img_path in enumerate(image_files, start=1):
|
||||
print("=" * 60)
|
||||
print(f"Processing image {idx}/{len(image_files)}: {Path(img_path).name}")
|
||||
print("=" * 60)
|
||||
|
||||
inp, orig, scale, pad_x, pad_y = preprocess(img_path)
|
||||
if inp is None: continue
|
||||
|
||||
outputs = amlnn.inference(inp, inputs_data_format='NHWC')
|
||||
|
||||
boxes, landms, scores = postprocess_retinaface(outputs, priors)
|
||||
|
||||
if len(boxes) > 0:
|
||||
print(f" Detected {len(boxes)} objects:")
|
||||
for i, sc in enumerate(scores, 1):
|
||||
print(f" {i}. face ({sc:.2f})")
|
||||
else:
|
||||
print(" No objects detected")
|
||||
|
||||
for box, lm in zip(boxes, landms):
|
||||
x1 = int((box[0] * 320 - pad_x) / scale)
|
||||
y1 = int((box[1] * 320 - pad_y) / scale)
|
||||
x2 = int((box[2] * 320 - pad_x) / scale)
|
||||
y2 = int((box[3] * 320 - pad_y) / scale)
|
||||
cv2.rectangle(orig, (x1, y1), (x2, y2), (0, 255, 0), 2)
|
||||
for lx, ly in lm.reshape(5, 2):
|
||||
cv2.circle(orig, (int((lx*320-pad_x)/scale), int((ly*320-pad_y)/scale)), 2, (0, 0, 255), -1)
|
||||
|
||||
save_path = os.path.join(res_dir, Path(img_path).name)
|
||||
cv2.imwrite(save_path, orig)
|
||||
print(f" Result saved to: {save_path}")
|
||||
|
||||
if args.loglevel == 'INFO':
|
||||
print("\nI Performance analysis visualization starting...")
|
||||
|
||||
amlnn.visualize()
|
||||
amlnn.uninit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue