2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

OpenCVを使ってバーコードを読み取ってみた

Posted at

#Background
OpenCV4.5.3が2021年7月にリリースされました。ここ最近はDNNモジュールやG-APIのアップデートが多いのですが、それでもいくつかのマイナーな改変があったりします。今回はなんと意外になかったバーコードリーダーがOpenCV(contrib)で追加されました。
ここでは、設定方法から開発、検出結果まで書いてみようと思います。

#Method
まず最初に下記のリンクをクローンしてファイルをダウンロードします。
https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode
この中にある sr.prototxt sr.caffemodel はモデルとして使います。

次にバーコード読み取り用のオブジェクトを生成します。

    std::string prot_path = "opencv_3rdparty/sr.prototxt";
    std::string model_path = "opencv_3rdparty/sr.caffemodel";

    //バーコード文字列の配列
    std::vector< std::string > decode_info;
    //バーコードタイプの配列 例)EAN_8 EAN_13
    std::vector<cv::barcode::BarcodeType> decoded_type;
    //バーコードを検出した短形の頂点 (row )
    std::vector<cv::Point> corners;

    std::vector< std::vector<cv::Point> > rects;
    
    //検出用のオブジェクト生成
    auto bardet = cv::makePtr<cv::barcode::BarcodeDetector>(prot_path, model_path);

detectAndDecodeを使って画像からバーコードを検出します。
バーコードの数は複数ある場合はその分のデータが配列として格納されています。

    // srcMat:入力画像(in)
    // decode_info:バーコード文字列(out)
    // decoded_type:バーコードタイプ(out)
    // corners:バーコード短形(out)
    bardet->detectAndDecode(srcMat,decode_info,decoded_type,corners);

あとはdecode_infoからバーコードの文字列が入っているのでここから抽出したり、cornersからバーコードを検出した場所の座標点が入っているのでcv:rectangleで四角形を描画すればいいと思います。

#Development

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/barcode.hpp>

/*
 * バーコード検出
 * @param  cv::Mat srcMat 入力画像
 * @return cv::Mat dstMat バーコードをマークした画像
 */
cv::Mat trackBarcode(cv::Mat srcMat){

    cv::Mat dstMat;
    srcMat.copyTo(dstMat);
    std::string prot_path = "opencv_3rdparty/sr.prototxt";
    std::string model_path = "opencv_3rdparty/sr.caffemodel";

    std::vector< std::string > decode_info;
    std::vector<cv::barcode::BarcodeType> decoded_type;
    std::vector<cv::Point> corners;
    std::vector< std::vector<cv::Point> > rects;
    
    //ここでバーコード検出
    auto bardet = cv::makePtr<cv::barcode::BarcodeDetector>(prot_path, model_path);
    bardet->detectAndDecode(srcMat,decode_info,decoded_type,corners);

    if (decode_info.empty()){
            std::cout <<  "バーコードなし" << std::endl;
    } else{
        
        // row:(図形の数) * 4 columns:2 で出力されるので
                // 図形数 * 4 * 2 の 3次元にrectsへ格納
        std::vector<cv::Point> figs;
        for (int i = 0; i < corners.size(); i++){
            figs.push_back(corners[i]);
            if(i % 4 == 3){
                rects.push_back(figs);
                figs.erase(figs.begin(), figs.end());
            }
        }

        for(int i = 0; i < rects.size(); i++){
            //バーコードのある場所に緑で描画
            cv::rectangle(dstMat,
                            rects[i][1],
                            rects[i][3],
                            cv::Scalar(0,255,0),
                            1,
                            cv::LINE_AA);

            std::cout << rects[i] << std::endl;
            std::cout << decode_info[i] << std::endl;

            //バーコード文字列を赤字で描画
            cv::putText(dstMat,
                        decode_info[i],
                        rects[i][0],
                        cv::FONT_HERSHEY_SIMPLEX,
                        0.5, 
                        cv::Scalar(0,0,200), 
                        1, 
                        cv::LINE_AA
                        );

            std::cout << string_barcode_type(decoded_type[i]) << std::endl; 
        }
    }
    return dstMat;
}

int main(int argc, const char* argv[])
{
    std::string name = "Effect";
    cv::VideoCapture cap(0); //カメラを起動
    cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
    cap.set(cv::CAP_PROP_FRAME_HEIGHT, 960);

    cv::namedWindow(name, cv::WINDOW_AUTOSIZE);

    int count = 0;
    while(true){
        cv::Mat frame;
        cap.read(frame); 
        
        //ここで画像処理
        cv::Mat dst = trackBarcode(frame);
        int key = cv::waitKey(5);
        if (key == 27){  //`esc` キー
            break;
        } else if(key == 115){ //`s` キー
            cv::imwrite("output_save_" + std::to_string(count++) + ".png", dst);
        }

        cv::imshow(name, dst);
    }

    cv::destroyAllWindows();    
    return 0;
}


#Consequence

高解像度のカメラであればバーコードの読み取りはできます。Mac内蔵のカメラを使っています。 バーコードが複数あっても読み取れます。 大きすぎるとバーコードの文字列の抽出が不安定になります。 布なのでヨレヨレなのが原因、、、なはず。

PostScript

記事の投稿が3ヶ月空いてしまった:tired_face:

2
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?