Try it in 2 minutes

  1. Open https://p0k1pglt1e.execute-api.us-east-1.amazonaws.com/ on a laptop or a phone. No account, no install.
  2. See an example: a real crack in a concrete wall, measured at labelled points (A–F) with the printed card beside it.
  3. See a 3-month follow-up (simulation): four synthetic photos with known ground truth (blurred → too far → close → three months later). Each one goes to the AWS Lambda service and the agent's decision drives the next step. Open How it was measured to see every tool call, who decided it (Amazon Nova 2 Lite or the rule check), its latency and its cost. At the end the agent proposes a ticket that waits for you: Alert a professional → Prepare the report (approve) or Not now.
  4. Optional, on a phone: Measure a crack. Hold any bank-size card flat next to the crack; the live camera guide finds it, asks you to come closer, hold straight and still, and takes the photo. The card is blurred in every image the service returns or keeps.

Inspiration

You notice a crack in your wall. Was it there last year? Is it getting wider? Nobody can tell from memory, and a phone photo has no scale, so two photos taken months apart cannot be compared. The only options are a plastic tell-tale gauge glued across the crack, or a paid visit from an expert. So people do one of two things: they panic, or they ignore it, and both can be expensive.

The question that matters is simple and measurable: is it growing, and by how much? We wanted anyone with a phone to be able to answer it.

Who it helps: homeowners and tenants who want to know whether to worry; property managers who follow many cracks across buildings; insurers who need dated evidence; surveyors and engineers who want fixed measurement points and a history before they visit, delivered as a PDF report.

Why a bank card

Bank cards, national ID cards, driving licences and almost every transport, loyalty or gift card follow the same international standard, ISO/IEC 7810 ID-1: 85.60 × 53.98 mm, corner radius 3.18 mm, 0.76 mm thick, with tolerances of about ±0.15 %, in every country. So almost everyone already carries a calibrated reference in their pocket, and nothing has to be printed or bought.

Business cards are not standardised (85 × 55 mm in Europe, 88.9 × 50.8 mm in the US, usually with square corners), so the shape check refuses them. On 20 synthetic scenes per format, square-corner business cards of both sizes were never accepted (0 of 40). A rare 85 × 55 mm card with rounded corners was accepted in 6 of 20 scenes, with a scale up to ~2 % off: that is why the app asks for a bank, ID, transport or loyalty card.

How we use the card, and why it is more than "an object of known width":

  • Sub-pixel edges. Line segments (cv2.createLineSegmentDetector on the L, a and b channels) propose card-shaped quadrilaterals. Each side is then re-measured at full resolution with sub-pixel edge points, rounded corners excluded, and fitted with a robust line. The corners are the intersections of the four lines, giving an exact homography to the 85.60 × 53.98 mm rectangle.
  • Rounded corners verified. Each 3.18 mm arc must be found where the four lines predict it; together with the strict ID-1 proportions, this is what tells a real card from a business card or any other rectangle.
  • Pose with the real focal length. The phone's 35 mm-equivalent focal length is read from EXIF on the device before the photo is resized, so the card's proportions are checked for that exact camera, and the tilt comes from the homography.
  • Thickness. The card's face sits up to 0.76 mm in front of the wall; the scale is corrected for that offset.
  • Fingers. You hold the card with a fingertip. A finger crossing an edge is detected (redder than plaster or concrete for every skin tone in our photos, and thicker than a shadow or a joint) and its edge points are discarded; a side hidden under a finger can be rebuilt from the other three if the result is still a flat ID-1 rectangle. Otherwise the app explains how to hold the card and asks for a retake.
  • Uncertainty propagated. A Monte Carlo over the four fitted lines, plus the ISO size tolerance and the thickness term, gives a scale uncertainty that grows with the distance from the card; it enters every width's uncertainty.
  • The card also measures the camera. Its high-contrast edges give the blur of this exact photo in pixels, which is deconvolved from every width profile. That is what makes sub-pixel widths possible.
  • Privacy. The card is measured first, its geometry is stored, and then its interior is blurred in every image returned and in the stored copy. A follow-up visit reuses the stored geometry: a blurred photo is never measured again.

Precision mode. For long-term monitoring, a printed ChArUco card (A6, free PDF in the app) taped once beside the crack gives a ~15× smaller scale error on synthetic scenes and the exact same frame at every visit.

