I'd like to adaptively threshold this image to find the outer border using findContours()
from OpenCV. I use an adaptive threshold for the usual reason: global thresholding, even with Otsu's method, does not adequately compensate for differences in brightness between different parts of the image.
Unfortunately, adaptive thresholding creates breakages at some of the intersections with thick gridlines. This is because, for the pixels at the intersection, the thick gridlines take up so much of the surrounding region that the local threshold is raised above the (only moderately dark) value of the pixels at the intersection. Surprisingly, this effect still holds to some degree even for large threshold windows.
Of course, this makes the adaptive threshold useless for finding contours in these types of images. However, it's still much better overall than other algorithms like Canny at coming up with connected edges.
I've been able to reconnect the edges after adaptive thresholding by manually filling in all the one- and two-pixel gaps in the image (I actually threshold on a scaled-down image to save runtime; the gaps are larger in the above full-size image). Here is the OpenCV code I used (written for the Android bindings). 0 is black and -1 is white.
private void fillGaps(Mat image) {
int size = image.rows() * image.cols();
byte[] src = new byte[size], dst = new byte[size];
image.get(0, 0, src);
int c = image.cols();
int start = 2 * c + 2;
int end = size - start;
for (int i = start; i < end; i++) {
if (src[i+1] == -1 && src[i-1] == -1 || src[i+c] == -1 && src[i-c] ==-1){
// 1-pixel gap
dst[i] = -1;
} else if (src[i+1] == 0 && src[i+2 ] == -1 && src[i-1] == -1) {
// 2-pixel horizontal gap
dst[i] = -1; dst[i+1] = -1;
} else if (src[i+c] == 0 && src[i+2*c] == -1 && src[i-c] == -1) {
// 2-pixel vertical gap
dst[i] = -1; dst[i+c] = -1;
}
}
image.put(0, 0, dst);
}
Here is the scaled-down image before and after filling in the gaps:
Although this works fairly well here, it is a crude technique, doesn't fill in all the gaps, and sometimes joins the grid with other nearby contours.
What is a reliable way to avoid disconnected contours after an adaptive threshold?