madoo.image-detail/v1
Madoo image detail — Fine detail and residuals of an image on small patches, globally and per tone band, and the patches to look at closely with a context rectangle ready for Crop (Image Detail).
| Reference | madoo.image-detail/v1 |
| Schema id | urn:madoo:schema:image-detail:v1 |
| Version | 1.0.0 |
| Served at | GET /api/v1/json-schemas/madoo.image-detail/v1 · MCP get_json_schema |
Where it appears
Section titled “Where it appears”- Produced by:
image/detail→detail
Fields
Section titled “Fields”| Field | Type | Required | Rules | Description |
|---|---|---|---|---|
schema |
"madoo.image-detail/v1" |
yes | ||
algorithm_version |
string | yes | ||
image |
object | yes | ||
image.width |
integer | yes | ≥ 1 | After EXIF orientation. |
image.height |
integer | yes | ≥ 1 | |
image.orientation_applied |
boolean | yes | True when the EXIF orientation rotated or mirrored the image. | |
color |
object | yes | ||
color.working_space |
"srgb-8" |
yes | ||
color.profile_policy |
one of "srgb", "converted", "assumed_srgb" |
yes | srgb: the file is sRGB; converted: its ICC profile was converted to sRGB; assumed_srgb: an RGB image without a profile, read as sRGB. | |
color.source_depth |
integer or null | yes | Bits per channel of the file. | |
color.analysis_depth |
8 |
yes | ||
warnings |
array of string | yes | Facts that limit how the numbers may be read: a JPEG source (compression can contaminate detail measurements), several frames or pages (the first was measured), a source deeper than 8 bits (measured at 8 bits). | |
settings |
object | yes | ||
settings.patch_size |
integer | |||
settings.stride |
integer | |||
settings.alpha_min |
integer | |||
settings.max_patches |
integer | |||
settings.context_size |
integer | |||
settings.min_separation |
integer | |||
settings.max_regions |
integer | |||
settings.mask_threshold |
integer | |||
settings.step |
integer | |||
settings.luma_min |
number | |||
settings.luma_max |
number | |||
settings.flat_gradient_max |
number | |||
settings.select |
array of one of "low_gradient", "detail", "bright_transition" |
|||
settings.mask |
boolean | |||
patches |
object | yes | ||
patches.examined |
integer | yes | ≥ 0 | |
patches.opaque |
integer | yes | ≥ 0 | Patches whose pixels and 3 px neighbourhood are all at alpha_min or more. |
patches.eligible |
integer | yes | ≥ 0 | |
patches.area_share |
number or null | yes | ≥ 0; ≤ 1 | Share of the image covered by eligible patches. |
patches.excluded |
object | yes | ||
patches.excluded.alpha |
integer | ≥ 0 | ||
patches.excluded.too_dark |
integer | ≥ 0 | ||
patches.excluded.too_bright |
integer | ≥ 0 | ||
patches.excluded.too_structured |
integer | ≥ 0 | ||
patches.excluded.outside_mask |
integer | ≥ 0 | ||
global |
object | yes | ||
global.n |
integer | yes | ≥ 0 | Eligible patches. Every figure below is the median across them of the per-patch value. |
global.status |
one of "ok", "not_observable" |
yes | not_observable: the measurement is undefined here (see reason); its numbers are null. Zero is a valid measurement, never a stand-in for null. | |
global.reason |
one of null, "no_eligible_pixels", "too_few_samples", "zero_variance", "no_eligible_patches", "mask_empty_in_region" |
yes | ||
global.gradient |
number or null | Per patch: mean of |Y(x+1,y) - Y(x-1,y)| + |Y(x,y+1) - Y(x,y-1)| (central differences, the two axes summed). | ||
global.residual_mad_3 |
number or null | Per patch: 1.4826 x median(|r - median(r)|) of r = luma minus its 3x3 mean, in luma units. | ||
global.residual_mad_7 |
number or null | Same with the 7x7 mean. | ||
global.residual_corr_x |
number or null | ≥ -1; ≤ 1 | Per patch: 3x3 residuals one pixel apart horizontally, centred on the patch mean, sum(a*b) / sum((a^2 + b^2) / 2); null when that energy is below 1e-12. | |
global.residual_corr_y |
number or null | ≥ -1; ≤ 1 | Same for vertically adjacent pixels. | |
bands |
array of objects | yes | min items 5; max items 5 | Five equal luma bands of the patch means: [0, 0.2), [0.2, 0.4), [0.4, 0.6), [0.6, 0.8), [0.8, 1]. |
bands[].n |
integer | yes | ≥ 0 | Eligible patches. Every figure below is the median across them of the per-patch value. |
bands[].status |
one of "ok", "not_observable" |
yes | not_observable: the measurement is undefined here (see reason); its numbers are null. Zero is a valid measurement, never a stand-in for null. | |
bands[].reason |
one of null, "no_eligible_pixels", "too_few_samples", "zero_variance", "no_eligible_patches", "mask_empty_in_region" |
yes | ||
bands[].gradient |
number or null | Per patch: mean of |Y(x+1,y) - Y(x-1,y)| + |Y(x,y+1) - Y(x,y-1)| (central differences, the two axes summed). | ||
bands[].residual_mad_3 |
number or null | Per patch: 1.4826 x median(|r - median(r)|) of r = luma minus its 3x3 mean, in luma units. | ||
bands[].residual_mad_7 |
number or null | Same with the 7x7 mean. | ||
bands[].residual_corr_x |
number or null | ≥ -1; ≤ 1 | Per patch: 3x3 residuals one pixel apart horizontally, centred on the patch mean, sum(a*b) / sum((a^2 + b^2) / 2); null when that energy is below 1e-12. | |
bands[].residual_corr_y |
number or null | ≥ -1; ≤ 1 | Same for vertically adjacent pixels. | |
bands[].l_min |
number | |||
bands[].l_max |
number | |||
selected |
array of objects | yes | ||
selected[].role |
one of "low_gradient", "detail", "bright_transition", "center_fallback" |
yes | low_gradient: the eligible patch with the lowest gradient; detail: the opaque patch with the highest gradient; bright_transition: the opaque patch with the highest mean + gradient; center_fallback: no candidate patch. | |
selected[].patch |
object or null | yes | ||
selected[].context |
object | yes | Pixels of the oriented image, origin top left; covers columns x..x+width-1 and rows y..y+height-1. | |
selected[].context.x |
integer | yes | ≥ 0 | |
selected[].context.y |
integer | yes | ≥ 0 | |
selected[].context.width |
integer | yes | ≥ 1 | |
selected[].context.height |
integer | yes | ≥ 1 | |
selected[].patch_in_context |
object or null | |||
selected[].patch_in_context.x |
integer | |||
selected[].patch_in_context.y |
integer | |||
selected[].mean |
number or null | |||
selected[].gradient |
number or null | |||
selected[].residual_mad_3 |
number or null | |||
selected[].residual_mad_7 |
number or null | |||
selected[].residual_corr_x |
number or null | ≥ -1; ≤ 1 | ||
selected[].residual_corr_y |
number or null | ≥ -1; ≤ 1 | ||
selected[].score |
number or null |
The complete schema
Section titled “The complete schema”madoo.image-detail/v1 (JSON Schema)
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "urn:madoo:schema:image-detail:v1", "title": "Madoo image detail", "description": "What Image Detail (image/detail) measured at native resolution on small patches: fine detail and residuals (which include texture, noise, sharpening and compression — not proof of film grain), and the regions it selected for a closer look.", "type": "object", "required": [ "schema", "algorithm_version", "image", "color", "warnings", "settings", "patches", "global", "bands", "selected" ], "properties": { "schema": { "const": "madoo.image-detail/v1" }, "algorithm_version": { "type": "string" }, "image": { "$ref": "#/$defs/image" }, "color": { "$ref": "#/$defs/color" }, "warnings": { "$ref": "#/$defs/warnings" }, "settings": { "type": "object", "properties": { "patch_size": { "type": "integer" }, "stride": { "type": "integer" }, "alpha_min": { "type": "integer" }, "max_patches": { "type": "integer" }, "context_size": { "type": "integer" }, "min_separation": { "type": "integer" }, "max_regions": { "type": "integer" }, "mask_threshold": { "type": "integer" }, "step": { "type": "integer" }, "luma_min": { "type": "number" }, "luma_max": { "type": "number" }, "flat_gradient_max": { "type": "number" }, "select": { "type": "array", "items": { "enum": [ "low_gradient", "detail", "bright_transition" ] } }, "mask": { "type": "boolean" } } }, "patches": { "type": "object", "required": [ "examined", "opaque", "eligible", "area_share", "excluded" ], "properties": { "examined": { "type": "integer", "minimum": 0 }, "opaque": { "type": "integer", "minimum": 0, "description": "Patches whose pixels and 3 px neighbourhood are all at alpha_min or more." }, "eligible": { "type": "integer", "minimum": 0 }, "area_share": { "type": [ "number", "null" ], "minimum": 0, "maximum": 1, "description": "Share of the image covered by eligible patches." }, "excluded": { "type": "object", "properties": { "alpha": { "type": "integer", "minimum": 0 }, "too_dark": { "type": "integer", "minimum": 0 }, "too_bright": { "type": "integer", "minimum": 0 }, "too_structured": { "type": "integer", "minimum": 0 }, "outside_mask": { "type": "integer", "minimum": 0 } } } } }, "global": { "type": "object", "required": [ "n", "status", "reason" ], "properties": { "n": { "type": "integer", "minimum": 0, "description": "Eligible patches. Every figure below is the median across them of the per-patch value." }, "status": { "$ref": "#/$defs/status" }, "reason": { "$ref": "#/$defs/reason" }, "gradient": { "type": [ "number", "null" ], "description": "Per patch: mean of |Y(x+1,y) - Y(x-1,y)| + |Y(x,y+1) - Y(x,y-1)| (central differences, the two axes summed)." }, "residual_mad_3": { "type": [ "number", "null" ], "description": "Per patch: 1.4826 x median(|r - median(r)|) of r = luma minus its 3x3 mean, in luma units." }, "residual_mad_7": { "type": [ "number", "null" ], "description": "Same with the 7x7 mean." }, "residual_corr_x": { "type": [ "number", "null" ], "minimum": -1, "maximum": 1, "description": "Per patch: 3x3 residuals one pixel apart horizontally, centred on the patch mean, sum(a*b) / sum((a^2 + b^2) / 2); null when that energy is below 1e-12." }, "residual_corr_y": { "type": [ "number", "null" ], "minimum": -1, "maximum": 1, "description": "Same for vertically adjacent pixels." } } }, "bands": { "type": "array", "minItems": 5, "maxItems": 5, "description": "Five equal luma bands of the patch means: [0, 0.2), [0.2, 0.4), [0.4, 0.6), [0.6, 0.8), [0.8, 1].", "items": { "type": "object", "required": [ "n", "status", "reason" ], "properties": { "n": { "type": "integer", "minimum": 0, "description": "Eligible patches. Every figure below is the median across them of the per-patch value." }, "status": { "$ref": "#/$defs/status" }, "reason": { "$ref": "#/$defs/reason" }, "gradient": { "type": [ "number", "null" ], "description": "Per patch: mean of |Y(x+1,y) - Y(x-1,y)| + |Y(x,y+1) - Y(x,y-1)| (central differences, the two axes summed)." }, "residual_mad_3": { "type": [ "number", "null" ], "description": "Per patch: 1.4826 x median(|r - median(r)|) of r = luma minus its 3x3 mean, in luma units." }, "residual_mad_7": { "type": [ "number", "null" ], "description": "Same with the 7x7 mean." }, "residual_corr_x": { "type": [ "number", "null" ], "minimum": -1, "maximum": 1, "description": "Per patch: 3x3 residuals one pixel apart horizontally, centred on the patch mean, sum(a*b) / sum((a^2 + b^2) / 2); null when that energy is below 1e-12." }, "residual_corr_y": { "type": [ "number", "null" ], "minimum": -1, "maximum": 1, "description": "Same for vertically adjacent pixels." }, "l_min": { "type": "number" }, "l_max": { "type": "number" } } } }, "selected": { "type": "array", "items": { "type": "object", "required": [ "role", "patch", "context" ], "properties": { "role": { "enum": [ "low_gradient", "detail", "bright_transition", "center_fallback" ], "description": "low_gradient: the eligible patch with the lowest gradient; detail: the opaque patch with the highest gradient; bright_transition: the opaque patch with the highest mean + gradient; center_fallback: no candidate patch." }, "patch": { "oneOf": [ { "$ref": "#/$defs/rect" }, { "type": "null" } ] }, "context": { "$ref": "#/$defs/rect", "description": "Up to context_size, centred on the patch and moved inside the image, never enlarged." }, "patch_in_context": { "type": [ "object", "null" ], "properties": { "x": { "type": "integer" }, "y": { "type": "integer" } } }, "mean": { "type": [ "number", "null" ] }, "gradient": { "type": [ "number", "null" ] }, "residual_mad_3": { "type": [ "number", "null" ] }, "residual_mad_7": { "type": [ "number", "null" ] }, "residual_corr_x": { "type": [ "number", "null" ], "minimum": -1, "maximum": 1 }, "residual_corr_y": { "type": [ "number", "null" ], "minimum": -1, "maximum": 1 }, "score": { "type": [ "number", "null" ] } } } } }, "$defs": { "rect": { "type": "object", "required": [ "x", "y", "width", "height" ], "properties": { "x": { "type": "integer", "minimum": 0 }, "y": { "type": "integer", "minimum": 0 }, "width": { "type": "integer", "minimum": 1 }, "height": { "type": "integer", "minimum": 1 } }, "description": "Pixels of the oriented image, origin top left; covers columns x..x+width-1 and rows y..y+height-1." }, "image": { "type": "object", "required": [ "width", "height", "orientation_applied" ], "properties": { "width": { "type": "integer", "minimum": 1, "description": "After EXIF orientation." }, "height": { "type": "integer", "minimum": 1 }, "orientation_applied": { "type": "boolean", "description": "True when the EXIF orientation rotated or mirrored the image." } } }, "color": { "type": "object", "required": [ "working_space", "profile_policy", "source_depth", "analysis_depth" ], "properties": { "working_space": { "const": "srgb-8" }, "profile_policy": { "enum": [ "srgb", "converted", "assumed_srgb" ], "description": "srgb: the file is sRGB; converted: its ICC profile was converted to sRGB; assumed_srgb: an RGB image without a profile, read as sRGB." }, "source_depth": { "type": [ "integer", "null" ], "description": "Bits per channel of the file." }, "analysis_depth": { "const": 8 } } }, "sampling": { "type": "object", "required": [ "method", "step", "eligible_pixels", "sampled_pixels" ], "properties": { "method": { "enum": [ "grid", "full" ] }, "step": { "type": "integer", "minimum": 1 }, "eligible_pixels": { "type": "integer", "minimum": 0, "description": "Pixels of the region that may be measured: opaque enough and inside the mask." }, "sampled_pixels": { "type": "integer", "minimum": 0 } } }, "warnings": { "type": "array", "items": { "type": "string" }, "description": "Facts that limit how the numbers may be read: a JPEG source (compression can contaminate detail measurements), several frames or pages (the first was measured), a source deeper than 8 bits (measured at 8 bits)." }, "status": { "enum": [ "ok", "not_observable" ], "description": "not_observable: the measurement is undefined here (see reason); its numbers are null. Zero is a valid measurement, never a stand-in for null." }, "reason": { "type": [ "string", "null" ], "enum": [ null, "no_eligible_pixels", "too_few_samples", "zero_variance", "no_eligible_patches", "mask_empty_in_region" ] } }}