はじめに
Goで使えるOpenCV, GoCVを使ったリアルタイムのQRコードのスキャンをGUIアプリ実現するtherecipe/qt内で実装したので備忘録として残そうと思います.
前提条件
- Go, OpenCVがインストール済みであること
- Webカメラが用意されていること
環境
- OS: macOS 13 Ventura
- Go 1.19
- OpenCV: 4.6.0
- GoCV: v0.31.0
- therecipe/qt: v0.0.0-20200904063919-c0c124a5770d
- gozbar: v0.0.0-20191118235142-b46f682b2cab
インストール
gozbar
gozbarは読み取ったQRを画像に変換し,情報を取り出すライブラリです.
以下のコマンドで簡単に取り込むことが出来ます
go get -u https://github.com/MordFustang21/gozbar
GoCV
go get -u -d gocv.io/x/gocv
さらに, pkgconfigにOpenCVのパスを通す必要があるので以下を入力します.
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/Cellar/opencv/4.6.0/lib/pkgconfig"
export CGO_LDFLAGS="-L/usr/local/Cellar/opencv/4.6.0/lib -lopencv_stitching -lopencv_superres
-lopencv_videostab -lopencv_aruco -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib
-lopencv_dnn_objdetect -lopencv_dpm
-lopencv_face -lopencv_photo -lopencv_fuzzy -lopencv_hfs
-lopencv_img_hash -lopencv_line_descriptor -lopencv_optflow
-lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_stereo
-lopencv_structured_light -lopencv_phase_unwrapping -lopencv_surface_matching
-lopencv_tracking -lopencv_datasets -lopencv_dnn -lopencv_plot
-lopencv_xfeatures2d -lopencv_shape -lopencv_video -lopencv_ml -lopencv_ximgproc -lopencv_calib3d
-lopencv_features2d -lopencv_highgui -lopencv_videoio -lopencv_flann -lopencv_xobjdetect
-lopencv_imgcodecs -lopencv_objdetect -lopencv_xphoto -lopencv_imgproc -lopencv_core"
export CGO_CPPFLAGS="-I/usr/local/Cellar/opencv/4.6.0/include"
次にGoCVのディレクトリに入ってmake install
する必要があるのですが, 自分の環境だとこのようなエラーが出てきました.
cd go/pkg/mod/gocv.io/x/gocv@v0.31.0
sudo make install
(略)
cd: OLDPWD not set.
GoCVのIssuesを見てみると, 環境がMacの場合で発生するらしく, 解決策としてmake
の代わりにgmake
を使わないといけないみたいです.(参考元)
その場合, brew install gmake
でgmakeをインストールしてください. インストールが出来たら以下のコマンドを実行します.
cd go/pkg/mod/gocv.io/x/gocv@v0.31.0
sudo gmake install
gmakeでのインストールが完了したら,
go run go/pkg/mod/gocv.io/x/gocv@v0.31.0/cmd/version/main.go
を実行してインストールできているか確認してください. 正常にインストールされていれば以下が表示されると思います.
gocv version: 0.31.0
opencv lib version: 4.6.0
therecipe/qt
brew install qt
インストール完了後, .zshrc
に環境変数を設定し設定を反映させます.
export QT_HOMEBREW=true
source ~/.zshrc
その後, リポジトリをクローンしセットアップを実行します.
go get -v -tags=no_env github.com/therecipe/qt/cmd/...
$(go env GOPATH)/bin/qtsetup
コードを書く
インストールを全て完了したらこのような感じにコードを書きます.
package main
import (
"log"
"os"
"github.com/mordfustang21/gozbar"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/widgets"
"gocv.io/x/gocv"
)
func QRScan() string {
webcam, _ := gocv.VideoCaptureDevice(0)
img := gocv.NewMat()
var qrinfo string
for {
webcam.Read(&img)
if img.Empty() {
continue
}
qr, _ := img.ToImage()
qrimg := gozbar.FromImage(qr)
//ScanしたQRコードを画像に変換し,それを取り込む
s := gozbar.NewScanner()
err := s.SetConfig(gozbar.QRCODE, gozbar.CFG_ENABLE, 1)
if err != nil {
log.Fatal("error setting config", err)
}
//ScanするためのConfig
res := s.Scan(qrimg)
//Scan
if res == nil { //Scanできた場合,結果を表示(resはerror handling!)
qrimg.First().Each(func(r string) {
//fmt.Println(r)
qrinfo = r
})
webcam.Close()
s.Destroy()
return qrinfo
}
}
}
func main() {
// QWidgetsをスタートさせるために必要な処理
app := widgets.NewQApplication(len(os.Args), os.Args)
// ウィンドウ生成
window := widgets.NewQMainWindow(nil, 0)
window.SetMinimumSize2(360, 420)
window.SetWindowTitle("QR Reader")
window.SetWindowFlags(core.Qt__CustomizeWindowHint | core.Qt__WindowCloseButtonHint | core.Qt__WindowMinimizeButtonHint)
//最大化ボタン無効化
//window.SetAttribute(core.Qt__WA_DeleteOnClose, true)
// ウィジェットを作成し,NewQVBoxLayoutを使ってレイアウトを作成
// ウィンドウの中央にウィジェットを配置
widget := widgets.NewQWidget(nil, 0)
widget.SetLayout(widgets.NewQVBoxLayout())
window.SetCentralWidget(widget)
// 文字を表示させるラベルの生成,文字入力フォームの作成,表の作成
label := widgets.NewQLabel2("now:", nil, 0)
widget.Layout().AddWidget(label)
// ボタン生成,QRコード読み取り処理を行う
button := widgets.NewQPushButton2("QR Scan", nil)
widget.Layout().AddWidget(button)
button.ConnectClicked(func(bool) {
res := QRScan()
label.SetText("now:" + res)
})
// ウィンドウ表示
window.Show()
// Qtのループを開始,app.Exit()が呼ばれるかユーザによって終了されるまで継続
app.Exec()
}
その後, go.modを作成します. 以下のような流れで作成してください.
qrという名前は各自で変更してください.
go mod init qr
go mod tidy
コードが書けたので,文字をQRコード化するサイトから生成したQRコードを読み取ってみましょう.
go run main.go
実行すると以下のウインドウが出現し, QR ScanをクリックするとWebカメラを検出しスキャンを開始します.
Macの場合,カメラを使用するかのダイアログが出てくるので必ず許可してください.
Webカメラのランプが点灯したらスキャン可能という合図なので,スマートフォンなどでQRを読み取ります.
QRを検出すると, now: に書かれてる部分に文字が出力されます.
しっかりと出力されましたね!
余談
本来,IMShowを用いてカメラ画面を表示させたかったのですが,
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSWindow drag regions should only be invalidated on the Main Thread!'
*** First throw call stack:
(
0 CoreFoundation 0x00007ff80ecf1c3b __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007ff80e843e25 objc_exception_throw + 48
2 CoreFoundation 0x00007ff80ed19ea6 _CFBundleGetValueForInfoKey + 0
3 AppKit 0x00007ff811da66b6 -[NSWindow(NSWindow_Theme) _postWindowNeedsToResetDragMarginsUnlessPostingDisabled] + 307
4 AppKit 0x00007ff811d9323e -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1247
5 AppKit 0x00007ff811d92d58 -[NSWindow initWithContentRect:styleMask:backing:defer:] + 42
6 AppKit 0x00007ff8120638dd -[NSWindow initWithContentRect:styleMask:backing:defer:screen:] + 50
7 libopencv_highgui.4.6.0.dylib 0x0000000004f87c50 cvNamedWindow + 344
8 libopencv_highgui.4.6.0.dylib 0x0000000004f82742 _ZN2cv11namedWindowERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEi + 1245
9 QR_Reader 0x0000000004379f8c Window_New + 44
10 QR_Reader 0x0000000004068fe4 runtime.asmcgocall.abi0 + 100
)
libc++abi: terminating with uncaught exception of type NSException
SIGABRT: abort
PC=0x7ff80eb6630e m=7 sigcode=0
signal arrived during cgo executio
と表示されてしまい,実装することができませんでした.
IMShowを使用した時のみこの画面が表示されたのでOpenCV側から弾かれてしまった感じですね.
あとがき
導入にかなり時間がかかったライブラリたちですが,いざ導入が完了すればさまざまな用途で使えるのでしばらくはGoオンリーでもやっていけそうです.
これを活用すれば勤怠管理や入場管理をするアプリケーションを開発できるかもしれませんね!
参考:
therecipe/qt を用いて Go から GUI を利用する方法(Windows/Mac)
MordFustang21/gozbar
GoCV issues#348
GoCV Getting Started(macOS)
GoCV Hello-Video