0

I have the following sample of handwriting taken with three different writing instruments:

enter image description here

Looking at the writing, I can tell that there is a distinct difference between the first two and the last one. My goal is to determine an approximation of the stroke thickness for each letter, allowing me to group them based on being thin or thick.

So far, I have tried looking into stroke width transform, but I have struggled to translate it to my example.

I am able to preprocess the image such that I am just left with just the contours of the test in question. For example, here is thick from the last line:

enter image description here

CodeBender
  • 35,668
  • 12
  • 125
  • 132
  • This *is* the perfect use-case for the stroke width transform. Perhaps if you posted your attempt as another question you could get help implementing it? There are other ways of trying this problem of course, I think Kamil's answer is something you can easily try and see how well it works...but the stroke width transform would actually work really nice here, if you got it working. – alkasm Oct 03 '17 at 04:32
  • @AlexanderReynolds Thanks for the offer. I did spend a fair bit of time looking into SWT, primarily by locating different examples people had posted online and trying to extract only the width calculation into my project. However, I do not need a precise value, so I wanted to go with a lighter approach. With that said, I would like to explore SWT in the future as it looks very promising for other applications. – CodeBender Oct 03 '17 at 17:28

3 Answers3

2

I suggest detecting contours with cv::findContours as you are doing and then compare bounding rectangle area and contour area. The thicker writing the greater coefficent (contourArea/boundingRectArea) will be.

Kamil Szelag
  • 708
  • 3
  • 12
  • I really like this approach, especially since the OP is simply interested in grouping, the values don't matter so long as they're sufficiently different for thick and thin letters. Do you think it'd be even better to use the perimeter of the contour? Of course you'd want to do some contour approximation to smooth the contour. I'm just thinking of cases where the area may be small but with a large bounding box, compared to when the area may be big but with a stronger fit bounding box. E.g. L and M might produce a similar sized bounding box but with largely different areas. – alkasm Oct 03 '17 at 04:28
  • Maybe thinning and then perimeter would a little bit better – Kamil Szelag Oct 03 '17 at 07:36
  • @KamilSzelag Thanks, I was able to get what I wanted using this approach. First, I had to convert the image to grayscale, then findContours, followed by filtering out extremes and then calculating the coefficient. As far as thinning or erosion, it would be problematic for thin lines such as pencil. – CodeBender Oct 03 '17 at 17:25
0

This approach will help you. This will calcuate the stroke width.

from skimage.feature import peak_local_max
from skimage import img_as_float


def adaptive_thresholding(image):
    output_image = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,21,2)
    return output_image


def stroke_width(image):
    dist = cv2.distanceTransform(cv2.subtract(255,image), cv2.DIST_L2, 5)
    im = img_as_float(dist)
    coordinates = peak_local_max(im, min_distance=15)
    pixel_strength = []
    for element in coordinates:
        x = element[0]
        y = element[1]
        pixel_strength.append(np.asarray(dist)[x,y])
    mean_pixel_strength = np.asarray(pixel_strength).mean()
    return mean_pixel_strength



image = cv2.imread('Small3.JPG', 0)
process_image = adaptive_thresholding(image)
stroke_width(process_image)

Bharath_Raja
  • 622
  • 8
  • 16
0

A python implementation for this might go something like this, using Stroke Width Transform implementation of SWTloc.

Full Disclosure: I am the author of this library.

EDIT : Post v2.0.0

Transforming The Image

import swtloc as swt

imgpath = 'images/path_to_image.jpeg'
swtl = swt.SWTLocalizer(image_paths=imgpath)
swtImgObj = swtl.swtimages[0]
# Perform SWT Transformation with numba engine
swt_mat = swtImgObj.transformImage(auto_canny_sigma=1.0, gaussian_blurr=False,
                                   minimum_stroke_width=3, maximum_stroke_width=50,
                                   maximum_angle_deviation=np.pi/3)

Transform Results


Localize Letters

localized_letters = swtImgObj.localizeLetters()

Letter Localization


Plot Histogram of Each Letters Strokes Widths

import seaborn as sns
import matplotlib.pyplot as plt

all_sws = []
for letter_label, letter in localized_letters.items():
    all_sws.append(letter.stroke_widths_mean)
sns.displot(all_sws, bins=31)

Histogram

From the distribution plot, it can be inferred that there might be three fontsize of the text available in the image - [3, 15, 27]

Dravidian
  • 9,945
  • 3
  • 34
  • 74