Alexander Soare
Alexander Soare

Reputation: 3247

Can I add a bias to Otsu thresholding in OpenCV?

Here's my example. Left to right:

  1. original image
  2. grayscale + (3,3) gaussian blur
  3. Otsu thresholding + invert pixels

enter image description here

I want to capture more of the faint part of the pen stroke. I understand that Otsu Thresholding tries to apply the threshold point in between two peaks of the pixel intensity histogram, but I'd like to bias that a bit so I can capture some of the lighter pixels.

Is it possible out of the box? Or do I need to do something manual?

Upvotes: 3

Views: 1744

Answers (3)

shortcipher3
shortcipher3

Reputation: 1380

An alternative to a biased Otsu threshold is to do region based thresholding, something like this:

thr = .8
blur_hor = cv2.filter2D(img[:, :, 0], cv2.CV_32F, kernel=np.ones((11,1,1), np.float32)/11.0, borderType=cv2.BORDER_CONSTANT)
blur_vert = cv2.filter2D(img[:, :, 0], cv2.CV_32F, kernel=np.ones((1,11,1), np.float32)/11.0, borderType=cv2.BORDER_CONSTANT)
output = ((img[:,:,0]<blur_hor*thr) | (img[:,:,0]<blur_vert*thr)).astype(np.uint8)*255

Upvotes: 2

stateMachine
stateMachine

Reputation: 5805

In C++, I often "tune out" the threshold value returned by the (otsu) thresholding function multiplying it by a factor and passing it back to the (fixed) thresholding function:

//get the threshold computed by otsu:
double otsuThresh = cv::threshold( inputImage, otsuBinary, 0, 255,cv::THRESH_OTSU );

//tune the threshold value:
otsuThresh = 0.5 * otsuThresh;

//threshold the input image with the new value:
cv::threshold( inputImage, binaryFixed, otsuThresh, 255, cv::THRESH_BINARY );

Upvotes: 2

Alexander Soare
Alexander Soare

Reputation: 3247

I have an answer courtesy of the rubber duck phenomenon.

th, th_img = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)

The 0th index of the returned tuple (th) is the threshold value that the Otsu Binarization algorithm chose. I can discard th_img and apply whatever bias I like to th before using it in regular binary thresholding.

desired_th = th*1.2
_, th_img = cv2.threshold(blur, desired_th, 255, cv2.THRESH_BINARY)

Here's what I get. By cleaning up the unwanted speckles that could appear on the outside, I'll get what I was looking for.

enter image description here

Upvotes: 8

Related Questions