michael
michael

Reputation: 84

remove demarcation from text image - image processing

Hi I need to write a program that remove demarcation from gray scale image(image with text in it) i read about thresholding and blurring but still i dont see how can i do it.

my image is an image of a hebrew text like that:

input and i need to remove the demarcation(assuming that the demarcation is the smallest element in the image) the output need to be something like that

output I want to write the code in python using opencv, what topics do i need to learn to be able to do that, and how?

thank you.

Edit: I can use only cv2 functions

Upvotes: 0

Views: 790

Answers (3)

J.D.
J.D.

Reputation: 4561

The symbols you want to remove are significantly smaller than all other shapes, you can use that to determine witch ones to remove.

First use threshold to convert the image to binary. Next, you can use findContours to detect the shapes and then contourArea to determine if the shape is larger than a threshold.

Finally you can can create a mask to remove the unwanted shapes, draw the larger symbols on a new image or draw the smaller symbols in white over the original symbols in the original image - making them disappear. I used that last technique in the code below.

Result:

enter image description here

Code:

import cv2
# load image as grayscale
img = cv2.imread('1MioS.png',0)
# convert to binary. Inverted, so you get white symbols on black background
_ , thres = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY_INV)
# find contours in the thresholded image (this gives all symbols)
contours, hierarchy = cv2.findContours(thres, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# loop through the contours, if the size of the contour is below a threshold, 
# draw a white shape over it in the input image
for cnt in contours:
    if cv2.contourArea(cnt) < 250:
        cv2.drawContours(img,[cnt],0,(255),-1)
# display result
cv2.imshow('res', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Update

To find the largest contour, you can loop through them and keep track of the largest value:

maxArea = 0
for cnt in contours:
    currArea = cv2.contourArea(cnt)
    if currArea > maxArea:
        maxArea = currArea
print(maxArea)

I also whipped up a little more complex version, that creates a sorted list of the indexes and sizes of the contours. Then it looks for the largest relative difference in size of all contours, so you know which contours are 'small' and 'large'. I do not know if this works for all letters / fonts.

# create a list of the indexes of the contours and their sizes
contour_sizes = []
for index,cnt in enumerate(contours):
    contour_sizes.append([index,cv2.contourArea(cnt)])

# sort the list based on the contour size. 
# this changes the order of the elements in the list
contour_sizes.sort(key=lambda x:x[1])

# loop through the list and determine the largest relative distance
indexOfMaxDifference = 0
currentMaxDifference = 0
for i in range(1,len(contour_sizes)):
    sizeDifference = contour_sizes[i][1] / contour_sizes[i-1][1] 
    if sizeDifference > currentMaxDifference:
        currentMaxDifference = sizeDifference
        indexOfMaxDifference = i

# loop through the list again, ending (or starting) at the indexOfMaxDifference, to draw the contour
for i in range(0, indexOfMaxDifference):
    cv2.drawContours(img,contours,contour_sizes[i][0]     ,(255),-1)

To get the background color you can do use minMaxLoc. This returns the lowest color value and it's position of an image (also the max value, but you don't need that). If you apply it to the thresholded image - where the background is black -, it will return the location of a background pixel (big odds it will be (0,0) ). You can then look up this pixel in the original color image.

# get the location of a pixel with background color
min_val, _, min_loc, _ = cv2.minMaxLoc(thres)
# load color image
img_color = cv2.imread('1MioS.png')
# get bgr values of background
b,g,r = img_color[min_loc]
# convert from numpy object
background_color = (int(b),int(g),int(r))

and then to draw the contours

cv2.drawContours(img_color,contours,contour_sizes[i][0],background_color,-1)

and of course

cv2.imshow('res', img_color)

Upvotes: 3

Rotem
Rotem

Reputation: 32144

You can solve it by removing all the small clusters.

I found a Python solution (using OpenCV) here.

For supporting smaller fonts, I added the following heuristic:
"The largest size of the demarcation cluster is 1/500 of the largest letter cluster".
The heuristic can be refined, by statistical analysts (or improved by other heuristics, such as demarcation locations relative to the letters).

import numpy as np
import cv2

I = cv2.imread('Goodluck.png', cv2.IMREAD_GRAYSCALE)

J = 255 - I  # Invert I
img = cv2.threshold(J, 127, 255, cv2.THRESH_BINARY)[1] # Convert to binary

# https://answers.opencv.org/question/194566/removing-noise-using-connected-components/
nlabel,labels,stats,centroids = cv2.connectedComponentsWithStats(img, connectivity=8)

labels_small = []
areas_small = []

# Find largest cluster:
max_size = np.max(stats[:, cv2.CC_STAT_AREA])
thresh_size = max_size / 500  # Set the threshold to maximum cluster size divided by 500.

for i in range(1, nlabel):
    if stats[i, cv2.CC_STAT_AREA] < thresh_size:
        labels_small.append(i)
        areas_small.append(stats[i, cv2.CC_STAT_AREA])

    mask = np.ones_like(labels, dtype=np.uint8)

for i in labels_small:
    I[labels == i] = 255

cv2.imshow('I', I)

cv2.waitKey(0)

Here is a MATLAB code sample (kept threshold = 200):

clear

I = imbinarize(rgb2gray(imread('בהצלחה.png')));

figure;imshow(I);

J = ~I;

%Clustering
CC  = bwconncomp(J);

%Cover all small clusters with zewros.
for i = 1:CC.NumObjects
    C = CC.PixelIdxList{i}; %Cluster coordinates.

    %Fill small clusters with zeros.
    if numel(C) < 200
        J(C) = 0;
    end    
end

J = ~J;

figure;imshow(J);

Result:
enter image description here

Upvotes: 1

jhill515
jhill515

Reputation: 943

This looks like a problem for template matching since you have what looks like a known font and can easily understand what the characters and/or demarcations are. Check out https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_template_matching/py_template_matching.html

Admittedly, the tutorial talks about finding the match; modification is up to you. In that case, you know the exact shape of the template itself, so using that information along with the location of the match, just overwrite the image data with the appropriate background color (based on the examples above, 255).

Upvotes: 1

Related Questions