6

I have an image that contains some values in meters handwritten, I would like to find the position of the letter m so I can crop it and leave only the number.

This is an example:

Original Image: The input images are like the following, actually this one is the best handwritten input I can get, normally it's much worse.

enter image description here

Train Image: I have a list of many types of the m letter, cut from the different handwritten images I have.

enter image description here

Resulting Image: The result I want to get

enter image description here

I have already tried using opencv template matching functions but it didn't work, also found this github but it also uses template matching. I would like to know if there is any other way to solve this.

Programmer Man
  • 1,314
  • 1
  • 9
  • 29
  • 1
    keep in mind they might also use uppercase `M` eg. `3.15 M` – jamylak Feb 13 '18 at 14:27
  • Exactly the list of letters m I have all cases, I tried doing a template matching loop and get the best match but none worked. – Programmer Man Feb 13 '18 at 14:32
  • What about using something as simple as lenet on connected components? The "m" (right-most cc) can simply be ignored – Miki Feb 13 '18 at 20:41

2 Answers2

6

It seems that the letter is always at the end of the number. If this is true, you can follow a much simpler approach:

  1. Find all contours;

  1. Create list of bounding boxes (i.e. one box for each contour);

  1. Identify which of these is the right-most bounding box;

  1. Use the (x,y,width,height) information of all other boxes to create a ROI and crop just the numbers;

Source code for Python 2.7 and OpenCV 2.4:

import cv2

### load input image and convert it to grayscale
img = cv2.imread("input.png")
print("img shape=", img.shape)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#### extract all contours
_, contours, _  = cv2.findContours(gray.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# debug: draw all contours
#cv2.drawContours(img, contours, -1, (0, 0, 255), 2)
#cv2.imwrite("all_contours.jpg", img)

#### create one bounding box for every contour found
bb_list = []
for c in contours:  
    bb = cv2.boundingRect(c)
    # save all boxes except the one that has the exact dimensions of the image (x, y, width, height)
    if (bb[0] == 0 and bb[1] == 0 and bb[2] == img.shape[1] and bb[3] == img.shape[0]):
        continue
    bb_list.append(bb)

# debug: draw boxes
#img_boxes = img.copy()
#for bb in bb_list:
#   x,y,w,h = bb
#   cv2.rectangle(img_boxes, (x, y), (x+w, y+h), (0, 0, 255), 2)
#cv2.imwrite("boxes.jpg", img_boxes)    

#### sort bounding boxes by the X value: first item is the left-most box
bb_list.sort(key=lambda x:x[0])

# debug: draw the last box of the list (letter M)
#print("letter M @ ", bb_list[-1])
#x,y,w,h = bb_list[-1]
#cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)
#cv2.imwrite("last_contour.jpg", img)

### remove the last item from the list, i.e. remove box for letter M
bb_list = bb_list[:-1]

### and now the fun part: create one large bounding box to rule them all
x_start, _, _, _ = bb_list[0]
x_end, _, w_end, _ = bb_list[-1]

x = x_start
w = (x_end + w_end) - x_start

bb_list.sort(key=lambda y:y[1]) # sort by Y value: the first item has the smallest Y value 
_, y, _, _ = bb_list[0]

bb_list.sort(key=lambda y:y[3]) # sort by Height value: the last item has the largest Height value 
_, _, _, h = bb_list[-1]

print("x=", x, "y=", y, "w=", w, "h=", h)

# debug: draw the final region of interest
roi_img = img.copy()
cv2.rectangle(roi_img, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.imwrite("roi.jpg", roi_img)

# crop to the roi
crop_img = img[y:y+h, x:x+w]
cv2.imwrite("crop.jpg", crop_img)
karlphillip
  • 92,053
  • 36
  • 243
  • 426
0

What about computing the contiguous points sets, and deleting the last one "to the right" in coordinates from the image ? that would get rid of the m letter

Typically, read this to get the idea behind : Connected-component labeling

Related post on SO with Python on connected-component in terms of pixels

Blupon
  • 959
  • 1
  • 12
  • 16
  • 1
    Thanks for answering, but unfortunately there are cases where m is connected to the last number so contiguous points won't work. – Programmer Man Feb 13 '18 at 14:20
  • what about computing the average vertical coordinate in regular vertical slices of the initial image, and deleting the blacks pixel in the (small enough ?) slices that are associated with the lowest/highest average (depending on your coordinates axis direction) ? Since "m" is ratter horizontal...but we could perhaps find a matching number with specific pixels distribution, mmh – Blupon Feb 13 '18 at 14:35