Files
boc/iom/visual_geolocation/confidence_model.py
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

294 lines
10 KiB
Python

"""
Confidence Model
Probabilistic confidence estimation for geolocation
"""
from typing import Dict, List, Optional
from dataclasses import dataclass
import math
@dataclass
class ConfidenceReport:
"""Confidence report for geolocation estimate"""
overall_confidence: float
position_probability: Dict # Probability distribution over area
uncertainty_radius: float # meters
supporting_evidence: List[Dict]
contradicting_evidence: List[Dict]
unknown_factors: List[str]
class ConfidenceModel:
"""
Probabilistic confidence model for geolocation
Instead of just returning a coordinate, the model reports:
- Probability distribution over position
- Uncertainty radius
- Supporting evidence
- Contradicting evidence
- Unknown factors
"""
def __init__(self):
self.evidence_weights = {
"gps": 0.9,
"visual": 0.7,
"semantic": 0.6,
"geometric": 0.5,
"temporal": 0.4,
"map": 0.6,
"similarity": 0.5
}
def calculate_confidence(self, estimate, evidence_package) -> ConfidenceReport:
"""
Calculate comprehensive confidence report
Args:
estimate: GeolocationEstimate
evidence_package: EvidencePackage
Returns:
ConfidenceReport with full probabilistic analysis
"""
# Analyze supporting evidence
supporting = self._find_supporting_evidence(evidence_package, estimate)
# Analyze contradicting evidence
contradicting = self._find_contradicting_evidence(evidence_package, estimate)
# Identify unknown factors
unknown = self._identify_unknown_factors(evidence_package)
# Calculate position probability
probability = self._calculate_position_probability(estimate, evidence_package)
# Calculate uncertainty radius
uncertainty = self._calculate_uncertainty(estimate, evidence_package)
# Calculate overall confidence
overall = self._calculate_overall_confidence(
estimate, supporting, contradicting, unknown
)
return ConfidenceReport(
overall_confidence=overall,
position_probability=probability,
uncertainty_radius=uncertainty,
supporting_evidence=supporting,
contradicting_evidence=contradicting,
unknown_factors=unknown
)
def _find_supporting_evidence(self, evidence, estimate) -> List[Dict]:
"""Find evidence that supports the estimate"""
supporting = []
# GPS evidence
if evidence.metadata.gps_lat and evidence.metadata.gps_lng:
distance = self._haversine_distance(
evidence.metadata.gps_lat, evidence.metadata.gps_lng,
estimate.lat, estimate.lng
)
if distance < 100: # Within 100m
supporting.append({
"type": "gps",
"description": f"GPS coordinates match within {distance:.1f}m",
"confidence": 0.9,
"distance": distance
})
# Visual evidence
for obj in evidence.visual_objects:
if obj.confidence > 0.7:
supporting.append({
"type": "visual",
"description": f"Detected {obj.label} with {obj.confidence:.2f} confidence",
"confidence": obj.confidence
})
# Semantic evidence
for text in evidence.text_detections:
if text.confidence > 0.8:
supporting.append({
"type": "semantic",
"description": f"Detected text: '{text.text}'",
"confidence": text.confidence
})
return supporting
def _find_contradicting_evidence(self, evidence, estimate) -> List[Dict]:
"""Find evidence that contradicts the estimate"""
contradicting = []
# Check if GPS contradicts other methods
if evidence.metadata.gps_lat and evidence.metadata.gps_lng:
gps_distance = self._haversine_distance(
evidence.metadata.gps_lat, evidence.metadata.gps_lng,
estimate.lat, estimate.lng
)
if gps_distance > 1000: # More than 1km difference
contradicting.append({
"type": "gps_mismatch",
"description": f"GPS and estimate differ by {gps_distance:.0f}m",
"severity": "high"
})
# Check for conflicting visual evidence
# (e.g., detected objects not expected at location)
return contradicting
def _identify_unknown_factors(self, evidence) -> List[str]:
"""Identify factors that are unknown or uncertain"""
unknown = []
if not evidence.metadata.gps_lat:
unknown.append("GPS coordinates missing")
if not evidence.metadata.compass_heading:
unknown.append("Camera orientation unknown")
if not evidence.metadata.timestamp:
unknown.append("Timestamp missing")
if len(evidence.visual_objects) == 0:
unknown.append("No visual objects detected")
if len(evidence.text_detections) == 0:
unknown.append("No text detected")
return unknown
def _calculate_position_probability(self, estimate, evidence) -> Dict:
"""Calculate probability distribution over position"""
# Simplified: Gaussian distribution around estimate
# In production, use full Bayesian inference
return {
"type": "gaussian",
"center": {"lat": estimate.lat, "lng": estimate.lng},
"std_dev": estimate.accuracy / 2, # 95% CI ≈ 2*std_dev
"confidence_interval": {
"95_percent": estimate.accuracy,
"99_percent": estimate.accuracy * 1.5
}
}
def _calculate_uncertainty(self, estimate, evidence) -> float:
"""Calculate uncertainty radius in meters"""
base_uncertainty = estimate.accuracy
# Increase uncertainty if few evidence sources
evidence_count = len(evidence.visual_objects) + len(evidence.text_detections)
if evidence_count < 3:
base_uncertainty *= 1.5
# Decrease uncertainty if GPS available
if evidence.metadata.gps_lat:
base_uncertainty *= 0.8
return base_uncertainty
def _calculate_overall_confidence(self, estimate, supporting, contradicting, unknown) -> float:
"""Calculate overall confidence score"""
# Base confidence from estimate
base = estimate.confidence
# Boost from supporting evidence
support_boost = sum(s.get("confidence", 0.5) * 0.1 for s in supporting)
# Penalty from contradicting evidence
contradict_penalty = sum(0.2 for c in contradicting if c.get("severity") == "high")
contradict_penalty += sum(0.1 for c in contradicting if c.get("severity") == "medium")
# Penalty from unknown factors
unknown_penalty = len(unknown) * 0.05
# Calculate final
confidence = base + support_boost - contradict_penalty - unknown_penalty
# Clamp to [0, 1]
return max(0.0, min(1.0, confidence))
def _haversine_distance(self, lat1, lng1, lat2, lng2) -> float:
"""Calculate distance between two coordinates in meters"""
R = 6371000 # Earth radius in meters
phi1 = math.radians(lat1)
phi2 = math.radians(lat2)
delta_phi = math.radians(lat2 - lat1)
delta_lambda = math.radians(lng2 - lng1)
a = math.sin(delta_phi / 2) ** 2 + \
math.cos(phi1) * math.cos(phi2) * math.sin(delta_lambda / 2) ** 2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
return R * c
# Example usage
def example_confidence():
"""Example: Confidence calculation"""
from evidence_extractor import EvidencePackage, ImageMetadata, VisualObject, TextDetection
from geolocation_engine import GeolocationEstimate
from datetime import datetime
model = ConfidenceModel()
# Create sample evidence
evidence = EvidencePackage(
image_id="demo_001",
timestamp=datetime.now(),
metadata=ImageMetadata(
gps_lat=13.7563,
gps_lng=100.5018,
altitude=10.0,
compass_heading=90.0
),
visual_objects=[
VisualObject(label="street_light", confidence=0.85, bbox=[100, 200, 50, 150]),
VisualObject(label="building", confidence=0.92, bbox=[0, 0, 640, 480])
],
semantic_objects=[],
text_detections=[
TextDetection(text="Bangkok", confidence=0.95, bbox=[200, 100, 100, 50])
],
geometric_features=[],
environmental_signals=[],
temporal_signals={}
)
# Create sample estimate
estimate = GeolocationEstimate(
lat=13.7563,
lng=100.5018,
accuracy=10.0,
confidence=0.9,
method="gps",
evidence={}
)
# Calculate confidence
report = model.calculate_confidence(estimate, evidence)
print(f"Confidence Report:")
print(f" Overall confidence: {report.overall_confidence:.2f}")
print(f" Uncertainty radius: {report.uncertainty_radius:.1f}m")
print(f" Supporting evidence: {len(report.supporting_evidence)}")
for evidence in report.supporting_evidence:
print(f" - {evidence['type']}: {evidence['description']}")
print(f" Contradicting evidence: {len(report.contradicting_evidence)}")
print(f" Unknown factors: {len(report.unknown_factors)}")
for factor in report.unknown_factors:
print(f" - {factor}")
return report
if __name__ == '__main__':
example_confidence()