0

So the question revolve around character segmentation. My problem is the following:

I want to segment characters, based on y-axis pixel numbers, following this ( in python) : source

What i already done to get here:

  • read image io.imread
  • swap axis np.swapaxes
  • sum the numbers of each column (now row) - > got y array

I got to the point where i have two arrays (both of them are exactly what I use);

x = [94, 72, 2, 2, 1, 66, 1, 13, 1, 16, 1, 8, 1, 5, 1, 47, 1, 1, 1, 3, 1, 17, 14, 87, 100]
y = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

y is the thresholded binary array of the y-axis, (0 if pixel count < 1275, 1 otherwise)

x is the itertools groupby version of the y array.

I have the avarege distance of the letters too, so i know which are the wrongly segmented parts. (according to the x, the avg is 28.)

And this is the image i would like to segment, it has 4 letters, "a","l","m","a":

picture which i would like to segment

So in theory, if i could somehow merge the parts where the number of the ones are lower than the avg, and turn the "separating" zeros to ones, i should get a list which is as long as the width of the image, and has zeros only where it should have.

If i use cv.line on the y array, it indeed does segment the characters, drawing a red line where the array is 0, but it oversegments it.

oversegmented image

What i would like to do is "modify" or just re-do the y array, based on the x.

I tried a lot of methods, but i just cant find the algorithm to go over the x, find the wrong values, delete the zeros inbetween, and modify a list according to that.

My best shot is this easy, nothing-like-my-original-idea piece:

num = 0
betterarray = []
for i in range(len(y)):
  if( num == 1 and y[i] == 0 and y[i+1] == 1):
    betterarray.append(1)
  else :
    betterarray.append(y[i])
  num = y[i]

It does deletes the (most of the time) one column only bad segmentations, but as I guessed, it does delete some good segmentations aswell.

1 Answers1

0

You should identify the wrongly segmented letters by comparing your segments to the peak segment average and modifying the x array by combining any peak segments that are smaller than the average.

def locate_oversegmentation(array, mask, avg):
    length = len(array)

    for i in range(length):
        // less than average peak
        if (mask[i]==1 and array[i]<=avg):
            if (i-2>=0):
                // previous peak is less than avg
                if (array[i-2]<=avg):
                    mask[i-1] = 1
            if (i+2<=length):
                // next peak is less than avg
                if (array[i+2]<=avg):
                    mask[i+1] = 1
    return mask

This function takes in array x and a compact version of array y by grouping consecutive 0's and 1's. compact_y = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] It will return a new array changing 0's between below avg peaks to 1's. The output array is a guide to combining peaks in array x.

Example:

x = [94, 72, 2, 2, 1, 66, 1, 13, 1, 16, 1, 8, 1, 5, 1, 47, 1, 1, 1, 3, 1, 17, 14, 87, 100]
compact_y = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
avg = 28
guide = locate_oversegmentation(x, compact_y, avg)

>> guide = [0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,0,1,0]

Apply the guide on array x by adding consecutive 1's together in array x.

slick
  • 36
  • 5
  • Btw there is definitely an easier way to do these kinds of masking functions using the numpy library but I couldn't tell you off the top of my head. – slick Jul 02 '21 at 15:58