C++のOpenCVについて

AI実装検定のご案内

OpenCVは、画像処理や動画処理、物体検出、特徴点抽出、カメラキャリブレーション、DNN推論などに使われる代表的なコンピュータビジョンライブラリです。

C++、Python、Javaなど複数の言語から利用できますが、OpenCVの主要APIはC++を中心に設計されているため、C++から使うとライブラリ本来の構造を理解しやすいという特徴があります。

C++版OpenCVは、リアルタイム処理や製品開発、産業用画像処理、ロボット、組み込みシステムなどでよく使われます。

Python版より環境構築やビルドはやや複雑ですが、処理速度やメモリ制御、アプリケーションへの組み込みやすさという点で大きなメリットがあります。

目次

C++でOpenCVを使うメリット

高速な画像処理がしやすい

C++は実行速度が速く、リアルタイム性が求められる画像処理に向いています。

たとえば、カメラ映像を1フレームずつ処理する場合や、産業用検査装置で大量の画像を短時間で解析する場合には、C++版OpenCVがよく使われます。

ただし、Python版OpenCVも内部ではC++実装を呼び出しているため、単純に cv2.Canny()cv2.GaussianBlur() のような関数を呼ぶだけであれば、Pythonでも十分高速な場合があります。

C++版が特に有利になるのは、アプリケーション全体を低遅延に作りたい場合や、ピクセル単位の独自処理を多く書く場合、組み込み機器や製品に組み込む場合です。

製品開発や組み込みに向いている

C++版OpenCVは、実務の画像処理システムでよく使われます。

たとえば、以下のような分野です。

  • 工場の外観検査
  • 監視カメラシステム
  • ロボットビジョン
  • 自動運転・ADAS
  • 医療画像処理
  • ドローン映像解析
  • 産業用カメラ制御

C++は処理速度だけでなく、既存のC++アプリケーションやネイティブライブラリとの連携もしやすいため、業務システムやハードウェア制御を含む開発に向いています。

OpenCVの仕組みを理解しやすい

OpenCVでは、cv::Matcv::Pointcv::Rectcv::Scalar などのC++クラスが多く使われます。

C++でOpenCVを学ぶと、画像データがどのようにメモリ上で扱われているか、コピーと参照がどのように動くか、処理の入力と出力がどのように渡されるかを理解しやすくなります。

Python版ではこれらの細かい部分が隠れているため、学習初期には便利ですが、実務で高速化や不具合調査を行う場合にはC++の理解が役立ちます。

C++ OpenCVの基本構造

基本的なコード

C++でOpenCVを使う最小構成は、以下のようになります。

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

