|
@@ -5,6 +5,11 @@ import * as path from 'path';
|
|
|
import { MPOB_CLASSES, HEALTH_ALERT_CLASSES } from '../constants/mpob-standards';
|
|
import { MPOB_CLASSES, HEALTH_ALERT_CLASSES } from '../constants/mpob-standards';
|
|
|
import { DetectionResult } from '../interfaces/palm-analysis.interface';
|
|
import { DetectionResult } from '../interfaces/palm-analysis.interface';
|
|
|
|
|
|
|
|
|
|
+export interface ScanResult {
|
|
|
|
|
+ detections: DetectionResult[];
|
|
|
|
|
+ raw_tensor_sample: number[][];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
@Injectable()
|
|
@Injectable()
|
|
|
export class ScannerProvider implements OnModuleInit {
|
|
export class ScannerProvider implements OnModuleInit {
|
|
|
private session!: onnx.InferenceSession;
|
|
private session!: onnx.InferenceSession;
|
|
@@ -70,11 +75,26 @@ export class ScannerProvider implements OnModuleInit {
|
|
|
originalWidth: number,
|
|
originalWidth: number,
|
|
|
originalHeight: number,
|
|
originalHeight: number,
|
|
|
threshold: number = 0.25,
|
|
threshold: number = 0.25,
|
|
|
- ): Promise<DetectionResult[]> {
|
|
|
|
|
|
|
+ ): Promise<ScanResult> {
|
|
|
const data = outputTensor.data as Float32Array;
|
|
const data = outputTensor.data as Float32Array;
|
|
|
// Expected shape: [1, 300, 6]
|
|
// Expected shape: [1, 300, 6]
|
|
|
// Each candidate: [x1, y1, x2, y2, confidence, class_index]
|
|
// Each candidate: [x1, y1, x2, y2, confidence, class_index]
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Capture first 5 raw rows before NMS filtering — the AI's unfiltered "thought process"
|
|
|
|
|
+ const sampleRows = Math.min(5, outputTensor.dims[1]);
|
|
|
|
|
+ const raw_tensor_sample: number[][] = [];
|
|
|
|
|
+ for (let i = 0; i < sampleRows; i++) {
|
|
|
|
|
+ const offset = i * 6;
|
|
|
|
|
+ raw_tensor_sample.push([
|
|
|
|
|
+ parseFloat(data[offset].toFixed(6)),
|
|
|
|
|
+ parseFloat(data[offset + 1].toFixed(6)),
|
|
|
|
|
+ parseFloat(data[offset + 2].toFixed(6)),
|
|
|
|
|
+ parseFloat(data[offset + 3].toFixed(6)),
|
|
|
|
|
+ parseFloat(data[offset + 4].toFixed(6)),
|
|
|
|
|
+ parseFloat(data[offset + 5].toFixed(6)),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
const results: DetectionResult[] = [];
|
|
const results: DetectionResult[] = [];
|
|
|
const numCandidates = outputTensor.dims[1];
|
|
const numCandidates = outputTensor.dims[1];
|
|
|
|
|
|
|
@@ -94,17 +114,16 @@ export class ScannerProvider implements OnModuleInit {
|
|
|
class: className,
|
|
class: className,
|
|
|
confidence: parseFloat(confidence.toFixed(4)),
|
|
confidence: parseFloat(confidence.toFixed(4)),
|
|
|
is_health_alert: HEALTH_ALERT_CLASSES.includes(className),
|
|
is_health_alert: HEALTH_ALERT_CLASSES.includes(className),
|
|
|
- // HEAVY LIFTING: Multiply ratio (0.0-1.0) by original pixels
|
|
|
|
|
box: [
|
|
box: [
|
|
|
- data[offset] * originalWidth, // x1
|
|
|
|
|
- data[offset + 1] * originalHeight, // y1
|
|
|
|
|
- data[offset + 2] * originalWidth, // x2
|
|
|
|
|
- data[offset + 3] * originalHeight // y2
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+ data[offset] * originalWidth,
|
|
|
|
|
+ data[offset + 1] * originalHeight,
|
|
|
|
|
+ data[offset + 2] * originalWidth,
|
|
|
|
|
+ data[offset + 3] * originalHeight,
|
|
|
|
|
+ ],
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return results;
|
|
|
|
|
|
|
+ return { detections: results, raw_tensor_sample };
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|