How to crop and extract stamp from an image with OpenCV?

Issue

I am new to OpenCV.
I have a "simple" image of a stamp that I have already processed a bit, as you can see in the code below.
Now I have the problem of cropping the image to get just the stamp.
The dots and the stripes on the edges interfere with my current code recognizing the stamp.
The images can be different so it is not an option to fix the location of the image.

Code:

img = cv2.imread('./images/image.JPG')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (3,3), 0) 
edges = cv2.Canny(image=img_blur, threshold1=100, threshold2=200)

First Example Image

Second Example Image

Real Images
First Real Example Image

Second Real Example Image

Solution

Here’s a simple method:

  1. Obtain binary image. We load the image, convert to grayscale, Gaussian blur, then Otsu’s threshold to obtain a binary image.

  2. Remove small artifacts and noise. Create a rectangular structuring element and morph open to remove small bits of noise. Then morph close to combine individual contours into a single contour with the assumption that a stamp is a single contour.

  3. Detect and extract stamp ROI. Find contours, filter using contour area and shape approximation. The idea is that if a contour has four vertices then it’s a square shape. We can extract the stamp ROI using Numpy slicing and save the stamp


Extracted ROI results


Here’s the results with the other two input images from the comments. The assumption is that for each image, there’s only one stamp, or one group of adjacent stamps. For these cases, we sort by contour area and assume the largest contour is the stamp.

enter image description here

enter image description here

Code

import cv2

# Load image, grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread("1.jpg")
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Morph operations to remove small artifacts and noise
open_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, open_kernel, iterations=1)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, close_kernel, iterations=2)

# Find contours, filter using contour area, and shape approximation
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
    peri = cv2.arcLength(c, True)
    area = cv2.contourArea(c)
    approx = cv2.approxPolyDP(c, 0.05 * peri, True)
    # Assumption is if the contour has 4 vertices then its a square shape
    # 2nd assumption is that there's only one stamp, or one group of stamps
    if len(approx) == 4 and area > 100:
        x,y,w,h = cv2.boundingRect(approx)
        ROI = original[y:y+h, x:x+w]
        cv2.imshow("ROI", ROI)
        cv2.imwrite("ROI.png", ROI)
        break
cv2.waitKey()

Answered By – nathancy

Answer Checked By – Clifford M. (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.