I have the following image which is a receipt image and a lot of white space around the receipt in focus. I would like to crop the white space. I can't manually crop it so I'm looking for a way that I could do it.
Cropped one:
Tried this code from the following post: How to remove whitespace from an image in OpenCV?
gray = load_image(IMG_FILE) # image file
gray = 255*(gray < 128).astype(np.uint8)
coords = cv2.findNonZero(gray) # Find all non-zero points (text)
x, y, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box
rect = load_image(IMG_FILE)[y:y+h, x:x+w] # Crop the image - note we do this on the original image
it's cropping a tiny part of the white space.
Here's a simple approach:
Obtain binary image. Load the image, convert to grayscale, apply a large Gaussian blur, and then Otsu's threshold
Perform morphological operations. We first morph open with a small kernel to remove noise then morph close with a large kernel to combine the contours
Find enclosing bounding box and crop ROI. We find the coordinates of all non-zero points, find the bounding rectangle, and crop the ROI.
Here's the detected ROI to crop highlighted in green
Cropped ROI
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, (25,25), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Perform morph operations, first open to remove noise, then close to combine
noise_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, noise_kernel, iterations=2)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, close_kernel, iterations=3)
# Find enclosing boundingbox and crop ROI
coords = cv2.findNonZero(close)
x,y,w,h = cv2.boundingRect(coords)
cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
crop = original[y:y+h, x:x+w]
cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.imshow('image', image)
cv2.imshow('crop', crop)
cv2.waitKey()
I am the original author of the code that you tried. The reason why it didn't work was because you have some noisy pixels that are surrounding the text that is throwing off the algorithm. If you remove the noise with a simple opening morphological operation, you get the result you need. This was in fact done in the second version of my answer which you unfortunately didn't try:
import cv2
import numpy as np
gray = load_image(IMG_FILE) # image file
# Threshold the image so that black text is white
gray = 255*(gray < 128).astype(np.uint8)
# Additionally do an opening operation with a 2 x 2 kernel
O = np.ones(2, dtype=np.uint8)
gray_morph = cv2.morphologyEx(gray, cv2.MORPH_OPEN, O)
# Continue where we left off
coords = cv2.findNonZero(gray_morph) # Find all non-zero points (text)
x, y, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box
rect = load_image(IMG_FILE)[y:y+h, x:x+w] # Crop the image
We thus get:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With