O OpenCV afia as arestas (arestas sem furos)

Estou tentando detectar a forma retangular maior / maior e desenhar a caixa delimitadora na área detectada. No meu caso de uso, muitas vezes (e nem sempre) o objeto que representa a forma do retângulo está na cor branca e o fundo também na cor muito semelhante ao branco.

Antes de detectar contornos, pré-processei a imagem para detectar a borda perfeita. Meu problema é que não consigo detectar as bordas perfeitamente e tenho muito ruído, mesmo depois de desfocar e usar 'limiar adaptável' ou 'limiar'.

A imagem original que usei para a detecção de contornos

Eu tentei uma maneira diferente de detectar a borda perfeita em diferentes condições de iluminação sem sucesso.

Como posso processar a imagem para detectar bordas perfeitas (bordas sem furos) para a detecção de contornos?

Abaixo está o código que estou usando

public static Mat findRectangleX(Mat original) {
  Mat src = original.clone();
  Mat gray = new Mat();
  Mat binary = new Mat();
  MatOfPoint2f approxCurve;
  List<MatOfPoint> contours = new ArrayList<MatOfPoint>();

    if (original.type() != CvType.CV_8U) {
        Imgproc.cvtColor(original, gray, Imgproc.COLOR_BGR2GRAY);
    } else {
        original.copyTo(gray);
    }

    Imgproc.GaussianBlur(gray, gray, new Size(5,5),0);
    Imgproc.adaptiveThreshold(gray, binary, 255,Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY_INV,11, 1);

    //Imgproc.threshold(gray, binary,0,255,Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);


    double maxArea = 0;
    Imgproc.findContours(binary, contours, new Mat(),Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);

    for (int i = 0; i<contours.size();i++) {
        MatOfPoint contour = contours.get(i);
        MatOfPoint2f temp = new MatOfPoint2f(contour.toArray());
        double area = Imgproc.contourArea(contour);
        approxCurve = new MatOfPoint2f();
        Imgproc.approxPolyDP(temp, approxCurve, Imgproc.arcLength(temp, true) * 0.03, true);

        if (approxCurve.total() == 4 ) {
            Rect rect = Imgproc.boundingRect(contours.get(i));
            Imgproc.rectangle(src, rect.tl(), rect.br(), new Scalar(255, 0, 0, .8), 4);
            if(maxArea < area)
                maxArea = area;
        }
    }

    Log.v(TAG, "Total contours found : " + contours.size());
    Log.v(TAG, "Max area :" + maxArea);

    return src;

}

Eu procurei problemas semelhantes no stackoverflow e tente o exemplo de código, mas algum deles funcionou para mim. A dificuldade que eu acho é o objeto branco sobre fundo branco.

Como posso processar a imagem para afiar as bordas para a detecção de contorno?

Como posso detectar a forma retangular maior / maior e desenhar uma linha retangular na forma detectada?

// Atualizado em: 20/02/2017

Eu tentei a solução sugerida por @ Nejc no post abaixo. A segmentação é melhor, mas ainda tenho orifícios no contorno e o findcontours falha na detecção do contorno maior. Abaixo está o código fornecido por @Nejc e traduzido para java.

public static Mat process(Mat original){
    Mat src = original.clone();
    Mat hsvMat = new Mat();
    Mat saturation = new Mat();
    Mat sobx = new Mat();
    Mat soby = new Mat();
    Mat grad_abs_val_approx = new Mat();

    Imgproc.cvtColor(src, hsvMat, Imgproc.COLOR_BGR2HSV);
    List<Mat> hsv_channels = new ArrayList<Mat>(3);
    Core.split(hsvMat, hsv_channels);
    Mat hue = hsv_channels.get( 0 );
    Mat sat = hsv_channels.get( 1 );
    Mat val = hsv_channels.get( 2 );

    Imgproc.GaussianBlur(sat, saturation, new Size(9, 9), 2, 2);
    Mat imf = new Mat();
    saturation.convertTo(imf, CV_32FC1, 0.5f, 0.5f);

    Imgproc.Sobel(imf, sobx, -1, 1, 0);
    Imgproc.Sobel(imf, soby, -1, 0, 1);

    sobx = sobx.mul(sobx);
    soby = soby.mul(soby);

    Mat abs_x = new Mat();
    Core.convertScaleAbs(sobx,abs_x);
    Mat abs_y = new Mat();
    Core.convertScaleAbs(soby,abs_y);
    Core.addWeighted(abs_x, 1, abs_y, 1, 0, grad_abs_val_approx);

    sobx.release();
    soby.release();


    Mat filtered = new Mat();
    Imgproc.GaussianBlur(grad_abs_val_approx, filtered, new Size(9, 9), 2, 2);

    final MatOfDouble mean = new MatOfDouble();
    final MatOfDouble stdev = new MatOfDouble();
    Core.meanStdDev(filtered, mean, stdev);

    Mat thresholded = new Mat();
    Imgproc.threshold(filtered, thresholded, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_TOZERO);

    /*
    Mat thresholded_bin = new Mat();
    Imgproc.threshold(filtered, thresholded_bin, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_BINARY);
    Mat converted = new Mat();
    thresholded_bin.convertTo(converted, CV_8UC1);
    */

    return thresholded;
}

Aqui está a imagem que eu tenho depois de executar o código acima

Imagem após usar a solução @Nejc

1) Por que meu código traduzido não gera a mesma imagem como @Nejc? O mesmo código aplicado à mesma imagem deve produzir a mesma saída?

2) perdi alguma coisa ao traduzir?

3) Para meu entendimento, por que multiplicamos a imagem por si só nesta instrução sobx = sobx.mul (sobx); ?

questionAnswers(2)

yourAnswerToTheQuestion