What's New
- Billing (metered, pay-for-value) — All prediction endpoints now return a
billingobject in the response. You are only charged when the algorithm returns a valid VHS measurement. Failed predictions, rejected images, and rechecks are always free. - API Key Security — API keys are now stored as SHA-256 hashes. Raw keys are never stored server-side. Treat your API key like a password — it cannot be recovered if lost.
Authentication
All endpoints require the x-api-key header. Include your API key with every request:
x-api-key: YOUR_API_KEYAPI keys are provisioned per customer. Request an API key.
Endpoints Overview
| Method | Path | Description | Billable |
|---|---|---|---|
GET | /v3/health | Service health check | No |
POST | /v3/predict | Single or batch image prediction | Yes |
POST | /v3/predict/stream | Streaming batch prediction (SSE) | Yes |
POST | /v3/pop-session/start | Start a PoP capture session | No |
POST | /v3/pop-session | Send a frame to a PoP session | Yes |
POST | /v3/corners | Detect screen corners | No |
POST | /v3/rectify | Perspective correction | No |
POST | /v3/preprocess | Corner detection + rectification | No |
GET /v3/health
Returns the current health status of the API service and loaded models.
Response
{
"status": "healthy",
"models_loaded": 5,
"pop_models_loaded": 1,
"pop_pipeline_enabled": true,
"model_version": "3.4.0-ensemble"
}Response Fields
| Field | Type | Description |
|---|---|---|
status | string | Service health status ("healthy" or "degraded") |
models_loaded | integer | Number of prediction models loaded (expected: 5) |
pop_models_loaded | integer | Number of PoP pipeline models loaded (expected: 1) |
pop_pipeline_enabled | boolean | Whether the Picture-of-Picture pipeline is available |
model_version | string | Current model version string |
POST /v3/predict
Submit one or more base64-encoded radiograph images for cardiac measurement. Returns VHS, VLAS, landmark coordinates, confidence metrics, and an annotated image.
Request Headers
| Header | Required | Value |
|---|---|---|
x-api-key | Yes | Your API key |
Content-Type | Yes | application/json |
Single Image Request
{
"image": "<base64-encoded-image>",
"uuid": "patient-123",
"render": true
}Batch Request (up to 10 images)
{
"images": ["<base64>", "<base64>"],
"uuid": "patient-123",
"render": true
}Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
image | string | Yes | Base64-encoded JPEG or PNG image |
uuid | string | No | Patient identifier for historical tracking. Also used for 24h recheck billing grace. |
render | boolean | No | If true, returns a rendered_image with overlaid landmarks and scores. Default: false. |
images | string[] | No | Array of base64-encoded images for batch mode (max 10). Use instead of image for batch requests. |
Single Image Response
{
"vhs": 10.4,
"vlas": 2.8,
"prediction": [
{ "x": 0.42, "y": 0.15, "class": "heart_top" },
{ "x": 0.41, "y": 0.44, "class": "heart_bottom" },
{ "x": 0.25, "y": 0.30, "class": "heart_right" },
{ "x": 0.57, "y": 0.29, "class": "heart_left" },
{ "x": 0.49, "y": 0.40, "class": "VLAS" },
{ "x": 0.36, "y": 0.12, "class": "Vertebra A" },
{ "x": 0.55, "y": 0.12, "class": "Vertebra B" }
],
"result_code": 1,
"result_description": "VHS and VLAS predicted successfully with high confidence",
"model_confidence": "optimal",
"spread": 0.0032,
"pop_detected": false,
"model_version": "3.4.0-ensemble",
"rendered_image": "<base64-encoded-annotated-image>",
"historic_vhs": [["2026-03-27", 11.38]],
"historic_vlas": [["2026-03-27", 1.88]],
"billing": {
"billable": true,
"reason": "valid_vhs",
"call_type": "single",
"monthly_usage": 47,
"tier": "paygo",
"rate": 3.00
}
}Response Fields
| Field | Type | Description |
|---|---|---|
vhs | number | Vertebral Heart Score |
vlas | number | Vertebral Lung-to-Apex Scale |
prediction | object[] | Array of 7 landmark coordinates (x, y, class). Coordinates are normalized 0.0–1.0. |
result_code | integer | Numeric result code (see Result Codes) |
result_description | string | Human-readable result description |
model_confidence | string | Confidence level: "optimal", "good", or "review" |
spread | number | null | Ensemble spread metric (lower is better). Returns null for PoP images which use a single dedicated model instead of the ensemble. |
pop_detected | boolean | Whether a picture-of-picture was detected and corrected |
model_version | string | Model version used for prediction |
rendered_image | string | Base64-encoded annotated image with landmarks and measurements overlaid. Only present when render: true is set in the request. |
historic_vhs | null | [date, number][] | Previous measurements for this uuid as [[date, value]] pairs. Populated when a uuid is provided. Empty array if no history exists. |
historic_vlas | null | [date, number][] | Previous measurements for this uuid as [[date, value]] pairs. Populated when a uuid is provided. Empty array if no history exists. |
billing | object | Billing metadata for this request. See Billing section. |
Rejection Response
When an image is rejected (result_code 6), the response includes additional fields: classifier_confidence, rejected, and rejection_reason. VHS and VLAS will be null.
{
"classifier_confidence": 0.7937,
"model_version": "3.4.0-ensemble",
"rejected": true,
"rejection_reason": "not_a_valid_radiograph",
"result_code": 6,
"result_description": "Image rejected — not a valid radiograph.",
"vhs": null,
"vlas": null,
"billing": {
"billable": false,
"reason": "no_valid_vhs",
"monthly_usage": 47,
"tier": "paygo"
}
}When an image is rejected, the following standard fields are absent from the response: prediction, model_confidence, spread, pop_detected, rendered_image, historic_vhs, historic_vlas.
Batch Response
{
"vhs": 11.32,
"vlas": 1.87,
"best_photo_index": 0,
"best_vhs": 11.32,
"best_vlas": 1.87,
"median_vhs": 11.32,
"median_vlas": 1.87,
"vhs_std": null,
"vlas_std": null,
"vhs_confidence": "optimal",
"vlas_confidence": "optimal",
"n_photos_processed": 2,
"n_photos_with_landmarks": 1,
"processing_started": true,
"per_photo": [
{
"vhs": 11.32,
"vlas": 1.87,
"spread": 0.0023,
"model_confidence": "optimal",
"landmarks_detected": true,
"axes_perpendicular": true,
"pop_detected": false
},
{
"vhs": null,
"vlas": null,
"spread": null,
"model_confidence": "review",
"landmarks_detected": false,
"axes_perpendicular": false,
"rejected": true,
"rejection_reason": "not_a_valid_radiograph",
"classifier_confidence": 0.7937
}
],
"historic_vhs": [["2024-01-26", 9.23]],
"historic_vlas": [["2024-01-26", 2.12]],
"model_version": "3.4.0-ensemble",
"billing": {
"billable": true,
"reason": "valid_vhs",
"call_type": "session",
"monthly_usage": 48,
"tier": "paygo",
"rate": 4.00
}
}Batch Response Fields
| Field | Type | Description |
|---|---|---|
vhs | float | null | Best VHS measurement from the batch |
vlas | float | null | Best VLAS measurement from the batch |
best_photo_index | integer | Index of the best image in the batch |
best_vhs | float | Best VHS across frames with detected landmarks |
best_vlas | float | Best VLAS across frames with detected landmarks |
median_vhs | float | Median VHS across frames with detected landmarks |
median_vlas | float | Median VLAS across frames with detected landmarks |
vhs_std | float | null | Standard deviation of VHS across frames |
vlas_std | float | null | Standard deviation of VLAS across frames |
vhs_confidence | string | Batch-level VHS confidence: optimal, good, or review |
vlas_confidence | string | Batch-level VLAS confidence: optimal, good, or review |
n_photos_processed | integer | Total number of images processed |
n_photos_with_landmarks | integer | Number of images where landmarks were detected |
processing_started | boolean | Whether processing began |
per_photo | object[] | Per-image results array |
per_photo[].vhs | float | null | VHS for this image |
per_photo[].vlas | float | null | VLAS for this image |
per_photo[].spread | float | null | Ensemble spread for this image. null for PoP images. |
per_photo[].model_confidence | string | Confidence tier for this image |
per_photo[].landmarks_detected | boolean | Whether landmarks were found |
per_photo[].axes_perpendicular | boolean | Whether cardiac axes are perpendicular |
per_photo[].pop_detected | boolean | Whether PoP was detected (only on successful frames) |
historic_vhs | array | Historical VHS as [[date, value]] pairs (top-level, not per-photo) |
historic_vlas | array | Historical VLAS as [[date, value]] pairs (top-level, not per-photo) |
billing | object | Billing metadata for this request. See Billing section. |
Error Responses
| HTTP Status | Meaning | Example Body |
|---|---|---|
400 | Bad Request — missing or invalid fields | {"error": "Missing required field: image"} |
403 | Forbidden — invalid or missing API key | {"error": "Invalid API key"} |
413 | Payload Too Large — image exceeds 10 MB | {"error": "Image exceeds maximum size of 10MB"} |
422 | Unprocessable — corner detection failed (corners/preprocess only) | {"error": "Corner detection failed"} |
500 | Internal Server Error | {"error": "Internal server error"} |
503 | Service Unavailable — PoP pipeline not loaded | {"error": "PoP pipeline not available"} |
POST /v3/predict/stream
Submit a batch of images and receive results as Server-Sent Events (SSE). The request body is the same as the batch /v3/predict endpoint.
SSE Events
processing_started
event: processing_started
data: {"n_images": 2, "model_version": "3.4.0-ensemble"}photo (emitted per image)
event: photo
data: {
"index": 0,
"vhs": 11.32,
"vlas": 1.87,
"spread": 0.0023,
"model_confidence": "optimal",
"landmarks_detected": true,
"axes_perpendicular": true,
"n_total": 2,
"model_version": "3.4.0-ensemble"
}summary
event: summary
data: {
"best_vhs": 11.32,
"best_vlas": 1.87,
"median_vhs": 11.32,
"median_vlas": 1.87,
"vhs_std": null,
"vlas_std": null,
"vhs_confidence": "optimal",
"vlas_confidence": "optimal",
"best_photo_index": 0,
"n_photos_processed": 2,
"n_photos_with_landmarks": 1
}Billing: Stream predictions are billed at the session rate ($4.00 paygo) as a single event, regardless of how many images are in the batch.
error
event: error
data: {"index": 1, "uuid": "patient-456", "error": "Invalid image data"}POST /v3/pop-session/start
Initialize a new Picture-of-Picture (PoP) capture session. The session guides the client through a multi-frame capture flow to obtain the best possible radiograph image from a screen photo.
Request
Send an empty JSON body:
POST /v3/pop-session/start
Content-Type: application/json
x-api-key: YOUR_API_KEY
{}Response
{
"session_id": "0960abaa8179476d",
"session_status": "ready",
"pop_pipeline_enabled": true,
"models_loaded": 5,
"pop_models_loaded": 1,
"model_version": "3.4.0-ensemble",
"session_config": {
"max_frames": 20,
"max_consecutive_failures": 5,
"session_ttl_seconds": 300
}
}Response Fields
| Field | Type | Description |
|---|---|---|
session_id | string | Unique session identifier to include in subsequent requests |
session_status | string | Current session state: "ready" |
pop_pipeline_enabled | boolean | Whether the PoP pipeline is available |
models_loaded | integer | Number of prediction models loaded |
pop_models_loaded | integer | Number of PoP pipeline models loaded |
model_version | string | Model version identifier |
session_config | object | Session configuration including max frames, max consecutive failures, and session TTL |
POST /v3/pop-session
Send a captured frame to an active PoP session. The server evaluates the frame quality and either requests another frame or completes the session with a prediction.
Request
{
"image": "<base64-encoded-frame>",
"session_id": "sess_abc123def456",
"uuid": "patient-123"
}Response: Continue — Successful Frame
{
"session_id": "0960abaa8179476d",
"session_status": "continue",
"good_frames": 1,
"frame_result": {
"vhs": 10.99,
"vlas": 1.51,
"spread": null,
"model_confidence": "good",
"landmarks_detected": true,
"pop_detected": true
}
}Response: Continue — Failed Frame
{
"session_id": "0960abaa8179476d",
"session_status": "continue",
"reason": "0/3 good frames so far, need more",
"frame_index": 0,
"frames_processed": 1,
"good_frames": 0,
"result_code": 5,
"result_description": "Neither VHS nor VLAS could be calculated — check image quality.",
"frame_result": {
"vhs": null,
"vlas": null,
"spread": null,
"model_confidence": "review",
"landmarks_detected": false,
"pop_detected": true
},
"model_version": "3.4.0-ensemble"
}No billing field is present during continue — billing is only evaluated on session completion or failure. Note: spread is always null for PoP predictions.
Response: Complete (session succeeded)
{
"session_id": "sess_abc123def456",
"session_status": "complete",
"frame_index": 3,
"rectified_image": "<base64-encoded-rectified-image>",
"prediction": {
"vhs": 10.4,
"vlas": 2.8,
"prediction": [
{ "x": 0.42, "y": 0.15, "class": "heart_top" },
{ "x": 0.41, "y": 0.44, "class": "heart_bottom" },
{ "x": 0.25, "y": 0.30, "class": "heart_right" },
{ "x": 0.57, "y": 0.29, "class": "heart_left" },
{ "x": 0.49, "y": 0.40, "class": "VLAS" },
{ "x": 0.36, "y": 0.12, "class": "Vertebra A" },
{ "x": 0.55, "y": 0.12, "class": "Vertebra B" }
],
"result_code": 0,
"result_description": "Success",
"model_confidence": "optimal",
"spread": 0.0028,
"model_version": "3.4.0-ensemble",
"rendered_image": "<base64>"
},
"billing": {
"billable": true,
"reason": "valid_vhs",
"call_type": "session",
"monthly_usage": 49,
"tier": "paygo",
"rate": 4.00
}
}Response: Failed (session could not complete)
{
"session_id": "sess_abc123def456",
"session_status": "failed",
"frame_index": 20,
"error": "max_frames_exceeded",
"billing": {
"billable": false,
"reason": "no_valid_vhs",
"monthly_usage": 49,
"tier": "paygo"
}
}A PoP session is billed as a single event at the session rate when it completes with a valid VHS. Failed sessions are never billed.
Session Completion Logic
| Condition | Result |
|---|---|
| Frame quality is sufficient | Session completes with prediction |
Frame count reaches max_frames | Session fails with max_frames_exceeded |
Session exceeds session_ttl_seconds | Session fails with session_timeout |
| Frame quality insufficient | Returns continue with frame result details |
POST /v3/corners
Detect the four corners of a radiograph screen in a photograph. Returns corner coordinates for use with the /v3/rectify endpoint.
Request
{
"image": "<base64-encoded-image>"
}Response
{
"corners": [[137.48, 100.30], [490.50, 81.32], [495.35, 421.38], [154.89, 421.57]],
"order": "TL,TR,BR,BL",
"image_width": 640,
"image_height": 482,
"model_version": "3.4.0-ensemble"
}Response Fields
| Field | Type | Description |
|---|---|---|
corners | float[][] | Four corner coordinates as [x, y] arrays in pixel space |
order | string | Corner ordering: "TL,TR,BR,BL" (top-left, top-right, bottom-right, bottom-left) |
image_width | integer | Width of the input image in pixels |
image_height | integer | Height of the input image in pixels |
model_version | string | Model version identifier |
POST /v3/rectify
Apply perspective correction to an image using the provided corner coordinates. Returns a rectified (de-warped) image.
Request
{
"image": "<base64-encoded JPEG>",
"corners": [[137.48, 100.30], [490.50, 81.32], [495.35, 421.38], [154.89, 421.57]]
}Response
{
"rectified_image": "<base64-encoded JPEG>",
"width": 346,
"height": 330,
"model_version": "3.4.0-ensemble"
}POST /v3/preprocess
Combined corner detection and perspective rectification in a single call. Equivalent to calling /v3/corners followed by /v3/rectify.
Request
{
"image": "<base64-encoded-image>"
}Response
{
"rectified_image": "<base64-encoded JPEG>",
"corners": [[137.48, 100.30], [490.50, 81.32], [495.35, 421.38], [154.89, 421.57]],
"order": "TL,TR,BR,BL",
"width": 346,
"height": 330,
"model_version": "3.4.0-ensemble"
}Landmarks
Each prediction returns 7 anatomical landmarks identified on the radiograph. Coordinates are normalized (0.0–1.0) relative to image dimensions.
| Class | Description |
|---|---|
heart_top | Cranial-most point of the cardiac silhouette |
heart_bottom | Caudal-ventral apex of the cardiac silhouette |
heart_right | Right-most (cranial) extent of the heart border |
heart_left | Left-most (caudal) extent of the heart border |
VLAS | Caudal-dorsal point where the left atrium meets the vertebral column |
Vertebra A | Cranial edge of T4 vertebra (start of vertebral measurement) |
Vertebra B | Caudal edge of the vertebra where the long-axis measurement ends |
Result Codes
| Code | Description | Billable |
|---|---|---|
0 | Abnormal result — unexpected prediction state | No |
1 | VHS and VLAS predicted successfully with high confidence | Yes |
2 | VHS and VLAS predicted successfully with moderate confidence | Yes |
3 | VHS and VLAS predicted but confidence is low — review recommended | Yes |
4 | VHS predicted successfully, VLAS could not be calculated | Yes |
5 | Neither VHS nor VLAS could be calculated — check image quality | No |
6 | Image rejected — not a valid radiograph | No |
7 | PoP image detected but perspective correction failed | No |
Measurements
VHS (Vertebral Heart Score)
The long axis (heart_top to heart_bottom) and short axis (heart_right to heart_left) are measured and mapped onto the thoracic vertebral column starting at T4 (Vertebra A).
VHS = (long_axis_vertebrae + short_axis_vertebrae)Normal range (canine): 8.5 – 10.5 vertebrae
VLAS (Vertebral Lung-to-Apex Scale)
Distance from heart_top to the VLAS landmark, measured in vertebral units.
VLAS = distance(heart_top, VLAS_landmark) in vertebral unitsNormal range (canine): < 2.5 vertebrae. Values ≥ 2.5 suggest left atrial enlargement.
Confidence Scoring
Confidence is determined per-image based on image type. For clean (direct) radiographs, the 5-fold ensemble produces a spread value representing variance between model predictions. For PoP (picture-of-picture) images, a single dedicated model is used and confidence is based on geometry validation and heatmap sharpness. Use model_confidence as the primary confidence indicator for all image types.
Note: spread will be null for PoP predictions. Use model_confidence as the primary confidence indicator.
Clean Radiographs (Ensemble)
| Confidence | Spread Threshold | Recommendation |
|---|---|---|
| Optimal | < 0.005 | Results are reliable |
| Good | < 0.008 | Results are usable; visual verification suggested |
| Review | ≥ 0.008 | Manual review recommended; consider re-submitting a higher-quality image |
PoP Images (Single Model)
| Confidence | Criteria | Recommendation |
|---|---|---|
| Optimal | Valid geometry + high heatmap sharpness | Results are reliable |
| Good | Valid geometry + valid VHS range | Results are usable; visual verification suggested |
| Review | Geometry check failed or VHS out of range | Manual review recommended; try repositioning the camera |
Rate Limits
| Parameter | Limit |
|---|---|
| Max batch size | 10 images per request |
| Max image size | 10 MB per image |
| Accepted formats | JPEG, PNG |
| PoP session TTL | 300 seconds (5 minutes) |
| Max PoP frames per session | 20 frames |
| Request timeout | 300 seconds |
| Cold start latency | ~3–4 seconds |
| Warm inference (clean images) | ~1.5 seconds per image |
| Warm inference (PoP images) | ~2–3 seconds per image (includes perspective correction preprocessing) |
Input Resolution
Images larger than 1080px on their longest side are automatically downsampled before processing. This ensures consistent performance across different camera resolutions and monitor types. No action required from the client — preprocessing is handled server-side. EXIF orientation data is also automatically applied.
Billing
Overview
The V3 API uses pay-for-value billing. You are only charged when the algorithm returns a valid VHS measurement (result codes 1–4). Failed predictions, rejected images, and utility endpoints are always free.
Billing Rules
| Rule | Description |
|---|---|
| Valid VHS = billed | Result codes 1–4 incur a charge |
| No VHS = free | Result codes 0, 5, 6, 7 are never charged |
| 24h recheck grace | Same uuid within 24 hours is free. Pass a patient identifier in the uuid field to enable this. |
| Session = one charge | Batch, stream, and PoP session calls are billed as a single event at the session rate |
| Utility endpoints = free | /health, /corners, /rectify, /preprocess, /pop-session/start are never billed |
Pricing Tiers
| Tier | Monthly Volume | Single Image | Session / Stream / PoP |
|---|---|---|---|
| Pay-as-you-go | 0–100 calls | $3.00 | $4.00 |
| Starter | 101–500 calls | $2.50 | $3.50 |
| Professional | 501–2,000 calls | $2.00 | $3.00 |
| Enterprise | 2,001+ calls | Custom | Custom |
New API keys default to the pay-as-you-go tier. Contact us for volume pricing or enterprise agreements.
Call Types
| Endpoint | Call Type | Rate |
|---|---|---|
POST /v3/predict (single image) | single | Single rate |
POST /v3/predict (batch images) | session | Session rate |
POST /v3/predict/stream | session | Session rate |
POST /v3/pop-session (on complete) | session | Session rate |
Billing Response Object
Three example shapes depending on the billing outcome:
Billable call
{
"billable": true,
"reason": "valid_vhs",
"call_type": "single",
"monthly_usage": 47,
"tier": "paygo",
"rate": 3.00
}Non-billable call
{
"billable": false,
"reason": "no_valid_vhs",
"monthly_usage": 47,
"tier": "paygo"
}Non-billable recheck
{
"billable": false,
"reason": "recheck_24h",
"monthly_usage": 47,
"tier": "paygo"
}Billing Response Fields
| Field | Type | Description |
|---|---|---|
billable | boolean | Whether this call was counted as a billable event |
reason | string | Why: "valid_vhs", "no_valid_vhs", "recheck_24h", or "billing_error" |
call_type | string | "single" or "session". Only present on billable calls. |
monthly_usage | integer | Total billable calls this billing period |
tier | string | Current pricing tier: "paygo", "starter", "professional", or "enterprise" |
rate | number | Dollar amount charged. Only present on billable calls. |
Tips for Minimizing Costs
- Always pass
uuid— enables 24h recheck grace so repeat scans of the same patient within a day are free. - Use batch or stream for multiple images of the same patient — charged once at the session rate instead of per-image.
- Pre-validate images before sending — ensure images are valid radiographs to avoid wasted requests.
Code Examples
Python — Single Image
import requests
import base64
# Read and encode the radiograph
with open("radiograph.jpg", "rb") as f:
image_data = base64.b64encode(f.read()).decode()
# Send prediction request
response = requests.post(
"https://api.radanalyzer.com/v3/predict",
headers={
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json"
},
json={
"image": image_data,
"uuid": "patient-123",
"render": True
}
)
data = response.json()
print(f"VHS: {data['vhs']}")
print(f"VLAS: {data['vlas']}")
print(f"Confidence: {data['model_confidence']}")
print(f"Spread: {data['spread']}")
print(f"Result: {data['result_description']}")JavaScript — PoP Session Flow
const API_BASE = "https://api.radanalyzer.com";
const headers = {
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json"
};
// Step 1: Start a PoP session
const startRes = await fetch(`${API_BASE}/v3/pop-session/start`, {
method: "POST",
headers,
body: JSON.stringify({})
});
const { session_id } = await startRes.json();
// Step 2: Send frames until session completes
let sessionComplete = false;
while (!sessionComplete) {
const frameBase64 = await captureFrame(); // your capture function
const frameRes = await fetch(`${API_BASE}/v3/pop-session`, {
method: "POST",
headers,
body: JSON.stringify({
image: frameBase64,
session_id: session_id,
uuid: "patient-123"
})
});
const result = await frameRes.json();
if (result.session_status === "complete") {
console.log("VHS:", result.prediction.vhs);
console.log("VLAS:", result.prediction.vlas);
sessionComplete = true;
} else if (result.session_status === "failed") {
console.error("Session failed:", result.error);
sessionComplete = true;
} else {
console.log("Feedback:", result.feedback);
// Show feedback to user, capture another frame
}
}cURL — Health Check
curl -X GET https://api.radanalyzer.com/v3/health \
-H "x-api-key: YOUR_API_KEY"Changelog
| Version | Date | Changes |
|---|---|---|
3.4.0 | April 2026 | Custom PoP landmark model (replaces v3.3 ensemble), resolution normalization, EXIF orientation handling, geometry-based confidence for PoP, reduced cold start time |
3.3.0 | March 2026 | PoP pipeline, session-based capture, corner detection, rectification |
3.0.0 | March 2026 | Initial V3 — 5-fold ensemble, batch mode, streaming, confidence scoring |