cv::QRCodeDetector の使い方【OpenCV/C++】〜QRコードを読み取る〜

OpenCV for C++
📌 準備: OpenCV の環境構築がまだの方はこちら → C++ 環境構築ガイド / C#(OpenCvSharp)セットアップ

cv::QRCodeDetector の使い方【OpenCV/C++】〜QRコードを読み取る〜

cv::QRCodeDetector は OpenCV 標準機能だけで QR コードの検出・デコードができるクラスです。ZBar などの外部ライブラリを追加せずに、detectAndDecode() 1 呼び出しで文字列まで取り出せます。

cv::QRCodeDetector detector;
std::string text = detector.detectAndDecode(img, points);

動作環境

  • OpenCV 4.x(4.5 以降を推奨)
  • コンパイル例:
g++ -std=c++17 main.cpp `pkg-config --cflags --libs opencv4` -o main

環境構築がまだの方は環境構築ガイドを先にどうぞ。


基本の使い方

完全プログラム

#include <iostream>
#include <opencv2/opencv.hpp>

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: ./main <image_path>" << std::endl;
        return 1;
    }

    // 画像の読み込み
    cv::Mat img = cv::imread(argv[1]);
    if (img.empty()) {
        std::cerr << "画像を読み込めませんでした: " << argv[1] << std::endl;
        return 1;
    }

    cv::QRCodeDetector detector;

    // QR コードの検出とデコードを一括実行
    // points: QR コード四隅の座標(InputOutputArray)
    cv::Mat points;
    std::string decoded = detector.detectAndDecode(img, points);

    if (decoded.empty()) {
        std::cout << "QR コードが検出されませんでした" << std::endl;
        return 0;
    }

    std::cout << "デコード結果: " << decoded << std::endl;

    // 検出した領域を描画(points は 1×4 の CV_32FC2)
    if (!points.empty()) {
        // polylines は vector<vector<Point>> を受け取るため変換
        std::vector<cv::Point> contour;
        for (int i = 0; i < 4; ++i) {
            cv::Point2f pt = points.at<cv::Point2f>(0, i);
            contour.emplace_back(static_cast<int>(pt.x), static_cast<int>(pt.y));
        }
        std::vector<std::vector<cv::Point>> contours = {contour};
        cv::polylines(img, contours, true, cv::Scalar(0, 255, 0), 3);
    }

    cv::imshow("QR Result", img);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}

実行結果

デコード結果: https://example.com

ウィンドウには QR コード四隅を結んだ緑色の矩形が重畳描画されます。

コード解説

行のポイント 説明
cv::QRCodeDetector detector; デフォルトコンストラクタで生成するだけで使える
detector.detectAndDecode(img, points) 検出 + デコードを 1 呼び出しで完結。戻り値は std::string
戻り値が空文字 検出失敗または読み取り不可。エラー例外は投げない
points.at<cv::Point2f>(0, i) points は 1×4 の CV_32FC2。型に注意
cv::polylines 閉じた多角形として描画。isClosed=true を渡す

引数と戻り値

detectAndDecode()

引数 型 説明
img InputArray 入力画像(グレースケール・カラー両対応)
points OutputArray QR 四隅の座標(1×4, CV_32FC2)。不要なら cv::noArray()
straight_qrcode OutputArray 射影補正後の QR 画像。省略可(cv::noArray())
戻り値 std::string デコードされた文字列。失敗時は空文字

detect()

引数 型 説明
img InputArray 入力画像
points OutputArray QR 四隅の座標
戻り値 bool 検出できたか

decode()

引数 型 説明
img InputArray 入力画像
points InputArray detect() で得た四隅座標
straight_qrcode OutputArray 射影補正後の QR 画像。省略可
戻り値 std::string デコード文字列。失敗時は空文字

実践例

例 1: detect → decode を分けて呼ぶ

検出だけ先に行い、成功時のみデコードする分割パターンです。処理の成否を段階的に確認したい実務では、こちらの方が制御しやすいです。

