diff --git a/README.md b/README.md index e212520..c9cd90b 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,50 @@ Client-side image deskewing tool. Upload a photo taken at an angle, place refere Everything runs in the browser -- no server, no uploads. +## Example + + + + + + + + + +
+ Before — angled photograph with a paper sheet as a known-size reference
+ Before — angled shot +
+ After — perspective-corrected top-down view
+ After — corrected perspective +
+ Measured — the corrected image with measurement annotations baked in
+ Measured — annotations baked in +
+ ## How it works -1. **Upload** a JPG or HEIC image (HEIC is converted automatically) -2. **Review EXIF** data -- camera, lens, focal length -3. **Place datums** on the image -- rectangles or lines with known real-world dimensions -4. **Run correction** -- OpenCV.js computes a perspective transform and outputs a corrected image +1. **Upload** a JPG or HEIC image (HEIC is converted automatically). Past uploads are cached and reopen straight into Measure with their datums and annotations restored. +2. **Review EXIF** data -- camera, lens, focal length. +3. **Place datums** on the image -- rectangles, lines, or ellipses (circles) with known real-world dimensions. Each datum carries a 1--5 confidence score. One datum can be flagged as the world-axis reference to fix the output orientation. +4. **Deskew** -- OpenCV.js computes a perspective transform and produces a corrected image at a configurable px/mm scale, with per-datum residuals and a global RMS error reported. +5. **Measure** -- annotate the corrected image with lines, rectangles, ellipses, circles, and angles in real-world units. Export the bare PNG, or the image with measurements baked in (full resolution or current viewport). ### The algorithm -The highest-confidence rectangle datum defines the initial perspective correction via `getPerspectiveTransform`. All other datums (rectangles and lines) are projected through that transform and measured. Per-axis weighted scale corrections are computed from the discrepancies, folded back into the destination rectangle, and a single clean `warpPerspective` produces the output. One matrix, one warp, no post-hoc distortion. +The pipeline picks a **primary** datum that fixes the output gauge (orientation, position, scale): any datum the user explicitly flagged as the world-axis reference, otherwise the highest-confidence rectangle, then ellipse, then line. The primary's known dimensions are mapped onto an axis-aligned output frame via `cv.getPerspectiveTransform` -- that's the warm-start homography. -Datum confidence scores (1--5) act as weights in the correction. +That homography is then refined by an alternating-minimization loop around `cv.findHomography` (which internally runs DLT + Levenberg-Marquardt). On every outer pass, each datum is turned into output-space point correspondences using its shape constraint: + +- **Rectangle** -- Procrustes-fit an ideal `w × h` rectangle to the current projection of the user-marked corners. +- **Line** -- preserve the projected midpoint and direction, rescale to the expected length. +- **Ellipse** -- sample 12 points along the user ellipse, project them, then radially snap to a circle of the expected diameter centred on the projected user-marked centre. Forces circles to stay circular under perspective. + +Confidence scores (1--5) are realised as integer replication of correspondences -- `cv.findHomography` has no native weighting. The primary additionally gets a ×3 gauge boost so its anchors don't drift while secondary datums are being satisfied. The loop runs until the homography stops moving (max-entry relative delta below 1e-6) or 30 iterations, with period-2 oscillation detection logging a warning if the alternating minimization fails to converge. + +A single `cv.warpPerspective` with Lanczos resampling produces the corrected image at the requested px/mm scale; output bounds are derived by projecting the source image corners through the final homography and clamped to a 12288 px maximum dimension to keep WASM heap usage bounded. + +Per-datum residuals are reported alongside the result: edge length error and corner perpendicularity for rectangles, length error for lines, and isotropy / skew / equivalent-diameter error from the projected conic for ellipses, plus a global RMS percentage across all of them. ## Quick start @@ -57,7 +89,7 @@ pnpm type-check # vue-tsc ## Datum presets -Rectangles: A3, A4, A5, A6, 15x10 cm. Custom dimensions supported. Lines: any length. +Rectangles: A3, A4, A5, A6, 15×10 cm. Circles: €1, €2, US 25¢, UK 1p, CD. Custom dimensions supported on every type; lines accept any length. ## How Skwik compares @@ -73,8 +105,8 @@ There are plenty of tools that do *part* of what Skwik does, but none that combi | [Toolschimp Image Measure](https://www.toolschimp.com/image-measure) | ✅ | ❌ | ✅ | ✅ | ❌ | | [Aspose Deskew](https://products.aspose.app/imaging/image-deskew) | ❌ | ❌ | ❌ | ❌ | ❌ | -Most deskew tools just pull 4 corners to a rectangle without any real-world dimensions -- the output has no scale. Most measurement tools calibrate against a single reference and don't correct perspective. Skwik uses multiple weighted datums (rectangles + lines, each with a confidence score) to solve both problems in one pass, and lets you measure distances or export with a scale bar on the corrected image. +Most deskew tools just pull 4 corners to a rectangle without any real-world dimensions -- the output has no scale. Most measurement tools calibrate against a single reference and don't correct perspective. Skwik uses multiple weighted datums (rectangles, lines, and ellipses, each with a confidence score) to solve both problems in one pass, and lets you measure distances or export with a scale bar on the corrected image. ## License -MIT +GNU General Public License v3.0