opencv.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import cv2
  2. from time import time
  3. import numpy as np
  4. from compress import compress_image
  5. BorderWidth = 5
  6. def isWhiteBorder(img: "np.ndarray") -> "bool":
  7. row = np.concatenate([img[:BorderWidth], img[-BorderWidth:]], axis=0).reshape((BorderWidth * 2, -1))
  8. col = np.concatenate([img[:, :BorderWidth], img[:, -BorderWidth:]], axis=1).reshape((BorderWidth * 2, -1))
  9. full = np.concatenate([row, col], axis=1)
  10. return np.average(full) > 127
  11. def resize(img: "np.ndarray", height: "int" = None, width: "int" = None) -> "np.ndarray":
  12. if height is None and width is None:
  13. return img
  14. h, w = img.shape[:2]
  15. if height is not None:
  16. dim = int(w * height / h), height
  17. else:
  18. dim = width, int(h * width / w)
  19. return cv2.resize(img, dim, interpolation=cv2.INTER_AREA) # noqa
  20. def distance(pa: "list", pb: "list") -> "float":
  21. return np.sqrt((pa[0] - pb[0]) ** 2 + (pa[1] - pb[1]) ** 2)
  22. def rect_area(points: "np.ndarray") -> "float":
  23. return distance(points[0], points[1]) * distance(points[0], points[3])
  24. def transform(img: "np.ndarray", points: "np.ndarray") -> "np.ndarray":
  25. tl, bl, br, tr = points
  26. width = int(max(distance(tl, tr), distance(bl, br)))
  27. height = int(max(distance(tl, bl), distance(tr, br)))
  28. rect = np.array([tl, tr, bl, br], dtype="float32")
  29. dest = np.array(
  30. [[0, 0], [width - 1, 0],
  31. [0, height - 1], [width - 1, height - 1]],
  32. dtype="float32"
  33. )
  34. matrix = cv2.getPerspectiveTransform(rect, dest)
  35. return cv2.warpPerspective(img, matrix, (width, height))
  36. def preprocess(img: "np.ndarray") -> "np.ndarray":
  37. # img = compress_image(img, 1000 * 600)
  38. # img = resize(img, height=500)
  39. # RGB -> gray
  40. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  41. # 二值化
  42. thresh_type = cv2.THRESH_BINARY
  43. if isWhiteBorder(gray):
  44. thresh_type = cv2.THRESH_BINARY_INV
  45. _, thresh = cv2.threshold(gray, 158, 255, thresh_type)
  46. # 边缘检测
  47. contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  48. if not contours:
  49. return img
  50. max_contour = max(contours, key=cv2.contourArea) # noqa 找到最大的轮廓
  51. # 图像校正
  52. approx = cv2.approxPolyDP(max_contour, 0.02 * cv2.arcLength(max_contour, True), True)
  53. if len(approx) == 4: # 较为规则,可以校正
  54. return transform(gray, approx.reshape(4, 2))
  55. else: # 不太规则,尝试裁剪
  56. rect = cv2.minAreaRect(max_contour) # noqa 计算最小外接矩形
  57. box = np.intp(cv2.boxPoints(rect)) # noqa 获取矩形的四个角点
  58. if rect_area(box) * 5 < img.shape[0] * img.shape[1]:
  59. return img
  60. return img[min(box[:, 1]):max(box[:, 1]), min(box[:, 0]):max(box[:, 0])]
  61. def main():
  62. # TODO 10 18
  63. import os
  64. dd = r"C:\Users\huimv\Pictures\Saved Pictures"
  65. for name in os.listdir(dd):
  66. if name.endswith(".ini"):
  67. continue
  68. img = cv2.imread(rf"{dd}\{name}")
  69. st = time()
  70. deal = preprocess(img)
  71. print(name, time() - st)
  72. cv2.imshow(name, deal)
  73. if __name__ == '__main__':
  74. main()