I have a fairly blurry 432x432 image of a Sudoku puzzle that doesn\'t adaptively threshold well (take the mean over a block size of 5x5 pixels, then subtract 2):
A pretty good solution is to use morphological closing to make the brightness uniform and then use a regular (non-adaptive) Otsu threshold:
// Divide the image by its morphologically closed counterpart
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(19,19));
Mat closed = new Mat();
Imgproc.morphologyEx(image, closed, Imgproc.MORPH_CLOSE, kernel);
image.convertTo(image, CvType.CV_32F); // divide requires floating-point
Core.divide(image, closed, image, 1, CvType.CV_32F);
Core.normalize(image, image, 0, 255, Core.NORM_MINMAX);
image.convertTo(image, CvType.CV_8UC1); // convert back to unsigned int
// Threshold each block (3x3 grid) of the image separately to
// correct for minor differences in contrast across the image.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Mat block = image.rowRange(144*i, 144*(i+1)).colRange(144*j, 144*(j+1));
Imgproc.threshold(block, block, -1, 255, Imgproc.THRESH_BINARY_INV+Imgproc.THRESH_OTSU);
}
}
Result:
Take a look at Smoothing Images OpenCV tutorial. Except GaussianBlur there are also medianBlur
and bilateralFilter
which you can also use to reduce noise. I've got this image from your source image (top right):
Update: And the following image I got after removing small contours:
Update: also you can sharpen image (for example, using Laplacian
). Look at this discussion.
Always apply gaussian for better results.
cvAdaptiveThreshold(original_image, thresh_image, 255,
CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 11, 2);