OpenCV4.5.3をGoから呼び出してQRCode読み取りを実装した

2021-09-14

はじめに

やっと…長いビルドが終わり…やっとだぜ…やっと動かして遊べる… OpenCV4.5.3をGoCVから呼び出して動かしてみましょう。

まずはgocvでQRCodeを読み出して文字列を取得するまでをやります。

インストールやらはこちらの記事を参考にしてください。 Raspberry piで環境作ってやってますけど、WindowsやMacでもコードは共通なはずです。

Raspberry piにGoとgocv(OpenCV 4)環境をインストールする

Raspberry pi にOpenCV 4.5.3のビルドで100%になったけど進まなくなったときの対処

Hello,GoCV

公式サイトに書かれているコードを書き出します。

package main

import (
    "gocv.io/x/gocv"
)

func main() {
    webcam, _ := gocv.OpenVideoCapture(0)
    window := gocv.NewWindow("Hello")
    img := gocv.NewMat()

    for {
        webcam.Read(&img)
        window.IMShow(img)
        window.WaitKey(1)
    }
}

SSHで接続していたのですが、ERRORがでます。 これはGUIが無いのにカメラの映像を出そうとしてるからですね。 実行してみましょう。

go run readQRCode.go
[ WARN:0] global /tmp/opencv/opencv-4.5.3/modules/videoio/src/cap_gstreamer.cpp (1081) open OpenCV | GStreamer warning: Cannot query video position: status=1, value=1, duration=-1
Unable to init server: Could not connect: Connection refused
terminate called after throwing an instance of 'cv::Exception'
  what():  OpenCV(4.5.3) /tmp/opencv/opencv-4.5.3/modules/highgui/src/window_gtk.cpp:635: error: (-2:Unspecified error) Can't initialize GTK backend in function 'cvInitSystem'

SIGABRT: abort
PC=0x7496ef14 m=0 sigcode=4294967290
signal arrived during cgo execution

goroutine 1 [syscall, locked to thread]:
runtime.cgocall(0xd3df0, 0x1047f58)
        /usr/local/go/src/runtime/cgocall.go:156 +0x5c fp=0x1047f40 sp=0x1047f28 pc=0x3fba0
gocv.io/x/gocv._Cfunc_Window_New(0xff4b88, 0x0)
        _cgo_gotypes.go:7582 +0x2c fp=0x1047f54 sp=0x1047f40 pc=0xc9044
gocv.io/x/gocv.NewWindow({0x1168e3, 0x5})
        /home/pi/work/pkg/mod/gocv.io/x/gocv@v0.28.0/highgui.go:39 +0x74 fp=0x1047f70 sp=0x1047f54 pc=0xc9320
main.main()
        /home/pi/src/QRCode/readQRCode.go:12 +0x50 fp=0x1047fbc sp=0x1047f70 pc=0xc9e40
runtime.main()
        /usr/local/go/src/runtime/proc.go:255 +0x278 fp=0x1047fe4 sp=0x1047fbc pc=0x732f8
runtime.goexit()
        /usr/local/go/src/runtime/asm_arm.s:838 +0x4 fp=0x1047fe4 sp=0x1047fe4 pc=0x9dfd4

trap    0x0
error   0x0
oldmask 0x0
r0      0x0
r1      0x7ed57654
r2      0x0
r3      0x8
r4      0x76fc3968
r5      0x6
r6      0x7ed57654
r7      0xaf
r8      0xd9419c
r9      0xd941b8
r10     0xd941d0
fp      0xff67a8
ip      0x0
sp      0x7ed57650
lr      0xffffffff
pc      0x7496ef14
cpsr    0x10
fault   0x0
exit status 2

リモートデスクトップでつなぎ直して実行すると映像が取得できるはずです。 GUIがあるとこんな風に表示されます。

これで動いていることは確認出来ました。 からかい上手の高木さん 12巻がでましたね。 癒やされる。

QRCodeを生成するコードを作ります

QRCodeの生成するコードを書きましょう。 Goでパッケージ作ってくれている人がいるのでくっそ簡単です。

go get github.com/boombuler/barcode  
package main

import (
    "image/png"
    "os"

    "github.com/boombuler/barcode"
    "github.com/boombuler/barcode/qr"
)

func main() {
    // Create the barcode
    qrCode, _ := qr.Encode("ちんこちんこちーんこビーム", qr.M, qr.Auto)

    // Scale the barcode to 200x200 pixels
    qrCode, _ = barcode.Scale(qrCode, 200, 200)

    // create the output file
    file, _ := os.Create("qrcode.webp")
    defer file.Close()

    // encode the barcode as png
    png.Encode(file, qrCode)
}

生成されたのがこれです。

どうかしてるとしか思えないですがまぁいいでしょう。

QRCodeを読み取るコードを作成

gocvのサンプルみたいなものはネット上に転がっていないので、PythonやC++で書かれたOpenCVのコードを参考に組み立てていきます。

points := gocv.NewMat()
straight_qrcode := gocv.NewMat()

実行結果を取得するMat型を作るときはこうやって書きます。

var points gocv.Mat

こんな風に書くと、C言語で書かれたOpenCVを呼び出すときにNull参照になって落ちます。

カメラ画像をinputに検出して、QRコードの文字列にDecodeしています。

qr.Detect(img, &points)
strtmp = qr.Decode(img, points, &straight_qrcode)

やっていることはこれだけです。

コード全体はこうなりました。

package main

import (
    "fmt"

    "gocv.io/x/gocv"
)

func main() {
    webcam, _ := gocv.OpenVideoCapture(0)
    window := gocv.NewWindow("Hello")
    img := gocv.NewMat()

    qr := gocv.NewQRCodeDetector()

    fmt.Println("QRCode Read")

    points := gocv.NewMat()
    straight_qrcode := gocv.NewMat()

    var strtmp string
    for {
        webcam.Read(&img)
        window.IMShow(img)
        qr.Detect(img, &points)
        strtmp = qr.Decode(img, points, &straight_qrcode)

        // strtmp := qr.DetectAndDecode(img, &points, &straight_qrcode)

        if len(strtmp) > 0 {
            fmt.Println(strtmp)
        }
    }
}

実行結果

QRコードをかざしたら文字列が取得できると思います。

まとめ

QRCodeの読み取りと結果の習得は、秒速でしたわ。ガハハ。 Raspberry piで動かしてもそこそこ動くし、Pythonで書いたようなソフトよりも体感早い気がします。 良いんじゃないでしょうか。 今日はこんなところですかね。