OpenCV

【C++】OpenCVを活用したマスク処理で画像領域を自在に抽出する方法

C++とOpenCVを利用したマスク処理は、対象画像から任意の領域のみを抽出できる便利な技法です。

画像とマスク画像を読み込み、例えばcv::bitwise_andでマスク領域だけを処理対象にする方式です。

白色部分が処理対象、黒色部分が無視対象として機能します。

マスク処理の基本

マスク画像の役割と仕様

白色と黒色の意味

マスク画像は、特定の領域を処理するために用いる2値画像です。

白い部分は処理対象の領域を表し、黒い部分は対象外として扱います。

つまり、白い部分には効果が適用され、黒い部分には元の状態が保たれます。

マスク適用の効果

マスクを適用することで、画像の中から必要な領域だけを取り出したり、部分的な加工を行ったりすることが可能になります。

これにより、全体に処理を施す手間が省け、効率的に目的の部分だけにフィルタや変換が実行できます。

対象領域の選定基準

対象領域を定める際には、以下のポイントに注意します。

  • 処理したい部分の形状や大きさ
  • 広がりや境界の明確さ
  • 他の領域との違いが分かりやすいか

複数の条件を考慮しながら領域を選ぶことで、意図した処理結果を得やすくなります。

マスク画像の作成方法

手動作成のアプローチ

画像編集ツールの利用方法

画像編集ソフト(PhotoshopやGIMPなど)を使って、背景画像を読み込み必要な部分だけを白く塗りつぶす操作でマスク画像を作成できます。

レイヤー機能などを活用すると、部分的な修正や調整が容易になります。

手動による作成は直感的な操作が可能なため、細かな領域の調整が必要な場合に適しています。

プログラムによる生成手法

描画関数を用いたマスク生成

OpenCVの描画関数を利用して、プログラム上でマスク画像を生成する方法があります。

たとえば、cv::rectanglecv::circleなどの関数を使って、指定した形状を白で描画することができます。

以下は、矩形のマスクを作成するサンプルコードです。

#include <opencv2/opencv.hpp>
#include <iostream>
int main(){
    // マスク画像のサイズを400x600、背景は黒で初期化
    cv::Mat mask = cv::Mat::zeros(400, 600, CV_8UC1);
    // cv::rectangleで指定した領域に白い矩形を描画
    cv::rectangle(mask, cv::Point(100, 100), cv::Point(500, 300), cv::Scalar(255), -1);
    // マスク画像をウィンドウで表示
    cv::imshow("Mask", mask);
    cv::waitKey(0);
    return 0;
}
ウィンドウが表示され、白い矩形が描かれたマスク画像が確認できます。

輪郭検出による抽出

画像内の特定の形状や領域を自動的に抽出するために、cv::findContours関数を利用する方法もあります。

検出した輪郭に基づいて、特定の条件(面積や形状)を満たす部分だけをマスクとして抽出することが可能です。

これにより、手動でマスクを描く手間を大幅に減らすことができます。

画像へのマスク適用手順

画像とマスクの読み込み方法

まずは、対象の画像と作成したマスク画像を読み込む必要があります。

OpenCVのcv::imread関数を使って、画像ファイルやマスク画像ファイルを読み込みます。

マスク画像はグレースケールで取り込むのが一般的です。

マスクの前処理手法

閾値処理のポイント

読み込んだマスク画像がすでに2値化されていない場合は、cv::thresholdを用いてしっかりと2値化する必要があります。

閾値を適切に設定することで、意図した領域が確実に白になり、対象外が黒になるように調整します。

例えば、閾値128を基準にして処理する場合、白い部分が255、黒い部分が0となる設定が一般的です。

以下はその例です。

#include <opencv2/opencv.hpp>
#include <iostream>
int main(){
    // マスク画像をグレースケールで読み込み
    cv::Mat mask = cv::imread("mask.png", cv::IMREAD_GRAYSCALE);
    if(mask.empty()){
        std::cerr << "マスク画像の読み込みに失敗しました" << std::endl;
        return -1;
    }
    cv::Mat binaryMask;
    // 閾値128で2値化処理を実施
    cv::threshold(mask, binaryMask, 128, 255, cv::THRESH_BINARY);
    cv::imshow("Binary Mask", binaryMask);
    cv::waitKey(0);
    return 0;
}
ウィンドウが表示され、二値化されたマスク画像が確認できます。

マスクによる領域抽出

cv::bitwise_andの利用

マスクが用意できたら、cv::bitwise_andを利用して画像とマスクを組み合わせ、特定領域の抽出を行います。

この関数は、対象の画素同士の論理積をとることで、マスクで指定された部分だけを残す処理を行います。

以下は、実際に画像とマスクを組み合わせるサンプルコードです。

#include <opencv2/opencv.hpp>
#include <iostream>
int main(){
    // 入力画像とマスク画像を読み込み
    cv::Mat image = cv::imread("image.jpg");
    cv::Mat mask = cv::imread("mask.png", cv::IMREAD_GRAYSCALE);
    if(image.empty() || mask.empty()){
        std::cerr << "画像またはマスクの読み込みに失敗しました" << std::endl;
        return -1;
    }
    cv::Mat binaryMask;
    cv::threshold(mask, binaryMask, 128, 255, cv::THRESH_BINARY);
    cv::Mat result;
    // マスク画像の白い領域のみ抽出
    cv::bitwise_and(image, image, result, binaryMask);
    cv::imshow("Result", result);
    cv::waitKey(0);
    return 0;
}
ウィンドウが表示され、マスクが適用された画像が確認できます。