What it does

  • Measures the crack in millimetres: median, P90 and sustained maximum width, each with a 1σ uncertainty and a confidence, at up to six fixed points (A–F) labelled on the photo and stored in card millimetres, like a surveyor's gauge points.
  • Answers "is it growing?": at the next visit, the new photo is measured at the same points. Each change is tested for significance on the whole crack, on each 10 mm stretch and at each point, and the outcome is always shown: no significant change, or widened by X mm.
  • Guides the photo: a live camera guide (OpenCV.js in the browser) finds the card, asks you to come closer, hold straight and still, then shoots. It only guides; the measurement runs on the server, on the full photo.
  • Decides the next step from the image (agent): a retake (blur, glare, missing card, oblique view), a closer photo of a precise region drawn on the image when the crack is too thin for the current resolution, a comparison with the previous visit, a ticket, or the next visit.
  • Keeps a human in control: a ticket is only proposed; nothing is sent until you approve it, and every step is traced.
  • Produces a dated PDF report with the photos, the points, the widths and their history.

The image changes the plan (the simulation in the demo, re-run on 24 Oct with the rule policy; a synthetic crack of known width drawn on a real CC0 plaster-wall texture): the edge blur measured on the card is 3.1 px, above the 2.2 px limit → request_retake. The sharp retake is too far for this thin crack → request_closer_photo, 2.1× closer, with a region in card millimetres. The closer photo gives a sustained maximum of 0.56 ± 0.06 mm (2σ; truth 0.57) and a median of 0.34 mm (truth 0.35) → schedule_next_visit. Three months later the median width grew by 0.46 mm (truth 0.42) and the maximum reads 1.00 mm (truth 1.02) → compare_with_previous, then propose_ticket, pending human approval. The traced length is over-estimated (101 mm for 70, then 143 mm for 82).

How we built it

OpenCV 5.0.0 (opencv-python-headless 5.0.0.93), classic computer vision, no learned model.

  1. Reference card: the bank card as above, or the printed ChArUco (cv2.aruco.CharucoDetector.detectBoard, CharucoBoard.matchImagePoints, then cv2.findHomography with USAC_MAGSAC and a least-squares polish). Either way the result is a frame in millimetres.
  2. Quality: blur in pixels on the card's step edges, saturation on the wall only, tilt from the homography.
  3. Rectification: cv2.warpPerspective into the card frame, at a resolution chosen from the native pixels per millimetre.
  4. Segmentation: 8-bit median background, multi-scale Hessian ridge filter built from GaussianBlur and Sobel (σ 0.2–3.4 mm), hysteresis with connectedComponentsWithStats, shape and contrast filters, Zhang–Suen thinning (in NumPy, because ximgproc is contrib-only in OpenCV 5).
  5. Width: perpendicular profiles sampled with cv2.remap, each deconvolved with the blur measured on the card. Resolved cracks use the full width at half maximum; unresolved ones use the darkness integral divided by the crack's own depth. The local background is read just outside the blurred edges, so a neighbouring dark line cannot narrow the width. This more than halves the error of the pixel-level distance-transform baseline (0.15 vs 0.39 mm on the synthetic held-out set).
  6. Main crack and points: candidates are scored in the card frame (closeness to the card, length in a 10 cm zone, darkness², long straight man-made lines penalised); fragments that continue each other across the card are joined. Five evenly spaced points plus the widest one are fixed in card millimetres.
  7. Change: both visits live in the card frame. If the card was moved, ORB + findHomography(USAC_MAGSAC) + findTransformECC on a band-passed image register the wall texture. Each change is z-tested with a calibrated per-photo error (Bonferroni, ≥ 0.1 mm).
  8. Live guide: OpenCV.js 4.12 in a Web Worker on ~640 px preview frames, loaded from a pinned CDN URL and checked against its SHA-256 before it runs, with a same-origin fallback.
  9. Agent: Amazon Nova 2 Lite (us.amazon.nova-2-lite-v1:0) through the Bedrock Converse API, with five tools, temperature 0. A deterministic policy computes a suggestion first; the model's tool call is then checked against the measured precondition that justifies it: it can only ask for a closer photo if the crack is really unresolved, only ask for a retake if the photo is really unusable, must compare when a previous visit exists, cannot drop a ticket justified by a threshold, and every ticket needs a human. An overridden proposal stays in the trace with its reason. If the model fails, refuses, or the daily budget is used, the rules decide alone and the trace says so.

AWS. One container-image Lambda on arm64 (Graviton), Python 3.12, 3008 MB, behind an API Gateway HTTP API with throttled routes. Inspections run as asynchronous jobs: the request returns a job id at once, the function invokes itself asynchronously (up to 120 s) and the page polls, so no photo can hit API Gateway's 29 s limit. A private S3 bucket holds uploads (5-minute presigned POST, deleted after one day); DynamoDB holds traces, tickets, usage caps and an anonymous visit log; CloudWatch JSON logs are kept 14 days; the function role is least-privilege. Everything is deployed by one CloudFormation (SAM) template. The arm64 image is built without Docker: pip --platform manylinux_2_28_aarch64 + one reproducible layer appended with crane to the official Lambda base image pinned by digest (a Dockerfile building the same image is included).

