71 lines
2.8 KiB
Go
71 lines
2.8 KiB
Go
|
|
package validation
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"materialanalyzer/internal/model"
|
||
|
|
)
|
||
|
|
|
||
|
|
func ValidateAsset(asset model.MaterialAsset, hist model.ColorHistogram) model.ValidationReport {
|
||
|
|
report := model.ValidationReport{
|
||
|
|
Status: "valid",
|
||
|
|
CheckedAt: time.Now().UTC().Format(time.RFC3339),
|
||
|
|
}
|
||
|
|
checkRange(&report, "visual.brightness", asset.Visual.Brightness, 0, 1)
|
||
|
|
checkRange(&report, "visual.contrast", asset.Visual.Contrast, 0, 1)
|
||
|
|
checkRange(&report, "visual.saturation", asset.Visual.Saturation, 0, 1)
|
||
|
|
checkRange(&report, "texture.orientation", asset.Texture.PrimaryOrientationDeg, 0, 180)
|
||
|
|
if asset.Texture.Entropy <= 0 {
|
||
|
|
report.Errors = append(report.Errors, "texture.entropy must be > 0")
|
||
|
|
}
|
||
|
|
if len(hist.Values) != 256 {
|
||
|
|
report.Errors = append(report.Errors, fmt.Sprintf("histogram length = %d, want 256", len(hist.Values)))
|
||
|
|
}
|
||
|
|
for _, vector := range asset.Embeddings.Vectors {
|
||
|
|
if vector.Dimensions <= 0 {
|
||
|
|
report.Errors = append(report.Errors, "embedding "+vector.Name+" has invalid dimension")
|
||
|
|
}
|
||
|
|
if vector.ByteLength != int64(vector.Dimensions*4) {
|
||
|
|
report.Errors = append(report.Errors, fmt.Sprintf("embedding %s byte length = %d, want %d", vector.Name, vector.ByteLength, vector.Dimensions*4))
|
||
|
|
}
|
||
|
|
if vector.Model == "dinov2-large" && vector.Dimensions != 1024 {
|
||
|
|
report.Errors = append(report.Errors, "dinov2-large dimension must be 1024")
|
||
|
|
}
|
||
|
|
if vector.Model == "ViT-L/14" && vector.Dimensions != 768 {
|
||
|
|
report.Errors = append(report.Errors, "ViT-L/14 dimension must be 768")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if asset.Semantic.Description == "" {
|
||
|
|
report.Errors = append(report.Errors, "semantic.description is required")
|
||
|
|
}
|
||
|
|
if asset.Semantic.MaterialType == "" {
|
||
|
|
report.Errors = append(report.Errors, "semantic.material_type is required")
|
||
|
|
}
|
||
|
|
if asset.Semantic.SurfaceFinish == "" {
|
||
|
|
report.Errors = append(report.Errors, "semantic.surface_finish is required")
|
||
|
|
}
|
||
|
|
if asset.Semantic.ColorFamily == "" {
|
||
|
|
report.Errors = append(report.Errors, "semantic.color_family is required")
|
||
|
|
}
|
||
|
|
if asset.Semantic.Confidence <= 0 || asset.Semantic.Confidence > 1 {
|
||
|
|
report.Errors = append(report.Errors, "semantic.confidence must be in (0,1]")
|
||
|
|
}
|
||
|
|
if len(asset.Canonical.BrightnessDistribution) != asset.Canonical.BrightnessHistogramBins {
|
||
|
|
report.Warnings = append(report.Warnings, "canonical brightness histogram bin count mismatch")
|
||
|
|
}
|
||
|
|
if len(asset.Canonical.GradientHistogram) != asset.Canonical.GradientHistogramBins {
|
||
|
|
report.Warnings = append(report.Warnings, "canonical gradient histogram bin count mismatch")
|
||
|
|
}
|
||
|
|
if len(report.Errors) > 0 {
|
||
|
|
report.Status = "invalid"
|
||
|
|
}
|
||
|
|
return report
|
||
|
|
}
|
||
|
|
|
||
|
|
func checkRange(report *model.ValidationReport, name string, value, min, max float64) {
|
||
|
|
if value < min || value > max {
|
||
|
|
report.Errors = append(report.Errors, fmt.Sprintf("%s = %.4f, want [%.2f,%.2f]", name, value, min, max))
|
||
|
|
}
|
||
|
|
}
|