simple-scan.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import cv2
  2. import numpy as np
  3. import glob
  4. def order_points(pts):
  5. '''Rearrange coordinates to order:
  6. top-left, top-right, bottom-right, bottom-left'''
  7. rect = np.zeros((4, 2), dtype='float32')
  8. pts = np.array(pts)
  9. s = pts.sum(axis=1)
  10. # Top-left point will have the smallest sum.
  11. rect[0] = pts[np.argmin(s)]
  12. # Bottom-right point will have the largest sum.
  13. rect[2] = pts[np.argmax(s)]
  14. diff = np.diff(pts, axis=1)
  15. # Top-right point will have the smallest difference.
  16. rect[1] = pts[np.argmin(diff)]
  17. # Bottom-left will have the largest difference.
  18. rect[3] = pts[np.argmax(diff)]
  19. # return the ordered coordinates
  20. return rect.astype('int').tolist()
  21. def scan(img):
  22. # Resize image to workable size
  23. dim_limit = 1080
  24. max_dim = max(img.shape)
  25. if max_dim > dim_limit:
  26. resize_scale = dim_limit / max_dim
  27. img = cv2.resize(img, None, fx=resize_scale, fy=resize_scale)
  28. # Create a copy of resized original image for later use
  29. orig_img = img.copy()
  30. # Repeated Closing operation to remove text from the document.
  31. kernel = np.ones((5, 5), np.uint8)
  32. img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations=5)
  33. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  34. gray = cv2.GaussianBlur(gray, (11, 11), 0)
  35. # Edge Detection.
  36. canny = cv2.Canny(gray, 0, 200)
  37. canny = cv2.dilate(canny, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (21, 21)))
  38. # Finding contours for the detected edges.
  39. contours, hierarchy = cv2.findContours(canny, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
  40. # Keeping only the largest detected contour.
  41. page = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
  42. # Detecting Edges through Contour approximation
  43. if len(page) == 0:
  44. return orig_img
  45. # loop over the contours
  46. for c in page:
  47. # approximate the contour
  48. epsilon = 0.02 * cv2.arcLength(c, True)
  49. corners = cv2.approxPolyDP(c, epsilon, True)
  50. # if our approximated contour has four points
  51. if len(corners) == 4:
  52. break
  53. # Sorting the corners and converting them to desired shape.
  54. corners = sorted(np.concatenate(corners).tolist())
  55. # For 4 corner points being detected.
  56. # Rearranging the order of the corner points.
  57. corners = order_points(corners)
  58. # Finding Destination Co-ordinates
  59. w1 = np.sqrt((corners[0][0] - corners[1][0]) ** 2 + (corners[0][1] - corners[1][1]) ** 2)
  60. w2 = np.sqrt((corners[2][0] - corners[3][0]) ** 2 + (corners[2][1] - corners[3][1]) ** 2)
  61. # Finding the maximum width.
  62. w = max(int(w1), int(w2))
  63. h1 = np.sqrt((corners[0][0] - corners[2][0]) ** 2 + (corners[0][1] - corners[2][1]) ** 2)
  64. h2 = np.sqrt((corners[1][0] - corners[3][0]) ** 2 + (corners[1][1] - corners[3][1]) ** 2)
  65. # Finding the maximum height.
  66. h = max(int(h1), int(h2))
  67. # Final destination co-ordinates.
  68. destination_corners = order_points(np.array([[0, 0], [w - 1, 0], [0, h - 1], [w - 1, h - 1]]))
  69. h, w = orig_img.shape[:2]
  70. # Getting the homography.
  71. homography, mask = cv2.findHomography(np.float32(corners), np.float32(destination_corners), method=cv2.RANSAC,
  72. ransacReprojThreshold=3.0)
  73. # Perspective transform using homography.
  74. un_warped = cv2.warpPerspective(orig_img, np.float32(homography), (w, h), flags=cv2.INTER_LINEAR)
  75. # Crop
  76. final = un_warped[:destination_corners[2][1], :destination_corners[2][0]]
  77. return final
  78. for img_path in glob.glob('inputs/*.jpg'):
  79. try:
  80. img = cv2.imread(img_path)
  81. print(img_path)
  82. scanned_img = scan(img)
  83. # cv2.imshow("scanner", scanned_img)
  84. cv2.imwrite('outputs/' + img_path.split('/')[-1], scanned_img)
  85. print("scanned")
  86. key = cv2.waitKey(0)
  87. if key == 27:
  88. break
  89. except:
  90. print('fail')
  91. cv2.destroyAllWindows()