File size: 5,176 Bytes
675da12
eca0f43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
675da12
 
 
 
 
 
eca0f43
 
 
 
 
675da12
 
 
eca0f43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <algorithm>
#include <array>
#include <cmath>
#include <iostream>
#include <string>
#include <vector>

using namespace cv;

static std::string argVal(int argc, char** argv, const std::string& key, const std::string& def)
{
    for (int i = 1; i + 1 < argc; ++i)
        if (key == argv[i]) return argv[i + 1];
    return def;
}

struct Layer { float mn, mx; std::vector<int> ars; int step, fm; };

int main(int argc, char** argv)
{
    std::string model  = argVal(argc, argv, "--model",  "opencv_face_detector_uint8_2026jul.onnx");
    std::string image  = argVal(argc, argv, "--image",  "example_outputs/input_image.png");
    std::string output = argVal(argc, argv, "--output", "example_outputs/output_image.png");
    float thr = std::stof(argVal(argc, argv, "--conf", "0.4"));

    const int sz = 300;
    Mat img = imread(image);
    if (img.empty())
    {
        std::cerr << "could not read image: " << image << std::endl;
        return 1;
    }

    Mat inp;
    resize(img, inp, Size(sz, sz));
    inp.convertTo(inp, CV_32F);
    subtract(inp, Scalar(104, 177, 123), inp);
    if (!inp.isContinuous()) inp = inp.clone();

    int blobShape[] = {1, sz, sz, 3};
    Mat blob(4, blobShape, CV_32F, inp.data);
    dnn::Net net = dnn::readNetFromONNX(model);
    net.setInput(blob);
    std::vector<Mat> outs;
    net.forward(outs, net.getUnconnectedOutLayersNames());

    const float* loc = nullptr;
    const float* conf = nullptr;
    for (size_t i = 0; i < outs.size(); ++i)
    {
        const Mat& o = outs[i];
        size_t tot = o.total();
        const float* p = (const float*)o.data;
        if (tot == 35568) loc = p;
        else if (tot == 17784) conf = p;
    }

    std::vector<Layer> layers = {
        {30, 60, {2}, 8, 38},
        {60, 111, {2, 3}, 16, 19},
        {111, 162, {2, 3}, 32, 10},
        {162, 213, {2, 3}, 64, 5},
        {213, 264, {2}, 100, 5},
        {264, 315, {2}, 300, 5},
    };
    std::vector<Vec4f> priors;
    for (const Layer& L : layers)
    {
        std::vector<float> ratios = {1.0f};
        for (int a : L.ars) { ratios.push_back((float)a); ratios.push_back(1.0f / a); }
        for (int y = 0; y < L.fm; ++y)
            for (int x = 0; x < L.fm; ++x)
            {
                float cx = (x + 0.5f) * L.step;
                float cy = (y + 0.5f) * L.step;
                std::vector<Vec2f> boxes = {{L.mn, L.mn}, {std::sqrt(L.mn * L.mx), std::sqrt(L.mn * L.mx)}};
                for (size_t k = 1; k < ratios.size(); ++k)
                {
                    float a = ratios[k];
                    boxes.push_back({L.mn * std::sqrt(a), L.mn / std::sqrt(a)});
                }
                for (const Vec2f& b : boxes)
                    priors.push_back({cx, cy, b[0], b[1]});
            }
    }

    const float var[4] = {0.1f, 0.1f, 0.2f, 0.2f};
    int n = (int)priors.size();
    std::vector<Rect2f> boxes;
    std::vector<float> scores;
    for (int i = 0; i < n; ++i)
    {
        float c0 = conf[i * 2], c1 = conf[i * 2 + 1];
        float m = std::max(c0, c1);
        float e0 = std::exp(c0 - m), e1 = std::exp(c1 - m);
        float s = e1 / (e0 + e1);
        if (s <= thr) continue;
        float pcx = priors[i][0] / sz, pcy = priors[i][1] / sz;
        float pw = priors[i][2] / sz, ph = priors[i][3] / sz;
        float cx = pcx + loc[i * 4] * var[0] * pw;
        float cy = pcy + loc[i * 4 + 1] * var[1] * ph;
        float bw = pw * std::exp(loc[i * 4 + 2] * var[2]);
        float bh = ph * std::exp(loc[i * 4 + 3] * var[3]);
        boxes.push_back(Rect2f(cx - bw / 2, cy - bh / 2, bw, bh));
        scores.push_back(s);
    }

    std::vector<int> order(scores.size());
    for (size_t i = 0; i < order.size(); ++i) order[i] = (int)i;
    std::sort(order.begin(), order.end(), [&](int a, int b){ return scores[a] > scores[b]; });
    std::vector<char> removed(order.size(), 0);
    std::vector<int> pick;
    for (size_t oi = 0; oi < order.size(); ++oi)
    {
        if (removed[oi]) continue;
        int i = order[oi];
        pick.push_back(i);
        for (size_t oj = oi + 1; oj < order.size(); ++oj)
        {
            if (removed[oj]) continue;
            int j = order[oj];
            const Rect2f& a = boxes[i];
            const Rect2f& b = boxes[j];
            float xx1 = std::max(a.x, b.x), yy1 = std::max(a.y, b.y);
            float xx2 = std::min(a.x + a.width, b.x + b.width);
            float yy2 = std::min(a.y + a.height, b.y + b.height);
            float inter = std::max(0.f, xx2 - xx1) * std::max(0.f, yy2 - yy1);
            float iou = inter / (a.area() + b.area() - inter + 1e-9f);
            if (iou > 0.3f) removed[oj] = 1;
        }
    }

    int W = img.cols, H = img.rows;
    for (int i : pick)
    {
        const Rect2f& b = boxes[i];
        rectangle(img, Point(int(b.x * W), int(b.y * H)),
                  Point(int((b.x + b.width) * W), int((b.y + b.height) * H)), Scalar(0, 255, 0), 2);
    }
    imwrite(output, img);
    std::cout << "opencv_face_detector_uint8 " << pick.size() << " faces" << std::endl;
    return 0;
}