103 lines
4.5 KiB
Markdown
103 lines
4.5 KiB
Markdown
# Scanned PDF margin cropping
|
|
|
|
Copienator has one automatic crop detector. It finds strongly coloured or dark
|
|
ink, recovers nearby weaker strokes, and removes substantial blank areas above
|
|
and below the detected content. It is designed for scanned student work on
|
|
plain, lined, or gridded paper, including mildly skewed pages and recurring
|
|
punched-hole artifacts.
|
|
|
|
## Review utility
|
|
|
|
Run from the repository root:
|
|
|
|
```sh
|
|
python -m copienator.crop_blank_margins Interro01/Copies tmp/cropped-copies
|
|
```
|
|
|
|
The input can be a directory or one PDF. Directory processing includes only
|
|
PDFs directly inside that directory. The utility writes processed PDFs, an HTML
|
|
comparison gallery, JPEG previews, and JSON/CSV reports into the output
|
|
directory. Source PDFs are never modified.
|
|
|
|
Available options:
|
|
|
|
- `--dpi 200`: analysis resolution.
|
|
- `--padding-mm 6`: space retained around detected content.
|
|
- `--min-crop-mm 5`: minimum worthwhile removal at either edge.
|
|
|
|
The output retains filenames, page order, page count, colour, rotation, and the
|
|
embedded scan data. Cropping changes the PDF CropBox rather than rasterizing the
|
|
page. Red shading in `index.html` shows the removed part of each original page.
|
|
A `review-*` status records uncertainty; one edge can still be cropped while the
|
|
other remains unchanged.
|
|
|
|
## Detection
|
|
|
|
The detector uses colour and darkness as strong ink seeds. It recovers connected
|
|
weak strokes within a 2 mm neighbourhood using directional contrast, which
|
|
limits growth along paper lines. Two seed thresholds are compared so unstable
|
|
boundaries can be flagged for review.
|
|
|
|
For dark neutral paper, it deskews the scan and confirms repeated horizontal or
|
|
vertical ruling before suppressing paper-line pixels. It uses short directional
|
|
openings to tolerate broken or bent grid lines. Very dark fraction bars and
|
|
diagram axes remain protected. Repeated components with similar size and
|
|
alignment in the outer 15 mm are treated as punched holes only when at least
|
|
three span a substantial part of the page. Writing in the same side column still
|
|
protects its margin.
|
|
|
|
On confirmed ruled paper, faint sparse strokes in the central 80% of the page
|
|
use a moderately more sensitive component filter. The outermost 9% on each side
|
|
uses a stricter filter because punched holes, torn binding edges, and page
|
|
numbers normally appear there.
|
|
|
|
The large-blank refinement changes an edge only when it finds at least 30 mm of
|
|
additional empty paper. A 2 mm recovery neighbourhood is applied before the
|
|
normal padding. Apparently blank pages and pages without reliable ink seeds are
|
|
kept at full height.
|
|
|
|
This remains a heuristic. Extremely faint isolated pencil marks, unusually
|
|
damaged ruling, and repeated handwriting shaped like hole artifacts can be
|
|
ambiguous. Review crops before generating answer coordinates.
|
|
|
|
## Optional GUI step
|
|
|
|
After **Séparer et réordonner les pages**, the GUI offers **Rogner les zones
|
|
vides**. It can process the whole evaluation or a selected PDF. It runs at 200
|
|
dpi with 6 mm padding and uses five worker processes by default. The CLI form is:
|
|
|
|
```sh
|
|
python -m copienator crop-margins EVALUATION --workers 5
|
|
```
|
|
|
|
The batch is fully prepared before any source is replaced. Detection failures
|
|
and interruptions leave the working PDFs intact; replacement errors roll back.
|
|
Each successful run saves the untrimmed PDFs and its report under
|
|
`.copienator/runs/crop-margins-*/`. The **Archivage** step removes these backups
|
|
and reports while keeping the cropped copies and execution logs.
|
|
|
|
Cropping must run before label detection. If a selected PDF already has a
|
|
same-named JSON coordinate file, the command stops before changing any PDFs.
|
|
When `ALWAYS_CROP` is true in `config.py`, this facultative step starts
|
|
automatically when first reached; the default configuration keeps it manual.
|
|
|
|
## Performance
|
|
|
|
Separate worker processes isolate MuPDF and each worker uses one OpenCV thread.
|
|
The detector uses native channel operations, vectorized component filtering,
|
|
cached separable background filtering, and a coarser Hough voting step for skew
|
|
candidates. Report rows remain ordered by copy and page regardless of worker
|
|
completion order.
|
|
|
|
On the Ryzen 7 PRO 7840U, an end-to-end benchmark took 56.48 seconds for 48 PDFs
|
|
of 10 pages, including rendering, detection, PDF writing, backups, and
|
|
replacement. The fixture uses Interro01 and DS08VA scans and cycles pages in
|
|
shorter copies, so it does not contain 480 distinct scans. Runtime depends on the
|
|
CPU, storage, and scan content.
|
|
|
|
Run the focused checks with:
|
|
|
|
```sh
|
|
python -m unittest tests.test_crop_blank_margins tests.test_ink_detection tests.test_crop_margins_command -v
|
|
```
|