2

I am near new to OpenCV world. I am working on a project which need (for now) to detect numbers in an image, select them and save.

This is the code I used:

# Importing modules

import cv2
import numpy as np


# Read the input image 
im = cv2.imread('C:\\Users\\User\\Desktop\\test.png')

# Convert to grayscale and apply Gaussian filtering
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
im_gray = cv2.GaussianBlur(im_gray, (5, 5), 0)

# Threshold the image
ret, im_th = cv2.threshold(im_gray, 90, 255, cv2.THRESH_BINARY_INV)

# Find contours in the image
image, ctrs, hier = cv2.findContours(im_th.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Bounding rectangle for a set of points
i = 0

#rects = [cv2.boundingRect(ctr) for ctr in ctrs]
#rects.sort()

for ctr in ctrs:
    x, y, w, h = cv2.boundingRect(ctrs[i])

    # Getting ROI
    roi = im[y:y+h, x:x+w]

    #cv2.imshow('roi',roi)
    #cv2.waitKey()

    i += 1

    cv2.imwrite('C:\\Users\\User\\Desktop\\crop\\' + str(i) + '.jpg', roi)

#print(rects)    
print("OK - NO ERRORS")

It works a half. The problem is the output numbers (in image format, it need to be that way) aren't ordered by the original image (below).

Original test image

This is the output:

wrong output

What is wrong in the code ?

Also, you can note the rects variable. I used it to do some debug and I noted an interesting thing: if I sort it's content, in console the array of images order is right.

sorted array

Is there a way to sort the images in the original order ?

I also saw this very similar post but I can't understand the solution.

Thank you very much.

BlueTrack
  • 442
  • 3
  • 7
  • 19

1 Answers1

1

There is no natural order given that the ROIs could be spread out in two dimensional space.

If you want to order them by x coordinate you could do:

sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])

and then loop over sorted_ctrs instead of ctrs.

Edit: More precisely:

import cv2
import numpy as np

# Read the input image
im = cv2.imread('JnUpW.png')

# Convert to grayscale and apply Gaussian filtering
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
im_gray = cv2.GaussianBlur(im_gray, (5, 5), 0)

# Threshold the image
ret, im_th = cv2.threshold(im_gray, 90, 255, cv2.THRESH_BINARY_INV)

# Find contours in the image
image, ctrs, hier = cv2.findContours(im_th.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Sort the bounding boxes
sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])

for i, ctr in enumerate(sorted_ctrs):
    # Get bounding box
    x, y, w, h = cv2.boundingRect(ctr)

    # Getting ROI
    roi = im[y:y+h, x:x+w]

    # Write to disk
    cv2.imwrite(str(i) +  '.jpg', roi)

#print(rects)
print("OK - NO ERRORS")
Jonas Adler
  • 10,365
  • 5
  • 46
  • 73
  • Thank you for the answer. I inserted your code right after the i=0 counter and put sorted_ctrs but the output is still the same. Images are not in good order. – BlueTrack Jul 09 '17 at 21:29
  • Now, just to know, by what this process of sorting depends ? I mean, how it is done ? Who decide to take first the number 5 and not to start from the 1 ? – BlueTrack Jul 10 '17 at 09:46
  • 1
    Well, if you read the doc: http://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=findcontours#cv2.findContours, it seems they don't make any guarantee about the order at all. So I'd assume that it is an implementation detail and you should as a user assume it is random. – Jonas Adler Jul 10 '17 at 10:42