The architecture and agent-flow diagrams are in the gallery and in docs/ in the repository.

Evaluation

All numbers come from scripts in the repository. Every real photo in the demo and in the evaluation was taken by us on real walls with an ordinary phone. The synthetic benchmark and the 3-month simulation are labelled as synthetic everywhere, and synthetic and real results are reported separately.

Real photos (43 photos in the measurement tables, plus 10 bank-card-only photos for the detector tests; only the printed chart has a ground truth):

Test Result
Printed chart, bars of known width 0.2–3 mm (12 photos) mean absolute error 0.061 mm and 0.075 mm (two chart versions); smallest bar found on ≥ 80 % of photos: 0.20 mm
Two "visits" of the chart (3 bars widened by 0.2–0.5 mm) width-change error 0.027 mm; significant widening flagged in 4/4 photo pairs; per 10 mm stretch, 8 of 12 widened-bar comparisons flagged, and 1 of 17 unchanged ones
Bank card vs printed card in the same 13 photos bank card accepted in 13/13; width at the same crack points −3.1 % to +1.3 % (median −1.7 %), 12/13 inside the announced ±2σ scale uncertainty
Bank-card decision under JPEG re-compression (15 photos, quality 80–100) same accept/refuse decision at every quality, scale within 0.5 %; on 6 photos showing only the printed card, no pixel blurred
Repeatability, concrete wall, card next to the crack (5 photos) 2.9 % coefficient of variation of the width across photos (3.0 % on the same 10 mm stretches)
Repeatability, rendered facade (12 photos) and card taped on the crack (12 photos) 6.7–30 % depending on the series

The facade cracks are deep and irregular: their apparent width depends on the viewing angle (4.0–4.1 mm seen square, 2.8–3.7 mm at 14–20°), and the segmentation merges the mortar joints they touch. On the clean concrete crack the same pipeline repeats within 3 %. Hence the product rule: the bank card for a quick check, the printed card left on the wall for monitoring.

Synthetic, held-out (300 photos and 40 two-visit pairs generated with seeds never used for tuning; cracks 0.2–5 mm, 3–9 px/mm, tilt 0–45°, blur, JPEG, glare, motion blur, stains, joints):

Metric Value
Card detected 299 / 300
Crack detected 258 / 300 (27 of the 41 misses are cracks under 0.5 mm)
Point-wise width MAE 0.15 mm (pixel-level baseline: 0.39 mm)
Per-image median-width MAE 0.12 mm
Per-image sustained max-width MAE 0.26 mm, bias +0.13 mm (over-estimation)
Uncertainty coverage (error ≤ 2σ) 83 % of images (still under-estimated)
Widening MAE between two visits 0.08 mm
Change alert precision / recall, false alerts 1.00 / 0.80, 0 on 8 unchanged pairs; a 0.1–0.2 mm widening is flagged in about half the pairs, ≥ 0.3 mm in all
Bank card, scale error at the crack 0.60 % median, 2.49 % P90 (119 of 200 scenes accepted; 70 % inside the announced domain); printed card 0.04 %

The synthetic numbers are optimistic (flat wall, procedural concrete, uniform crack depth).

Agent: 17 scripted scenarios of the decision policy pass (no card, glare, blur, oblique, retake budget, thin crack, prior-based depth, far photo with no crack, history, stable, growth, local widening, wide crack, near-threshold with uncertainty, step budget…), plus a full loop closer photo → compare → ticket pending approval, plus a determinism test: a mocked model that always asks for a closer photo is replayed on the observations recorded from two real follow-ups; the trace must still be compare → ticket, with the model's proposal logged as overridden.

Failure cases (shown in the video and the report): a 0.27 mm synthetic crack over a dark stain gets a sustained maximum 4.7 mm too wide, because the stain is segmented as crack; thin cracks can be missed even when a person sees them (41 of 300, 27 under 0.5 mm; the median relative error is 56 % below 1 px and 35 % at 1–2 px, which is why the agent asks for a closer photo rather than reporting a number); on rendered facades the crack is merged with the mortar joints it touches; texture can pass for a crack (16 % of detected points are not on a real crack); long straight dark lines are filtered as joints and can hide a straight crack; curved surfaces break the planar assumption; one photo per visit cannot reliably show a widening below ~0.3 mm; the bank card is refused in 40 % of the hard synthetic scenes (mostly a finger or a hand over an edge) — a refusal with a retake instruction, never a silent wrong scale.

Challenges we ran into

