circe 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
- }