OpenCV/C++ Mat の基礎① 〜画像を行列として扱う〜

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

OpenCV/C++ Mat の基礎① 〜画像を行列として扱う〜

OpenCV で画像を扱う中心的なクラスが cv::Mat です。画像ファイルを読み込んだ結果も、処理途中の中間データも、すべて cv::Mat として扱います。本記事では Mat の作成・初期化・画素アクセス という実装上の基礎を C++ コードで押さえます。

先出し: Mat を生成して画素に直接アクセスする最小パターンは次のとおりです。

cv::Mat img(480, 640, CV_8UC3, cv::Scalar(0, 0, 0)); // 黒画像を生成
img.at<cv::Vec3b>(100, 200) = cv::Vec3b(0, 255, 0);   // (row=100, col=200) を緑に

動作環境

項目 内容
OpenCV 4.x
言語 C++17
ビルド例 g++ -std=c++17 main.cpp \pkg-config –cflags –libs opencv4“

基本の使い方

cv::Mat を直接生成し、初期化・画素アクセスまでを一本のプログラムで確認します。

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

int main()
{
    // --- 1. コンストラクタで生成(高さ4, 幅4, 8ビットグレースケール, 初期値0)
    cv::Mat gray(4, 4, CV_8UC1, cv::Scalar(0));

    // --- 2. 特定画素に書き込む(行, 列の順)
    gray.at<uchar>(1, 2) = 128;
    gray.at<uchar>(3, 3) = 255;

    // --- 3. 画素値を読み出して表示
    std::cout << "gray(1,2) = " << static_cast<int>(gray.at<uchar>(1, 2)) << std::endl;
    std::cout << "gray(3,3) = " << static_cast<int>(gray.at<uchar>(3, 3)) << std::endl;

    // --- 4. Mat 全体を行列として出力
    std::cout << "Mat =\n" << gray << std::endl;

    // --- 5. BGR カラー画像を生成して1画素だけ緑にする
    cv::Mat color(4, 4, CV_8UC3, cv::Scalar(0, 0, 0));
    color.at<cv::Vec3b>(2, 1) = cv::Vec3b(0, 255, 0); // BGR 順

    std::cout << "color(2,1) = " << color.at<cv::Vec3b>(2, 1) << std::endl;

    return 0;
}

実行結果

gray(1,2) = 128
gray(3,3) = 255
Mat =
[  0,   0,   0,   0;
   0,   0, 128,   0;
   0,   0,   0,   0;
   0,   0,   0, 255]
color(2,1) = [0, 255, 0]

コード解説

内容
cv::Mat gray(4, 4, CV_8UC1, cv::Scalar(0)) 高さ・幅・型・初期値を指定して生成。CV_8UC1 は 8 ビット符号なし1チャンネル
gray.at<uchar>(row, col) グレースケール画素の読み書き。引数は (行, 列) の順
static_cast<int>(...) uchar のまま cout すると文字コードが出力されるためキャスト必須
CV_8UC3 8 ビット符号なし3チャンネル(BGR)。OpenCV の色順は BGR
cv::Vec3b 3チャンネル 8bit 画素の型。at<cv::Vec3b>(row, col) でアクセス

引数と戻り値

Mat コンストラクタ(主要形式)

引数 説明
rows int 高さ(ピクセル数)
cols int 幅(ピクセル数)
type int 型定数(CV_8UC1 等)
s cv::Scalar 初期値(省略可)

よく使う型定数

定数 意味 用途例
CV_8UC1 uint8, 1ch グレースケール
CV_8UC3 uint8, 3ch BGR カラー
CV_32FC1 float32, 1ch 浮動小数点処理
CV_64FC1 double, 1ch 精度が必要な演算

主なメンバー

メンバー 説明
rows int 高さ
cols int
channels() int チャンネル数
type() int 型定数(CV_8UC3 等)
empty() bool データが空なら true

実践例

実践例①:zeros / ones で初期化した Mat を作る

cv::Mat::zeroscv::Mat::ones は全要素を 0 または 1 で初期化した Mat を返す静的メソッドです。

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

int main()
{
    // 全要素 0 の 3x3 float Mat
    cv::Mat z = cv::Mat::zeros(3, 3, CV_32FC1);
    // 全要素 1 の 3x3 float Mat
    cv::Mat o = cv::Mat::ones(3, 3, CV_32FC1);

    // スカラー倍して足し合わせる(行列演算もそのまま使える)
    cv::Mat result = z + o * 5.0f;

    std::cout << "result =\n" << result << std::endl;
    return 0;
}

実行結果

result =
[5, 5, 5;
 5, 5, 5;
 5, 5, 5]

実践例②:画像ファイルを読み込んで画素値を確認する

実務では cv::imread で読み込んだ結果が cv::Mat です。empty() チェックを忘れずに。

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

int main()
{
    cv::Mat img = cv::imread("sample.jpg");
    if (img.empty()) {
        std::cerr << "画像の読み込みに失敗しました" << std::endl;
        return 1;
    }

    std::cout << "サイズ: " << img.cols << " x " << img.rows << std::endl;
    std::cout << "チャンネル数: " << img.channels() << std::endl;
    std::cout << "型: " << img.type() << " (CV_8UC3=" << CV_8UC3 << ")" << std::endl;

    // 左上画素の BGR 値を表示
    cv::Vec3b px = img.at<cv::Vec3b>(0, 0);
    std::cout << "左上画素 BGR: " << static_cast<int>(px[0])
              << ", " << static_cast<int>(px[1])
              << ", " << static_cast<int>(px[2]) << std::endl;

    return 0;
}

実行結果(例)

サイズ: 640 x 480
チャンネル数: 3
型: 16 (CV_8UC3=16)
左上画素 BGR: 32, 87, 210

img.type() が返す整数は CV_8UC3 マクロの値(16)と一致します。型が想定どおりか確認するのに使えます。


つまずきポイント

⚠️ at<> の型と Mat の型が一致しないと実行時アサーションが出る

CV_8UC3 の Mat に img.at<uchar>(r, c) とアクセスすると、Debug ビルドで cv::Exception が投げられます。

// NG: CV_8UC3 の Mat に uchar でアクセス
uchar val = img.at<uchar>(0, 0); // アサーション失敗

// OK: チャンネル数・型に合わせる
cv::Vec3b px = img.at<cv::Vec3b>(0, 0);

型と at<> のテンプレート引数の対応を必ず合わせてください。

Mat 型 at<> の型
CV_8UC1 uchar
CV_8UC3 cv::Vec3b
CV_32FC1 float
CV_32FC3 cv::Vec3f

⚠️ at<> の座標は (行, 列) = (y, x) の順

画面座標では「(x, y)」で書きがちですが、at<>(row, col) = (y, x) の順です。cv::Point を使うときは (x, y) 順なのと逆になるため、混在するコードでは特に注意してください。

img.at<uchar>(y, x)   // Mat::at は (row, col) = (y, x)
img.at<uchar>(pt)     // cv::Point pt(x, y) を渡す形式もある(内部で変換される)

⚠️ imread が空 Mat を返す場合は作業ディレクトリと日本語パスを確認

cv::imread はファイルが見つからなくてもエラーを投げず、空の cv::Mat を返します。相対パスはプログラムの実行ディレクトリ基準です。IDE(Visual Studio 等)ではプロジェクトファイルのある場所と実行ディレクトリが異なることがあります。また Windows 環境では日本語を含むパスを正しく扱えないケースがあるため、パスには ASCII のみを使うのが安全です。


関連する関数

  • cv::imread / cv::imwrite — ファイルから Mat を読み込む・保存する
  • cv::cvtColor — BGR ↔ グレースケール等の色空間変換
  • cv::Mat::clone / cv::Mat::copyTo — Mat の深いコピー(浅いコピーとの違いに注意)
  • cv::Mat::ptr<> — 行ポインタを直接取得するループ高速化手法(at<> より高速)

まとめ

  • cv::Mat は型定数(CV_8UC1 等)とサイズを指定して生成し、cv::Scalar で初期値を与える。
  • 画素アクセスは at<T>(row, col) で行う。T は Mat の型と必ず一致させること。
  • imread 直後の empty() チェックは必須。空 Mat のまま処理を続けると実行時クラッシュになる。

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

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

→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

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