Samuel
Samuel

Reputation: 511

Crop exact document paper from image by removing black border from photos in Java/Python

I have taken some pictures of index cards but now I have the problem that the photographing was not perfect of course and there is a black border on each photo. I would like to crop the photo so that only the index card is left without a border.

Online I could find a similar problem, but it was only about computer images, so you could assume that the black had RGB (0,0,0), which is not the case with me. In the middle there is also black text, which I don't want to cut out.

Do you have any ideas how I can approach this?

Here is an example picture (it is in German):

enter image description here

Upvotes: 3

Views: 3434

Answers (2)

nathancy
nathancy

Reputation: 46600

The idea is to threshold the image to obtain a binary image then find contours and sort using the contour area. The largest contour should be the index card. We can then apply a four point perspective transform to obtain a birds-eye view of the image. Here are the results:

Binary image

enter image description here

Result

enter image description here

The result is dark so to increase the contrast look at Automatic contrast and brightness adjustment of a color photo of a sheet of paper with OpenCV. Also it's slightly skewed so you should perform skew correction. Take a look at Python OpenCV skew correction, How to de-skew an image, and Detect image orientation angle based on text direction

I'll leave these steps to you :)

Code

from imutils.perspective import four_point_transform
import cv2
import numpy

# Load image, grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread("1.jpg")
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]

# Find contours and sort for largest contour
cnts = cv2.findContours(thresh, 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)
displayCnt = None

for c in cnts:
    # Perform contour approximation
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    if len(approx) == 4:
        displayCnt = approx
        break

# Obtain birds' eye view of image
warped = four_point_transform(image, displayCnt.reshape(4, 2))

cv2.imshow("thresh", thresh)
cv2.imshow("warped", warped)
cv2.imshow("image", image)
cv2.imwrite("thresh.png", thresh)
cv2.imwrite("warped.png", warped)
cv2.imwrite("image.png", image)
cv2.waitKey()

Upvotes: 3

Swati Srivastava
Swati Srivastava

Reputation: 1157

Maybe, this previous post's answer is helpful https://stackoverflow.com/a/21568925/9851541 Further, this may also prove helpful

from PIL import ImageChops

     def trim(im, border):
         bg = Image.new(im.mode, im.size, border)
         diff = ImageChops.difference(im, bg)
         bbox = diff.getbbox()
         if bbox:
             return im.crop(bbox)
         else:
             # found no content
             raise ValueError("cannot trim; image was empty")

Upvotes: 2

Related Questions