Most of what we learned came from our own photos of real walls, not from the synthetic set:

  • The card placed on the crack. People put the card right on the crack, which cuts it in two; the longest dark line was then a joint. Fragments that continue each other across the card are now joined, and the crack is chosen by closeness to the card and darkness (a crack is a cavity, a joint is a surface mark). On one series the right crack went from 3 of 7 photos to 7 of 7.
  • Mortar joints merged into the crack on rendered facades. Not solved; reported as a failure case.
  • Viewing angle on deep cracks. A deep crack seen at 15–20° hides part of itself behind its lip: 4.0 mm seen square, 2.8 mm at an angle. The angle across the crack is now reported with every measurement.
  • Fingers on the card. Some of our photos were refused because a finger lay along an edge; finger masking and side reconstruction now accept them.
  • The JPEG knife-edge. The same photo was accepted at JPEG quality 95 and refused at 92 (the app's re-encoding). The decision is now tested at qualities 80–100 on 15 real photos.
  • A wall patch blurred as if it were a bank card. Only an accepted card is blurred now, never a rejected candidate.
  • API Gateway's 29 s limit: a full-resolution photo plus the agent loop could exceed it, so inspections became asynchronous jobs.
  • A model overriding the measurement: Nova once asked for a closer photo of a crack about 6 mm wide. Every tool call is now checked against the measured precondition that justifies it.
  • OpenCV 5 moved several tools (AKAZE, BRISK and ximgproc thinning are contrib-only; findTransformECC needs its mask and filter-size arguments), and ECC diverged between differently lit visits until we band-passed the images.

Accomplishments that we're proud of

  • A crack gauge that needs nothing but a card from your wallet, within a few percent of a printed calibration target on the same real photos.
  • A mean error under 0.1 mm on a printed chart photographed with a phone, and 0.027 mm on its measured change.
  • The card does five jobs: scale, rectification, registration between visits, camera-blur measurement, and the privacy step that hides it.
  • An agent whose actions really depend on the image, that cannot be talked out of the measurement, with a human approving every ticket and a complete trace.
  • An evaluation with a held-out set, a baseline, real photos, failure cases and a reproducible re-run.

What we learned

Measuring the camera's blur on a known target is worth more than any amount of post-processing. Real photos break assumptions that synthetic data never will: fingers, card placement, viewing angle, JPEG quality. And an agent is most useful when it knows the limits of its own measurement: asking for a closer photo is a better answer than a wrong number, but only when the measurement says so.

What's next

  • A native iOS app with LiDAR: distance and wall plane from depth, a second scale check independent of the card, and curved surfaces.
  • Crack-width classes per country (for example the BRE Digest 251 damage categories in the UK), so that a width maps to the local reference scale instead of our configurable defaults.
  • A multi-visit trend model: the widening rate over many visits with its uncertainty, separating seasonal (thermal) movement from real growth.
  • Validation with a structural engineer on more cracks, and a calibrated uncertainty so that 2σ really covers 95 % of errors.

How CrackWatch meets the judging criteria

Criterion Where to look
Technical execution (30 %) OpenCV 5 pipeline above (ChArUco + ID-1 card, MAGSAC homography, Hessian ridges, blur-deconvolved widths, ORB + ECC registration); held-out synthetic set with a baseline; real photos with ground truth; automated tests
Innovation (20 %) A wallet card as a calibrated reference and a blur gauge; fixed measurement points like a surveyor's; active perception (the agent asks for a closer photo of a precise region)
Real-world impact (20 %) Answers "is it growing?" for homeowners, tenants, property managers, insurers and surveyors, with a dated PDF report; nothing to buy or print
User experience (10 %) Guided live camera, plain-language verdicts, comparison always shown, English and French, light and dark themes
Documentation (10 %) README with every number and the script that produced it, results files, diagrams, failure cases
Cloud, reproducibility, security (10 %) One CloudFormation template, arm64 Lambda built reproducibly without Docker, async jobs, least-privilege role, private S3 with 1-day expiry, EXIF/GPS dropped, card blurred, anonymous visit log
Agentic Vision prize OpenCV output changes the plan (trace above: retake → closer photo → compare → ticket); orchestration by Nova 2 Lite with tool use; guard-rails against measured preconditions; failure handling (fallback, budget, retake limit); observability (every step traced with model, latency and cost); human approval for every ticket; agent-flow diagram in docs/

Responsible use

CrackWatch is a monitoring and triage aid, not a structural diagnosis; the thresholds are configurable defaults, not engineering norms. Nothing is sent without a human decision. Photos are stored privately and deleted after one day; decoding drops EXIF and GPS (the focal length is read on the device first); any accepted bank card is blurred in every image returned and in the stored copy. The demo logs visits anonymously (salted hash; the IP address is never stored).

Team

Blanco, solo builder.

Built With

Share this project:

Updates

Submission history