import cv2 import matplotlib.pyplot as plt import numpy as np from skimage.filters import threshold_local import os from PIL import Image from rembg import remove def opencv_resize(image, ratio): width = int(image.shape[1] * ratio) height = int(image.shape[0] * ratio) dim = (width, height) return cv2.resize(image, dim, interpolation = cv2.INTER_AREA) def plot_rgb(image): plt.figure(figsize=(16,10)) return plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) def plot_gray(image): plt.figure(figsize=(16,10)) return plt.imshow(image, cmap='Greys_r') # approximate the contour by a more primitive polygon shape def approximate_contour(contour): peri = cv2.arcLength(contour, True) return cv2.approxPolyDP(contour, 0.032 * peri, True) def get_receipt_contour(contours): # loop over the contours for c in contours: approx = approximate_contour(c) # if our approximated contour has four points, we can assume it is receipt's rectangle if len(approx) == 4: return approx def contour_to_rect(image, contour): resize_ratio = 1000 / image.shape[0] pts = contour.reshape(4, 2) rect = np.zeros((4, 2), dtype = "float32") # top-left point has the smallest sum # bottom-right has the largest sum s = pts.sum(axis = 1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] # compute the difference between the points: # the top-right will have the minumum difference # the bottom-left will have the maximum difference diff = np.diff(pts, axis = 1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] return rect / resize_ratio def wrap_perspective(img, rect): # unpack rectangle points: top left, top right, bottom right, bottom left (tl, tr, br, bl) = rect # compute the width of the new image widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2)) widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2)) # compute the height of the new image heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2)) heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2)) # take the maximum of the width and height values to reach # our final dimensions maxWidth = max(int(widthA), int(widthB)) maxHeight = max(int(heightA), int(heightB)) # destination points which will be used to map the screen to a "scanned" view dst = np.array([ [0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype = "float32") # calculate the perspective transform matrix M = cv2.getPerspectiveTransform(rect, dst) # warp the perspective to grab the screen return cv2.warpPerspective(img, M, (maxWidth, maxHeight)) def bw_scanner(image): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) T = threshold_local(gray, 21, offset = 5, method = "gaussian") return (gray > T).astype("uint8") * 255 def remove_bg(path): input = cv2.imread(path) output = remove(input) return output def processed_result(filename): name = os.path.basename(filename) head,sep,tail = name.partition('.') image = remove_bg(filename) # Downscale image as finding receipt contour is more efficient on a small image resize_ratio = 1000 / image.shape[0] original = image.copy() image = opencv_resize(image, resize_ratio) # Convert to grayscale for further processing gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Get rid of noise with Gaussian Blur filter blurred = cv2.GaussianBlur(gray, (5, 5), 1) blurred = cv2.medianBlur(blurred,7) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10)) erosion = cv2.erode(blurred,kernel,iterations = 1) rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 50)) rectKernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 20)) dilated = cv2.dilate(erosion, rectKernel) opening = cv2.morphologyEx(dilated, cv2.MORPH_OPEN, rectKernel2) closing = cv2.morphologyEx(dilated, cv2.MORPH_CLOSE, rectKernel2) (thresh, blackAndWhiteImage) = cv2.threshold(closing, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU) edged = cv2.Canny(blackAndWhiteImage, 30, 30, apertureSize=3) # Detect all contours in Canny-edged image contours, hierarchy = cv2.findContours(blackAndWhiteImage, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) image_with_contours = cv2.drawContours(image.copy(), contours, -1, (0,255,0), 3) largest_contours = sorted(contours, key = cv2.contourArea, reverse = True)[:10] image_with_largest_contours = cv2.drawContours(image.copy(), largest_contours, -1, (0,255,0), 3) receipt_contour = get_receipt_contour(largest_contours) image_with_receipt_contour = cv2.drawContours(image.copy(), [receipt_contour], -1, (0, 255, 0), 2) scanned = wrap_perspective(original.copy(), contour_to_rect(original, receipt_contour)) temp_image = cv2.cvtColor(scanned.copy(), cv2.COLOR_BGR2RGB) blurred = cv2.GaussianBlur(temp_image, (5, 5), 1) blurred = cv2.medianBlur(blurred,7) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10)) erosion = cv2.erode(blurred,kernel,iterations = 1) # Detect white regions rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 50)) rectKernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 20)) dilated = cv2.dilate(erosion, rectKernel) opening = cv2.morphologyEx(dilated, cv2.MORPH_OPEN, rectKernel2) closing = cv2.morphologyEx(dilated, cv2.MORPH_CLOSE, rectKernel2) edged = cv2.Canny(opening, 30, 30, apertureSize=3) rho = 1 # distance resolution in pixels of the Hough grid theta = np.pi / 600 # angular resolution in radians of the Hough grid threshold = 10 # minimum number of votes (intersections in Hough grid cell) min_line_length = 50 # minimum number of pixels making up a line max_line_gap = 20 # maximum gap in pixels between connectable line segments line_image = np.copy(temp_image) * 0 # creating a blank to draw lines on minLineLength = 100 maxLineGap = 10 lines = cv2.HoughLinesP(edged, rho, theta, threshold, np.array([]), min_line_length, max_line_gap) for line in lines: for x1,y1,x2,y2 in line: cv2.line(line_image,(x1,y1),(x2,y2),(255,255,255),20) diff_x = abs(x1 - x2) diff_y = abs(y1 - y2) if(diff_y <= diff_x): cv2.line(line_image,(x1,y1),(x2,y1),(0,255,0),5) else: cv2.line(line_image,(x1,y1),(x1,y2),(0,0,255),5) lines_edges = cv2.addWeighted(temp_image, 0.8, line_image, 1, 0) result = bw_scanner(scanned) output = Image.fromarray(result) output.save("C:\\Users\\Amrit\\Btech_project\\Processed_img\\"+head+".png") #output.save("C:\\Users\\Amrit\\Btech_project\\o.png") def processed_image(img): image = remove(img) # Downscale image as finding receipt contour is more efficient on a small image resize_ratio = 1000 / image.shape[0] original = image.copy() image = opencv_resize(image, resize_ratio) # Convert to grayscale for further processing gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Get rid of noise with Gaussian Blur filter blurred = cv2.GaussianBlur(gray, (5, 5), 1) blurred = cv2.medianBlur(blurred,7) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10)) erosion = cv2.erode(blurred,kernel,iterations = 1) rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 50)) rectKernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 20)) dilated = cv2.dilate(erosion, rectKernel) opening = cv2.morphologyEx(dilated, cv2.MORPH_OPEN, rectKernel2) closing = cv2.morphologyEx(dilated, cv2.MORPH_CLOSE, rectKernel2) (thresh, blackAndWhiteImage) = cv2.threshold(closing, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU) edged = cv2.Canny(blackAndWhiteImage, 30, 30, apertureSize=3) # Detect all contours in Canny-edged image contours, hierarchy = cv2.findContours(blackAndWhiteImage, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) image_with_contours = cv2.drawContours(image.copy(), contours, -1, (0,255,0), 3) largest_contours = sorted(contours, key = cv2.contourArea, reverse = True)[:10] image_with_largest_contours = cv2.drawContours(image.copy(), largest_contours, -1, (0,255,0), 3) receipt_contour = get_receipt_contour(largest_contours) image_with_receipt_contour = cv2.drawContours(image.copy(), [receipt_contour], -1, (0, 255, 0), 2) scanned = wrap_perspective(original.copy(), contour_to_rect(original, receipt_contour)) temp_image = cv2.cvtColor(scanned.copy(), cv2.COLOR_BGR2RGB) blurred = cv2.GaussianBlur(temp_image, (5, 5), 1) blurred = cv2.medianBlur(blurred,7) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10)) erosion = cv2.erode(blurred,kernel,iterations = 1) # Detect white regions rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 50)) rectKernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 20)) dilated = cv2.dilate(erosion, rectKernel) opening = cv2.morphologyEx(dilated, cv2.MORPH_OPEN, rectKernel2) closing = cv2.morphologyEx(dilated, cv2.MORPH_CLOSE, rectKernel2) edged = cv2.Canny(opening, 30, 30, apertureSize=3) rho = 1 # distance resolution in pixels of the Hough grid theta = np.pi / 600 # angular resolution in radians of the Hough grid threshold = 10 # minimum number of votes (intersections in Hough grid cell) min_line_length = 50 # minimum number of pixels making up a line max_line_gap = 20 # maximum gap in pixels between connectable line segments line_image = np.copy(temp_image) * 0 # creating a blank to draw lines on minLineLength = 100 maxLineGap = 10 lines = cv2.HoughLinesP(edged, rho, theta, threshold, np.array([]), min_line_length, max_line_gap) for line in lines: for x1,y1,x2,y2 in line: cv2.line(line_image,(x1,y1),(x2,y2),(255,255,255),20) diff_x = abs(x1 - x2) diff_y = abs(y1 - y2) if(diff_y <= diff_x): cv2.line(line_image,(x1,y1),(x2,y1),(0,255,0),5) else: cv2.line(line_image,(x1,y1),(x1,y2),(0,0,255),5) lines_edges = cv2.addWeighted(temp_image, 0.8, line_image, 1, 0) result = bw_scanner(scanned) output = Image.fromarray(result) return result