Files
boc/quixzoom-video-pipeline/processing/source-reliability.js
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

524 lines
13 KiB
JavaScript

/**
* QUIXZOOM Video Pipeline — Source Reliability System
*
* Varje datapunkt får metadata:
* - Källa
* - Datum
* - Plats
* - Licensstatus
* - Kamerakvalitet
* - Objektionsgrad (hur mycket som är skymt)
* - AI:s konfidens
* - Verifieringsstatus
*
* AI:n lär sig ENDAST av observerbara fenomen, inte av åsikter eller narration.
*/
const crypto = require('crypto');
/**
* Källa-typer och deras tillförlitlighet
*/
const SOURCE_TYPES = {
// QUIXZOOM-egen data (högst tillförlitlighet)
'quixzoom_capture': {
baseReliability: 0.95,
licenseCertainty: 1.0,
metadataCompleteness: 0.95,
qualityControl: 'active_guided',
description: 'QUIXZOOM guided capture with active learning',
},
// Creative Commons-videor
'youtube_cc': {
baseReliability: 0.75,
licenseCertainty: 0.9,
metadataCompleteness: 0.6,
qualityControl: 'post_hoc',
description: 'YouTube Creative Commons video',
},
// Öppna dataset
'open_dataset': {
baseReliability: 0.85,
licenseCertainty: 0.95,
metadataCompleteness: 0.8,
qualityControl: 'curated',
description: 'Open research dataset (Cityscapes, etc.)',
},
// Öppna kartdata
'openstreetmap': {
baseReliability: 0.8,
licenseCertainty: 0.95,
metadataCompleteness: 0.7,
qualityControl: 'community',
description: 'OpenStreetMap data',
},
// Offentliga geodata
'public_geodata': {
baseReliability: 0.85,
licenseCertainty: 0.9,
metadataCompleteness: 0.75,
qualityControl: 'government',
description: 'Public geodata with appropriate license',
},
// Dashcam (med tillstånd)
'dashcam_licensed': {
baseReliability: 0.7,
licenseCertainty: 0.8,
metadataCompleteness: 0.5,
qualityControl: 'passive',
description: 'Dashcam footage with licensing permission',
},
// Walking tours (med tillstånd)
'walking_tour_licensed': {
baseReliability: 0.7,
licenseCertainty: 0.8,
metadataCompleteness: 0.5,
qualityControl: 'passive',
description: 'Walking tour video with licensing permission',
},
};
/**
* Kvalitetsdimensioner för varje datapunkt
*/
const QUALITY_DIMENSIONS = {
// Kamerakvalitet
camera: {
resolution: { weight: 0.3, min: 720, max: 4320 }, // p (720p to 8K)
fps: { weight: 0.2, min: 24, max: 60 },
stabilization: { weight: 0.2, boolean: true },
lensQuality: { weight: 0.3, min: 0, max: 1 },
},
// Bildkvalitet
image: {
sharpness: { weight: 0.25, min: 0, max: 1 },
exposure: { weight: 0.2, min: 0, max: 1 },
colorAccuracy: { weight: 0.15, min: 0, max: 1 },
noise: { weight: 0.2, min: 0, max: 1 }, // lower is better
compression: { weight: 0.2, min: 0, max: 1 }, // lower is better
},
// Objektionsgrad (hur mycket som är skymt)
occlusion: {
foreground: { weight: 0.3, min: 0, max: 1 }, // lower is better
weather: { weight: 0.25, min: 0, max: 1 }, // lower is better
lighting: { weight: 0.25, min: 0, max: 1 }, // lower is better
motion: { weight: 0.2, min: 0, max: 1 }, // lower is better
},
// AI-konfidens
aiConfidence: {
objectDetection: { weight: 0.3, min: 0, max: 1 },
sceneClassification: { weight: 0.25, min: 0, max: 1 },
ocr: { weight: 0.2, min: 0, max: 1 },
segmentation: { weight: 0.25, min: 0, max: 1 },
},
};
/**
* Beräkna tillförlitlighet för en datapunkt
*/
function calculateReliability(dataPoint) {
const sourceConfig = SOURCE_TYPES[dataPoint.sourceType];
if (!sourceConfig) {
console.warn(`[RELIABILITY] Unknown source type: ${dataPoint.sourceType}`);
return { overall: 0, breakdown: {} };
}
// 1. Bas-tillförlitlighet från källa
let reliability = sourceConfig.baseReliability;
// 2. Justera för licens-säkerhet
reliability *= sourceConfig.licenseCertainty;
// 3. Justera för metadata-kompletthet
const metadataScore = calculateMetadataScore(dataPoint);
reliability *= (0.5 + 0.5 * metadataScore); // 0.5-1.0 range
// 4. Justera för kamerakvalitet
const cameraScore = calculateCameraScore(dataPoint);
reliability *= (0.7 + 0.3 * cameraScore); // 0.7-1.0 range
// 5. Justera för bildkvalitet
const imageScore = calculateImageScore(dataPoint);
reliability *= (0.6 + 0.4 * imageScore); // 0.6-1.0 range
// 6. Justera för objektionsgrad
const occlusionScore = calculateOcclusionScore(dataPoint);
reliability *= (0.5 + 0.5 * occlusionScore); // 0.5-1.0 range
// 7. Justera för AI-konfidens
const aiScore = calculateAIConfidenceScore(dataPoint);
reliability *= (0.6 + 0.4 * aiScore); // 0.6-1.0 range
// 8. Justera för verifieringsstatus
const verificationMultiplier = getVerificationMultiplier(dataPoint.verificationStatus);
reliability *= verificationMultiplier;
return {
overall: Math.min(1.0, reliability),
breakdown: {
sourceBase: sourceConfig.baseReliability,
license: sourceConfig.licenseCertainty,
metadata: metadataScore,
camera: cameraScore,
image: imageScore,
occlusion: occlusionScore,
aiConfidence: aiScore,
verification: verificationMultiplier,
},
sourceType: dataPoint.sourceType,
qualityControl: sourceConfig.qualityControl,
};
}
/**
* Beräkna metadata-score
*/
function calculateMetadataScore(dataPoint) {
const required = ['timestamp', 'gps', 'source', 'license'];
const optional = ['device', 'camera', 'weather', 'operator'];
let score = 0;
// Required fields
for (const field of required) {
if (dataPoint.metadata?.[field]) score += 0.15;
}
// Optional fields
for (const field of optional) {
if (dataPoint.metadata?.[field]) score += 0.05;
}
return Math.min(1.0, score);
}
/**
* Beräkna kamerakvalitet
*/
function calculateCameraScore(dataPoint) {
const camera = dataPoint.camera || {};
let score = 0;
// Resolution
const resolution = camera.resolution || 1080;
score += Math.min(1.0, (resolution - 720) / (4320 - 720)) * 0.3;
// FPS
const fps = camera.fps || 30;
score += Math.min(1.0, (fps - 24) / (60 - 24)) * 0.2;
// Stabilization
if (camera.stabilization) score += 0.2;
// Lens quality
score += (camera.lensQuality || 0.5) * 0.3;
return score;
}
/**
* Beräkna bildkvalitet
*/
function calculateImageScore(dataPoint) {
const image = dataPoint.imageQuality || {};
let score = 0;
score += (image.sharpness || 0.5) * 0.25;
score += (image.exposure || 0.5) * 0.2;
score += (image.colorAccuracy || 0.5) * 0.15;
score += (1 - (image.noise || 0.5)) * 0.2; // lower noise is better
score += (1 - (image.compression || 0.5)) * 0.2; // lower compression is better
return score;
}
/**
* Beräkna objektionsgrad (hur mycket som är skymt)
* Lower is better (0 = inget skymt, 1 = helt skymt)
*/
function calculateOcclusionScore(dataPoint) {
const occlusion = dataPoint.occlusion || {};
let score = 0;
score += (1 - (occlusion.foreground || 0)) * 0.3;
score += (1 - (occlusion.weather || 0)) * 0.25;
score += (1 - (occlusion.lighting || 0)) * 0.25;
score += (1 - (occlusion.motion || 0)) * 0.2;
return score;
}
/**
* Beräkna AI-konfidens
*/
function calculateAIConfidenceScore(dataPoint) {
const ai = dataPoint.aiAnalysis || {};
let score = 0;
score += (ai.objectDetection || 0.5) * 0.3;
score += (ai.sceneClassification || 0.5) * 0.25;
score += (ai.ocr || 0.5) * 0.2;
score += (ai.segmentation || 0.5) * 0.25;
return score;
}
/**
* Verifierings-multiplier
*/
function getVerificationMultiplier(status) {
const multipliers = {
'unverified': 0.7,
'ai_verified': 0.85,
'human_verified': 0.95,
'expert_verified': 1.0,
'disputed': 0.5,
'rejected': 0.0,
};
return multipliers[status] || 0.7;
}
/**
* Filtrera bort opålitliga datapunkter
*/
function filterReliableData(dataPoints, minReliability = 0.6) {
return dataPoints.filter(dp => {
const reliability = calculateReliability(dp);
return reliability.overall >= minReliability;
});
}
/**
* Gruppera datapunkter efter tillförlitlighet
*/
function groupByReliability(dataPoints) {
const groups = {
high: [], // >= 0.8
medium: [], // 0.6-0.8
low: [], // 0.4-0.6
unreliable: [], // < 0.4
};
for (const dp of dataPoints) {
const reliability = calculateReliability(dp);
if (reliability.overall >= 0.8) groups.high.push({ ...dp, reliability });
else if (reliability.overall >= 0.6) groups.medium.push({ ...dp, reliability });
else if (reliability.overall >= 0.4) groups.low.push({ ...dp, reliability });
else groups.unreliable.push({ ...dp, reliability });
}
return groups;
}
/**
* Generera fullständig metadata för en datapunkt
*/
function generateDataPointMetadata(options) {
return {
// Identitet
id: options.id || crypto.randomUUID(),
timestamp: options.timestamp || new Date().toISOString(),
// Källa
sourceType: options.sourceType || 'unknown',
sourceUrl: options.sourceUrl,
sourceChannel: options.sourceChannel,
sourceUploader: options.sourceUploader,
// Licens
license: options.license || 'unknown',
licenseUrl: options.licenseUrl,
attribution: options.attribution,
// Plats
gps: {
latitude: options.latitude,
longitude: options.longitude,
accuracy: options.gpsAccuracy,
altitude: options.altitude,
},
// Kamera
camera: {
model: options.cameraModel,
resolution: options.resolution,
fps: options.fps,
stabilization: options.stabilization,
lensQuality: options.lensQuality,
},
// Bildkvalitet (beräknas av AI)
imageQuality: {
sharpness: options.sharpness,
exposure: options.exposure,
colorAccuracy: options.colorAccuracy,
noise: options.noise,
compression: options.compression,
},
// Objektionsgrad (beräknas av AI)
occlusion: {
foreground: options.foregroundOcclusion,
weather: options.weatherOcclusion,
lighting: options.lightingOcclusion,
motion: options.motionOcclusion,
},
// AI-analys
aiAnalysis: {
objectDetection: options.objectDetectionConfidence,
sceneClassification: options.sceneClassificationConfidence,
ocr: options.ocrConfidence,
segmentation: options.segmentationConfidence,
},
// Verifiering
verificationStatus: options.verificationStatus || 'unverified',
verifiedBy: options.verifiedBy,
verifiedAt: options.verifiedAt,
// Narration-filter
narrationFiltered: options.narrationFiltered || false,
narrationText: options.narrationText,
// Observerbara fenomen (detta är vad AI:n lär sig av)
observablePhenomena: options.observablePhenomena || [],
};
}
/**
* Extrahera observerbara fenomen från video (inte åsikter)
*/
function extractObservablePhenomena(analysis) {
const phenomena = [];
// Fysiska objekt
for (const obj of analysis.objects || []) {
phenomena.push({
type: 'physical_object',
class: obj.class,
confidence: obj.confidence,
bbox: obj.bbox,
observable: true,
});
}
// Infrastruktur-tillstånd
if (analysis.infrastructure) {
phenomena.push({
type: 'infrastructure_condition',
category: analysis.infrastructure.category,
condition: analysis.infrastructure.condition,
observable: true,
});
}
// Miljöförhållanden
if (analysis.environment) {
phenomena.push({
type: 'environmental_condition',
weather: analysis.environment.weather,
lighting: analysis.environment.lighting,
observable: true,
});
}
// Människor och aktivitet
if (analysis.people) {
phenomena.push({
type: 'human_activity',
count: analysis.people.count,
density: analysis.people.density,
observable: true,
});
}
// Filtrera bort åsikter och narration
return phenomena.filter(p => p.observable);
}
/**
* Huvudfunktion — processa en datapunkt
*/
function processDataPoint(rawData) {
// 1. Generera metadata
const metadata = generateDataPointMetadata(rawData);
// 2. Extrahera observerbara fenomen
const phenomena = extractObservablePhenomena(rawData.analysis || {});
// 3. Beräkna tillförlitlighet
const reliability = calculateReliability(metadata);
// 4. Filtrera narration
const narrationFiltered = filterNarration(rawData.narration);
return {
metadata,
phenomena,
reliability,
narrationFiltered,
usableForTraining: reliability.overall >= 0.6,
};
}
/**
* Filtrera bort narration och åsikter
*/
function filterNarration(narrationText) {
if (!narrationText) return null;
// Lista över indikatorer på åsikter/narration
const opinionIndicators = [
'think', 'believe', 'feel', 'opinion', 'should', 'must',
'terrible', 'amazing', 'worst', 'best', 'dangerous', 'safe',
];
const lowerText = narrationText.toLowerCase();
for (const indicator of opinionIndicators) {
if (lowerText.includes(indicator)) {
return {
original: narrationText,
filtered: true,
reason: `Contains opinion indicator: ${indicator}`,
usable: false,
};
}
}
return {
original: narrationText,
filtered: false,
usable: true,
};
}
module.exports = {
calculateReliability,
filterReliableData,
groupByReliability,
generateDataPointMetadata,
extractObservablePhenomena,
processDataPoint,
filterNarration,
SOURCE_TYPES,
QUALITY_DIMENSIONS,
};