LoginSignup
9
1

More than 1 year has passed since last update.

GoオンリーでGUI上からQRコード読み取りをしてみた

Last updated at Posted at 2022-11-01

はじめに

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のパスを通す必要があるので以下を入力します.

~/.zshrc
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に環境変数を設定し設定を反映させます.

.zshrc
export QT_HOMEBREW=true
source ~/.zshrc

その後, リポジトリをクローンしセットアップを実行します.

go get -v -tags=no_env github.com/therecipe/qt/cmd/...
$(go env GOPATH)/bin/qtsetup

コードを書く

インストールを全て完了したらこのような感じにコードを書きます.

main.go
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コードを読み取ってみましょう.

今回はこんなQRコードを用意しました.
qr.png

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

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1