circe 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 046e41e59ccb0202ad32729da191ba2f003a9e3a0370f4454996fdd544f47103
4
- data.tar.gz: 1d3d18f2bcacfc6ae6e8ff816d166b43f53686841e510fbf722a5a2c85f80080
3
+ metadata.gz: 8d735b7de777b161f27ca0b0d50282eca7e18f1cd9e5aa6e298e089f6ccd2b89
4
+ data.tar.gz: 5ba4acf8e3c3ef5950c981de11a31f4bdcdb7a0fb1cc4c534b1e62f96a99fb3a
5
5
  SHA512:
6
- metadata.gz: 17a645694b3ac252fe68048a58e63cbfe4d580a45ed8b3bdfda6cc0ff94e0ab821536a4bed63aff1a42b7ceba3637c889b0a767c9e51ab3e2f5bf4d4c193db53
7
- data.tar.gz: 5fcffb7041bcd9f53f379576847748a5579d77b681b71e0eae810f3ce090aca5d83644c6bba442bfc06f3fd4e8bfb6c5550dd6ae172f9636bdf263822a94446e
6
+ metadata.gz: 8ac69c1f5b7d3af53db342306629fc880ae8b6386ba0ee4ee02fcec391fcab29b9acc3aa972a01f2d99b31cf0267861d6553d2cdc54b839e2a36648801b83fd3
7
+ data.tar.gz: 76a3084d3b0fc496d39e9efc04733b17a64a31e5b4285ad450bf1dc681db132671febe821ae3ae717b68771a20901f28247ae62c89a2ec57a013ef4ca7bb6c4b
Binary file
Binary file
data/data/yolov8s.onnx ADDED
Binary file
data/ext/circe.cpp CHANGED
@@ -91,6 +91,7 @@ static VALUE eCirceError = Qundef;
91
91
  #include <chrono>
92
92
  #include <opencv2/highgui.hpp>
93
93
  #include <opencv2/imgproc.hpp>
94
+ #include <opencv2/core/mat.hpp>
94
95
 
95
96
  #include "yolo.h"
96
97
  #include "yunet.h"
@@ -121,6 +122,7 @@ static ID id_jpg;
121
122
 
122
123
  static ID id_label;
123
124
  static ID id_thickness;
125
+ static ID id_extra;
124
126
  static ID id_color;
125
127
 
126
128
  static Yolo *yolo;
