How to find playing cards in an image with OpenCV?

Issue

I’m currently developing a playing card detection program. I’m using Hough Line Transform in order to detect the positions of the cards, as it seems the most robust way. because it’s less dependent of environment conditions such as light and background (I think), instead of finding contours.

I’m using this image to test and after the transform, I get this result:

original img transformed
image_1 image_2

As you can see, the lines don’t close the polygons and I can’t make any conclusion regarding the position of the cards.

I already thought of using some criteria (such as angle etc) to group the lines belonging to the same card but I was wondering if there’s a better and faster way of finding each card’s position.

I used this code:

#include <cstdlib>
#include <cstdio>
#include "detector.h"
#include "data_structs.h"
#include <unistd.h>

#define MIN_LINE_LEN 80 
#define MAX_LINE_GAP 10 

//Variáveis globais 
Mat img;
Mat src;

int main( int argc, char** argv ){
    
    src = imread("img.jpg");
    src.copyTo(img);
    
    preProcessImg();
    
    detectCards();

    return 0;
}

//Prepara a img para ser analisada
void preProcessImg(){
    Mat aux_gray;
    
    cvtColor(img, aux_gray, CV_BGR2GRAY);    // Convert the image to grayscale
    GaussianBlur(aux_gray, img, Size(5,5), 0);
}

void detectCards(){
    vector<Vec4i> lines;

    //Detetar as linhas
    Canny(img, img, 30, 200);
    HoughLinesP(img, lines, 1, CV_PI/180, 80, MIN_LINE_LEN, MAX_LINE_GAP);
    
}

Solution

Instead of using Hough Line Transform to detect the cards and having to close the lines to form polygons, I propose another method. Here’s the main idea:

  • Convert image to grayscale
  • Gaussian blur the image
  • Otsu’s threshold
  • Find contours
  • Filter contours with contour area to ensure it matches a minimum
    threshold area

Binary image -> Result


Contours detected: 7

My implementation was in Python but you can convert it to C++ using the same strategy

import cv2
import numpy as np
import imutils

# Load image, grayscale, blur, Otsu's threshold
image = cv2.imread('1.jpg')
image = imutils.resize(image, width=500)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3, 3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Find contours and filter for cards using contour area
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
threshold_min_area = 400
number_of_contours = 0
for c in cnts:
    area = cv2.contourArea(c)
    if area > threshold_min_area:
        cv2.drawContours(image, [c], 0, (36,255,12), 3)
        number_of_contours += 1

print("Contours detected:", number_of_contours)
cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.waitKey()

Answered By – nathancy

Answer Checked By – Marie Seifert (AngularFixing Admin)

Leave a Reply

Your email address will not be published.