質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

90.22%

[OpenCV for Android]検出した直線全ての上に赤線を表示させたい[HoughLinesP]

解決済

回答 2

投稿 編集

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

mr_shirohige

score 7

前提・実現したいこと

Android StudioにてOpenCV for Androidを用い、端末内の画像を読み取り、直線検出を行ったのち、元画像にその直線全ての上に赤線を表示させる動作の実現を目指しています。

発生している問題・エラーメッセージ

五線譜の実線だけが引かれた画像をサンプルとして用いたのですが、直線が一本だけしか検出されず他の直線の上に赤線が引かれていません。
サンプルイメージ説明
エッジ検出までの実行結果イメージ説明
実行結果イメージ説明
初心者なため、おかしなコードの書き方をしているかもしれませんが、何卒訂正、解決策をご教授いただければ幸いです。

エラーメッセージ

該当のソースコード

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;

import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

import java.io.FileDescriptor;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {
    private static final int RESULT_PICK_IMAGEFILE = 1000;
    private ImageView imageView;
    Bitmap btmp;
    Bitmap rtn_btmap;

    static {
        System.loadLibrary("opencv_java3");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = (ImageView) findViewById(R.id.image_view);

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("image/*");
                startActivityForResult(intent, RESULT_PICK_IMAGEFILE);
            }
        });
    }

    protected void onRestart() {
        super.onRestart();
        onImageScan();
    }

    public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
        if (requestCode == RESULT_PICK_IMAGEFILE && resultCode == RESULT_OK) {
            Uri uri = null;
            if (resultData != null) {
                uri = resultData.getData();

                try {
                    Bitmap bmp = getBitmapFromUri(uri);
                    imageView.setImageBitmap(bmp);
                    btmp = Bitmap.createBitmap(bmp);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Bitmap getBitmapFromUri(Uri uri) throws IOException {
        ParcelFileDescriptor parcelFileDiscriptor = getContentResolver().openFileDescriptor(uri, "r");
        FileDescriptor fileDescriptor = parcelFileDiscriptor.getFileDescriptor();
        Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
        parcelFileDiscriptor.close();
        return image;
    }

    private void onImageScan() {
        Mat mat = new Mat();
        Utils.bitmapToMat(btmp, mat);

        mat = getThreshold(mat);
        rtn_btmap = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(mat, rtn_btmap);
        imageView.setImageBitmap(rtn_btmap);
    }

    private Mat getThreshold(Mat mat) {
        Imgproc.cvtColor(mat, mat, Imgproc.COLOR_BGR2GRAY);
        Imgproc.threshold(mat, mat, 225, 255, Imgproc.THRESH_BINARY_INV);
        Imgproc.Canny(mat,mat,10,225,3,true);
        Mat linesMat = new Mat();
        Imgproc.HoughLinesP(mat, linesMat, 1, Math.PI / 180, 300);
        Mat bmpmat = new Mat();
        Utils.bitmapToMat(btmp, bmpmat);
        double[] data;
        Point pt1 = new Point();
        Point pt2 = new Point();
        for (int i = 0; i < linesMat.cols(); i++){
                data = linesMat.get(0, i);
                pt1.x = data[0];
                pt1.y = data[1];
                pt2.x = data[2];
                pt2.y = data[3];
                Imgproc.line(bmpmat, pt1, pt2, new Scalar(255, 0, 0), 1);
        }
        return (bmpmat);
    }
}

試したこと

HoughLinesP()内のthresholdの値等のパラメータ調整を行ってみましたが、赤線が引かれる直線の位置が変化するのみででした。
このサイト様(http://google-os.blog.jp/archives/50716505.html)を参考にしHoughLinesP~Imgproc.lineの処理を置き換えて見たところ下記のエラーが出ました。(4890fcd826a22845b8803bb923300310.jpeg)
Caused by: java.lang.UnsupportedOperationException:Provided data element number (2) should be multiple of the Mat channels count (4)

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • iwanote

    2019/01/24 00:52

    そちらではなくMath.PI/180のほうですね

    キャンセル

  • mr_shirohige

    2019/01/24 01:15

    180の部分の数値を変更するということでよろしかったでしょうか?先ほどはその数値を変更していましたが…初心者ゆえ見当違いな発言でしたらご容赦ください

    キャンセル

  • iwanote

    2019/01/24 01:30

    すみません見間違えてました合ってますね…すみません

    キャンセル

回答 2

checkベストアンサー

+1

colとrowが逆ではないでしょうか.以下のC#コード(OpenCV for Unityを使用,Javaベースなので同じはず)で正しく検出できました.
人工画像で一本しか検出しないとは考えにくいので,パラメータよりも表示の方を疑うべきかと思います.

int rows = linesMat.rows();
for (int i = 0; i < rows; i++){
    data = linesMat.get(i, 0);
    pt1.x = data(i, 0)[0];
    pt1.y = data(i, 0)[1];
    pt2.x = data(i, 0)[2];
    pt2.y = data(i, 0)[3];
    line(bmpmat, pt1, pt2, new Scalar(255, 0, 0), 1);
}


あと,for文の条件式で毎回メソッド呼ぶより事前に変数に格納しておく方が処理時間短くなると思います.

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/01/24 02:32

    こちらのコードを参考にさせていただいたところ、無事想定していた検出結果を得ることができました。
    画像処理やプログラミングに関してまだまだ素人なため、精進したいと思います。
    また何か質問した際、お目に止まれば回答していただければ幸いです。
    非常に助かりました、回答ありがとうございました。

    キャンセル

  • 2019/01/24 03:01

    Matの構造はややこしいので気をつけてください.
    お役に立ててよかったです.

    キャンセル

+1

以下のコード、パラメータで検出できることを確認しました。
Python ですが、Java でも OpenCV の処理自体は同じはずなので、検出できるかと思います。

前処理

import cv2
import numpy as np

img = cv2.imread('test.jpeg')
# BGR -> グレースケール
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ガウシアンフィルタ
gauss = cv2.GaussianBlur(gray, (9, 9), 0)
# Canny
edges = cv2.Canny(gauss, 100, 150)

HoughLinesP() を使った直線検出

lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, 100, 10)
lines = lines.squeeze(axis=1)  # (NumLines, 1, 4) -> (NumLines, 4)

dst = img.copy()
for x1, y1, x2, y2 in lines:
    cv2.line(dst, (x1, y1), (x2, y2), (0, 0, 255), 1)

cv2.imwrite('houghlinesp_result.png', dst)

イメージ説明

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/01/24 02:41

    ご回答ありがとうございます。
    他の回答者様より、表示のコードのミスであるとご指摘いただき、参考にさせていただいたところ無事動作致しました。
    ガウシアンフィルタ等の処理やpythonでのOpenCVの記述法は、後学のために参考にさせていただきたいと思います。

    キャンセル

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

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