int main() {
    cv::Mat image = cv::imread("sample.jpg");

    if (image.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::imshow("Image", image);
    cv::waitKey(0);

    return 0;
}

このコードでは、画像を読み込み、表示し、キー入力があるまでウィンドウを開いたままにしています。

opencv2/opencv.hpp について

#include <opencv2/opencv.hpp> は、OpenCVの主要な機能をまとめて読み込むヘッダです。

学習用や小規模なサンプルコードでは便利ですが、実務では必要なヘッダだけを読み込むこともあります。

たとえば、画像の読み込みと表示だけであれば、以下のように書くこともできます。

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

必要なヘッダだけにすると、依存関係が明確になり、コンパイル時間を抑えやすくなります。

cv::Matとは

OpenCVで最も重要なクラス

C++版OpenCVで最も重要なのが cv::Mat です。

cv::Mat は画像データを表すためのクラスです。

cv::Mat image;

画像は、画素が縦横に並んだデータです。

たとえば、640×480のカラー画像であれば、概念的には以下のようなデータ構造になります。

高さ480 × 幅640 × 3チャンネル

C++でOpenCVを使う場合、ほとんどの画像処理関数は cv::Mat を入力や出力として受け取ります。

cv::Matの行と列

cv::Mat では、第1引数が行数、第2引数が列数です。

cv::Mat image(480, 640, CV_8UC3);

これは、幅640px、高さ480pxのカラー画像を作るという意味です。

注意点として、画像サイズを考えるときは「幅 × 高さ」で表現することが多いですが、cv::Mat のコンストラクタでは「高さ、幅」の順に指定します。

cv::Matの型

OpenCVでは、画像の画素型を次のように表します。

CV_8UC1
CV_8UC3
CV_32FC1
CV_64FC1

意味は以下の通りです。

CV_[ビット深度][符号・型]C[チャンネル数]

代表的な型は以下です。

意味
CV_8UC18bit unsigned char、1チャンネルの画像
CV_8UC38bit unsigned char、3チャンネルの画像
CV_32FC132bit float、1チャンネルの画像
CV_64FC164bit double、1チャンネルの画像

たとえば、黒いカラー画像を作る場合は以下のように書きます。

cv::Mat image(480, 640, CV_8UC3, cv::Scalar(0, 0, 0));

OpenCVのカラー形式

OpenCVは基本的にBGR順

OpenCVでカラー画像を扱うときに重要なのが、色の順番です。

一般的にはカラー画像はRGB順で考えることが多いですが、OpenCVでは通常BGR順で扱います。

つまり、1ピクセルの値は以下の順番です。

Blue, Green, Red

カラー画像のピクセルにアクセスする場合は、以下のようになります。

cv::Vec3b pixel = image.at<cv::Vec3b>(y, x);

uchar blue  = pixel[0];
uchar green = pixel[1];
uchar red   = pixel[2];

RGBに変換する方法

他のライブラリやGUI、Web系の処理と連携する場合、RGB順に変換したいことがあります。

その場合は cv::cvtColor() を使います。

cv::Mat rgb;
cv::cvtColor(image, rgb, cv::COLOR_BGR2RGB);

逆に、RGB画像をOpenCVで扱うためにBGRへ変換する場合は以下です。

cv::Mat bgr;
cv::cvtColor(rgb, bgr, cv::COLOR_RGB2BGR);

画像を読み込む

imread()の基本

画像を読み込むには cv::imread() を使います。

cv::Mat image = cv::imread("sample.jpg");

読み込みに失敗した場合、image.empty()true になります。

そのため、実務では必ず読み込みチェックを入れるべきです。

cv::Mat image = cv::imread("sample.jpg");

if (image.empty()) {
    std::cerr << "画像を読み込めませんでした。" << std::endl;
    return -1;
}

画像が読み込めない原因としては、以下がよくあります。

  • ファイルパスが間違っている
  • 実行ディレクトリが想定と違う
  • ファイル名や拡張子が違う
  • 画像ファイルが壊れている
  • 日本語パスや特殊文字を含むパスで問題が起きている
  • 対応していない画像形式を読み込もうとしている

グレースケールで読み込む

画像を最初からグレースケールで読み込みたい場合は、以下のようにします。

cv::Mat gray = cv::imread("sample.jpg", cv::IMREAD_GRAYSCALE);

アルファチャンネル込みで読み込む

PNG画像などでアルファチャンネルを保持したい場合は、cv::IMREAD_UNCHANGED を使います。

cv::Mat image = cv::imread("sample.png", cv::IMREAD_UNCHANGED);

通常の imread() では、アルファチャンネルが保持されない場合があります。

画像を表示する

imshow()の基本

画像を表示するには cv::imshow() を使います。

cv::imshow("Image", image);

第1引数はウィンドウ名、第2引数は表示する画像です。

waitKey()の役割

imshow() の後には、通常 cv::waitKey() を使います。

cv::waitKey(0);

waitKey(0) は、キー入力があるまで待機するという意味です。

これを書かないと、プログラムがすぐ終了してウィンドウが一瞬で閉じることがあります。

動画処理では、以下のように短い待ち時間を指定することが多いです。

cv::waitKey(1);

終了時にウィンドウを閉じたい場合は、以下を使うこともあります。

cv::destroyAllWindows();

画像を保存する

imwrite()の基本

画像を保存するには cv::imwrite() を使います。

cv::imwrite("output.jpg", image);

たとえば、読み込んだ画像を別名で保存する場合は以下です。

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

int main() {
    cv::Mat image = cv::imread("sample.jpg");

    if (image.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::imwrite("output.jpg", image);

    return 0;
}

グレースケール変換

cvtColor()を使う

カラー画像をグレースケールに変換するには、cv::cvtColor() を使います。

cv::Mat gray;
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);

完全なコード例は以下です。

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

int main() {
    cv::Mat image = cv::imread("sample.jpg");

    if (image.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::Mat gray;
    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);

    cv::imshow("Original", image);
    cv::imshow("Gray", gray);
    cv::waitKey(0);

    return 0;
}

グレースケール化は、二値化、エッジ検出、輪郭抽出などの前処理としてよく使われます。

二値化

threshold()の基本

二値化とは、画像を白と黒の2値に分ける処理です。

cv::threshold(gray, binary, 128, 255, cv::THRESH_BINARY);

各引数の意味は以下です。

cv::threshold(src, dst, thresholdValue, maxValue, thresholdType);
引数意味
src入力画像
dst出力画像
thresholdValueしきい値
maxValueしきい値を超えた場合の値
thresholdType二値化の種類

基本的なコード例は以下です。

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

int main() {
    cv::Mat image = cv::imread("sample.jpg");

    if (image.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::Mat gray, binary;

    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    cv::threshold(gray, binary, 128, 255, cv::THRESH_BINARY);

    cv::imshow("Gray", gray);
    cv::imshow("Binary", binary);
    cv::waitKey(0);

    return 0;
}

実務では固定しきい値だけでは不十分な場合がある

128 のような固定しきい値は分かりやすいですが、実務では照明条件や画像の明るさによって結果が大きく変わります。

そのため、実務では大津の二値化を使うことがあります。

cv::threshold(gray, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);

また、照明ムラがある画像では適応的二値化を使うこともあります。

cv::adaptiveThreshold(
    gray,
    binary,
    255,
    cv::ADAPTIVE_THRESH_GAUSSIAN_C,
    cv::THRESH_BINARY,
    11,
    2
);

ぼかし処理

GaussianBlur()の基本

ノイズ除去や前処理には、ぼかし処理がよく使われます。

代表的なのが cv::GaussianBlur() です。

cv::GaussianBlur(image, blurred, cv::Size(5, 5), 0);

コード例は以下です。

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

int main() {
    cv::Mat image = cv::imread("sample.jpg");

    if (image.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::Mat blurred;
    cv::GaussianBlur(image, blurred, cv::Size(5, 5), 0);

    cv::imshow("Original", image);
    cv::imshow("Blurred", blurred);
    cv::waitKey(0);

    return 0;
}

cv::Size(5, 5) はカーネルサイズです。

一般的には、3×35×57×7 のような正の奇数サイズを使います。

エッジ検出

Canny()の基本

画像の輪郭や境界線を検出するには、cv::Canny() がよく使われます。

cv::Canny(gray, edges, 100, 200);

基本的なコード例は以下です。

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

int main() {
    cv::Mat image = cv::imread("sample.jpg");

    if (image.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::Mat gray, edges;

    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    cv::Canny(gray, edges, 100, 200);

    cv::imshow("Original", image);
    cv::imshow("Edges", edges);
    cv::waitKey(0);

    return 0;
}

Cannyはしきい値調整が重要

100, 200 はあくまで例です。

どの値が適切かは画像によって変わります。

ノイズが多い画像では、Cannyの前にぼかしを入れることがあります。

cv::Mat gray, blurred, edges;

cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 1.5);
cv::Canny(blurred, edges, 50, 150);

実務では、エッジが出すぎる場合、出なさすぎる場合があるため、画像に合わせてしきい値を調整します。

リサイズ

resize()の基本

画像サイズを変更するには cv::resize() を使います。

cv::resize(image, resized, cv::Size(300, 200));

これは、幅300px、高さ200pxにリサイズする例です。

比率で縮小・拡大する場合は以下のように書きます。

cv::resize(image, resized, cv::Size(), 0.5, 0.5);

これは横方向・縦方向をそれぞれ50%にするという意味です。

ROIによる画像の切り出し

ROIとは

ROIは、Region of Interestの略で、画像の中で処理対象にしたい領域のことです。

OpenCVでは、cv::Rect を使って画像の一部を切り出せます。

cv::Rect roi(100, 50, 200, 150);
cv::Mat cropped = image(roi);

cv::Rect の引数は以下です。

cv::Rect(x, y, width, height)

clone()が必要な場合

以下のように書いた場合、cropped は元画像の一部を参照しているだけの場合があります。

cv::Mat cropped = image(roi);

完全に別データとしてコピーしたい場合は、clone() を使います。

cv::Mat croppedCopy = image(roi).clone();

ROIの範囲外アクセスに注意

ROIが画像の範囲を超えると、エラーになることがあります。

安全に処理したい場合は、画像全体の範囲と重なる部分だけを使うようにします。

cv::Rect roi(100, 50, 200, 150);
cv::Rect imageRect(0, 0, image.cols, image.rows);

roi = roi & imageRect;

if (roi.width > 0 && roi.height > 0) {
    cv::Mat cropped = image(roi).clone();
}

図形や文字の描画

線を描く

画像上に線を描くには cv::line() を使います。

cv::line(
    image,
    cv::Point(10, 10),
    cv::Point(200, 200),
    cv::Scalar(0, 255, 0),
    2
);

四角形を描く

四角形を描くには cv::rectangle() を使います。

cv::rectangle(
    image,
    cv::Rect(50, 50, 200, 100),
    cv::Scalar(255, 0, 0),
    2
);

円を描く

円を描くには cv::circle() を使います。

cv::circle(
    image,
    cv::Point(150, 150),
    50,
    cv::Scalar(0, 0, 255),
    3
);

文字を書く

文字を書くには cv::putText() を使います。

cv::putText(
    image,
    "OpenCV",
    cv::Point(50, 50),
    cv::FONT_HERSHEY_SIMPLEX,
    1.0,
    cv::Scalar(255, 255, 255),
    2
);

cv::Scalar() の色指定もBGR順です。

cv::Scalar(Blue, Green, Red)

輪郭抽出

findContours()の基本

物体の形状や領域を検出したい場合には、輪郭抽出を使います。

std::vector<std::vector<cv::Point>> contours;
cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

基本的なコード例は以下です。

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

int main() {
    cv::Mat image = cv::imread("sample.jpg");

    if (image.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::Mat gray, binary;

    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    cv::threshold(gray, binary, 128, 255, cv::THRESH_BINARY);

    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    cv::drawContours(image, contours, -1, cv::Scalar(0, 0, 255), 2);

    cv::imshow("Contours", image);
    cv::waitKey(0);

    return 0;
}

輪郭抽出で注意すべき点

findContours() は、基本的に白い領域を対象物、黒い領域を背景として扱います。

そのため、二値化した結果が逆になっている場合は、THRESH_BINARY_INV を使うことがあります。

cv::threshold(gray, binary, 128, 255, cv::THRESH_BINARY_INV);

輪郭抽出は、以下のような用途でよく使われます。

  • 物体の個数を数える
  • 面積を測る
  • 外接矩形を求める
  • 形状を判定する
  • 欠陥や異物を検出する

カメラ映像を扱う

VideoCaptureの基本

Webカメラや動画ファイルを扱うには、cv::VideoCapture を使います。

Webカメラを開く場合は以下です。

cv::VideoCapture cap(0);

0 は通常、最初のカメラを意味します。

ただし、カメラ番号は環境によって異なります。

複数カメラが接続されている場合は、12 を指定する必要があることもあります。

カメラ映像を表示するサンプル

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

int main() {
    cv::VideoCapture cap(0);

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

    while (true) {
        cv::Mat frame;
        cap >> frame;

        if (frame.empty()) {
            break;
        }

        cv::imshow("Camera", frame);

        if ((cv::waitKey(1) & 0xFF) == 27) {
            break;
        }
    }

    return 0;
}

27 はEscキーです。

環境によっては waitKey() の戻り値に上位ビットが含まれることがあるため、以下のように書くとより安全です。

(cv::waitKey(1) & 0xFF) == 27

動画ファイルを読み込む

動画ファイルを読み込む場合は、ファイルパスを指定します。

cv::VideoCapture cap("movie.mp4");

カメラや動画の読み込みでは、コーデック、OS、バックエンドの違いによって挙動が変わることがあります。

WindowsではDirectShowやMedia Foundation、LinuxではV4L2、macOSではAVFoundationなどが関係する場合があります。

OpenCVの主要モジュール

OpenCVは、複数のモジュールに分かれています。

代表的なモジュールは以下です。

モジュール役割
core基本データ構造、行列演算、cv::Mat
imgproc画像処理、フィルタ、変換、輪郭抽出
imgcodecs画像ファイルの読み書き
highgui画像表示、簡易GUI
videoioカメラ、動画ファイル入出力
features2d特徴点検出、特徴量記述
objdetect顔検出などの物体検出
dnn深層学習モデルの推論
calib3dカメラキャリブレーション、3D復元
ml機械学習

初心者が最初に使うことが多いのは、coreimgprocimgcodecshighguivideoio あたりです。

C++ OpenCVの環境構築

Windowsの場合

Windowsでは、主に以下の方法があります。

  • OpenCV公式ビルドを使う
  • Visual Studioにincludeパスとlibパスを設定する
  • DLLへのPATHを通す
  • vcpkgを使ってインストールする
  • ソースからビルドする

最近は、vcpkgを使う方法もよく使われます。

vcpkg install opencv4

Windowsでは、OpenCVのDLLが見つからずに実行エラーになることがあります。

たとえば、以下のようなDLLです。

opencv_worldxxx.dll
opencv_corexxx.dll
opencv_imgprocxxx.dll
opencv_imgcodecsxxx.dll
opencv_highguixxx.dll

opencv_world はOpenCVのビルド構成によって存在する場合としない場合があります。

macOSの場合

macOSでは、Homebrewを使う方法が一般的です。

brew install opencv

CMakeからOpenCVが見つからない場合は、OpenCV_DIRCMAKE_PREFIX_PATH の指定が必要になることがあります。

Ubuntu / Debianの場合

UbuntuやDebianでは、APTでインストールできます。

sudo apt update
sudo apt install libopencv-dev

ただし、APTで入るOpenCVのバージョンはディストリビューションに依存します。

最新バージョンを使いたい場合は、ソースビルド、vcpkg、Conan、Dockerなどを検討します。

CMakeでOpenCVを使う

基本的な構成

C++でOpenCVを使う場合、CMakeを使うのが一般的です。

ディレクトリ構成の例は以下です。

project/
  CMakeLists.txt
  main.cpp
  sample.jpg

CMakeLists.txtの例

cmake_minimum_required(VERSION 3.10)
project(OpenCVSample)

set(CMAKE_CXX_STANDARD 17)

find_package(OpenCV REQUIRED)

add_executable(OpenCVSample main.cpp)

target_link_libraries(OpenCVSample ${OpenCV_LIBS})

ビルド方法

mkdir build
cd build
cmake ..
cmake --build .

実行ファイルの場所や名前は、OSやビルド環境によって変わります。

CMakeでOpenCVが見つからない場合は、OpenCV_DIR を指定します。

cmake -DOpenCV_DIR=/path/to/opencv/build ..

Windowsの場合は、たとえば以下のようなパスになることがあります。

C:/opencv/build

ピクセルアクセス

グレースケール画像の場合

グレースケール画像の1ピクセルにアクセスするには、以下のように書きます。

uchar value = gray.at<uchar>(y, x);

値を書き換える場合は以下です。

gray.at<uchar>(y, x) = 255;

カラー画像の場合

カラー画像では、cv::Vec3b を使います。

cv::Vec3b pixel = image.at<cv::Vec3b>(y, x);

uchar blue  = pixel[0];
uchar green = pixel[1];
uchar red   = pixel[2];

値を書き換える場合は以下です。

image.at<cv::Vec3b>(y, x)[0] = 255;
image.at<cv::Vec3b>(y, x)[1] = 0;
image.at<cv::Vec3b>(y, x)[2] = 0;

これは青色になります。

OpenCVではBGR順である点に注意してください。

実務でよく使う画像処理の流れ

基本的な処理フロー

実務で画像処理を行う場合、以下のような流れになることが多いです。

画像読み込み
↓
グレースケール化
↓
ノイズ除去
↓
二値化
↓
輪郭抽出
↓
面積・座標・形状を計算
↓
結果を描画・保存

サンプルコード

以下は、一定以上の面積を持つ領域を検出し、赤枠で囲む例です。

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

int main() {
    cv::Mat image = cv::imread("sample.jpg");

    if (image.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::Mat gray, blurred, binary;

    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
    cv::threshold(blurred, binary, 128, 255, cv::THRESH_BINARY);

    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    for (const auto& contour : contours) {
        double area = cv::contourArea(contour);

        if (area > 500) {
            cv::Rect rect = cv::boundingRect(contour);
            cv::rectangle(image, rect, cv::Scalar(0, 0, 255), 2);
        }
    }

    cv::imshow("Result", image);
    cv::waitKey(0);

    return 0;
}

このような処理は、異物検出、部品検査、物体カウント、形状判定などに応用できます。

顔検出

Haar Cascadeによる顔検出

OpenCVでは、Haar Cascadeを使った顔検出もできます。

cv::CascadeClassifier faceCascade;
faceCascade.load("haarcascade_frontalface_default.xml");

検出処理は以下のように書きます。

std::vector<cv::Rect> faces;
faceCascade.detectMultiScale(gray, faces);

検出結果を描画するには以下です。

for (const auto& face : faces) {
    cv::rectangle(image, face, cv::Scalar(0, 255, 0), 2);
}

Haar Cascadeは古典的な手法

Haar Cascadeは軽量で扱いやすい一方、現在の実務ではDNNベースの顔検出やYOLO系の物体検出が使われることも多いです。

特に、角度変化、照明変化、部分的な隠れ、複雑な背景に対しては、DNN系の手法の方が安定しやすいです。

そのため、学習目的ではHaar Cascadeを使い、実務用途ではDNNベースの検出も検討するとよいです。

Python版OpenCVとの違い

C++版とPython版の比較

C++版とPython版には、それぞれメリットがあります。

項目C++版Python版
実行速度高速化しやすいOpenCV関数自体は内部C++で高速
開発の手軽さやや難しい手軽に書ける
環境構築やや複雑比較的簡単
製品組み込み向いている用途による
メモリ制御細かく制御できる抽象化されている
学習コスト高め低め

どちらを選ぶべきか

画像処理の概念を学ぶだけであれば、Python版の方が始めやすいです。

一方で、以下のような場合はC++版OpenCVを学ぶ価値が高いです。

  • リアルタイム処理を行いたい
  • C++アプリケーションに組み込みたい
  • 産業用画像処理を行いたい
  • 組み込み機器で動かしたい
  • メモリや速度を細かく制御したい
  • 製品レベルの画像処理システムを作りたい

C++ OpenCVでよくあるエラー

画像が読み込めない

もっともよくあるのが、imread() で画像が読み込めないケースです。

cv::Mat image = cv::imread("sample.jpg");

必ず以下のようにチェックします。

if (image.empty()) {
    std::cerr << "読み込み失敗" << std::endl;
}

原因としては、ファイルパスや実行ディレクトリの違いが多いです。

DLLが見つからない

Windowsでは、OpenCVのDLLが見つからずに実行できないことがあります。

対策としては、以下があります。

  • OpenCVの bin フォルダを環境変数PATHに追加する
  • 実行ファイルと同じフォルダにDLLを置く
  • Debug/Releaseのライブラリを混在させない
  • OpenCVのバージョンとリンク設定を一致させる

CMakeでOpenCVが見つからない

find_package(OpenCV REQUIRED) が失敗する場合、OpenCVの場所をCMakeに伝える必要があります。

cmake -DOpenCV_DIR=/path/to/opencv/build ..

環境によっては、Homebrew、vcpkg、APT、ソースビルドなどでインストール先が異なります。

色がおかしい

画像の色が赤青反転して見える場合、BGRとRGBの違いが原因であることが多いです。

OpenCVでは通常BGR順です。

RGBに変換する場合は以下です。

cv::cvtColor(image, rgb, cv::COLOR_BGR2RGB);

C++ OpenCVの学習順序

初心者におすすめの順番

C++でOpenCVを学ぶ場合、以下の順番で進めると理解しやすいです。

1. C++の基本
2. cv::Mat
3. imread / imshow / imwrite
4. グレースケール変換
5. 二値化
6. フィルタ処理
7. エッジ検出
8. 輪郭抽出
9. 動画・カメラ処理
10. 特徴点検出
11. 物体検出
12. DNN

最初からDNNや高度な物体検出に進むよりも、まずは cv::Mat と基本的な画像処理を理解することが重要です。

最小サンプルコード

画像を読み込んでエッジ検出する例

最後に、C++ OpenCVの基本をまとめたサンプルを紹介します。

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

int main() {
    cv::Mat image = cv::imread("sample.jpg");

    if (image.empty()) {
        std::cerr << "画像を読み込めませんでした。" << std::endl;
        return -1;
    }

    cv::Mat gray;
    cv::Mat edges;

    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    cv::Canny(gray, edges, 100, 200);

    cv::imshow("Original", image);
    cv::imshow("Gray", gray);
    cv::imshow("Edges", edges);

    cv::imwrite("edges.jpg", edges);

    cv::waitKey(0);

    return 0;
}

このコードには、OpenCVの基本要素が含まれています。

  • cv::Mat
  • cv::imread()
  • empty() チェック
  • cv::cvtColor()
  • cv::Canny()
  • cv::imshow()
  • cv::imwrite()
  • cv::waitKey()

まとめ

C++のOpenCVでは、まず cv::Mat を理解することが重要です。

cv::Mat は画像データを扱う中心的なクラスであり、画像の読み込み、変換、加工、保存、表示など、ほとんどの処理で使われます。

特に重要な関数は以下です。

cv::imread()
cv::imshow()
cv::waitKey()
cv::imwrite()
cv::cvtColor()
cv::threshold()
cv::GaussianBlur()
cv::Canny()
cv::findContours()
cv::VideoCapture

C++版OpenCVは、Python版より環境構築やビルドが難しい一方で、リアルタイム処理、産業用途、組み込み開発、製品レベルの画像処理に向いています。

入門段階では、画像を読み込む、表示する、グレースケール化する、二値化する、輪郭を抽出する、という基本処理をしっかり押さえることが大切です。

そのうえで、動画処理、特徴点検出、物体検出、DNN推論へ進むと、OpenCVをより実践的に活用できるようになります。

以上、C++のOpenCVについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次