bae705aa97
- 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
174 lines
4.8 KiB
JavaScript
174 lines
4.8 KiB
JavaScript
/**
|
|
* QUIXZOOM Weak Supervision API
|
|
*
|
|
* Endpoints:
|
|
* POST /api/detect — Detektera objekt i bild
|
|
* POST /api/detect/batch — Batch-detektion
|
|
* GET /api/models — Lista tillgängliga modeller
|
|
* GET /api/stats — Statistik
|
|
*/
|
|
|
|
const express = require('express');
|
|
const WeakSupervisionPipeline = require('../weak-supervision/pipeline');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
class WeakSupervisionAPI {
|
|
constructor(port = 3002) {
|
|
this.app = express();
|
|
this.pipeline = new WeakSupervisionPipeline({
|
|
confidenceThreshold: 0.3,
|
|
iouThreshold: 0.5,
|
|
});
|
|
this.port = port;
|
|
this.stats = {
|
|
totalRequests: 0,
|
|
totalImages: 0,
|
|
totalDetections: 0,
|
|
avgLatency: 0,
|
|
};
|
|
|
|
this.setupRoutes();
|
|
}
|
|
|
|
setupRoutes() {
|
|
this.app.use(express.json({ limit: '50mb' }));
|
|
|
|
// Health check
|
|
this.app.get('/health', (req, res) => {
|
|
res.json({ status: 'ok', service: 'weak-supervision' });
|
|
});
|
|
|
|
// Detektera en bild
|
|
this.app.post('/api/detect', async (req, res) => {
|
|
const start = Date.now();
|
|
|
|
try {
|
|
const { imagePath, imageBase64 } = req.body;
|
|
|
|
let filePath = imagePath;
|
|
|
|
// Om base64, spara till temporär fil
|
|
if (imageBase64) {
|
|
const buffer = Buffer.from(imageBase64, 'base64');
|
|
filePath = `/tmp/ws-${Date.now()}.jpg`;
|
|
fs.writeFileSync(filePath, buffer);
|
|
}
|
|
|
|
if (!filePath || !fs.existsSync(filePath)) {
|
|
return res.status(400).json({ error: 'Image not found' });
|
|
}
|
|
|
|
const result = await this.pipeline.detect(filePath);
|
|
|
|
// Uppdatera statistik
|
|
this.stats.totalRequests++;
|
|
this.stats.totalImages++;
|
|
this.stats.totalDetections += result.consolidated.length;
|
|
|
|
const latency = Date.now() - start;
|
|
this.stats.avgLatency = (this.stats.avgLatency * (this.stats.totalRequests - 1) + latency) / this.stats.totalRequests;
|
|
|
|
res.json({
|
|
success: true,
|
|
latency: latency,
|
|
detections: result.consolidated,
|
|
rawResults: {
|
|
yolo: result.modelResults.yolo?.length || 0,
|
|
groundingDINO: result.modelResults.groundingDINO?.length || 0,
|
|
sam2: result.modelResults.sam2?.length || 0,
|
|
},
|
|
});
|
|
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
// Batch-detektion
|
|
this.app.post('/api/detect/batch', async (req, res) => {
|
|
const start = Date.now();
|
|
|
|
try {
|
|
const { imagePaths } = req.body;
|
|
|
|
if (!Array.isArray(imagePaths)) {
|
|
return res.status(400).json({ error: 'imagePaths must be an array' });
|
|
}
|
|
|
|
const results = [];
|
|
|
|
for (const imagePath of imagePaths) {
|
|
if (!fs.existsSync(imagePath)) {
|
|
results.push({ path: imagePath, error: 'Not found' });
|
|
continue;
|
|
}
|
|
|
|
const result = await this.pipeline.detect(imagePath);
|
|
results.push({
|
|
path: imagePath,
|
|
detections: result.consolidated,
|
|
});
|
|
|
|
this.stats.totalDetections += result.consolidated.length;
|
|
}
|
|
|
|
this.stats.totalRequests++;
|
|
this.stats.totalImages += imagePaths.length;
|
|
|
|
const latency = Date.now() - start;
|
|
|
|
res.json({
|
|
success: true,
|
|
latency,
|
|
processed: imagePaths.length,
|
|
results,
|
|
});
|
|
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
// Lista modeller
|
|
this.app.get('/api/models', (req, res) => {
|
|
res.json({
|
|
models: [
|
|
{ name: 'yolov8', available: this.pipeline.models.yolo.available, weight: 0.3 },
|
|
{ name: 'groundingDINO', available: false, weight: 0.3 },
|
|
{ name: 'sam2', available: false, weight: 0.2 },
|
|
{ name: 'ocr', available: false, weight: 0.1 },
|
|
{ name: 'depth', available: false, weight: 0.1 },
|
|
],
|
|
});
|
|
});
|
|
|
|
// Statistik
|
|
this.app.get('/api/stats', (req, res) => {
|
|
res.json({
|
|
...this.stats,
|
|
uptime: process.uptime(),
|
|
});
|
|
});
|
|
}
|
|
|
|
start() {
|
|
this.app.listen(this.port, () => {
|
|
console.log(`[WS API] Listening on port ${this.port}`);
|
|
console.log(`[WS API] Endpoints:`);
|
|
console.log(` POST /api/detect`);
|
|
console.log(` POST /api/detect/batch`);
|
|
console.log(` GET /api/models`);
|
|
console.log(` GET /api/stats`);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Starta om detta är huvudfil
|
|
if (require.main === module) {
|
|
const api = new WeakSupervisionAPI();
|
|
api.start();
|
|
}
|
|
|
|
module.exports = WeakSupervisionAPI;
|