Reputation: 73
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
Upvotes: 1
Views: 1404
Reputation: 683
Here is a possible solution. You'll have to tweak some parameters, of course...
What my example code does:
threshold
and invert (bitwise_not
) the image to get a binary image with black background and white lettersdilate
to merge some small elements and decrease the number of detectionsfindContours
to... find contours :)boundingRect
and area
for each contour, returning rectangles where writings are detected (area can be used to filter small unwanted elements)After detection, the code proceed creating the new "texture image" you want:
total_width
is the sum of all rectangles widthsmean_height
is the mean of all rectagles heightstotal_lines
is the number of lines in the new image; calculated from total_width
and mean_height
, so that the resulting image is approximately squaresrc
image to the newImg
curr_line
and curr_width
tracks the position where to paste the src
rectanglecv.min()
to blend each new rectangle into newImg
; this is similar to "darken" blending mode in photoshopThe image showing detections:
The resulting texture image:
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()
Upvotes: 1