Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove extra whitespace from image in opencv? [duplicate]

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.

enter image description here

Cropped one:

enter image description here

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.

like image 597
user_12 Avatar asked Jan 07 '20 21:01

user_12


2 Answers

Here's a simple approach:

  1. Obtain binary image. Load the image, convert to grayscale, apply a large Gaussian blur, and then Otsu's threshold

  2. 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

  3. 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

enter image description here

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()
like image 54
nathancy Avatar answered Oct 24 '22 00:10

nathancy


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:

enter image description here

like image 35
rayryeng Avatar answered Oct 24 '22 00:10

rayryeng