camelot-dev / camelot-dev/camelot
Vector-graphics engine for Lattice (skip rasterize → OpenCV)
- Dominant language
- Python
- Stars
- 3.8k
- Forks
- 546
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 3
Description
**Background.** The Lattice flavor today recovers a table's ruled-line grid via a raster pipeline:
1. `ImageConversionBackend.convert(pdf_path, png_path, resolution=300)` — rasterize the page (pdfium / ghostscript / poppler)
2. `adaptive_threshold(png_path)` — binarize
3. `find_lines(threshold, direction='horizontal', ...)` — `cv2.findContours` on a morphologically-prepped mask
4. `find_contours(vertical_mask, horizontal_mask)` — table-bbox candidates from the joined mask
For PDFs with *native vector lines* (the common case — anything generated by a layout engine like LibreOffice, LaTeX, Word, Quarto, financial-report tools, etc.), every step after #1 is reconstructing information the PDF already carries as exact vector coordinates.
**Idea (originally raised by @bosd).** Add a second `engine='vector'` for Lattice that walks the playa layout tree directly:
| playa object | Carries |
|---|---|
| `LTLine` | One stroked segment with exact float `(x0, y0, x1, y1)` |
| `LTRect` | A rectangle — table cell grids are often drawn as a stack of `re` operators |
| `LTCurve` | General path; covers `re` plus the occasional diagonal/curved decoration |
So `find_lines(direction='horizontal')` becomes "iterate `page.layout`, keep LTLines with `y0 == y1`" plus "treat each LTRect's top/bottom edges as horizontal lines". Same for vertical via `x0 == x1` + side edges. The output is the same `[(x1, y1, x2, y2), …]` list that drives `find_contours` and downstream cell assignment.
**Advantages.**
- **Much faster.** The image-conversion backend is the heaviest non-network step in Lattice; skipping it (plus `adaptive_threshold`, plus `cv2.findContours`) on a typical multi-page report is a 10-50× speedup per page.
- **Pixel-perfect.** No 300dpi quantization, no morphological dilate/erode tuning, no `iterations` / `erode_iterations` / `line_scale` parameters that exist purely to undo raster artefacts. (Those params keep working for the raster engine.)
- **Lighter install.** Users on `engine='vector'` don't need OpenCV or any image-conversion backend at all. Could be repackaged as `camelot-py[lattice-vector]` vs `camelot-py[cv]`.
- **Better small-detail handling.** Lines thinner than ~2px at 300dpi (which raster path can drop entirely) come through unchanged.
**Limitations.**
- **Scanned PDFs have no native vectors.** Page is an embedded LTImage; nothing to walk. Vector engine must fail back to raster (or be skipped via auto-detect).
- **Some PDFs draw "lines" as thin filled regions / shaded paths.** Less common but worth a fallback.
- **Diagonal table dividers** (rare; some legal docs) aren't easily classified as h/v — vector engine can detect them explicitly and pass to a separate code path or simply ignore.
**Design sketch.**
\`\`\`python
def find_lines_from_layout(
layout, page_dimensions, direction="horizontal", angle_tol=0.5
):
\"\"\"Walk an LTPage/LTContainer, return ruled lines in PDF coords.\"\"\"
# ... LTLine, LTRect/edge expansion, LTCurve-as-path inspection
return processed_mask_or_None, lines # same shape as find_lines
# Lattice signature gains:
def __init__(self, ..., engine="raster"):
# 'raster' (default) — current OpenCV pipeline, unchanged.
# 'vector' — find_lines_from_layout, no image conversion.
# 'auto' — probe layout for LTLines; if none → fall back to raster.
\`\`\`
**Staged plan.**
- **Stage 1**: `find_lines_from_layout` helper + unit tests against fixtures with known vector lines (e.g. `tests/files/foo.pdf`). No Lattice integration yet — pure function.
- **Stage 2**: Lattice `engine='vector'` plumbing + integration tests across the existing lattice fixtures. Bench raster vs vector per fixture.
- **Stage 3**: `engine='auto'` heuristic + docs section in `advanced.rst`.
Tracking issue. Stage-1 POC PR coming next.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the existing Lattice find_lines and the Stage 1 plan, then inspect tests/files/foo.pdf for vector-line fixtures. Implement and test the standalone find_lines_from_layout behavior for LTLine and LTRect inputs, preserving the stated line-list shape; Lattice integration, auto-detection, benchmarking, and advanced.rst belong to later stages.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- opencv, python
- Domain
- backend, data
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 40/100