📸

ZigのOpenCVライブラリ「zigcv」を作っている

2022/08/28に公開

zigcv

はじめに

以前、Zig の Tensorflow Lite のライブラリを使って遊ぶ記事を書いた。

https://zenn.dev/ryoppippi/articles/4f4716118cc06b

この記事内では、静止画の処理を行うために、STB ライブラリを用いた。

こうなると次は動画の処理を行いたくなるものだ。

... ならば OpenCV を Zig から使えるようにしようではないか。

https://github.com/ryoppippi/zigcv

OpenCV の C バインディングを作る

OpenCV は C++で書かれているので、Zig から関数を呼び出すには C バインディングを作る必要がある。
1から OpenCV の C バインディングを作るのは大変なので、
今回は同じ手法で C バインディングを実装して呼び出している gocv のコードを利用させていただくことにした。
これで、工数の半分以上が省けることになる。

https://github.com/hybridgroup/gocv

Zig バインディングを作る

この記事の執筆中ではまだ完成はしていない。
しかし、Web カメラの画像を取得して文字や図を書き込んだり、画像をファイルから読み込んで加工、保存をすることはできる。
また、DNN のモデルを呼び出し推論を行うこともできるようになった。

Web カメラから画像を取得し、顔が含まれているならば Blur 処理をするコード
const std = @import("std");
const cv = @import("zigcv");

pub fn main() anyerror!void {
    var allocator = std.heap.page_allocator;
    var args = try std.process.argsWithAllocator(allocator);
    defer args.deinit();
    const prog = args.next();
    const device_id_char = args.next() orelse {
        std.log.err("usage: {s} [cameraID]", .{prog.?});
        std.os.exit(1);
    };
    const device_id = try std.fmt.parseUnsigned(c_int, device_id_char, 10);

    // open webcam
    var webcam = try cv.VideoCapture.init();
    try webcam.openDevice(device_id);
    defer webcam.deinit();

    // open display window
    const window_name = "Face Detect";
    var window = try cv.Window.init(window_name);
    defer window.deinit();

    // prepare image matrix
    var img = try cv.Mat.init();
    defer img.deinit();

    // load classifier to recognize faces
    var classifier = try cv.CascadeClassifier.init();
    defer classifier.deinit();

    classifier.load("./libs/gocv/data/haarcascade_frontalface_default.xml") catch {
        std.debug.print("no xml", .{});
        std.os.exit(1);
    };

    const size = cv.Size{ .width = 75, .height = 75 };
    while (true) {
        webcam.read(&img) catch {
            std.debug.print("capture failed", .{});
            std.os.exit(1);
        };
        if (img.isEmpty()) {
            continue;
        }
        const rects = try classifier.detectMultiScale(img, allocator);
        defer rects.deinit();
        const found_num = rects.items.len;
        std.debug.print("found {d} faces\n", .{found_num});
        for (rects.items) |r| {
            std.debug.print("x:\t{}, y:\t{}, w:\t{}, h:\t{}\n", .{ r.x, r.y, r.width, r.height });
            cv.gaussianBlur(img, &img, size, 0, 0, .{});
        }

        window.imShow(img);
        if (window.waitKey(1) >= 0) {
            break;
        }
    }
}

blur

実用例?

いくつかの実装例は以下においてある。

https://github.com/ryoppippi/zigcv/tree/main/examples

また、mattn 氏が前述の tensorflow Lite ライブラリと組み合わせた例を公開している。

https://github.com/mattn/zig-tflite-example

終わりに

まだまだ開発途中です。
API もまだ不安定ですし、テストも足りていません。
しかし、ある程度は実用できると思います。

Contribution 大歓迎です。
ぜひお待ちしています。

追記 2022/10/06

先日、Core 部分の実装が終わり、0.1.1 をリリースしました!

https://github.com/ryoppippi/zigcv/releases/tag/0.1.1

Discussion