【C++】OpenCVで実現する画像の色抽出:シンプルなコードで効果的な色選別
C++とOpenCVを活用すると、画像から特定の色のみを抽出でき、処理が効率的になります。
まず画像をBGR形式からHSV形式へ変換し、
その後、設定した範囲に該当するピクセルにマスクを適用し、元画像から該当部分のみを抜き出すので、直感的なコードで画像処理が実現できます。
色抽出処理の流れ
画像の読み込みと前処理
画像読み込みの際はcv::imread
を使い、対象画像を取得します。
取得後はサイズ調整やガウシアンフィルタの適用などの前処理を行うことで、後続の処理がよりスムーズになる場合があります。
以下は画像読み込みと前処理のサンプルコードです。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 画像のパス指定
std::string imagePath = "sample.jpg";
// 画像読み込み(カラー画像として)
cv::Mat src = cv::imread(imagePath, cv::IMREAD_COLOR);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました。" << std::endl;
return -1;
}
// 前処理:画像のリサイズ
cv::Mat resized;
cv::resize(src, resized, cv::Size(640, 480));
// 前処理:ノイズ除去のためのガウシアンフィルタ適用
cv::Mat preprocessed;
cv::GaussianBlur(resized, preprocessed, cv::Size(5, 5), 0);
cv::imshow("Preprocessed Image", preprocessed);
cv::waitKey(0);
return 0;
}
(プログラム実行後、前処理済み画像のウィンドウが表示されます)

色空間変換
BGRからHSVへの変換
OpenCVでは画像をBGR形式で読み込みますが、色の抽出を行う際にはHSV形式に変換する手法が人気です。
HSVは人間の色認識に近く扱いやすい特徴があります。
変換にはcv::cvtColor
を使用します。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
std::string imagePath = "sample.jpg";
cv::Mat src = cv::imread(imagePath, cv::IMREAD_COLOR);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました。" << std::endl;
return -1;
}
// BGRからHSVへ変換
cv::Mat hsv;
cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV);
cv::imshow("HSV Image", hsv);
cv::waitKey(0);
return 0;
}
(HSVに変換された画像が表示されます)