#include <iostream>
#include <opencv2/opencv.hpp>

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: ./main <image_path>" << std::endl;
        return 1;
    }

    cv::Mat img = cv::imread(argv[1]);
    if (img.empty()) {
        std::cerr << "画像を読み込めませんでした: " << argv[1] << std::endl;
        return 1;
    }

    cv::QRCodeDetector detector;
    cv::Mat points;

    // Step1: 検出
    bool found = detector.detect(img, points);
    if (!found) {
        std::cout << "QR コードが見つかりませんでした" << std::endl;
        return 0;
    }
    std::cout << "QR コードを検出しました" << std::endl;

    // Step2: デコード
    cv::Mat straight;
    std::string decoded = detector.decode(img, points, straight);
    if (decoded.empty()) {
        std::cout << "デコードに失敗しました(汚損・傾きが大きい可能性)" << std::endl;
        return 0;
    }

    std::cout << "デコード結果: " << decoded << std::endl;
    return 0;
}

例 2: カメラ映像からリアルタイム読み取り

#include <iostream>
#include <opencv2/opencv.hpp>

int main() {
    cv::VideoCapture cap(0);
    if (!cap.isOpened()) {
        std::cerr << "カメラを開けませんでした" << std::endl;
        return 1;
    }

    cv::QRCodeDetector detector;
    cv::Mat frame, points;

    while (true) {
        cap >> frame;
        if (frame.empty()) break;

        std::string decoded = detector.detectAndDecode(frame, points);

        if (!decoded.empty()) {
            std::cout << "読み取り: " << decoded << std::endl;

            // 検出枠を描画
            if (!points.empty()) {
                std::vector<cv::Point> contour;
                for (int i = 0; i < 4; ++i) {
                    cv::Point2f pt = points.at<cv::Point2f>(0, i);
                    contour.emplace_back(static_cast<int>(pt.x),
                                        static_cast<int>(pt.y));
                }
                std::vector<std::vector<cv::Point>> contours = {contour};
                cv::polylines(frame, contours, true, cv::Scalar(0, 255, 0), 2);
            }
        }

        cv::imshow("QR Scanner", frame);
        // q キーで終了
        if (cv::waitKey(30) == 'q') break;
    }

    cv::destroyAllWindows();
    return 0;
}

実行すると、カメラ映像ウィンドウが開き、QR コードが映ると緑枠が表示されターミナルに内容が出力されます。


つまずきポイント

⚠️ points の型を CV_32FC2 と意識する

detectAndDecode() が返す points は 1×4 の行列で、要素型は cv::Point2f(CV_32FC2)です。int 型の cv::Point でアクセスすると assertion エラーになります。

// NG: 型不一致でクラッシュ
cv::Point pt = points.at<cv::Point>(0, i);

// OK
cv::Point2f pt = points.at<cv::Point2f>(0, i);

⚠️ 戻り値が空文字でも例外は出ない

detectAndDecode() は検出・デコードに失敗しても例外を投げず、空の std::string を返します。戻り値のチェックを省略すると、空文字のまま処理が続いてバグの原因になります。必ず decoded.empty() で分岐してください。

⚠️ グレースケール変換は不要(でも有効)

cv::QRCodeDetector はカラー画像をそのまま受け付けます。ただし、照明条件が悪い場合はあらかじめグレースケール変換や二値化を施すと検出精度が上がることがあります。cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY) を前処理として試してみてください。


関連する関数

  • cv::polylines — 検出した QR コードの輪郭描画に使用
  • cv::VideoCapture — カメラ映像からのリアルタイム読み取り
  • cv::cvtColor — グレースケール変換による前処理

まとめ

cv::QRCodeDetector は外部ライブラリ不要で QR コードの検出・デコードを完結できます。detectAndDecode() で手軽に、detect() + decode() の分割呼び出しで段階的な制御が可能です。戻り値の空文字チェックと points の型(CV_32FC2)に注意すれば、実務のカメラ検査ラインや組込システムでもそのまま使えます。

🛠 画像処理のプロが開発するSDK/API

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

タイトルとURLをコピーしました