応用処理と活用例

部分領域へのフィルタ適用

ノイズ除去との組み合わせ

マスクを用いて特定の部分だけにフィルタ処理を適用する例として、ノイズ除去処理との組み合わせが挙げられます。

たとえば、画像全体にぼかし処理を実施した後、マスクを利用して元の画像の重要な部分だけを維持する方法があります。

以下のサンプルでは、マスク領域にのみガウシアンフィルタを適用して、部分的にぼかし効果を実現しています。

#include <opencv2/opencv.hpp>
#include <iostream>
int main(){
    // 入力画像とマスク画像を読み込み
    cv::Mat image = cv::imread("image.jpg");
    cv::Mat mask = cv::imread("mask.png", cv::IMREAD_GRAYSCALE);
    if(image.empty() || mask.empty()){
        std::cerr << "画像またはマスクの読み込みに失敗しました" << std::endl;
        return -1;
    }
    cv::Mat binaryMask;
    cv::threshold(mask, binaryMask, 128, 255, cv::THRESH_BINARY);
    cv::Mat blurred;
    // 画像全体に対してガウシアンフィルタを適用
    cv::GaussianBlur(image, blurred, cv::Size(15, 15), 0);
    // 元の画像をコピーし、マスク部分にぼかし処理を適用
    cv::Mat result;
    image.copyTo(result);
    blurred.copyTo(result, binaryMask);
    cv::imshow("Filtered Result", result);
    cv::waitKey(0);
    return 0;
}
ウィンドウが表示され、マスクで指定された部分にのみガウシアンフィルタが適用された画像が確認できます。

複数マスクの統合処理

領域ごとの個別処理

場合によっては、画像内の複数の領域に対して異なる処理を施す必要があります。

たとえば、顔部分の明るさ調整と背景のノイズ除去などです。

下記の表は、領域ごとに適用する処理内容の一例です。

領域処理内容
顔部分明るさ調整
背景ノイズ除去
物体領域エッジ抽出

次のサンプルコードでは、顔部分と背景を異なるマスクで分け、それぞれに個別の画像処理を実行しています。

#include <opencv2/opencv.hpp>
#include <iostream>
int main(){
    // 入力画像を読み込み
    cv::Mat image = cv::imread("image.jpg");
    if(image.empty()){
        std::cerr << "画像の読み込みに失敗しました" << std::endl;
        return -1;
    }
    // 複数のマスクを生成する例
    cv::Mat faceMask = cv::Mat::zeros(image.size(), CV_8UC1);
    cv::Mat backgroundMask = cv::Mat::zeros(image.size(), CV_8UC1);
    // 顔部分を矩形で定義した場合の例(サンプル用)
    cv::rectangle(faceMask, cv::Point(50, 50), cv::Point(150, 150), cv::Scalar(255), -1);
    // 背景は顔以外の部分として算出
    cv::bitwise_not(faceMask, backgroundMask);
    // 顔領域の明るさを調整(少し明るくする)
    cv::Mat faceAdjusted;
    image.copyTo(faceAdjusted);
    faceAdjusted += cv::Scalar(50, 50, 50);
    // 背景のノイズ除去(ガウシアンフィルタを適用)
    cv::Mat backgroundFiltered;
    cv::GaussianBlur(image, backgroundFiltered, cv::Size(9, 9), 0);
    cv::Mat result = image.clone();
    // マスクに基づき、顔領域と背景それぞれに別々の処理結果を適用
    faceAdjusted.copyTo(result, faceMask);
    backgroundFiltered.copyTo(result, backgroundMask);
    cv::imshow("Multi-mask Processing", result);
    cv::waitKey(0);
    return 0;
}
ウィンドウが表示され、顔部分が明るさ調整され、背景がぼやけた画像が確認できます。

エラー対処と注意点

マスク画像の品質チェック

画像処理を進める前に、読み込まれたマスク画像のサイズやチャンネル数が元の画像と一致しているか確認することが大切です。

サイズが異なる場合は、cv::resizeなどで調整する必要があります。

また、誤ってカラー画像で読み込まれると、想定外の挙動になる可能性があるため、グレースケールでの読み込みを確認してください。

一般的な課題の原因と解決策

画像処理でトラブルが発生する原因として、以下の項目が挙げられます。

  • 入力画像とマスク画像のサイズが一致しない
  • マスク画像がカラー形式で読み込まれている
  • 閾値設定が適切でなく、指定領域が正確に抽出できない

マスク適用時の処理不一致への対策

これらの問題に対しては、以下の対策を施すと改善につながります。

  • 画像のサイズや解像度を事前に確認し、cv::resizeで統一する
  • マスク画像の読み込み時に必ずグレースケールモードを指定する
  • 複数の閾値処理を試し、最も適した値を選定する

まとめ

今回説明した内容では、画像の特定領域を効果的に抽出するためのマスク処理の基本原理や、手動およびプログラムによるマスク画像の作成方法について紹介しました。

画像とマスクの読み込みから前処理、そしてcv::bitwise_andなどの関数を用いた実践的なマスク適用手順も取り上げました。

また、領域ごとに異なる処理を施す応用例や、処理過程で遭遇しやすいエラーへの対策についても触れました。

各サンプルコードは実際に動かしながら確認できるように作成しており、柔軟な画像処理を実現するヒントとして活用してもらえれば嬉しいです。

関連記事

Back to top button
目次へ