Easarc Vision has to work in a weaving shed where the air is full of lint, the lighting was installed in 2009, and the internet connection is a consumer broadband line that drops for twenty minutes most afternoons. Every architectural decision below follows from those three facts rather than from anything we would have chosen on a whiteboard.
This is a description of what we actually run, including the parts that are less elegant than we would like.
The constraints, stated honestly
- The cameras are already there. Requiring industrial machine-vision cameras would double the cost of the deployment and kill it. We take whatever ONVIF/RTSP camera is mounted, which means variable resolution, variable frame rate and no hardware trigger.
- The network is unreliable. Not slow — unreliable. Long stretches of fine, punctuated by total loss. Anything that assumes continuous connectivity fails in week one.
- Latency has a hard ceiling. A defect flagged after the roll has been cut is worth almost nothing. We target under two seconds from frame capture to a supervisor’s phone.
- Every customer’s defects are different. A process house and a garment unit share almost no taxonomy. A single global model is not useful to either.
- Data cannot leave India. This is a customer requirement before it is a legal one, and it removes several otherwise attractive managed services.
The shape of it
The edge tier
In each unit there is a small fanless x86 box on the LAN — the kind that costs about as much as a mid-range phone. It runs AWS IoT Greengrass, and it does four things.
Sampling, not streaming
We do not stream video to the cloud. At a typical printing line speed of 40 metres a minute, one frame every 2.25 seconds gives us a frame per 1.5 metres of cloth, which is finer than the defects we are looking for. That is roughly 1,600 frames per camera per hour instead of 100,000, and it is the single decision that makes the economics work at ₹4,999 per camera per month.
The sample interval is derived from the line speed, not fixed. When the machine slows, we sample less.
# Component recipe for the edge sampler, deployed by AWS IoT Greengrass
ComponentName: com.easarc.vision.sampler
ComponentVersion: 1.6.0
ComponentConfiguration:
DefaultConfiguration:
cameras:
- id: CAM-03
rtsp: rtsp://10.0.4.31:554/stream1
machine: PR-04
# Line speed is 40 m/min; one frame per 1.5 m of cloth.
sample_interval_ms: 2250
roi: { x: 0.08, y: 0.12, w: 0.84, h: 0.76 }
buffer:
path: /var/lib/easarc/spool
max_bytes: 21474836480 # 20 GB — about 14 hours at this rate
policy: drop_oldest
upload:
endpoint: ingest.easarctech.com
batch_size: 8
max_inflight: 2
backoff: exponentialBuffering, because the link will drop
Frames are written to a local spool before any upload is attempted. The spool holds 20 GB, which at our sample rate is around fourteen hours of production. When the link returns, the backlog drains at a rate limited so it does not saturate the same connection the office is using.
The policy when the spool fills is drop_oldest, and we argued about this. Dropping the newest keeps a continuous record of the earliest failure; dropping the oldest keeps the record closest to now. We chose oldest because a supervisor acting on the last hour is worth more than a complete archive of a fault that has already run through 8,000 metres.
Cropping to a region of interest
The camera sees the machine, the floor and usually part of a wall. We crop to the cloth path at the edge, which cuts the payload by half or more and — more importantly — removes most of the incidental footage of people working, which matters both for the DPDP Act and for how the operators feel about the camera.
Store-and-forward, not request-response
The edge box never waits for a reply. It uploads batches of eight frames to an ingestion endpoint, marks them acknowledged, and moves on. Everything downstream is asynchronous, which is what lets a twenty-minute outage be a twenty-minute delay rather than a twenty-minute hole.
The cloud tier
- Ingestion. An ECS Fargate service behind an ALB accepts the batch, writes frames to S3, and puts one message per frame on SQS. It does nothing else. The one job of this tier is to never be the reason a frame is lost.
- Inference. Lambda consumers pull from SQS and call a SageMaker endpoint running that customer’s model. Batching happens at the endpoint rather than in Lambda, so we get GPU efficiency without holding frames in a function.
- Event fan-out. A detection above the customer’s confidence threshold is published to EventBridge as a single well-typed event. Everything that reacts — the supervisor alert, the write to the Flow work order, the running defect-rate calculation — is an independent rule on that bus.
- Persistence. Defect events go to Aurora Serverless v2 alongside the order data, which is what makes “defect rate by shift for this buyer’s orders” a single query rather than a join across two systems.
The event on the bus looks like this.
{
"event_id": "evt_01J9ZK4M7Q2X8V",
"camera_id": "CAM-03",
"machine_id": "PR-04",
"shift": "B",
"operator_id": "OP-2291",
"work_order": "ESR-2026-0431",
"captured_at": "2026-08-08T14:21:06.412+05:30",
"received_at": "2026-08-08T14:21:07.884+05:30",
"defects": [
{
"class": "weft_bar",
"confidence": 0.91,
"bbox": [0.24, 0.38, 0.38, 0.47],
"severity": "major"
},
{
"class": "oil_stain",
"confidence": 0.77,
"bbox": [0.62, 0.61, 0.71, 0.73],
"severity": "minor"
}
],
"frame_uri": "s3://easarc-vision-ap-south-1/clips/2026/08/08/CAM-03/1421064.jpg",
"model_version": "rajhans-v7"
}The fields that matter most are the boring ones. work_order, machine_id, shift and operator_id are what turn a detection into something someone can act on, and all four are resolved at the edge from the machine the camera is bound to and the schedule in Flow — not inferred later.
Where the two seconds goes
| Stage | Typical | p95 |
|---|---|---|
| Capture, crop and encode at the edge | 90 ms | 160 ms |
| Upload to ingestion (batched) | 310 ms | 840 ms |
| S3 write and SQS enqueue | 40 ms | 110 ms |
| SQS to Lambda pickup | 120 ms | 390 ms |
| SageMaker inference | 280 ms | 460 ms |
| EventBridge fan-out and alert delivery | 210 ms | 520 ms |
| End to end | 1.05 s | 2.48 s |
The upload leg dominates and is the least predictable, because it is the only part running on somebody else’s broadband. This is why the alert path is optimistic: we deliver on the first detection above threshold rather than waiting to aggregate.
Models, and the part nobody enjoys
Every customer gets their own model, fine-tuned from a shared backbone on their own labelled examples. The first two weeks of any Vision deployment are spent collecting those examples, and this is the genuinely hard part of the product.
A new unit does not have a labelled dataset. What it has is a QC person who can point at a defect and name it. So the first fortnight runs in shadow mode: we sample, we run the base model, and we show the QC person a queue of candidate frames to confirm or reject on a tablet. Around 400 to 900 confirmed examples per defect class is where precision becomes useful.
Four things we got wrong first
- We streamed video initially. It worked beautifully in our office and fell over on the first real broadband line. Sampling was not a compromise; it was the correct design and we came to it late.
- We ran inference at the edge. It seemed obvious — lower latency, less bandwidth. In practice, updating a model across thirty dusty boxes in thirty different units is a fleet-management problem we had no business taking on at our size. Cloud inference costs us 300 ms and saves us an operations team.
- We alerted on every detection. Supervisors muted it within two days, which is worse than no alerting at all because it looks like it is working. Alerts are now rate-limited and threshold-based, and the default threshold is deliberately conservative.
- We showed the model’s confidence to the floor. “0.77 oil stain” means nothing to a supervisor and quietly undermines trust in the ones that are right. The floor now sees a defect and a severity; the confidence score lives in the analytics view where it belongs.
Data residency, concretely
Every component described here runs in ap-south-1. Frames land in an S3 bucket in Mumbai, inference runs on a SageMaker endpoint in Mumbai, and the Aurora cluster is in Mumbai with backups in the same region. Clip retention follows the plan — 30 days on Starter, 90 on Growth — and deletion is a lifecycle policy on the bucket rather than a job somebody has to remember to run.
The one thing that leaves the region is aggregate telemetry with no customer content in it: latency histograms, error counts, model version distribution. That is listed in our data processing addendum along with every sub-processor.
If you want to see the output rather than the plumbing, Easarc Vision has the inspection view, and the live demo has the quality dashboard with real-shaped data in it.