HSVと他色空間との比較
HSV色空間は、色相、彩度、明度の直感的な調整が可能なため、抽出対象となる色の範囲指定がしやすい点が魅力です。
これに対してRGBやBGRでは、各チャンネルの数値を直接扱わなければならず、特定の色の抽出が複雑になる場合があります。
PWM方式やLab色空間も用途に合わせた選択肢ですが、一般的に色範囲抽出の場合はHSVがよく採用されます。
抽出対象の色範囲設定
色相、彩度、明度のパラメータ設定
色抽出では抽出対象の色をHSVで定義します。
各パラメータは以下の意味を持ちます。
- 色相 (Hue): 色の種類の範囲を示す
- 彩度 (Saturation): 色の鮮やかさを示す
- 明度 (Value): 色の明るさを示す
抽出したい色に合わせて下限・上限の値を設定する必要があります。
たとえばオレンジ色であれば、色相は5~15の範囲、彩度や明度は比較的高めの値に設定するとよいでしょう。
パラメータ調整方法
パラメータの調整は、実際の画像や使用環境に合わせて微調整を進める必要があります。
以下のリストは調整時に確認しておくと良い点です。
- 画像の照明条件
- ターゲットとなる色の明るさの変化
- 同系色との区別の難しさ
これらを考慮して試行錯誤することで、目的に適ったパラメータを見つけやすくなります。
マスク作成と適用
cv::inRangeの利用
cv::inRange
関数はHSV画像と下限・上限のcv::Scalar
を入力に、対象範囲内のピクセルを白、それ以外を黒とするマスク画像を生成します。
これにより、抽出する色範囲のピクセルを簡単に選別できます。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
std::string imagePath = "sample.jpg";
cv::Mat src = cv::imread(imagePath, cv::IMREAD_COLOR);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました。" << std::endl;
return -1;
}
cv::Mat hsv;
cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV);
// オレンジ色の範囲設定
cv::Scalar lower(5, 150, 150);
cv::Scalar upper(15, 255, 255);
cv::Mat mask;
cv::inRange(hsv, lower, upper, mask);
cv::imshow("Mask", mask);
cv::waitKey(0);
return 0;
}
(作成されたマスク画像が表示され、抽出対象の範囲が白くなります)
cv::bitwise_andによる抽出
生成したマスクを用いることで、cv::bitwise_and
関数で元画像との論理積をとり、抽出する色だけを残すことができます。
これにより、対象色以外の領域が背景として黒く表示されます。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
std::string imagePath = "sample.jpg";
cv::Mat src = cv::imread(imagePath, cv::IMREAD_COLOR);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました。" << std::endl;
return -1;
}
cv::Mat hsv;
cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV);
cv::Scalar lower(5, 150, 150);
cv::Scalar upper(15, 255, 255);
cv::Mat mask;
cv::inRange(hsv, lower, upper, mask);
// マスクを適用して色抽出
cv::Mat result;
cv::bitwise_and(src, src, result, mask);
cv::imshow("Extracted Color", result);
cv::waitKey(0);
return 0;
}
(抽出された対象色だけが残った画像が表示されます)
結果確認とデバッグ
処理後の結果はcv::imshow
で表示し、抽出される色の範囲が正しく設定されているか確認します。
また、意図しない結果となった場合は、パラメータの範囲、前処理の内容、または画像自体の品質などを確認する必要があります。
画像表示ウィンドウとともに、端末上でエラーメッセージを出力することで、トラブルシューティングがスムーズになります。
色抽出の実装アプローチ
関数分割と処理構造
色抽出の処理は、複数の段階に分かれるため、各処理ごとに関数に分割すると見やすくなります。
たとえば、以下のような関数に分けると処理の流れが明確になります。
loadImage()
:画像を読み込む処理preprocessImage()
:画像の前処理convertToHSV()
:色空間の変換createMask()
:色抽出用マスクの作成applyMask()
:抽出用マスクを用いて元画像から対象色を抽出
関数として分割しておくと、後からのコード修正が容易になり、プログラム全体の保守性が向上します。
パラメータ管理の考慮点
抽出パラメータ(下限・上限の値)は、プログラム起動時に引数として渡すか、設定ファイルから読み込む方式にすることが考えられます。
こうすることで、リリース後も容易に値の調整が可能になり、さまざまな環境に柔軟に対応できます。
設定ファイルの例としてJSON形式やYAML形式がよく使われます。
エラー処理の実装
画像読み込み失敗時の対応
画像読み込みが失敗した場合、ユーザーに分かりやすいエラーメッセージを表示する処理が重要です。
cv::imread
の結果をチェックし、失敗時はエラーメッセージを表示してプログラムを安全に終了させるようにします。
また、ログ出力を行うことで、何が原因で失敗したのか後から確認しやすくなります。
色空間の特性と比較
HSV色空間の特徴
HSV色空間は、色相 (Hue)、彩度 (Saturation)、明度 (Value) の3つの要素で色を表現します。
これにより人間が視覚的に感じる色の違いが直感的に理解でき、特定の色の抽出がしやすくなります。
特に照明条件が異なる環境下でも比較的一貫した結果を得やすいという利点があります。
RGB・BGRとの違い
色表現の違いと注意点
RGBやBGRは各チャンネルが赤、緑、青を示し、単純な数値操作で色を区別しますが、色の明暗や鮮やかさの違いが直観的に反映されないため、細かな色の範囲指定には向かない場合があります。
対比すると、HSVは色相の環境変化に比較的強く、抽出が柔軟に行いやすいです。
注意点としては、RGBの値は環境光などの影響を受けやすく、周囲の明るさの変化に敏感に反応するため、特定の色を抜き出す際に誤抽出が起こる可能性がある点です。
色空間変換の数式概略
の説明
色空間変換は、一般に変換行列を用いた数学的演算で行われます。
BGRからHSVへ変換する際の数式は、各ピクセルの値
変換行列そのものは画像処理ライブラリ側で実装されていますが、基本的な概念としては以下のように表現できます。
ここで
理論的な背景を理解しておくと、異なる照明条件下での変換結果を予測しやすくなります。
パフォーマンス最適化の視点
処理速度向上のポイント
処理速度の向上には、画像のサイズを適切に調整することがひとつのポイントです。
大量のピクセルを扱うと計算負荷が増大するため、解像度のダウンサンプリングによって処理負荷を軽減できます。
また、処理の並列化(マルチスレッドやGPUの活用)も効果的な手段です。
メモリ利用効率の改善
メモリ使用量を最適化するには、中間処理結果の保持方法を見直すことや、使い終わった画像データを適切に解放する方法が重要です。
以下はC++でのOpenCVを使った場合の一例です。
- 不要な画像変数はスコープ内で定義しておく
- 大規模画像を扱う場合は処理ごとにメモリを確保・解放する
動画処理への応用事例
リアルタイム処理の工夫
動画からの色抽出では、各フレームに対して上記処理を適用します。
リアルタイム処理を実現するために以下の工夫が役立ちます。
- フレーム毎に必要最低限の前処理を実施する
- 並列処理を取り入れ、フレーム間のデータ転送を最小限にする
- ハードウェアのアクセラレーション(GPU利用など)を取り入れる
これにより、動画処理でも快適な速度で色抽出を行うことが期待できます。
デバッグと問題解決
結果検証の手法
結果はcv::imshow
で目視確認するほか、取得したマスクや抽出結果のヒストグラムを表示することで定量的な評価も可能です。
各ステップごとに確認しながら調整することで、最終的な出力に対する信頼性が向上します。
パラメータ調整のポイント
パラメータ調整では、一度に一つのパラメータを変更し、結果への影響を逐一確認することが重要です。
調整時に以下の点を留意すると良いです。
- 色相、彩度、明度それぞれの影響の把握
- サンプル画像を複数用意し、設定値の汎用性を検証する
- 画像の撮影条件によって異なる設定パターンの検討
一般的な問題と対策
色抽出の失敗例
抽出対象がうまく切り出せない場合は、以下の原因が考えられます。
- 照明条件の変化による色相のズレ
- 前処理の不足による画像ノイズの影響
- 対象色と近似する他の色との区別が難しいケース
これらの場合、パラメータ値の再調整や、前処理の工夫(例:さらにガウシアンフィルタを強める)などで解決を試みます。
輝度・彩度調整の不足対応策
画像全体が暗い場合は、彩度・明度の調整が効果的です。
以下の方法が参考になります。
- 画像全体の明るさを補正する(ヒストグラム平坦化など)
- 彩度の不足が原因の場合、コントラスト調整を行う
こうした対策は、マスク作成前に前処理として取り入れると、より正確な色抽出が期待できます。
まとめ
記事内では、色抽出処理の各段階について具体的なコード例とともに親しみやすく説明しました。
画像の読み込みから前処理、色空間変換、パラメータ設定、マスク作成と適用まで一連の流れを確認したことで、実際の開発時に役立つ知識が身につく内容を目指しました。
各セクションの工夫点やトラブルシューティングの方法も合わせて紹介したため、実践に取り組むときの参考になれば幸いです。