Java版OpenCVでラベリングした画像の色を変えたい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 906

jannema

score 7

 前提・実現したいこと

Java版OpenCV3.4.2で二値画像にラベリングをし、連結成分の面積が一番大きな部分以外を黒で塗りつぶしたいです(切り抜き)。
connectedComponentsWithStatsを用いて面積情報を取得するところまではできたのですが
指定したラベルの連結成分の色を変えることができずに困っています。

 該当のソースコード

 public void getframe(Mat src_img, Mat dst_img){

        int rows = src_img.width();
        int cols = src_img.height();

//二値化
        Mat gray_img = new Mat(cols,rows, CvType.CV_8UC1);
        Imgproc.cvtColor(src_img,gray_img,Imgproc.COLOR_RGB2GRAY);
        int blockSize=Math.min(cols,rows)/40*2+1;
        Imgproc.adaptiveThreshold(gray_img,dst_img,255,Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY_INV,blockSize,3);


//ラベリング実行
 Mat labelImage = new Mat(cols,rows,CvType.CV_32S);
        Mat stats = new Mat();
        Mat centroids = new Mat();

        int nLabels = Imgproc.connectedComponentsWithStats(dst_img,labelImage,stats,centroids,8,CvType.CV_32S);



//面積情報を取得
int[] result = new int[nLabels];
        int max = 0;
        int count = 0;

        for(int i=1;i<nLabels;i++){
            stats.get(i,Imgproc.CC_STAT_AREA,result);
            if(max < result[i]){
                max = result[i];
                count = i;
            }
        }

//連結成分の中で一番面積が大きいもの以外を黒塗り
     for(int i=1;i<nLabels;i++){
            if(i != count){
                //黒で塗りつぶす
            }
        }

}

 試したこと

c++でのconnectedComponentsにおける指定したラベルの塗りつぶしを参考にしています。

https://github.com/atinfinity/lab/wiki/OpenCV%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%83%A9%E3%83%99%E3%83%AA%E3%83%B3%E3%82%B0

しかし、Java版OpenCVに画素値を変更するVec3bが無いようで、
代わりに指定した座標の画素値を取り出す「mat.get()」と
指定した座標の画素値を変更する「mat.put()」を利用しようと試みたのですがどうにもうまくいきません。

そもそも参考にしているサイトにおいて

std::vector<cv::Vec3b> colors(nLabels);


というVec3b型の可変長配列を作っているところまでは理解できるのですが

colors[0] = cv::Vec3b(0, 0, 0);


上記のようにcolols[ラベルの番号]だけで指定したラベルにアクセスできているというのが理解できません。
colorsはあくまでnLabels個のVec3b型の配列として定義されただけなのに
なぜラベル番号を引数に入れるだけで該当ラベルの画素値を操作できるようになるのでしょうか。

伝わりづらいしし質問で申し訳ありませんがご教授いただけると幸いです。

std::vector<cv::Vec3b> colors(nLabels);
    colors[0] = cv::Vec3b(0, 0, 0);
    for(int label = 1; label < nLabels; ++label)
    {
        colors[label] = cv::Vec3b((rand()&255), (rand()&255), (rand()&255));
    }

    // ラベリング結果の描画
    cv::Mat dst(src.size(), CV_8UC3);
    for(int y = 0; y < dst.rows; ++y)
    {
        for(int x = 0; x < dst.cols; ++x)
        {
            int label = labelImage.at<int>(y, x);
            cv::Vec3b &pixel = dst.at<cv::Vec3b>(y, x);
            pixel = colors[label];
        }
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

上記のようにcolols[ラベルの番号]だけで指定したラベルにアクセスできているというのが理解できません。

std::vector<cv::Vec3b> colors;


と宣言した場合は空の配列なので、いきなりcolors[0]と指定はできませんが、

std::vector<cv::Vec3b> colors(n);


とn個の要素を用意する指定があるためにcolors[0]colors[n-1]にアクセスできます。

参考:C++ 動的配列クラス std::vector 入門
4-2.要素数を指定した宣言を参照。

指定したラベルにアクセスできるようにしているのは、以下のコードです。

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


ここの&は参照型といい、この型で宣言した変数に値の代入すると、参照元もその値に変更されるという便利な型なのです。
参考:参照 (C++)
C++入門:3章 C++の新しい文法-> 参照型

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

参照型について学ぶことができましたありがとうございました。

指定したラベルの色を変えるということはコードを書き換えた結果成功しました。
連結成分の面積(CC_STAS_AREA)を取得する際に

     stats.get(i,Imgproc.CC_STAT_AREA,result);
            if(max < result[i]){
                max = result[i];
                count = i;
            }

としていましたが

     stats.get(i,Imgproc.CC_STAT_AREA,result);
            if(max < result[0]){
                max = result[0];
                count = i;
            }

とすることで正しく連結成分の面積情報を取得することができました

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる