///|
pub struct ValidationReport {
mut valid : Bool
errors : Array[String]
warnings : Array[String]
} derive(Debug, Eq)
///|
pub fn ValidationReport::new() -> ValidationReport {
{ valid: true, errors: [], warnings: [] }
}
///|
fn ValidationReport::error(self : ValidationReport, message : String) -> Unit {
self.valid = false
self.errors.push(message)
}
///|
fn ValidationReport::warning(self : ValidationReport, message : String) -> Unit {
self.warnings.push(message)
}
///|
pub fn ValidationReport::message_count(self : ValidationReport) -> Int {
self.errors.length() + self.warnings.length()
}
///|
pub fn ValidationReport::to_json(self : ValidationReport) -> String {
let sb = StringBuilder()
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("],\"warnings\":[")
for i, warning in self.warnings {
if i > 0 {
sb.write_string(",")
}
sb.write_string("\"\{escape_json(warning)}\"")
}
sb.write_string("]}")
sb.to_string()
}
///|
pub fn validate_size(size : Size, name : String) -> ValidationReport {
let report = ValidationReport::new()
if size.width <= 0 {
report.error("\{name}: width must be positive")
}
if size.height <= 0 {
report.error("\{name}: height must be positive")
}
if size.width * size.height > 100000000 {
report.warning("\{name}: large allocation may be expensive")
}
report
}
///|
pub fn validate_image(image : GrayImage, name : String) -> ValidationReport {
let report = validate_size(image.size(), name)
if image.pixels.length() != max_i(0, image.width * image.height) {
report.error("\{name}: pixel buffer length does not match dimensions")
}
for value in image.pixels {
if value < 0 || value > 255 {
report.error("\{name}: pixel value is outside the 8-bit range")
break
}
}
report
}
///|
pub fn validate_mask(mask : Mask, name : String) -> ValidationReport {
let report = validate_size(
Size::new(width=mask.width, height=mask.height),
name,
)
if mask.values.length() != max_i(0, mask.width * mask.height) {
report.error("\{name}: mask buffer length does not match dimensions")
}
report
}
///|
pub fn validate_box(box : Box2D, size : Size) -> ValidationReport {
let report = ValidationReport::new()
if box.label.id < 0 {
report.error("box label id must be non-negative")
}
if box.rect.is_empty() {
report.error("box must have positive area")
}
if box.rect.x < 0 ||
box.rect.y < 0 ||
box.rect.right() > size.width ||
box.rect.bottom() > size.height {
report.warning("box extends outside image bounds")
}
report
}
///|
pub fn validate_scene(scene : SceneOutput) -> ValidationReport {
let report = validate_image(scene.image, "scene.image")
let mask_report = validate_mask(scene.mask, "scene.mask")
for error in mask_report.errors {
report.error(error)
}
for warning in mask_report.warnings {
report.warning(warning)
}
for box in scene.boxes {
let box_report = validate_box(box, scene.image.size())
for error in box_report.errors {
report.error(error)
}
for warning in box_report.warnings {
report.warning(warning)
}
}
for vector in scene.flow {
if !scene.image.in_bounds(vector.x, vector.y) {
report.warning("flow vector origin is outside image bounds")
}
}
report
}
///|
pub fn validate_manifest(manifest : DatasetManifest) -> ValidationReport {
let report = ValidationReport::new()
if manifest.name.length() == 0 {
report.error("manifest name must not be empty")
}
if manifest.version.length() == 0 {
report.error("manifest version must not be empty")
}
if !manifest.has_unique_names() {
report.error("manifest asset names must be unique")
}
for asset in manifest.assets {
if asset.name.length() == 0 {
report.error("asset name must not be empty")
}
if asset.image_path.length() == 0 {
report.error("asset image path must not be empty")
}
if asset.annotation_path.length() == 0 {
report.warning("asset has no annotation path")
}
}
report
}
///|
pub fn image_checksum(image : GrayImage) -> UInt {
let mut checksum : UInt = 2166136261
for value in image.pixels {
checksum = (checksum ^ value.reinterpret_as_uint()) * 16777619
}
checksum
}
///|
pub fn mask_checksum(mask : Mask) -> UInt {
let mut checksum : UInt = 2166136261
for value in mask.values {
checksum = (checksum ^ value.reinterpret_as_uint()) * 16777619
}
checksum
}
///|
pub fn scene_checksum(scene : SceneOutput) -> UInt {
let mut checksum = image_checksum(scene.image)
checksum = (checksum ^ mask_checksum(scene.mask)) * 16777619
for box in scene.boxes {
checksum = (checksum ^ box.rect.x.reinterpret_as_uint()) * 16777619
checksum = (checksum ^ box.rect.y.reinterpret_as_uint()) * 16777619
checksum = (checksum ^ box.rect.width.reinterpret_as_uint()) * 16777619
checksum = (checksum ^ box.rect.height.reinterpret_as_uint()) * 16777619
}
checksum
}
///|
pub fn manifest_checksum(manifest : DatasetManifest) -> UInt {
let mut checksum : UInt = 2166136261
for asset in manifest.assets {
checksum = (checksum ^ asset.name.length().reinterpret_as_uint()) * 16777619
checksum = (checksum ^ asset.seed) * 16777619
}
checksum
}