Search code examples
pythonimageopencvcropdetection

How do I detect the edge of a scanned baseball card and crop the image to just the card with python?


I'm scanning a bunch of baseball cards in protective sleeves. I'd like to crop just the card or slightly more then the exact edge of the card. I've been using Python for the past few years and would like some guidance how how best to do this. Here is a sample image.

enter image description here

I've reviewed some articles on OpenCV and Pillow but none are similar enough to help.


Solution

  • You have not said whether you want to include the blue border or just the card inside the blue border. Here is one way to do the latter in Python/OpenCV.

    Input:

    enter image description here

    import cv2
    import numpy as np
    
    # read the input image
    img = cv2.imread('baseball_card.jpg')
    
    # threshold on white
    lower = (210,210,210)
    upper = (235,235,235)
    thresh = cv2.inRange(img, lower, upper)
    
    # apply morphology
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
    morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
    morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
    
    # get largest contour
    contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = contours[0] if len(contours) == 2 else contours[1]
    big_contour = max(contours, key=cv2.contourArea)
    
    # get rotated rectangle
    rotrect = cv2.minAreaRect(big_contour)
    (center), (width,height), angle = rotrect
    box = cv2.boxPoints(rotrect)
    boxpts = np.intp(box)
    
    # draw rotated rectangle on copy of image
    rotrect_img = img.copy()
    cv2.drawContours(rotrect_img,[boxpts],0,(0,0,255),2)
    
    # create mask
    mask = np.zeros_like(img, dtype=np.uint8)
    cv2.drawContours(mask,[boxpts],0,(255,255,255),-1)
    
    # apply mask to input
    result = cv2.bitwise_and(img, mask)
    
    
    # ADDITION:
    # get bounding box of rotated rectangle box
    x,y,w,h = cv2.boundingRect(boxpts)
    
    # crop image to bounding box
    crop = img[y:y+h, x:x+w]
    
    # save images
    cv2.imwrite('baseball_card_thresh.jpg', thresh)
    cv2.imwrite('baseball_card_morph.jpg', morph)
    cv2.imwrite('baseball_card_rot_rect.jpg', rotrect_img)
    cv2.imwrite('baseball_card_mask.jpg', mask)
    cv2.imwrite('baseball_card_result.jpg', result)
    cv2.imwrite('baseball_card_crop.jpg', crop)
    
    # show results
    cv2.imshow('thresh', thresh)
    cv2.imshow('morph', morph)
    cv2.imshow('rotated rectangle', rotrect_img)
    cv2.imshow('mask', mask)
    cv2.imshow('result', result)
    cv2.imshow('crop', crop)
    cv2.waitKey(0)
    

    Threshold Image:

    enter image description here

    Morphology Cleaned Image:

    enter image description here

    Rotated Rectangle Image:

    enter image description here

    Mask Image:

    enter image description here

    Result Image:

    enter image description here

    Cropped Image:

    enter image description here