1

I'm trying to create texture-based image from handwriting manuscripts. After some preprocessing to the input images (binary image of text line form IAM database), segmented the lines into words/charachers using vertical profile projection. The segmented words/charachers are in diffrent size, and i want to concatinate/merge it to form the desired texture-based image. the size of the outputs images make the concatination impossible. I'm using openCV with python to do this, i want some ideas or methods to do such task. This method was inspired by this article : "Writer verification using texture-based features" by R. K. Hanusiak the article link in pages 219-220.

Concatenated images alined with it's center of mass

Text sample on the left, and it's texture-base images on the right

2

1 Answer 1

1

Here is a possible solution. You'll have to tweak some parameters, of course...

What my example code does:

  • apply threshold and invert (bitwise_not) the image to get a binary image with black background and white letters
  • apply a small dilate to merge some small elements and decrease the number of detections
  • use findContours to... find contours :)
  • calculate boundingRect and area for each contour, returning rectangles where writings are detected (area can be used to filter small unwanted elements)
  • prepare an image overlapping the source image with contours and rectangles (this part is necessary just to debug)

After detection, the code proceed creating the new "texture image" you want:

  • total_width is the sum of all rectangles widths
  • mean_height is the mean of all rectagles heights
  • total_lines is the number of lines in the new image; calculated from total_width and mean_height, so that the resulting image is approximately square
  • inside a loop, we will copy each rectangle from the src image to the newImg
  • curr_line and curr_width tracks the position where to paste the src rectangle
  • I've used cv.min() to blend each new rectangle into newImg; this is similar to "darken" blending mode in photoshop

The image showing detections:

enter image description here

The resulting texture image:

enter image description here

An the code...

import cv2 as cv
import numpy as np
import math

src = cv.imread("handwriting.jpg")
src_gray = cv.cvtColor(src,cv.COLOR_BGR2GRAY)

# apply threshold
threshold = 230
_, img_thresh = cv.threshold(src_gray, threshold, 255, 0)
img_thresh = cv.bitwise_not(img_thresh)

# apply dilate
dilatation_size = 1
dilatation_type = cv.MORPH_ELLIPSE
element = cv.getStructuringElement(dilatation_type, (2*dilatation_size + 1, 2*dilatation_size+1), (dilatation_size, dilatation_size))
img_dilate = cv.dilate(img_thresh, element)

# find contours
contours = cv.findContours(img_dilate, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)

# calculate rectangles and areas
boundRect = [None]*len(contours[1])
areas = [None]*len(contours[1])
for i, c in enumerate(contours[1]):
    boundRect[i] = cv.boundingRect(c)
    areas[i] = cv.contourArea(c)

# set drawing 
drawing = np.zeros((src.shape[0], src.shape[1], 3), dtype=np.uint8)

# you can use only contours bigger than some area
for i in range(len(contours[1])):
    if areas[i] > 1:
        color = (50,50,0)
        cv.rectangle(drawing, (int(boundRect[i][0]), int(boundRect[i][1])), \
          (int(boundRect[i][0]+boundRect[i][2]), int(boundRect[i][1]+boundRect[i][3])), color, 2)

# set newImg
newImg = np.ones((src.shape[0], src.shape[1], 3), dtype=np.uint8)*255
total_width = 0
mean_height = 0.0
n = len(boundRect)
for r in (boundRect):
    total_width += r[2]
    mean_height += r[3]/n

total_lines = math.ceil(math.sqrt(total_width/mean_height))
max_line_width = math.floor(total_width/total_lines)

# loop through rectangles and perform a kind of copy paste
curr_line = 0
curr_width = 0
for r in (boundRect):
    if curr_width > max_line_width:
        curr_line += 1
        curr_width = 0
    # this is the position in newImg, where to insert source rectangle
    pos = [curr_width, \
           curr_width + r[2], \
           math.floor(curr_line*mean_height), \
           math.floor(curr_line*mean_height) + r[3] ]
    s = src[r[1]:r[1]+r[3], r[0]:r[0]+r[2], :]
    d = newImg[pos[2]:pos[3], pos[0]:pos[1], :]
    newImg[pos[2]:pos[3], pos[0]:pos[1], :] = cv.min(d,s)
    curr_width += r[2]

cv.imwrite('detection.png',cv.subtract(src,drawing))
cv.imshow('blend',cv.subtract(src,drawing))

crop = int(max_line_width*1.1)
cv.imwrite('texture.png',newImg[:crop, :crop, :])
cv.imshow('newImg',newImg[:crop, :crop, :])

cv.waitKey()
Sign up to request clarification or add additional context in comments.

2 Comments

Note: I aligned the rectangles at the top of each line in the new image, and not at the "center of mass" as you mention... but you can adjust it if you want
thank you for your answer, it work for me, and i change it to use "center of mass" instead

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.