#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ヶ月空いてしまった