Automatic page splitting
This commit is contained in:
1 parent
d4404e4c27
commit
78f3e4b2f0
13 files changed
+1959
-40
No files matched your search
@@ -0,0 +1,88 @@
|
||||
import unittest
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import pymupdf
|
||||
|
||||
from copienator.auto_page_split import _analyse_page_full as analyse_page
|
||||
from copienator.fast_page_split import analyse_page_fast, analyse_pdf_page_fast, quick_page_content
|
||||
|
||||
|
||||
class FastPageSplitTests(unittest.TestCase):
|
||||
def test_direct_pdf_strip_respects_rotation_and_crop_offsets(self):
|
||||
image = np.full((1400, 2000, 3), 255, np.uint8)
|
||||
for y in range(150, 650, 70):
|
||||
for x in (150, 1300):
|
||||
cv2.putText(image, 'answer x y z', (x, y),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 1, (20, 20, 20), 2)
|
||||
cv2.putText(image, 'x + y = z', (870, 1000),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 1, (20, 20, 20), 2)
|
||||
for rotation in (0, 90, 180, 270):
|
||||
with self.subTest(rotation=rotation), pymupdf.open() as doc:
|
||||
source = np.ascontiguousarray(np.rot90(image, rotation//90))
|
||||
h,w = source.shape[:2]
|
||||
page = doc.new_page(width=w*72/150+20,height=h*72/150+30)
|
||||
box = pymupdf.Rect(10,15,w*72/150+10,h*72/150+15)
|
||||
pix = pymupdf.Pixmap(pymupdf.csRGB,w,h,source.tobytes(),False)
|
||||
page.insert_image(box,pixmap=pix)
|
||||
page.set_cropbox(box)
|
||||
page.set_rotation(rotation)
|
||||
rendered = page.get_pixmap(dpi=150,alpha=False)
|
||||
full = np.frombuffer(rendered.samples,np.uint8).reshape(rendered.height,rendered.width,3)
|
||||
reference = analyse_page(full,150,'a3')
|
||||
fast,overview = analyse_pdf_page_fast(page,150,'a3')
|
||||
self.assertIsNone(fast['analysis']['fallback'])
|
||||
self.assertEqual(fast['size_px'],reference['size_px'])
|
||||
self.assertLess(overview.size,full.size)
|
||||
self.assertEqual([o['keep'] for o in fast['outputs']],[True,True])
|
||||
for a,b in zip(fast['outputs'],reference['outputs']):
|
||||
self.assertLessEqual(max(abs(x-y) for x,y in zip(a['rect_px'],b['rect_px'])),2)
|
||||
|
||||
def test_plain_blank_and_sparse_pencil_use_full_detector(self):
|
||||
for pencil in (False, True):
|
||||
with self.subTest(pencil=pencil):
|
||||
image = np.full((1200, 850, 3), 255, np.uint8)
|
||||
if pencil:
|
||||
cv2.putText(image, 'pencil', (100, 600),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 1, (195, 195, 195), 2)
|
||||
mask, early = quick_page_content(image, 150)
|
||||
self.assertFalse(early)
|
||||
expected = analyse_page(image, 150, 'a4')
|
||||
actual = analyse_page_fast(image, 150, 'a4')
|
||||
self.assertEqual(actual['outputs'][0]['keep'], expected['outputs'][0]['keep'])
|
||||
self.assertEqual(actual['outputs'][0]['rect_px'], [0, 0, 850, 1200])
|
||||
self.assertEqual(bool(mask.any()), pencil)
|
||||
|
||||
def test_compact_roi_maps_bounds_back_to_full_spread(self):
|
||||
image = np.full((1400, 2000, 3), 255, np.uint8)
|
||||
for y in range(150, 650, 70):
|
||||
for x in (150, 1300):
|
||||
cv2.putText(image, 'answer x y z', (x, y),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 1, (20, 20, 20), 2)
|
||||
cv2.putText(image, 'x + y = z', (870, 1000),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 1, (20, 20, 20), 2)
|
||||
for mirrored in (False, True):
|
||||
with self.subTest(mirrored=mirrored):
|
||||
rgb = image[:, ::-1].copy() if mirrored else image
|
||||
reference = analyse_page(rgb, 150, 'a3')
|
||||
fast = analyse_page_fast(rgb, 150, 'a3')
|
||||
self.assertIsNone(fast['analysis']['fallback'])
|
||||
self.assertEqual(fast['size_px'], [2000, 1400])
|
||||
self.assertEqual(fast['extensions_mm'], reference['extensions_mm'])
|
||||
self.assertEqual([o['rect_px'] for o in fast['outputs']],
|
||||
[o['rect_px'] for o in reference['outputs']])
|
||||
self.assertTrue(all(o['keep'] for o in fast['outputs']))
|
||||
|
||||
def test_ambiguous_or_wide_writing_falls_back_without_truncation(self):
|
||||
image = np.full((700, 1000, 3), 255, np.uint8)
|
||||
image[200:230, 300:650] = 20
|
||||
reference = analyse_page(image, 100, 'a3')
|
||||
fast = analyse_page_fast(image, 100, 'a3')
|
||||
self.assertEqual(fast['analysis']['fallback'], 'full-spread')
|
||||
self.assertEqual(fast['outputs'], reference['outputs'])
|
||||
self.assertEqual(fast['crossings'], reference['crossings'])
|
||||
|
||||
def test_unknown_geometry_keeps_reference_behaviour(self):
|
||||
image = np.full((400, 500, 3), 255, np.uint8)
|
||||
self.assertEqual(analyse_page_fast(image, 100, 'unknown'),
|
||||
analyse_page(image, 100, 'unknown'))
|
||||
Reference in new issue
Block a user