Reputation: 95
I want to identify text in a set of images. There are some images with both white and black colored text.
I used otsu thresholding to binarize image
After contour identification and removal of non text regions I identified the required text region.
I need all the text in white color. But I don't know how to do it. I thought of using a bitwise operator but couldn't find a method. Can someone help me with this?
Expected output:
import cv2
import numpy as np
def process(img):
# read image
img_no = str(img)
rgb = cv2.imread(img_no + '.jpg')
# cv2.imshow('original', rgb)
# convert image to grayscale
gray = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY)
_, bw_copy = cv2.threshold(gray, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# bilateral filter
blur = cv2.bilateralFilter(gray, 5, 75, 75)
# cv2.imshow('blur', blur)
# morphological gradient calculation
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
grad = cv2.morphologyEx(blur, cv2.MORPH_GRADIENT, kernel)
# cv2.imshow('gradient', grad)
# binarization
_, bw = cv2.threshold(grad, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# cv2.imshow('otsu', bw)
# closing
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 1))
closed = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, kernel)
# cv2.imshow('closed', closed)
# finding contours
contours, hierarchy = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
mask = np.zeros(closed.shape, dtype=np.uint8)
mask1 = np.zeros(bw_copy.shape, dtype=np.uint8)
for idx in range(len(contours)):
x, y, w, h = cv2.boundingRect(contours[idx])
mask[y:y + h, x:x + w] = 0
area = cv2.contourArea(contours[idx])
aspect_ratio = float(w) / h
cv2.drawContours(mask, contours, idx, (255, 255, 255), -1)
r = float(cv2.countNonZero(mask[y:y + h, x:x + w])) / (w * h)
# identify region of interest
if r > 0.34 and 0.52 < aspect_ratio < 13 and area > 145.0:
cv2.drawContours(mask1, [contours[idx]], -1, (255, 255, 255), -1)
result = cv2.bitwise_and(bw_copy, mask1)
cv2.imshow('result', result)
print(img_no + " Done")
cv2.waitKey()
New Image
Accepted answer doesn't work with this picture.
Upvotes: 4
Views: 5218
Reputation: 851
I would recommend adjusting the colors in the original image before further processing. Identify the yellow band by walking down the first column of pixels. Convert the black in that region into white, and the yellow into black. Then convert any remaining yellow in the image to white. In both example images now all of the text is white on black, and ready for your subsequent processing.
Upvotes: 0
Reputation: 1839
At first glance this looks like a simple question but it is quite tricky to solve. However you already have all the ingredients needed to solve the problem and only require a slight tweak to your algorithm.
Here are the gists:
What you need is a an inverted image(wb_copy) of your thresholded image(bw_copy).
You have done a great job creating a mask
Run bitwise_and operation on both bw_copy and wb_copy with the mask above. You should get the result shown below.
As you can see, your answer is abit from both images. All you need to do is for every font blob, count the non-zero pixel from both images and select the one with the higher count. Doing so will provide the result you wanted.
Here are the modifications I made to the code
# finding contours
_,contours,_ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
mask = np.zeros(closed.shape, dtype=np.uint8)
mask1 = np.zeros(bw_copy.shape, dtype=np.uint8)
wb_copy = cv2.bitwise_not(bw_copy)
new_bw = np.zeros(bw_copy.shape, dtype=np.uint8)
for idx in range(len(contours)):
x, y, w, h = cv2.boundingRect(contours[idx])
mask[y:y + h, x:x + w] = 0
area = cv2.contourArea(contours[idx])
aspect_ratio = float(w) / h
cv2.drawContours(mask, contours, idx, (255, 255, 255), -1)
r = float(cv2.countNonZero(mask[y:y + h, x:x + w])) / (w * h)
# identify region of interest
if r > 0.34 and 0.52 < aspect_ratio < 13 and area > 145.0:
cv2.drawContours(mask1, [contours[idx]], -1, (255, 255, 255), -1)
bw_temp = cv2.bitwise_and(mask1[y:y + h, x:x + w],bw_copy[y:y + h, x:x + w])
wb_temp = cv2.bitwise_and(mask1[y:y + h, x:x + w],wb_copy[y:y + h, x:x + w])
bw_count = cv2.countNonZero(bw_temp)
wb_count = cv2.countNonZero(wb_temp)
if bw_count > wb_count:
new_bw[y:y + h, x:x + w]=np.copy(bw_copy[y:y + h, x:x + w])
else:
new_bw[y:y + h, x:x + w]=np.copy(wb_copy[y:y + h, x:x + w])
cv2.imshow('new_bw', new_bw)
Upvotes: 3