scan, split, OCR, prepare for LLM
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

test_post_crop.py 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. from __future__ import annotations
  2. import tempfile
  3. from pathlib import Path
  4. import cv2
  5. from src.split.slicer import crop_to_content, find_content_bounds, process_image
  6. from tests.helpers import create_test_image, image_to_path
  7. class TestFindContentBounds:
  8. def test_uniform_image(self) -> None:
  9. image = create_test_image(200, 150)
  10. image[:, :] = (255, 255, 255)
  11. x1, y1, x2, y2 = find_content_bounds(image)
  12. assert (x1, y1, x2, y2) == (0, 0, 200, 150)
  13. def test_single_dot_center(self) -> None:
  14. image = create_test_image(200, 150)
  15. image[:, :] = (255, 255, 255)
  16. image[75, 100] = (0, 0, 0)
  17. x1, y1, x2, y2 = find_content_bounds(image)
  18. assert abs(x1 - 100) <= 1 and abs(x2 - 100) <= 1
  19. assert abs(y1 - 75) <= 1 and abs(y2 - 75) <= 1
  20. def test_rectangle_content(self) -> None:
  21. image = create_test_image(200, 150)
  22. image[:, :] = (255, 255, 255)
  23. image[30:100, 50:140] = (0, 0, 0)
  24. x1, y1, x2, y2 = find_content_bounds(image)
  25. assert x1 == 50
  26. assert y1 == 30
  27. assert x2 == 139
  28. assert y2 == 99
  29. def test_content_at_edges(self) -> None:
  30. image = create_test_image(200, 150)
  31. image[:, :] = (255, 255, 255)
  32. image[0:10, :] = (0, 0, 0)
  33. image[:, 0:10] = (0, 0, 0)
  34. x1, y1, _x2, _y2 = find_content_bounds(image)
  35. assert y1 == 0
  36. assert x1 == 0
  37. def test_grayscale_content(self) -> None:
  38. image = create_test_image(100, 100)
  39. image[:, :] = (255, 255, 255)
  40. image[20:30, 20:30] = (128, 128, 128)
  41. x1, y1, x2, y2 = find_content_bounds(image)
  42. assert x1 <= 29 and x2 >= 20
  43. assert y1 <= 29 and y2 >= 20
  44. class TestCropToContent:
  45. def test_crop_centered_content(self) -> None:
  46. image = create_test_image(200, 150)
  47. image[:, :] = (255, 255, 255)
  48. image[30:50, 40:60] = (0, 0, 0)
  49. result = crop_to_content(image, border_px=10)
  50. assert result.shape[0] >= 20
  51. assert result.shape[1] >= 20
  52. def test_crop_uniform_image_no_change(self) -> None:
  53. image = create_test_image(100, 100)
  54. image[:, :] = (255, 255, 255)
  55. result = crop_to_content(image, border_px=10)
  56. assert result.shape == (100, 100, 3)
  57. def test_border_capped_by_image_edge(self) -> None:
  58. image = create_test_image(100, 100)
  59. image[:, :] = (255, 255, 255)
  60. image[10:30, 10:30] = (0, 0, 0)
  61. result = crop_to_content(image, border_px=200)
  62. assert result.shape == (100, 100, 3)
  63. def test_border_equals_distance_to_edge(self) -> None:
  64. image = create_test_image(100, 100)
  65. image[:, :] = (255, 255, 255)
  66. image[40:60, 30:70] = (0, 0, 0)
  67. result = crop_to_content(image, border_px=50)
  68. assert result.shape[1] <= 100
  69. assert result.shape[0] <= 100
  70. def test_dark_bg_no_crop_uniform(self) -> None:
  71. image = create_test_image(100, 100)
  72. image[:, :] = (0, 0, 0)
  73. result = crop_to_content(image, border_px=0)
  74. assert result.shape == (100, 100, 3)
  75. def test_zero_border_exact_crop(self) -> None:
  76. image = create_test_image(200, 150)
  77. image[:, :] = (255, 255, 255)
  78. image[20:100, 30:170] = (0, 0, 0)
  79. result = crop_to_content(image, border_px=0)
  80. assert result.shape == (80, 140, 3)
  81. class TestProcessImagePostCrop:
  82. def test_post_crop_reduces_size(self) -> None:
  83. image = create_test_image(400, 400)
  84. image[:, :] = (255, 255, 255)
  85. image[100:200, 100:200] = (0, 0, 0)
  86. path = image_to_path(image, ".png")
  87. with tempfile.TemporaryDirectory() as tmp:
  88. result = process_image(Path(path), 1, 1, Path(tmp), border_px=30, post_crop=True)
  89. assert len(result) == 1
  90. loaded = cv2.imread(str(result[0]))
  91. assert loaded.shape[0] < 400 or loaded.shape[1] < 400
  92. def test_post_crop_off_no_crop(self) -> None:
  93. image = create_test_image(400, 300)
  94. path = image_to_path(image, ".png")
  95. with tempfile.TemporaryDirectory() as tmp:
  96. result = process_image(Path(path), 1, 1, Path(tmp), post_crop=False)
  97. assert len(result) == 1
  98. loaded = cv2.imread(str(result[0]))
  99. assert loaded.shape == (400, 500, 3)
  100. def test_post_crop_e2e_2x2(self) -> None:
  101. image = create_test_image(400, 400)
  102. image[:, :] = (255, 255, 255)
  103. image[50:150, 50:150] = (0, 0, 0)
  104. image[50:150, 250:350] = (0, 0, 0)
  105. image[250:350, 50:150] = (0, 0, 0)
  106. image[250:350, 250:350] = (0, 0, 0)
  107. path = image_to_path(image, ".png")
  108. with tempfile.TemporaryDirectory() as tmp:
  109. result_crop = process_image(Path(path), 2, 2, Path(tmp) / "crop", border_px=30, post_crop=True)
  110. result_no = process_image(Path(path), 2, 2, Path(tmp) / "no", border_px=30, post_crop=False)
  111. assert len(result_crop) == 4
  112. for cp, np in zip(result_crop, result_no):
  113. c = cv2.imread(str(cp))
  114. n = cv2.imread(str(np))
  115. assert c.shape[0] < n.shape[0]
  116. assert c.shape[1] < n.shape[1]