- Move test scripts to tests/ directory for better organization - Add improved YOLOv5 postprocessing with reference implementation - Update gitignore to exclude *.mflow files and include main.spec - Add debug capabilities and coordinate scaling improvements - Enhance multi-series support with proper validation - Add AGENTS.md documentation and example utilities 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
212 lines
7.3 KiB
Python
212 lines
7.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Test script for postprocessing mode switching and visualization.
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
parent_dir = os.path.dirname(current_dir)
|
|
sys.path.append(parent_dir)
|
|
|
|
from core.nodes.exact_nodes import ExactPostprocessNode
|
|
|
|
def test_postprocess_node():
|
|
"""Test the ExactPostprocessNode for mode switching and configuration."""
|
|
|
|
print("=== Testing ExactPostprocessNode Mode Switching ===")
|
|
|
|
# Create node instance
|
|
try:
|
|
node = ExactPostprocessNode()
|
|
print("✓ ExactPostprocessNode created successfully")
|
|
|
|
# Check if NodeGraphQt is available
|
|
if not hasattr(node, 'set_property'):
|
|
print("⚠ NodeGraphQt not available - using mock properties")
|
|
return True # Skip tests that require NodeGraphQt
|
|
|
|
except Exception as e:
|
|
print(f"✗ Error creating node: {e}")
|
|
return False
|
|
|
|
# Test different postprocessing modes
|
|
test_modes = [
|
|
('fire_detection', 'No Fire,Fire'),
|
|
('yolo_v3', 'person,car,bicycle,motorbike,aeroplane'),
|
|
('yolo_v5', 'person,bicycle,car,motorbike,bus,truck'),
|
|
('classification', 'cat,dog,bird,fish'),
|
|
('raw_output', '')
|
|
]
|
|
|
|
print("\n--- Testing Mode Switching ---")
|
|
for mode, class_names in test_modes:
|
|
try:
|
|
# Set properties for this mode
|
|
node.set_property('postprocess_type', mode)
|
|
node.set_property('class_names', class_names)
|
|
node.set_property('confidence_threshold', 0.6)
|
|
node.set_property('nms_threshold', 0.4)
|
|
|
|
# Get configuration
|
|
config = node.get_postprocessing_config()
|
|
options = node.get_multidongle_postprocess_options()
|
|
|
|
print(f"✓ Mode: {mode}")
|
|
print(f" - Class names: {class_names}")
|
|
print(f" - Config: {config['postprocess_type']}")
|
|
if options:
|
|
print(f" - PostProcessor options created successfully")
|
|
else:
|
|
print(f" - Warning: PostProcessor options not available")
|
|
|
|
except Exception as e:
|
|
print(f"✗ Error testing mode {mode}: {e}")
|
|
return False
|
|
|
|
# Test validation
|
|
print("\n--- Testing Configuration Validation ---")
|
|
try:
|
|
# Valid configuration
|
|
node.set_property('postprocess_type', 'fire_detection')
|
|
node.set_property('confidence_threshold', 0.7)
|
|
node.set_property('nms_threshold', 0.3)
|
|
node.set_property('max_detections', 50)
|
|
|
|
is_valid, message = node.validate_configuration()
|
|
if is_valid:
|
|
print("✓ Valid configuration passed validation")
|
|
else:
|
|
print(f"✗ Valid configuration failed: {message}")
|
|
return False
|
|
|
|
# Invalid configuration
|
|
node.set_property('confidence_threshold', 1.5) # Invalid value
|
|
is_valid, message = node.validate_configuration()
|
|
if not is_valid:
|
|
print(f"✓ Invalid configuration caught: {message}")
|
|
else:
|
|
print("✗ Invalid configuration not caught")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ Error testing validation: {e}")
|
|
return False
|
|
|
|
# Test display properties
|
|
print("\n--- Testing Display Properties ---")
|
|
try:
|
|
display_props = node.get_display_properties()
|
|
expected_props = ['postprocess_type', 'class_names', 'confidence_threshold']
|
|
|
|
for prop in expected_props:
|
|
if prop in display_props:
|
|
print(f"✓ Display property found: {prop}")
|
|
else:
|
|
print(f"✗ Missing display property: {prop}")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ Error testing display properties: {e}")
|
|
return False
|
|
|
|
# Test business properties
|
|
print("\n--- Testing Business Properties ---")
|
|
try:
|
|
business_props = node.get_business_properties()
|
|
print(f"✓ Business properties retrieved: {len(business_props)} properties")
|
|
|
|
# Check key properties exist
|
|
key_props = ['postprocess_type', 'class_names', 'confidence_threshold', 'nms_threshold']
|
|
for prop in key_props:
|
|
if prop in business_props:
|
|
print(f"✓ Key property found: {prop} = {business_props[prop]}")
|
|
else:
|
|
print(f"✗ Missing key property: {prop}")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ Error testing business properties: {e}")
|
|
return False
|
|
|
|
print("\n=== All Tests Passed! ===")
|
|
return True
|
|
|
|
def test_visualization_integration():
|
|
"""Test visualization integration with different modes."""
|
|
|
|
print("\n=== Testing Visualization Integration ===")
|
|
|
|
try:
|
|
node = ExactPostprocessNode()
|
|
|
|
# Test each mode for visualization compatibility
|
|
test_cases = [
|
|
{
|
|
'mode': 'fire_detection',
|
|
'classes': 'No Fire,Fire',
|
|
'expected_classes': 2,
|
|
'description': 'Binary fire detection'
|
|
},
|
|
{
|
|
'mode': 'yolo_v3',
|
|
'classes': 'person,car,bicycle,motorbike,bus',
|
|
'expected_classes': 5,
|
|
'description': 'Object detection'
|
|
},
|
|
{
|
|
'mode': 'classification',
|
|
'classes': 'cat,dog,bird,fish,rabbit',
|
|
'expected_classes': 5,
|
|
'description': 'Multi-class classification'
|
|
}
|
|
]
|
|
|
|
for case in test_cases:
|
|
print(f"\n--- {case['description']} ---")
|
|
|
|
# Configure node
|
|
node.set_property('postprocess_type', case['mode'])
|
|
node.set_property('class_names', case['classes'])
|
|
|
|
# Get configuration for visualization
|
|
config = node.get_postprocessing_config()
|
|
parsed_classes = config['class_names']
|
|
|
|
print(f"✓ Mode: {case['mode']}")
|
|
print(f"✓ Classes: {parsed_classes}")
|
|
print(f"✓ Expected {case['expected_classes']}, got {len(parsed_classes)}")
|
|
|
|
if len(parsed_classes) == case['expected_classes']:
|
|
print("✓ Class count matches expected")
|
|
else:
|
|
print(f"✗ Class count mismatch: expected {case['expected_classes']}, got {len(parsed_classes)}")
|
|
return False
|
|
|
|
print("\n✓ Visualization integration tests passed!")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"✗ Error in visualization integration test: {e}")
|
|
return False
|
|
|
|
if __name__ == "__main__":
|
|
print("Starting ExactPostprocessNode Tests...\n")
|
|
|
|
success = True
|
|
|
|
# Run main functionality tests
|
|
if not test_postprocess_node():
|
|
success = False
|
|
|
|
# Run visualization integration tests
|
|
if not test_visualization_integration():
|
|
success = False
|
|
|
|
if success:
|
|
print("\n🎉 All tests completed successfully!")
|
|
print("ExactPostprocessNode is ready for mode switching and visualization!")
|
|
else:
|
|
print("\n❌ Some tests failed. Please check the implementation.")
|
|
sys.exit(1)
|