Cropping investigation

This commit is contained in:
sebastien committed 2026-10-11 10:12:59 +02:00
1 parent 48602c24f1
commit bd1964ffcd
10 files changed
+814 -24

No files matched your search

+87
View File
@@ -0,0 +1,87 @@
import unittest
from unittest.mock import patch
import cv2
import numpy as np
from copienator.ink_detection import detect_bounds
class PaperRulingTests(unittest.TestCase):
def scan(self, colour, *, warp=False):
image = np.full((1200, 850, 3), 255, np.uint8)
for y in range(20, 1200, 20):
cv2.line(image, (0, y), (849, y), colour, 1)
for x in range(10, 850, 20):
cv2.line(image, (x, 0), (x, 1199), colour, 1)
cv2.line(image, (80, 0), (80, 1199), (210, 70, 80), 2)
cv2.putText(image, 'answer', (120, 410), cv2.FONT_HERSHEY_SIMPLEX,
1, (20, 60, 180), 2)
cv2.putText(image, 'more', (120, 710), cv2.FONT_HERSHEY_SIMPLEX,
1, (20, 60, 180), 2)
if warp:
yy, xx = np.indices(image.shape[:2], dtype=np.float32)
image = cv2.remap(image, xx+2*np.sin(yy/95), yy,
cv2.INTER_LINEAR, borderValue=(255,255,255))
return image
def test_default_path_does_not_run_experimental_colour_model(self):
image = self.scan((70, 110, 225))
with patch('copienator.ink_detection.ruling_suppression',
side_effect=AssertionError('Expensive colour model invoked')):
result = detect_bounds(image, dpi=150)
self.assertLess(result['top_px'], 410)
self.assertGreater(result['bottom_px'], 710)
self.assertFalse(result.get('ruling_cleanup', False))
def test_saturated_coloured_grid_and_margin_do_not_seed_blank_edges(self):
for colour in [(65, 65, 65), (70, 110, 225), (155, 170, 240)]:
for warp in [False, True]:
with self.subTest(colour=colour, warp=warp):
image = self.scan(colour, warp=warp)
debug = {}
result = detect_bounds(image, dpi=150, colour_cleanup=True, diagnostics=debug)
self.assertGreater(result['top_px'], 280)
self.assertLess(result['bottom_px'], 810)
self.assertTrue(result['ruling_cleanup'])
self.assertTrue(debug['masks']['paper_suppressed'].any())
self.assertFalse(debug['masks']['foreground'][:250].any())
def test_writing_crossing_ruling_keeps_both_outer_bounds(self):
image = self.scan((70, 110, 225))
cv2.putText(image, 'top', (120, 200), cv2.FONT_HERSHEY_SIMPLEX,
.7, (190, 20, 20), 2)
cv2.putText(image, 'bottom', (120, 1000), cv2.FONT_HERSHEY_SIMPLEX,
.7, (20, 20, 20), 2)
result = detect_bounds(image, dpi=150, colour_cleanup=True)
self.assertLess(result['top_px'], 180)
self.assertGreater(result['bottom_px'], 1000)
def test_isolated_bars_and_axes_are_not_a_ruling_colour_family(self):
for colour in [(190, 20, 20), (20, 20, 20), (20, 90, 190)]:
with self.subTest(colour=colour):
image = self.scan((70, 110, 225))
cv2.line(image, (100, 1020), (700, 1020), colour, 3)
cv2.line(image, (425, 980), (425, 1120), colour, 3)
result = detect_bounds(image, dpi=150, colour_cleanup=True)
self.assertGreater(result['bottom_px'], 1120)
def test_grid_colour_with_a_thicker_answer_bar_is_preserved(self):
colour = (70, 110, 225)
image = self.scan(colour)
cv2.line(image, (150, 1020), (650, 1020), colour, 5)
self.assertGreater(detect_bounds(image, dpi=150, colour_cleanup=True)['bottom_px'], 1020)
def test_regular_fraction_bars_inside_an_answer_are_not_paper(self):
image = np.full((1200,850,3),255,np.uint8)
cv2.putText(image,'answer',(120,300),cv2.FONT_HERSHEY_SIMPLEX,
1,(20,60,180),2)
for y in (400,550,700,850,1000):
cv2.line(image,(130,y),(700,y),(20,20,20),3)
result = detect_bounds(image,dpi=150, colour_cleanup=True)
self.assertGreater(result['bottom_px'],1000)
self.assertFalse(result.get('ruling_cleanup',False))
if __name__ == '__main__':
unittest.main()