OpenCV afila los bordes (bordes sin agujeros)

Estoy tratando de detectar la forma rectangular más grande / más grande y dibujar un cuadro delimitador en el área detectada. En mi caso de uso, muy a menudo (y no siempre) el objeto que representa la forma del rectángulo es de color blanco y el fondo también es de color muy similar al blanco.

Antes de detectar contornos, he preprocesado la imagen para detectar el borde perfecto. Mi problema es que no puedo detectar bordes perfectamente y tengo mucho ruido incluso después de desenfocar y usar 'umbral adaptativo' o 'umbral'.

La imagen original que he usado para la detección de contornos.

He intentado diferentes formas de detectar el borde perfecto en diferentes condiciones de iluminación sin éxito.

¿Cómo puedo procesar la imagen para detectar un borde perfecto (bordes sin agujeros) para la detección de contornos?

A continuación se muestra el código que estoy 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;

}

He buscado problemas similares en stackoverflow e intento código de muestra, pero ninguno de ellos funcionó para mí. La dificultad, creo, es el objeto blanco sobre fondo blanco.

¿Cómo puedo procesar la imagen para afilar los bordes para la detección de contornos?

¿Cómo puedo detectar la forma de rectángulo más grande / más grande y dibujar una línea de rectángulo a la forma detectada?

// Actualizado en: 20/02/2017

He probado la solución sugerida por @Nejc en la publicación a continuación. La segmentación es mejor, pero todavía tengo agujeros en el contorno y findcontours falla al detectar el contorno más grande. A continuación se muestra el código proporcionado por @Nejc y traducido a 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;
}

Aquí está la imagen que obtuve después de ejecutar el código anterior

Imagen después de usar la solución @Nejc

1) ¿Por qué mi código traducido no muestra la misma imagen que @Nejc? ¿El mismo código aplicado a la misma imagen debería producir la misma salida?

2) ¿me perdí algo al traducir?

3) A mi entender, ¿por qué multiplicamos la imagen por sí misma en esta instrucción sobx = sobx.mul (sobx); ?

Respuestas a la pregunta(2)

Su respuesta a la pregunta