1

I have a hundreds of ID card images which some of them provided below: enter image description here enter image description here enter image description here enter image description here (Disclaimer: I downloaded the images from the Web, all rights (if exist) belong to their respective authors)

As seen, they are all different in terms of brightness, perspective angle and distance, card orientation. I'm trying to extract only rectangular card area and save it as a new image. To achieve this, I came to know that I must convert an image to grayscale image and apply some thresholding methods. Then, cv2.findCountours() is applied to threshold image to get multiple vector points. I have tried many methods and come to use cv2.adaptiveThreshold() as it is said that it finds a value for threshold (because, I can't manually set threshold values for each image). However, when I apply it to images, I am not getting what I want. For example:

enter image description here

My desired output should look like this: enter image description here

It seems like it also includes affine transformations to make the card area (Obama case) proper but I am finding it difficult to understand. If that's possible I'd further extract and save the image separately.

Is there any other method or algorithm that can achieve what I want? It should consider different lighting conditions and card orientations. I am expecting one-fits-all solution given there will be only one rectangle ID card. Please, guide me through this with whatever you think will be of help.

Note that I can't use CNNs as object detector, it must be based on purely image-processing. Thank you.

EDIT: The code for the above results is pretty simple:

image = cv2.imread(args["image"])
gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh_img = cv2.adaptiveThreshold(gray_img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,51,9)

cnts = cv2.findContours(thresh_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

area_treshold = 1000
for cnt in cnts:
    if cv2.contourArea(cnt) > area_treshold:
        x,y,w,h = cv2.boundingRect(cnt)
        print(x,y,w,h)
        cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 3)


resize = ResizeWithAspectRatio(image, width=640)
cv2.imshow("image", resize)
cv2.waitKey()

EDIT 2: I provide the gradient magnitude images below: enter image description here

Does it mean I must cover both low and high intensity values? Because the edges of the ID cards at the bottom are barely noticeable.

bit_scientist
  • 1,496
  • 2
  • 15
  • 34
  • You should show the code you have used and maybe someone could help. – Jarvis Jul 22 '22 at 07:26
  • Try to force better contrast between foreground and background. For example by placing the bright id card in front of a dark background – Micka Jul 22 '22 at 07:32
  • @Micka sorry, I didn't get you meant – bit_scientist Jul 22 '22 at 09:20
  • 1
    Threshold on the cards background color using cv2.imRange(). Get the background color from averaging the 4 corner regions of the image. Permit some amount of tolerance to allow for variations in the background color. – fmw42 Jul 22 '22 at 15:19
  • Please post the images individually. That way we can give you a way that actually works on your original images. – Red Jul 22 '22 at 15:48
  • 1
    Please post the thresholded images. In my own work, I have also found *Otsu* thresholding and `cv2.equalizeHist` to be helpful. – Michael Sohnen Jul 23 '22 at 17:57
  • @AnnZen I posted original images – bit_scientist Jul 24 '22 at 23:11
  • @nathancy, I hope you could help out here – bit_scientist Jul 26 '22 at 04:23
  • @fmw42, did you mean `cv2.inRange()` method? Can you give a simple working example for one image please? – bit_scientist Jul 26 '22 at 07:31
  • Yes. cv2.inRange(). It was a typo. Seems pretty straight-forward. Get a small subsection at each corner. Get the average color of each region. Get the average of the 4 corner averages. Use that as the basic color. Then decrease if for low color (lower) and increase it for high color (upper) for cv2.inRange() – fmw42 Jul 26 '22 at 15:25

0 Answers0