///|
pub struct ReproducibilityRecord {
name : String
first_checksum : UInt
second_checksum : UInt
stable : Bool
annotation_stable : Bool
} derive(Debug, Eq)
///|
pub fn check_plan_reproducibility(plan : ScenePlan) -> ReproducibilityRecord {
let first = render_plan(plan)
let second = render_plan(plan)
{
name: plan.name,
first_checksum: scene_checksum(first),
second_checksum: scene_checksum(second),
stable: image_checksum(first.image) == image_checksum(second.image),
annotation_stable: first.boxes == second.boxes &&
first.mask == second.mask &&
first.flow == second.flow,
}
}
///|
pub fn check_suite_reproducibility(
plans : Array[ScenePlan],
) -> Array[ReproducibilityRecord] {
let out : Array[ReproducibilityRecord] = []
for plan in plans {
out.push(check_plan_reproducibility(plan))
}
out
}
///|
pub struct IntegrityReport {
checked : Int
stable : Int
unstable : Int
duplicate_checksums : Int
errors : Array[String]
} derive(Debug, Eq)
///|
pub fn IntegrityReport::from_records(
records : Array[ReproducibilityRecord],
) -> IntegrityReport {
let mut stable = 0
let mut unstable = 0
let mut duplicate = 0
let errors : Array[String] = []
for record in records {
if record.stable && record.annotation_stable {
stable += 1
} else {
unstable += 1
errors.push(record.name + " is not reproducible")
}
}
for i in 0.. Bool {
self.unstable == 0
}
///|
pub fn IntegrityReport::to_json(self : IntegrityReport) -> String {
let sb = StringBuilder()
sb.write_string("{\"checked\":\{self.checked},\"stable\":\{self.stable},")
sb.write_string(
"\"unstable\":\{self.unstable},\"duplicate_checksums\":\{self.duplicate_checksums},",
)
sb.write_string("\"valid\":\{self.valid()},\"errors\":[")
for i, error in self.errors {
if i > 0 {
sb.write_string(",")
}
sb.write_string("\"\{escape_json(error)}\"")
}
sb.write_string("]}")
sb.to_string()
}
///|
pub fn audit_default_suite(size : Size) -> IntegrityReport {
IntegrityReport::from_records(
check_suite_reproducibility(default_suite(size~)),
)
}
///|
pub fn validate_scene_annotations(scene : SceneOutput) -> ValidationReport {
let report = validate_scene(scene)
if scene.mask.width > 0 &&
scene.mask.height > 0 &&
(
scene.mask.width != scene.image.width ||
scene.mask.height != scene.image.height
) {
report.error("scene mask dimensions must match image dimensions")
}
for box in scene.boxes {
if box.label.name.length() == 0 {
report.warning("scene contains an annotation without a label name")
}
if box.rect.area() == 0 {
report.error("scene contains an empty bounding box")
}
}
report
}
///|
pub struct ChecksumEntry {
name : String
checksum : UInt
pixels : Int
annotations : Int
} derive(Debug, Eq)
///|
pub fn ChecksumEntry::from_scene(scene : SceneOutput) -> ChecksumEntry {
{
name: scene.plan.name,
checksum: scene_checksum(scene),
pixels: scene.image.width * scene.image.height,
annotations: scene.boxes.length() +
scene.flow.length() +
scene.mask.count(1),
}
}
///|
pub fn checksum_table(scenes : Array[SceneOutput]) -> Array[ChecksumEntry] {
let out : Array[ChecksumEntry] = []
for scene in scenes {
out.push(ChecksumEntry::from_scene(scene))
}
out
}
///|
pub fn checksum_table_json(scenes : Array[SceneOutput]) -> String {
let entries = checksum_table(scenes)
let sb = StringBuilder()
sb.write_string("[")
for i, entry in entries {
if i > 0 {
sb.write_string(",")
}
sb.write_string(
"{\"name\":\"\{escape_json(entry.name)}\",\"checksum\":\{entry.checksum},\"pixels\":\{entry.pixels},\"annotations\":\{entry.annotations}}",
)
}
sb.write_string("]")
sb.to_string()
}
///|
pub fn validate_suite(plans : Array[ScenePlan]) -> ValidationReport {
let report = ValidationReport::new()
if plans.length() == 0 {
report.warning("suite contains no scene plans")
}
for plan in plans {
if plan.name.length() == 0 {
report.error("scene plan name must not be empty")
}
let size_report = validate_size(plan.size, plan.name)
for error in size_report.errors {
report.error(error)
}
for warning in size_report.warnings {
report.warning(warning)
}
}
report
}
///|
pub fn reproducibility_summary(plans : Array[ScenePlan]) -> String {
IntegrityReport::from_records(check_suite_reproducibility(plans)).to_json()
}
///|
pub fn compare_scene_outputs(
first : SceneOutput,
second : SceneOutput,
) -> String {
let image_error = image_mae(first.image, second.image)
let mask_error = if first.mask == second.mask { 0 } else { 1 }
let box_error = if first.boxes == second.boxes { 0 } else { 1 }
"{\"image_mae\":\{image_error},\"mask_mismatch\":\{mask_error},\"box_mismatch\":\{box_error}}"
}