Reputation:
I want to threshold and split a word image into connected components (letters). Due to prior perspective transforms or bad resolution it can happen that single pixel bridges form between letters.
The components can be relatively thin, which is why a simple erode+dilate might destroy them.
Is there a simple way to erode single pixel bridges without creating every possible permutation of the erosion kernel for a bridge?
Example 1: Word is Pflegt
, the letters P
, f
and l
are connected
Example 2: This is more severe, also CCL seems to label regions the same that do not even touch on faces (despite being 4-connected).
Source:
# Label image
ret, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
connectivity = 4
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(thresh, connectivity, cv2.CV_32S)
# Map component labels to hue val
label_hue = np.uint8(179 * labels / np.max(labels))
blank_ch = 255 * np.ones_like(label_hue)
labeled_img = cv2.merge([label_hue, blank_ch, blank_ch])
# cvt to BGR for display
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)
Upvotes: 3
Views: 552
Reputation: 3115
So, this was a bit tricky to achieve, especially the more severe image, but in my experience when working with such pixelated images, unsharp mask always helps. So this is what I tried:
import numpy as np
import cv2
image = cv2.imread("U:/SO/cca.png")
imgray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Unsharp mask here
gaussian = cv2.GaussianBlur(imgray, (7, 7), 10.0)
unsharp_image = cv2.addWeighted(imgray, 2.5, gaussian, -1.5, 0, imgray)
cv2.imwrite("cca_2_unsharp.jpg", unsharp_image)
unsharp_image = cv2.erode(unsharp_image, np.ones((3,3), np.uint8), iterations=1)
ret,thresh = cv2.threshold(unsharp_image, 127, 255, cv2.THRESH_BINARY +
cv2.THRESH_OTSU)
cv2.imwrite("cca_2_thresh.jpg", thresh)
connectivity = 4
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(thresh,
connectivity, cv2.CV_32S)
# Map component labels to hue val
label_hue = np.uint8(179 * labels / np.max(labels))
blank_ch = 255 * np.ones_like(label_hue)
labeled_img = cv2.merge([label_hue, blank_ch, blank_ch])
# cvt to BGR for display
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)
cv2.imwrite("cca_2_res.jpg", labeled_img)
Results:
As you can see, the more severe image is still not working relatively well. In my trials, I did not need the erosion for the first image but it doesn't affect the result that much. I will try some other approaches and update my answer. Let me know what you think!
Upvotes: 3