web-dev-qa-db-ja.com

opencvでのCanny操作の低しきい値と高しきい値の自動計算

OpenCVでは、キャニー演算子の低しきい値と高しきい値が必須です。

cvCanny(input,output,thresh1,thresh2)

Matlabには、これらを自動的に計算するオプションがあります:

Edge(input,'canny')

Edge用のMatlabのコードを調べましたが、これは実際にそれらを自動的に計算するのは簡単ではありません。

Opencvの自動しきい値計算とともに、キャニー演算子の実装を知っていますか?

ありがとう

32
hoffer

Cannyのしきい値を自動的に計算する方法を探していたときに、この答えを見つけました。

これが、Cannyのアルゴリズムの自動しきい値を決定するための優れた方法を探している人に役立つことを願っています...


画像が前景と背景で構成されている場合、前景オブジェクトのエッジは次のように抽出して使用できます。

  1. 以下を使用して大津のしきい値を計算します。

    double otsu_thresh_val = cv::threshold(
        orig_img, _img, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU
    );
    

    _imgは必要ありません。 otsu_thresh_valのみに関心がありますが、残念ながら、現在、OpenCVにはしきい値のみを計算できるメソッドはありません。

  2. 大津のしきい値をより高いしきい値として使用し、Cannyのアルゴリズムの半分より低いしきい値として半分を使用します。

    double high_thresh_val  = otsu_thresh_val,
           lower_thresh_val = otsu_thresh_val * 0.5;
    cv::Canny( orig_img, cannyOP, lower_thresh_val, high_thresh_val );
    

これに関連する詳細情報は、 このペーパー:Canny OperatorでのOtsuメソッドの適用に関する研究 にあります。 Otsuの実装の説明は here にあります。

46
VP.

入力グレースケールイメージの平均値を使用し、標準偏差を使用して下限と上限のしきい値を定義できます。より詳細な説明とopencvコードはここにあります: http://www.kerrywong.com/2009/05/07/canny-Edge-detection-auto-thresholding/

16
Luca Del Tongo

また、これをOpenCVビルドに配置することにより、これを自動的に行うためのコードが用意されています。 OpenCV-usersメーリングリストで見つけたので、保証はありません。 :)

議論: http://opencv-users.1802565.n2.nabble.com/Automatic-thresholding-in-cvCanny-td5871024.html GitHub(コード): https:// Gist .github.com/7568

7
Dan

このリンクを確認してください: http://www.pyimagesearch.com/2015/04/06/zero-parameter-automatic-canny-Edge-detection-with-python-and-opencv/

彼らはbasic statisticsを使用して同様のソリューションを実装し、Canny Edge検出の低しきい値と高しきい値を決定します。

def auto_canny(image, sigma=0.33):
     # compute the median of the single channel pixel intensities
     v = np.median(image)

    # apply automatic Canny Edge detection using the computed median
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) * v))
    edged = cv2.Canny(image, lower, upper)

    # return the edged image
    return edged
4
Ryan Marten

Matlab Canny Edge検出のソースコードに目を通し、OpenCV 3でJavaで記述しました。

private static Mat getpartialedge(Mat image){
    double nonEdgeRate = 0.6;
    double thresholdRate = 0.6;
    double w = image.cols();
    double h = image.rows();
    int bins = 256;
    Mat sobel = new Mat();
    Mat sobelx = new Mat();
    Mat sobely = new Mat();
    Mat sobelxabs = new Mat();
    Mat sobelyabs = new Mat(); 
    Size gsz = new Size(5, 5);
    if(false) {
        Imgproc.Canny(image, sobel, 41, 71);
    }else {

        //Imgproc.GaussianBlur(graycopy,graycopy, gsz, 2);
        //Imgproc.dilate(image, image, kernel8);
        Imgproc.GaussianBlur(image, image, gsz, 2);


        int apertureSize = 3;
        Imgproc.Sobel(image, sobelx, CvType.CV_16S, 1, 0, apertureSize, 1, 0);
        Core.convertScaleAbs(sobelx, sobelxabs);
        Imgproc.Sobel(image, sobely, CvType.CV_16S, 0, 1, apertureSize, 1, 0);
        Core.convertScaleAbs(sobely, sobelyabs);
        Core.addWeighted(sobelxabs, 1, sobelyabs, 1, 0, sobel);
        sobel.convertTo(sobel, CvType.CV_8U);


        Mat equalized = new Mat();
        Imgproc.equalizeHist(sobel, equalized);
        Imgcodecs.imwrite(filePath + "aftersobel(eq).png", equalized);
        Imgcodecs.imwrite(filePath + "aftersobel.png", sobel);


        Mat hist = new Mat();
        List<Mat> matList = new ArrayList<Mat>();
        matList.add(sobel);
        Imgproc.calcHist(matList, new MatOfInt(0), new Mat(), hist, new MatOfInt(bins), new MatOfFloat(0f, 256f));
        float accu = 0;
        float t = (float) (nonEdgeRate * w * h);
        float bon = 0;
        float[] accutemp = new float[bins];
        for (int i = 0; i < bins; i++) {
            float tf[] = new float[1];
            hist.get(i, 0, tf);
            accu = accu + tf[0];
            accutemp[i] = accu;
            if (accu > t) {
                bon = (float) i;
                break;
            }
        }
        Imgproc.threshold(sobel, sobel, bon, 255, Imgproc.THRESH_BINARY);
        double ut = bon;
        double lt = thresholdRate * bon;


        Imgproc.Canny(image, sobel, lt, ut);
        //Imgproc.dilate(sobel, sobel, kernel2);
    }
    return sobel;
}

ファイルパスは、出力イメージを保持する場所です。また、入力画像はU8データ型のグレースケール画像でなければなりません。基本原則は、明るさによってnonEdgeRate(60%)ピクセルを非Edgeピクセルとして除外することです。ヒストグラムを使用して明るさを並べ替え、その下に60%のピクセルがあるように上限しきい値を設定します。下限しきい値は、上限しきい値にthresholdRate(0.6)を掛けて設定されます。

二重のnonEdgeRate = 0.6と二重のthresholdRate = 0.6は、特定のユースケースで自分で調整することに注意してください。元の値は、matlabでは0.7と0.4です。

3
fangzhan shi

同じ問題に対する別のアプローチがあります。このソリューションには、エッジ検出の最適なしきい値の選択も含まれます。

  • まずグレースケール画像のmedianを計算します。
  • グレースケールイメージの中央値に基づいて、2つの値(下限と上限のしきい値)を選択します。

次の擬似コードは、その実行方法を示しています。

v = np.median(gray_img)
sigma = 0.33

#---- apply optimal Canny Edge detection using the computed median----
lower_thresh = int(max(0, (1.0 - sigma) * v))
upper_thresh = int(min(255, (1.0 + sigma) * v))

これらのしきい値をキャニーエッジ検出機能のパラメーターとして修正します。

イラスト:統計でガウス曲線を観察する場合、曲線の両側から0.33の間の値が分布で考慮されます。これらのポイントの外側の値はすべて外れ値と見なされます。画像はデータと見なされるため、この概念もここで想定されます。

1
Jeru Luke

Luca Del Tongoが示唆したように、グレー画像からしきい値を計算できます。 in Java OpenCVを使用...

MatOfDouble mu = new MatOfDouble();
MatOfDouble stddev = new MatOfDouble();
Core.meanStdDev(greyMat, mu, stddev);
threshold1 = mu.get(0, 0)[0];
threshold2 = stddev.get(0, 0)[0];
Imgproc.Canny(greyMat, outputMat, threshold1, threshold2);
0
Buddhisthead