ZBarを使ってQRコードを読み取る

## 概要
ZBarは、さまざまなプラットフォームでバーコードを読み取るためのオープンソースのライブラリです。UnitV2のサンプルのcode_detectorでZ-Barライブラリが使われています。
ZBarでQRコード読み取りを行ってみましょう。

## 環境構築
構築手順に入る前に、まずPCに以下のバージョンのUbuntuをインストールした環境で、構築しました。

[M5UnitV2のクロスコンパイル環境を構築する手順](https://qiita.com/nnn112358/pbulic/9e15450265b689300216)

```
Ubuntu 22.04.3 LTS(x64)
OpenCV ver 0.4.4
ncnn ver 23102
zbar ver 0.10
```

## 作業フォルダの作成
作業フォルダを作成します。

```
% mkdir z_bar_opencv
% cd z_bar_opencv
```

## プログラムの作成

ZBarとOpenCVでQRコードを読み取るプログラムを作成します。

```
% gedit main.cpp
```

```main.cpp
#include <iomanip>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <zbar.h>

using namespace std;
using namespace cv;
using namespace zbar;

int main(int argc, char **argv) {
    int cam_idx = 0;

    if(argc == 2) {
        cam_idx = atoi(argv[1]);
    }

    VideoCapture cap(cam_idx);
    if(!cap.isOpened()) {
        cerr << "Could not open camera." << endl;
        exit(EXIT_FAILURE);
    }
    if(!cap.set(cv::CAP_PROP_FRAME_WIDTH, 640))
        cout << "camera set width error" << endl;
    if(!cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480))
        cout << "camera set height error" << endl;

    //  zbar scannerを生成
    ImageScanner scanner;

    // zbarのconfig設定
    scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);

    while(cap.isOpened()) {
        // 撮像してグレースケール変換
        cv::Mat frame, frame_gray;
        cap >> frame;
        cvtColor(frame, frame_gray, cv::COLOR_BGR2GRAY);

        // 画像をcv:matからImageクラスへ変換
        Image image(frame_gray.cols, frame_gray.rows, "Y800", (uchar *)(frame_gray.data), frame_gray.cols * frame_gray.rows);

        // 画像をzbarへ入力
        scanner.scan(image);

        int counter = 0;
        for(Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end(); ++symbol) {
            // 読み取り結果をコンソールに表示
            cout << counter << " decoded " << symbol->get_type_name() << " symbol \"" << symbol->get_data() << '"' << endl;
            cout << "Location: (" << symbol->get_location_x(0) << "," << symbol->get_location_y(0) << ")" << endl;
            cout << "Size: " << symbol->get_location_size() << endl;

            // QRコードの位置を描画する
            if(symbol->get_location_size() == 4) {
                // rectangle(frame, Rect(symbol->get_location_x(i), symbol->get_location_y(i), 10, 10), Scalar(255, 128, 0));
                line(frame, Point(symbol->get_location_x(0), symbol->get_location_y(0)), Point(symbol->get_location_x(1), symbol->get_location_y(1)), Scalar(255, 128, 0), 2, 8, 0);
                line(frame, Point(symbol->get_location_x(1), symbol->get_location_y(1)), Point(symbol->get_location_x(2), symbol->get_location_y(2)), Scalar(255, 128, 0), 2, 8, 0);
                line(frame, Point(symbol->get_location_x(2), symbol->get_location_y(2)), Point(symbol->get_location_x(3), symbol->get_location_y(3)), Scalar(255, 128, 0), 2, 8, 0);
                line(frame, Point(symbol->get_location_x(3), symbol->get_location_y(3)), Point(symbol->get_location_x(0), symbol->get_location_y(0)), Scalar(255, 128, 0), 2, 8, 0);
            }

            counter++;
        }
        image.set_data(NULL, 0);
        
        #ifdef __x86_64__
  	imshow("Results", frame);
        waitKey(30);
        #endif
    }

    return 0;
}
```


## CMakeLists.txt ファイルの作成

cmakeでコンパイルするために、CMakeLists.txtを編集します。

```
gedit CMakeLists.txt
```

```CMakeLists.txt
if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
PROJECT(example)
cmake_minimum_required(VERSION 3.5)


if(TARGET_COMPILER STREQUAL "arm")
    message(STATUS "TARGET_COMPILE STREQUAL arm")
    SET(CMAKE_CXX_COMPILER arm-none-linux-gnueabihf-g++)
    SET(CMAKE_C_COMPILER arm-none-linux-gnueabihf-gcc)
    SET(NCNN_INSTALL_DIR /opt/external/ncnn/install/arm/)
    SET(ZBAR_INSTALL_DIR /opt/external/zbar-0.10/build/arm)
    SET(OpenCV_DIR /opt/external/opencv/build/arm)
else()
    message(STATUS "TARGET_COMPILE STREQUAL X64")
    SET(NCNN_INSTALL_DIR /opt/external/ncnn/install/x64/)
    SET(ZBAR_INSTALL_DIR /opt/external/zbar-0.10/build/x64/)
    SET(OpenCV_DIR /opt/external/opencv/build/x64)
endif()

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
set(Target z_bar_opencv)

find_package(OpenCV REQUIRED)
add_executable(${Target} main.cpp)
link_directories(${OpenCV_LIBRARY_DIRS})

include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${OpenCV_INCLUDE_DIRS} )
include_directories(${NCNN_INSTALL_DIR}/include/ncnn)
include_directories(${ZBAR_INSTALL_DIR}/include/)
target_link_libraries(${Target} ${OpenCV_LIBRARIES})
target_link_libraries (${Target} ${NCNN_INSTALL_DIR}/lib/libncnn.a -fopenmp)
target_link_libraries (${Target} ${ZBAR_INSTALL_DIR}/lib/libzbar.so)
```


### ビルド
Ubuntu用のバイナリをcmakeでコンパイルして、生成されたバイナリを実行します。

```
% cmake .
% make
% ./bin/z_bar_opencv 
```

UnitV2(arm環境)で動くバイナリをビルドする場合は、Ubuntu用のバイナリで作成されたキャッシュを削除した上で、以下のコマンドでビルドします。

```
% rm CMakeCache.txt 
% cmake -B build/arm -DTARGET_COMPILE=arm .
% cmake --build build/arm
```
## 作成したプログラム

作成したプログラムは以下のGitHubへ格納しています。

https://github.com/nnn112358/opencv_zbar/tree/main


## 参考資料
この記事を作成するにあたり、以下のウェブサイトを参考にしました。

https://github.com/m5stack/UnitV2Framework/blob/main/main/code_detector.cpp

https://github.com/rportugal/opencv-zbar/