@@ -175,18 +177,21 @@ circe_annotate(Mat& img, Rect& box, VALUE v_annotation, int *state) {
175
177
  VALUE v_label = Qnil;
176
178
  VALUE v_color = ULONG2NUM(0x0000ff);
177
179
  VALUE v_thickness = INT2NUM(1);
180
+ VALUE v_extra = Qtrue;
178
181
 
179
182
  VALUE s_label = rb_id2sym(id_label);
180
183
  VALUE s_color = rb_id2sym(id_color);
181
184
  VALUE s_thickness = rb_id2sym(id_thickness);
182
-
185
+ VALUE s_extra = rb_id2sym(id_extra);
186
+
183
187
  switch (TYPE(v_annotation)) {
184
188
  case T_NIL:
185
189
  break;
186
190
  case T_HASH:
187
- v_thickness = rb_hash_aref(v_annotation, s_thickness);
188
- v_color = rb_hash_aref(v_annotation, s_color);
189
- v_label = rb_hash_aref(v_annotation, s_label);
191
+ v_thickness = rb_hash_lookup2(v_annotation, s_thickness, v_thickness);
192
+ v_color = rb_hash_lookup2(v_annotation, s_color, v_color );
193
+ v_label = rb_hash_lookup2(v_annotation, s_label, v_label );
194
+ v_extra = rb_hash_lookup2(v_annotation, s_extra, v_extra );
190
195
  break;
191
196
  case T_ARRAY:
192
197
  switch(RARRAY_LENINT(v_annotation)) {
@@ -226,46 +231,74 @@ circe_annotate(Mat& img, Rect& box, VALUE v_annotation, int *state) {
226
231
  rb_hash_aset(r, s_label, v_label );
227
232
  rb_hash_aset(r, s_color, v_color );
228
233
  rb_hash_aset(r, s_thickness, v_thickness);
234
+ rb_hash_aset(r, s_extra, v_extra );
229
235
  return r;
230
236
  }
231
237
 
232
238
 
233
239
 
234
240
  void
235
- yunet_process_features(vector<YuNet::Face>& faces,
236
- Mat& img, VALUE v_features, int *state)
241
+ yunet_process_features(cv::Mat& faces, Mat& img, VALUE v_features, int *state)
237
242
  {
238
- for (int i = 0; i < faces.size(); i++) {
239
- Rect box = faces[i].first;
240
- YuNet::Landmark lmark = faces[i].second;
243
+ for (int i = 0; i < faces.rows; i++) {
244
+ // Face
245
+ int x_f = static_cast<int>(faces.at<float>(i, 0));
246
+ int y_f = static_cast<int>(faces.at<float>(i, 1));
247
+ int w_f = static_cast<int>(faces.at<float>(i, 2));
248
+ int h_f = static_cast<int>(faces.at<float>(i, 3));
249
+ // Right eye
250
+ int x_re = static_cast<int>(faces.at<float>(i, 4));
251
+ int y_re = static_cast<int>(faces.at<float>(i, 5));
252
+ // Left eye
253
+ int x_le = static_cast<int>(faces.at<float>(i, 6));
254
+ int y_le = static_cast<int>(faces.at<float>(i, 7));
255
+ // Nose tip
256
+ int x_nt = static_cast<int>(faces.at<float>(i, 8));
257
+ int y_nt = static_cast<int>(faces.at<float>(i, 9));
258
+ // Right corner mouth
259
+ int x_rcm = static_cast<int>(faces.at<float>(i, 10));
260
+ int y_rcm = static_cast<int>(faces.at<float>(i, 11));
261
+ // Left corner mouth
262
+ int x_lcm = static_cast<int>(faces.at<float>(i, 12));
263
+ int y_lcm = static_cast<int>(faces.at<float>(i, 13));
264
+ // Confidence
265
+ float confidence = faces.at<float>(i, 14);
241
266
 
242
- VALUE v_type = ID2SYM(id_face);
267
+ VALUE v_type = ID2SYM(id_face);
243
268
  VALUE v_box = rb_ary_new_from_args(4,
244
- INT2NUM(box.x ), INT2NUM(box.y ),
245
- INT2NUM(box.width), INT2NUM(box.height));
269
+ INT2NUM(x_f), INT2NUM(y_f),
270
+ INT2NUM(w_f), INT2NUM(h_f));
246
271
  VALUE v_landmark = rb_ary_new_from_args(5,
247
- rb_ary_new_from_args(2, INT2NUM(lmark[0].x),
248
- INT2NUM(lmark[0].y)),
249
- rb_ary_new_from_args(2, INT2NUM(lmark[1].x),
250
- INT2NUM(lmark[1].y)),
251
- rb_ary_new_from_args(2, INT2NUM(lmark[2].x),
252
- INT2NUM(lmark[2].y)),
253
- rb_ary_new_from_args(2, INT2NUM(lmark[3].x),
254
- INT2NUM(lmark[3].y)),
255
- rb_ary_new_from_args(2, INT2NUM(lmark[4].x),
256
- INT2NUM(lmark[4].y)));
257
- VALUE v_feature = rb_ary_new_from_args(3, v_type, v_box,
258
- v_landmark);
272
+ rb_ary_new_from_args(2, INT2NUM(x_re),
273
+ INT2NUM(y_re)),
274
+ rb_ary_new_from_args(2, INT2NUM(x_le),
275
+ INT2NUM(y_le)),
276
+ rb_ary_new_from_args(2, INT2NUM(x_nt),
277
+ INT2NUM(y_nt)),
278
+ rb_ary_new_from_args(2, INT2NUM(x_rcm),
279
+ INT2NUM(y_rcm)),
280
+ rb_ary_new_from_args(2, INT2NUM(x_lcm),
281
+ INT2NUM(y_lcm)));
282
+ VALUE v_confidence = DBL2NUM(confidence);
283
+ VALUE v_feature = rb_ary_new_from_args(4, v_type, v_box, v_landmark,
284
+ v_confidence);
285
+
259
286
  rb_ary_push(v_features, v_feature);
287
+
260
288
 
261
289
  if (!img.empty() && rb_block_given_p()) {
290
+ cv::Rect box = cv::Rect(x_f, y_f, w_f, h_f);
262
291
  VALUE v_annotation = rb_yield_splat(v_feature);
263
- VALUE cfg = circe_annotate(img, box, v_annotation, state);
264
-
265
- if (! NIL_P(cfg)) {
266
- for (const auto& p : lmark) {
267
- cv::circle(img, p, 3, cv::Scalar(255, 0, 0), 2);
268
- }
292
+ VALUE cfg = circe_annotate(img, box, v_annotation, state);
293
+ VALUE s_extra = rb_id2sym(id_extra);
294
+
295
+ if (!NIL_P(cfg) && RTEST(rb_hash_aref(cfg, s_extra))) {
296
+ cv::Scalar color = cv::Scalar(255, 0, 0);
297
+ cv::circle(img, cv::Point(x_le, y_le ), 3, color, 2);
298
+ cv::circle(img, cv::Point(x_re, y_re ), 3, color, 2);
299
+ cv::circle(img, cv::Point(x_nt, y_nt ), 3, color, 2);
300
+ cv::circle(img, cv::Point(x_rcm, y_rcm), 3, color, 2);
301
+ cv::circle(img, cv::Point(x_lcm, y_lcm), 3, color, 2);
269
302
  }
270
303
  }
271
304
  }
@@ -283,11 +316,11 @@ yolo_process_features(vector<Yolo::Item>& items,
283
316
  Rect box = std::get<2>(items[i]);
284
317
 
285
318
  VALUE v_type = ID2SYM(id_class);
286
- VALUE v_name = rb_str_new(name.c_str(), name.size());
287
- VALUE v_confidence = DBL2NUM(confidence);
288
319
  VALUE v_box = rb_ary_new_from_args(4,
289
320
  INT2NUM(box.x ), INT2NUM(box.y ),
290
321
  INT2NUM(box.width), INT2NUM(box.height));
322
+ VALUE v_name = rb_str_new(name.c_str(), name.size());
323
+ VALUE v_confidence = DBL2NUM(confidence);
291
324
  VALUE v_feature = rb_ary_new_from_args(4, v_type, v_box,
292
325
  v_name, v_confidence);
293
326
  rb_ary_push(v_features, v_feature);
@@ -349,9 +382,10 @@ circe_m_analyze(int argc, VALUE* argv, VALUE self) {
349
382
  }
350
383
 
351
384
  if (RTEST(v_face)) {
352
- vector<YuNet::Face> faces;
385
+ cv::Mat faces;
353
386
  yunet->process(i_img, faces);
354
387
  yunet_process_features(faces, o_img, v_features, &state);
388
+ faces.release();
355
389
  if (state) goto exception;
356
390
  }
357
391
 
@@ -375,6 +409,7 @@ circe_m_analyze(int argc, VALUE* argv, VALUE self) {
375
409
  std::vector<uchar> buf;
376
410
  cv::imencode(format, o_img, buf);
377
411
  v_image = rb_str_new(reinterpret_cast<char*>(buf.data()), buf.size());
412
+ buf.clear();
378
413
  }
379
414
 
380
415
  i_img.release();
@@ -425,6 +460,7 @@ void Init_core(void) {
425
460
  id_jpg = rb_intern_const("jpg" );
426
461
  id_label = rb_intern_const("label" );
427
462
  id_thickness = rb_intern_const("thickness");
463
+ id_extra = rb_intern_const("extra" );
428
464
  id_color = rb_intern_const("color" );
429
465
 
430
466
 
data/ext/extconf.rb CHANGED
@@ -10,6 +10,6 @@ cflags, ldflags, libs = pkg_config('opencv4')
10
10
 
11
11
  $LDFLAGS += " #{ldflags} #{libs}"
12
12
  $INCFLAGS += " #{cflags}"
13
- $CXXFLAGS += "-std=c++17"
13
+ $CXXFLAGS += " -std=c++17"
14
14
 
15
15
  create_makefile("circe/core")
data/ext/yolo.h CHANGED
@@ -17,7 +17,7 @@ private:
17
17
  static constexpr float INPUT_WIDTH = 640.0;
18
18
  static constexpr float INPUT_HEIGHT = 640.0;
19
19
  static constexpr float CONFIDENCE_THRESHOLD = 0.25;
20
- static constexpr float SCORE_THRESHOLD = 0.45;
20
+ static constexpr float SCORE_THRESHOLD = 0.50;
21
21
  static constexpr float NMS_THRESHOLD = 0.50;
22
22
 
23
23
  bool letterBoxForSquare = true;
data/ext/yunet.h CHANGED
@@ -2,37 +2,37 @@
2
2
  #define __YUNET__
3
3
 
4
4
  #include <string>
5
- #include <vector>
6
- #include <array>
7
- #include <utility>
8
-
9
- #include <opencv2/dnn.hpp>
10
-
5
+ #include <opencv2/objdetect/face.hpp>
6
+ #include <opencv2/core.hpp>
11
7
 
12
8
  class YuNet
13
9
  {
14
10
 
15
11
  public:
16
- typedef std::array<cv::Point, 5> Landmark;
17
- typedef std::pair<cv::Rect, Landmark> Face;
18
-
19
- private:
20
- static constexpr int MODEL_WIDTH = 512;
21
- static constexpr float CONF_THRESHOLD = 0.4f;
22
- static constexpr float NMS_THRESHOLD = 0.3f;
23
-
24
- const std::vector<float> VARIANCES = { 0.1f, 0.2f };
25
- const std::vector<int> STEPS = { 8, 16, 32, 64 };
26
- const std::vector<std::vector<int>> MIN_SIZES = {
27
- { 10, 16, 24 }, { 32, 48 }, { 64, 96 }, { 128, 192, 256 } };
12
+ YuNet(const std::string& model_path,
13
+ const cv::Size& input_size = cv::Size(320, 320),
14
+ float conf_threshold = 0.6f,
15
+ float nms_threshold = 0.3f,
16
+ int top_k = 5000,
17
+ int backend_id = cv::dnn::DNN_BACKEND_OPENCV,
18
+ int target_id = cv::dnn::DNN_TARGET_CPU)
19
+ {
20
+ model = cv::FaceDetectorYN::create(model_path, "", input_size,
21
+ conf_threshold,
22
+ nms_threshold, top_k,
23
+ backend_id, target_id);
24
+ }
28
25
 
29
- public:
30
- YuNet(const std::string& model);
31
26
  ~YuNet() {};
32
- void process(const cv::Mat& img, std::vector<Face>& faces);
27
+
28
+ void process(const cv::Mat& img, cv::Mat& faces) {
29
+ model->setInputSize(img.size());
30
+ model->detect(img, faces);
31
+ }
33
32
 
34
33
  private:
35
- cv::dnn::Net net;
34
+
35
+ cv::Ptr<cv::FaceDetectorYN> model;
36
36
  };
37
37
 
38
38
  #endif
data/lib/circe/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Circe
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2.1'
3
3
  end
data/lib/circe.rb CHANGED
@@ -3,9 +3,9 @@ class Circe
3
3
  private
4
4
 
5
5
  # Don't know how to do it inside the c extension
6
- DATA_DIR = File.join(__dir__, '..', 'data').freeze
7
- ONNX_YOLO = [ File.join(DATA_DIR, 'yolov5s.onnx'), 640, 640 ]
8
- ONNX_YUNET = [ File.join(DATA_DIR, 'face_detection_yunet_2022mar.onnx') ]
6
+ DATA_DIR = File.join(__dir__, '..', 'data').freeze
7
+ ONNX_YOLO = [ File.join(DATA_DIR, 'yolov8s.onnx'), 480, 640 ]
8
+ ONNX_YUNET = [ File.join(DATA_DIR, 'face_detection_yunet_2023mar.onnx') ]
9
9
 
10
10
  end
11
11
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: circe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stéphane D'Alu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-30 00:00:00.000000000 Z
11
+ date: 2024-07-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2+
14
14
 
@@ -22,14 +22,16 @@ extensions:
22
22
  extra_rdoc_files: []
23
23
  files:
24
24
  - circe.gemspec
25
- - data/face_detection_yunet_2022mar.onnx
26
- - data/yolov5s.onnx
25
+ - data/face_detection_yunet_2023mar.onnx
26
+ - data/yolov5su-sim.onnx
27
+ - data/yolov5su.onnx
28
+ - data/yolov8s-sim.onnx
29
+ - data/yolov8s.onnx
27
30
  - ext/camera_model.h
28
31
  - ext/circe.cpp
29
32
  - ext/extconf.rb
30
33
  - ext/yolo.cpp
31
34
  - ext/yolo.h
32
- - ext/yunet.cpp
33
35
  - ext/yunet.h
34
36
  - lib/circe.rb
35
37
  - lib/circe/version.rb
@@ -52,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
54
  - !ruby/object:Gem::Version
53
55
  version: '0'
54
56
  requirements: []
55
- rubygems_version: 3.4.20
57
+ rubygems_version: 3.5.9
56
58
  signing_key:
57
59
  specification_version: 4
58
60
  summary: Face and object recognition
Binary file
data/ext/yunet.cpp DELETED
@@ -1,132 +0,0 @@
1
- #include <cmath>
2
- #include <string>
3
- #include <vector>
4
- #include <numeric>
5
- #include <algorithm>
6
-
7
- #include <opencv2/dnn.hpp>
8
-
9
- #include "yunet.h"
10
-
11
-
12
- YuNet::YuNet(const std::string& model_filename)
13
- {
14
- net = cv::dnn::readNetFromONNX(model_filename);
15
- net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
16
- net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
17
- }
18
-
19
-
20
- void YuNet::process(const cv::Mat& img, std::vector<YuNet::Face>& faces)
21
- {
22
- /* -- Preparing for image size -- */
23
- cv::Size model_size;
24
- model_size.width = MODEL_WIDTH;
25
- model_size.height = MODEL_WIDTH * img.rows / img.cols;
26
- model_size.height = (model_size.height / 32) * 32;
27
-
28
- std::pair<int32_t, int32_t> feature_map_2th = {
29
- (model_size.height + 1) / 2 / 2,
30
- (model_size.width + 1) / 2 / 2
31
- };
32
-
33
- std::vector<std::pair<int32_t, int32_t>> feature_map_list;
34
- feature_map_list.push_back({ (feature_map_2th.first + 1) / 2 ,
35
- (feature_map_2th.second + 1) / 2 });
36
-
37
- for (int32_t i = 0; i < 3; i++) {
38
- const auto& previous = feature_map_list.back();
39
- feature_map_list.push_back({ (previous.first + 1) / 2 ,
40
- (previous.second + 1) / 2 });
41
- }
42
-
43
- std::vector<std::vector<float>> prior_list;
44
- for (int i = 0; i < static_cast<int32_t>(feature_map_list.size()); i++) {
45
- const auto& min_sizes = MIN_SIZES[i];
46
- const auto& feature_map = feature_map_list[i];
47
- for (int y = 0; y < feature_map.first; y++) {
48
- for (int x = 0; x < feature_map.second; x++) {
49
- for (const auto& min_size : min_sizes) {
50
- float s_kx = static_cast<float>(min_size) / model_size.width;
51
- float s_ky = static_cast<float>(min_size) / model_size.height;
52
- float cx = (x + 0.5f) * STEPS[i] / model_size.width;
53
- float cy = (y + 0.5f) * STEPS[i] / model_size.height;
54
- prior_list.push_back({ cx, cy, s_kx, s_ky });
55
- }
56
- }
57
- }
58
- }
59
-
60
-
61
- /* -- Pre-process -- */
62
- cv::Mat blob;
63
- cv::dnn::blobFromImage(img, blob, 1.0, model_size);
64
-
65
- /* -- Inference -- */
66
- std::vector<cv::Mat> outputs;
67
- net.setInput(blob);
68
- net.forward(outputs, { "conf", "iou", "loc" });
69
-
70
- /* -- Post Process -- */
71
- const cv::Mat& mat_conf = outputs[0];
72
- const cv::Mat& mat_iou = outputs[1];
73
- const cv::Mat& mat_loc = outputs[2];
74
- const cv::Size image_size = img.size();
75
-
76
- // Get score list
77
- std::vector<float> cls_score;
78
- for (int32_t row = 0; row < mat_conf.rows; row++) {
79
- float val = mat_conf.at<float>(cv::Point(1, row));
80
- cls_score.push_back(std::clamp(val, 0.0f, 1.0f));
81
- }
82
-
83
- std::vector<float> iou_score;
84
- for (int32_t row = 0; row < mat_iou.rows; row++) {
85
- float val = mat_conf.at<float>(cv::Point(0, row));
86
- iou_score.push_back(std::clamp(val, 0.0f, 1.0f));
87
- }
88
-
89
- std::vector<float> score;
90
- for (int32_t row = 0; row < mat_conf.rows; row++) {
91
- score.push_back(std::sqrt(cls_score[row] * iou_score[row]));
92
- }
93
-
94
- // All bbox
95
- std::vector<cv::Rect> bbox_all;
96
- for (int row = 0; row < mat_loc.rows; row++) {
97
- float cx = mat_loc.at<float>(cv::Point(0, row));
98
- float cy = mat_loc.at<float>(cv::Point(1, row));
99
- float w = mat_loc.at<float>(cv::Point(2, row));
100
- float h = mat_loc.at<float>(cv::Point(3, row));
101
-
102
- cx = prior_list[row][0] + cx * VARIANCES[0] * prior_list[row][2];
103
- cy = prior_list[row][1] + cy * VARIANCES[0] * prior_list[row][3];
104
- w = prior_list[row][2] * std::exp(w * VARIANCES[0]);
105
- h = prior_list[row][3] * std::exp(h * VARIANCES[1]);
106
-
107
- bbox_all.push_back({
108
- static_cast<int32_t>((cx - w / 2) * image_size.width),
109
- static_cast<int32_t>((cy - h / 2) * image_size.height),
110
- static_cast<int32_t>(w * image_size.width),
111
- static_cast<int32_t>(h * image_size.height) });
112
- }
113
-
114
- // Non-Maximum Suppression
115
- std::vector<int> indices;
116
- cv::dnn::NMSBoxes(bbox_all, score, CONF_THRESHOLD, NMS_THRESHOLD, indices);
117
-
118
- // Get valid bbox and landmark
119
- faces.clear();
120
- for (int idx : indices) {
121
- Landmark landmark; // (landmark is 5 points)
122
- for (int i = 0; i < static_cast<int>(landmark.size()); i++) {
123
- cv::Point& p = landmark[i];
124
- float x = mat_loc.at<float>(cv::Point(4 + i * 2, idx));
125
- float y = mat_loc.at<float>(cv::Point(4 + i * 2 + 1, idx));
126
- p.x = static_cast<int32_t>((prior_list[idx][0] + x * VARIANCES[0] * prior_list[idx][2]) * image_size.width);
127
- p.y = static_cast<int32_t>((prior_list[idx][1] + y * VARIANCES[0] * prior_list[idx][3]) * image_size.height);
128
- }
129
-
130
- faces.push_back({ bbox_all[idx], landmark });
131
- }
132
- }