-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathobjdetect.zig
507 lines (434 loc) · 18.2 KB
/
objdetect.zig
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
const std = @import("std");
const c = @import("c_api.zig");
const core = @import("core.zig");
const utils = @import("utils.zig");
const assert = std.debug.assert;
const epnn = utils.ensurePtrNotNull;
const Mat = core.Mat;
const Rect = core.Rect;
const Rects = core.Rects;
const Size = core.Size;
/// CascadeClassifier is a cascade classifier class for object detection.
///
/// For further details, please see:
/// http://docs.opencv.org/master/d1/de5/classcv_1_1CascadeClassifier.html
///
pub const CascadeClassifier = struct {
ptr: c.CascadeClassifier,
const Self = @This();
pub fn init() !Self {
var ptr = c.CascadeClassifier_New();
ptr = try epnn(ptr);
return Self{ .ptr = ptr };
}
pub fn deinit(self: *Self) void {
assert(self.ptr != null);
_ = c.CascadeClassifier_Close(self.ptr);
self.*.ptr = null;
}
/// Load cascade classifier from a file.
///
/// For further details, please see:
/// http://docs.opencv.org/master/d1/de5/classcv_1_1CascadeClassifier.html#a1a5884c8cc749422f9eb77c2471958bc
pub fn load(self: *Self, name: []const u8) !void {
const result = c.CascadeClassifier_Load(self.ptr, @as([*]const u8, @ptrCast(name)));
if (result == 0) {
return error.CascadeClassifierLoadFailed;
}
}
/// DetectMultiScale detects objects of different sizes in the input Mat image.
/// The detected objects are returned as a slice of image.Rectangle structs.
///
/// For further details, please see:
/// http://docs.opencv.org/master/d1/de5/classcv_1_1CascadeClassifier.html#aaf8181cb63968136476ec4204ffca498
///
pub fn detectMultiScale(self: Self, img: Mat, allocator: std.mem.Allocator) !Rects {
const rec: c.struct_Rects = c.CascadeClassifier_DetectMultiScale(self.ptr, img.ptr);
defer Rect.deinitRects(rec);
return try Rect.toArrayList(rec, allocator);
}
/// DetectMultiScaleWithParams calls DetectMultiScale but allows setting parameters
/// to values other than just the defaults.
///
/// For further details, please see:
/// http://docs.opencv.org/master/d1/de5/classcv_1_1CascadeClassifier.html#aaf8181cb63968136476ec4204ffca498
///
pub fn detectMultiScaleWithParams(
self: Self,
img: Mat,
scale: f64,
min_neighbors: i32,
flags: i32,
min_size: Size,
max_size: Size,
allocator: std.mem.Allocator,
) !Rects {
const rec: c.struct_Rects = c.CascadeClassifier_DetectMultiScaleWithParams(
self.ptr,
img.ptr,
scale,
min_neighbors,
flags,
min_size.toC(),
max_size.toC(),
);
defer Rect.deinitRects(rec);
return try Rect.toArrayList(rec, allocator);
}
};
/// HOGDescriptor is a Histogram Of Gradiants (HOG) for object detection.
///
/// For further details, please see:
/// https://docs.opencv.org/master/d5/d33/structcv_1_1HOGDescriptor.html#a723b95b709cfd3f95cf9e616de988fc8
///
pub const HOGDescriptor = struct {
ptr: c.HOGDescriptor,
const Self = @This();
pub fn init() !Self {
var ptr = c.HOGDescriptor_New();
ptr = try epnn(ptr);
return .{ .ptr = ptr };
}
pub fn deinit(self: *Self) void {
assert(self.ptr != null);
c.HOGDescriptor_Close(self.ptr);
self.*.ptr = null;
}
pub fn load(self: *Self, name: []const u8) !void {
const result = c.HOGDescriptor_Load(self.ptr, utils.castZigU8ToC(name));
if (result == 0) {
return error.HOGDescriptorLoadFailed;
}
}
/// DetectMultiScale detects objects in the input Mat image.
/// The detected objects are returned as a slice of image.Rectangle structs.
///
/// For further details, please see:
/// https://docs.opencv.org/master/d5/d33/structcv_1_1HOGDescriptor.html#a660e5cd036fd5ddf0f5767b352acd948
///
pub fn detectMultiScale(self: Self, img: Mat, allocator: std.mem.Allocator) !Rects {
const rec: c.struct_Rects = c.HOGDescriptor_DetectMultiScale(self.ptr, img.ptr);
defer Rect.deinitRects(rec);
return try Rect.toArrayList(rec, allocator);
}
/// DetectMultiScaleWithParams calls DetectMultiScale but allows setting parameters
/// to values other than just the defaults.
///
/// For further details, please see:
/// https://docs.opencv.org/master/d5/d33/structcv_1_1HOGDescriptor.html#a660e5cd036fd5ddf0f5767b352acd948
///
pub fn detectMultiScaleWithParams(
self: Self,
img: Mat,
hit_thresh: f64,
win_stride: Size,
padding: Size,
scale: f64,
final_threshold: f64,
use_meanshift_grouping: bool,
allocator: std.mem.Allocator,
) !Rects {
const rec: c.struct_Rects = c.HOGDescriptor_DetectMultiScaleWithParams(
self.ptr,
img.ptr,
hit_thresh,
win_stride.toC(),
padding.toC(),
scale,
final_threshold,
use_meanshift_grouping,
);
defer Rect.deinitRects(rec);
return try Rect.toArrayList(rec, allocator);
}
/// HOGDefaultPeopleDetector returns a new Mat with the HOG DefaultPeopleDetector.
///
/// For further details, please see:
/// https://docs.opencv.org/master/d5/d33/structcv_1_1HOGDescriptor.html#a660e5cd036fd5ddf0f5767b352acd948
///
pub fn getDefaultPeopleDetector() !Mat {
return Mat.initFromC(c.HOG_GetDefaultPeopleDetector());
}
/// SetSVMDetector sets the data for the HOGDescriptor.
///
/// For further details, please see:
/// https://docs.opencv.org/master/d5/d33/structcv_1_1HOGDescriptor.html#a09e354ad701f56f9c550dc0385dc36f1
///
pub fn setSVMDetector(self: Self, det: Mat) void {
_ = c.HOGDescriptor_SetSVMDetector(self.ptr, det.ptr);
}
};
/// GroupRectangles groups the object candidate rectangles.
///
/// For further details, please see:
/// https://docs.opencv.org/4.x/de/de1/group__objdetect__common.html#ga3dba897ade8aa8227edda66508e16ab9
///
pub fn groupRectangles(rects: []const Rect, group_threshold: i32, eps: f64, allocator: std.mem.Allocator) !Rects {
if (group_threshold < -1) return error.InvalidGroupThreshold;
var c_rects_array = try allocator.alloc(c.Rect, rects.len);
defer allocator.free(c_rects_array);
for (rects, 0..) |rect, i| c_rects_array[i] = rect.toC();
const c_rects = c.Rects{
.rects = @as([*]c.Rect, @ptrCast(c_rects_array.ptr)),
.length = @as(i32, @intCast(c_rects_array.len)),
};
const result_c_rects = c.GroupRectangles(c_rects, group_threshold, eps);
defer Rect.deinitRects(result_c_rects);
return Rect.toArrayList(result_c_rects, allocator);
}
/// QRCodeDetector groups the object candidate rectangles.
///
/// For further details, please see:
/// https://docs.opencv.org/master/de/dc3/classcv_1_1QRCodeDetector.html
///
pub const QRCodeDetector = struct {
ptr: c.QRCodeDetector,
const Self = @This();
pub fn init() !Self {
var ptr = c.QRCodeDetector_New();
ptr = try epnn(ptr);
return .{ .ptr = ptr };
}
pub fn deinit(self: *Self) void {
assert(self.ptr != null);
c.QRCodeDetector_Close(self.ptr);
self.*.ptr = null;
}
/// DetectAndDecode Both detects and decodes QR code.
///
/// Returns true as long as some QR code was detected even in case where the decoding failed
/// For further details, please see:
/// https://docs.opencv.org/master/de/dc3/classcv_1_1QRCodeDetector.html#a7290bd6a5d59b14a37979c3a14fbf394
///
pub fn detectAndDecode(self: Self, input: Mat, points: *Mat, straight_qrcode: *Mat) []const u8 {
const result = c.QRCodeDetector_DetectAndDecode(self.ptr, input.ptr, points.*.ptr, straight_qrcode.*.ptr);
return std.mem.span(result);
}
/// Detect detects QR code in image and returns the quadrangle containing the code.
///
/// For further details, please see:
/// https://docs.opencv.org/master/de/dc3/classcv_1_1QRCodeDetector.html#a64373f7d877d27473f64fe04bb57d22b
///
pub fn detect(self: Self, input: Mat, points: *Mat) bool {
return c.QRCodeDetector_Detect(self.ptr, input.ptr, points.*.ptr);
}
/// Decode decodes QR code in image once it's found by the detect() method. Returns UTF8-encoded output string or empty string if the code cannot be decoded.
///
/// For further details, please see:
/// https://docs.opencv.org/master/de/dc3/classcv_1_1QRCodeDetector.html#a4172c2eb4825c844fb1b0ae67202d329
///
pub fn decode(self: Self, input: Mat, points: Mat, straight_qrcode: *Mat) []const u8 {
const result = c.QRCodeDetector_Decode(self.ptr, input.ptr, points.ptr, straight_qrcode.*.ptr);
return std.mem.span(result);
}
/// Detects QR codes in image and finds of the quadrangles containing the codes.
///
/// Each quadrangle would be returned as a row in the `points` Mat and each point is a Vecf.
/// Returns true if QR code was detected
/// For usage please see TestQRCodeDetector
/// For further details, please see:
/// https://docs.opencv.org/master/de/dc3/classcv_1_1QRCodeDetector.html#aaf2b6b2115b8e8fbc9acf3a8f68872b6
pub fn detectMulti(self: Self, input: Mat, points: *Mat) bool {
return c.QRCodeDetector_DetectMulti(self.ptr, input.ptr, points.*.ptr);
}
/// Decode decodes QR code in image once it's found by the detect() method. Returns UTF8-encoded output string or empty string if the code cannot be decoded.
///
/// For further details, please see:
/// https://docs.opencv.org/master/de/dc3/classcv_1_1QRCodeDetector.html#a4172c2eb4825c844fb1b0ae67202d329
///
pub fn detectAndDecodeMulti(
self: Self,
input: Mat,
allocator: std.mem.Allocator,
) !struct {
is_detected: bool,
decoded: []const []const u8,
qr_codes: []Mat,
points: Mat,
arena: std.heap.ArenaAllocator,
pub fn deinit(self_: *@This()) void {
self_.arena.deinit();
}
} {
var arena = std.heap.ArenaAllocator.init(allocator);
var arena_allocator = arena.allocator();
var c_decoded: c.CStrings = undefined;
var c_qr_codes: c.Mats = undefined;
defer c.CStrings_Close(c_decoded);
var points = try Mat.init();
const result = c.QRCodeDetector_DetectAndDecodeMulti(
self.ptr,
input.toC(),
&c_decoded,
points.toC(),
&c_qr_codes,
);
var decoded = try arena_allocator.alloc([]const u8, @as(usize, @intCast(c_decoded.length)));
var qr_codes = try arena_allocator.alloc(Mat, @as(usize, @intCast(c_qr_codes.length)));
if (result) {
for (decoded, 0..) |*item, i| {
item.* = try arena_allocator.dupe(u8, std.mem.span(c_decoded.strs[i]));
}
for (qr_codes, 0..) |*item, i| {
item.* = try Mat.initFromC(c_qr_codes.mats[i]);
}
}
return .{
.is_detected = result,
.decoded = decoded,
.qr_codes = qr_codes,
.points = points,
.arena = arena,
};
}
};
const testing = std.testing;
const imgcodecs = @import("imgcodecs.zig");
test "objdetect CascadeClassifier" {
var img = try imgcodecs.imRead("libs/gocv/images/face.jpg", .color);
defer img.deinit();
try testing.expectEqual(false, img.isEmpty());
var classifier = try CascadeClassifier.init();
defer classifier.deinit();
try classifier.load("./libs/gocv/data/haarcascade_frontalface_default.xml");
var rects = try classifier.detectMultiScale(img, testing.allocator);
defer rects.deinit();
try testing.expectEqual(@as(usize, 1), rects.items.len);
}
test "objdetect CascadeClassifierWithParams" {
var img = try imgcodecs.imRead("libs/gocv/images/face.jpg", .color);
defer img.deinit();
try testing.expectEqual(false, img.isEmpty());
var classifier = try CascadeClassifier.init();
defer classifier.deinit();
try classifier.load("libs/gocv/data/haarcascade_frontalface_default.xml");
var rects = try classifier.detectMultiScaleWithParams(img, 1.1, 3, 0, Size.init(0, 0), Size.init(0, 0), testing.allocator);
defer rects.deinit();
try testing.expectEqual(@as(usize, 1), rects.items.len);
}
test "objdetect HOGDescriptor" {
var img = try imgcodecs.imRead("libs/gocv/images/face.jpg", .color);
defer img.deinit();
try testing.expectEqual(false, img.isEmpty());
var hog = try HOGDescriptor.init();
defer hog.deinit();
var d: Mat = try HOGDescriptor.getDefaultPeopleDetector();
defer d.deinit();
hog.setSVMDetector(d);
var rects = try hog.detectMultiScale(img, testing.allocator);
defer rects.deinit();
try testing.expectEqual(@as(usize, 1), rects.items.len);
}
test "objdetect HOGDescriptorWithParams" {
var img = try imgcodecs.imRead("libs/gocv/images/face.jpg", .color);
defer img.deinit();
try testing.expectEqual(false, img.isEmpty());
var hog = try HOGDescriptor.init();
defer hog.deinit();
var d: Mat = try HOGDescriptor.getDefaultPeopleDetector();
defer d.deinit();
hog.setSVMDetector(d);
var rects = try hog.detectMultiScaleWithParams(
img,
0,
Size.init(0, 0),
Size.init(0, 0),
1.05,
2.0,
false,
testing.allocator,
);
defer rects.deinit();
try testing.expectEqual(@as(usize, 1), rects.items.len);
}
test "objdetect groupRectangles" {
var rects = [_]Rect{
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 20, 20),
Rect.init(10, 10, 25, 25),
Rect.init(10, 10, 25, 25),
Rect.init(10, 10, 25, 25),
Rect.init(10, 10, 25, 25),
Rect.init(10, 10, 25, 25),
Rect.init(10, 10, 25, 25),
Rect.init(10, 10, 25, 25),
Rect.init(10, 10, 25, 25),
Rect.init(10, 10, 25, 25),
Rect.init(10, 10, 25, 25),
};
var results = try groupRectangles(rects[0..], 1, 0.1, testing.allocator);
defer results.deinit();
try testing.expectEqual(@as(usize, 2), results.items.len);
}
test "objdetect QRCodeDetector" {
var img = try imgcodecs.imRead("libs/gocv/images/qrcode.png", .color);
try testing.expectEqual(false, img.isEmpty());
defer img.deinit();
var detector = try QRCodeDetector.init();
defer detector.deinit();
var bbox = try Mat.init();
defer bbox.deinit();
var qr = try Mat.init();
defer qr.deinit();
var res = detector.detect(img, &bbox);
try testing.expectEqual(true, res);
const res2 = detector.decode(img, bbox, &qr);
const res3 = detector.detectAndDecode(img, &bbox, &qr);
try testing.expectEqualStrings(res2, res3);
}
test "objdetect Multi QRCodeDetector" {
var img = try imgcodecs.imRead("libs/gocv/images/multi_qrcodes.png", .color);
try testing.expectEqual(false, img.isEmpty());
defer img.deinit();
var detector = try QRCodeDetector.init();
defer detector.deinit();
var mbox = try Mat.init();
defer mbox.deinit();
var qr = try Mat.init();
defer qr.deinit();
var res = detector.detectMulti(img, &mbox);
try testing.expectEqual(true, res);
try testing.expectEqual(@as(i32, 2), mbox.rows());
var res2 = try detector.detectAndDecodeMulti(img, testing.allocator);
defer res2.deinit();
try testing.expectEqual(true, res2.is_detected);
try testing.expectEqual(@as(usize, 2), res2.decoded.len);
testing.expectEqualStrings("foo", res2.decoded[0]) catch {
try testing.expectEqualStrings("bar", res2.decoded[0]);
try testing.expectEqualStrings("foo", res2.decoded[1]);
return;
};
try testing.expectEqualStrings("bar", res2.decoded[1]);
}
//* implementation done
//* pub const CascadeClassifier = ?*anyopaque;
//* pub const HOGDescriptor = ?*anyopaque;
//* pub const QRCodeDetector = ?*anyopaque;
//* pub extern fn CascadeClassifier_New(...) CascadeClassifier;
//* pub extern fn CascadeClassifier_Close(cs: CascadeClassifier) void;
//* pub extern fn CascadeClassifier_Load(cs: CascadeClassifier, name: [*c]const u8) c_int;
//* pub extern fn CascadeClassifier_DetectMultiScale(cs: CascadeClassifier, img: Mat) struct_Rects;
//* pub extern fn CascadeClassifier_DetectMultiScaleWithParams(cs: CascadeClassifier, img: Mat, scale: f64, minNeighbors: c_int, flags: c_int, minSize: Size, maxSize: Size) struct_Rects;
//* pub extern fn HOGDescriptor_New(...) HOGDescriptor;
//* pub extern fn HOGDescriptor_Close(hog: HOGDescriptor) void;
//* pub extern fn HOGDescriptor_Load(hog: HOGDescriptor, name: [*c]const u8) c_int;
//* pub extern fn HOGDescriptor_DetectMultiScale(hog: HOGDescriptor, img: Mat) struct_Rects;
//* pub extern fn HOGDescriptor_DetectMultiScaleWithParams(hog: HOGDescriptor, img: Mat, hitThresh: f64, winStride: Size, padding: Size, scale: f64, finalThreshold: f64, useMeanshiftGrouping: bool) struct_Rects;
//* pub extern fn HOG_GetDefaultPeopleDetector(...) Mat;
//* pub extern fn HOGDescriptor_SetSVMDetector(hog: HOGDescriptor, det: Mat) void;
//* pub extern fn GroupRectangles(rects: struct_Rects, groupThreshold: c_int, eps: f64) struct_Rects;
//* pub extern fn QRCodeDetector_New(...) QRCodeDetector;
//* pub extern fn QRCodeDetector_DetectAndDecode(qr: QRCodeDetector, input: Mat, points: Mat, straight_qrcode: Mat) [*c]const u8;
//* pub extern fn QRCodeDetector_Detect(qr: QRCodeDetector, input: Mat, points: Mat) bool;
//* pub extern fn QRCodeDetector_Decode(qr: QRCodeDetector, input: Mat, inputPoints: Mat, straight_qrcode: Mat) [*c]const u8;
//* pub extern fn QRCodeDetector_Close(qr: QRCodeDetector) void;
//* pub extern fn QRCodeDetector_DetectMulti(qr: QRCodeDetector, input: Mat, points: Mat) bool;
//* pub extern fn QRCodeDetector_DetectAndDecodeMulti(qr: QRCodeDetector, input: Mat, decoded: [*c]CStrings, points: Mat, mats: [*c]struct_Mats) bool;