切り抜きたい(再発明 Ver.)


東です。

諸般の事情で、手元にある大量の画像をマスク処理するという事案が発生しました。
簡単に図で説明するとこんな感じです。
マスク処理

PhotoShopとかその手のソフトでやればできるんでしょうが、
・PhotoShop持ってないし。
・PhotoShop以外でよさげなソフトってどれ?
・1,000枚とか2,000枚とかの画像を手動で処理したくないし。
そういうわけで開発することにしました。OpenCV触りたかったし。

お題

素材画像(カラー)、マスク画像(グレースケール)を与えると、素材画像のうちマスク画像の黒色の部分が透過した画像を出力せよ。
ただし、素材画像の大きさは統一されていない。
ただし、素材画像の大きさがマスク画像の大きさを超えることはない。

どうやるの?

OpenCV CookBookのこのあたりにアルファチャンネル付PNG(=透過PNG)の取り扱い方が書かれています。
どうやら、素材画像をRGBの各チャンネルに分解して、マスク画像を4番目のチャンネル(アルファチャンネル)として貼り付けてあげれば良いようです。
ただし、素材画像の大きさに応じて、マスク画像の貼り付け位置を調整してあげる必要があります。
貼り付け位置の調整にはROI(Region of Interest、注目領域)を使います。

実装

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

int main(int argc, char** argv) {
    const char* input  = argv[1]; // 素材画像
    const char* mask   = argv[2]; // マスク画像
    const char* output = argv[3]; // 加工後の画像
    
    cv::Mat image_src  = cv::imread(input);
    cv::Mat image_mask = cv::imread(mask, 0);
    cv::Mat image_dest;
    
    // 素材画像をチャンネル(RGB)ごとに分離してvectorに格納する
    std::vector<cv::Mat> mv;
    cv::split(image_src, mv);
    
    // 注目領域:マスク画像の中心の素材画像の大きさの領域
    cv::Rect rect((image_mask.cols - image_src.cols) / 2, 
                  (image_mask.rows - image_src.rows) / 2,
                  image_src.cols,
                  image_src.rows);
    // vectorの最後尾にマスク画像の注目領域を追加する
    mv.push_back(cv::Mat(image_mask, rect));
    
    // vectorを結合して加工後の画像とする
    cv::merge(mv, image_dest);
    
    // 加工後の画像を出力する
    cv::imwrite(output, image_dest);
    
    return 0;
}

こんな感じでコンパイルします。

$ g++ -o mask mask.cpp -lopencv_core -lopencv_highgui

処理の流れを簡単に図で説明するとこんな感じです。
処理の流れ

終わりに

これでコマンド一発でマスク処理できる!OpenCVにも触れて大満足!
と、ひとりで悦に入っていたんですが...これってImageMagick使えばコマンドでできるんじゃない?再発明しちゃったんじゃない?ということに気付いてしまいました。

そういう訳で次回に続きます。


This entry was posted in 技術. Bookmark the permalink.