export interface Point { x: number; y: number; } interface Rect { left: number; top: number; right: number; bottom: number; } export interface DocumentPrediction { prob: number; probOutFromFrame: number; points: Point[]; } interface Index { col: number; row: number; } interface VectorParams { indexes: Index[]; centers: Point[]; } export interface Framing { top: boolean; left: boolean; right: boolean; bottom: boolean; } export class Document { prob: number; probOutFromFrame: number; points: Point[]; constructor(elements: number[]) { this.prob = elements[0] || 0; this.probOutFromFrame = elements[1] || 0; this.points = []; for (let index = 0; index < 4; index++) { this.points.push({ x: elements[index * 2 + 2] || 0, y: elements[index * 2 + 3] || 0, }); } } minMaxXY(): number[] { let maxX = this.points[2]?.x || 0; let maxY = this.points[2]?.y || 0; let minX = this.points[0]?.x || 0; let minY = this.points[0]?.y || 0; this.points.forEach((point) => { maxX = Math.max(maxX, point.x || 0); maxY = Math.max(maxY, point.y || 0); minX = Math.min(minX, point.x || 0); minY = Math.min(minY, point.y || 0); }); return [minX, minY, maxX, maxY]; } center(): Point { let xs = 0; let ys = 0; this.points.forEach((point) => { xs += point.x || 0; ys += point.y || 0; }); return { x: xs / this.points.length, y: ys / this.points.length, }; } rectF(): Rect { const [minX, minY, maxX, maxY] = this.minMaxXY(); return { left: minX || 0, top: minY || 0, right: maxX || 0, bottom: maxY || 0, }; } } export const postProcessOutput = ( results: any, frameWidth: number, frameHeight: number ): DocumentPrediction[] => { 'worklet'; const quadrantsDim = 5; const vectorItemSize = 10; const vectorSize = 50; const sigmoidF = (x: number): number => { return 1 / (1 + Math.E ** -x); }; function splitToVectors(sourceArray: T[]): T[][] { const vectors: T[][] = []; let currentVector: T[] = []; for (let item of sourceArray) { currentVector.push(item); if (currentVector.length === vectorSize) { vectors.push(currentVector); currentVector = []; } } if (currentVector.length > 0) { vectors.push(currentVector); } return vectors; } function getVectorParams( sourceArray: number[], index: number, threshold: number = 0.9 ): VectorParams { const indexes: Index[] = []; for (let i = 0; i < 5; i++) { const prob = sigmoidF(sourceArray[i * 10] as number); if (prob > threshold) { indexes.push({ col: index, row: i }); } } const k = 0.5 / quadrantsDim; const centers = indexes.map((idx) => { const x = idx.col / quadrantsDim - 0.5 + k; const y = idx.row / quadrantsDim - 0.5 + k; return { x: +x.toFixed(4), y: +y.toFixed(4), }; }); return { indexes, centers, }; } const predictions: DocumentPrediction[] = []; splitToVectors(results).forEach((vector, vectorId) => { const { indexes, centers } = getVectorParams(vector, vectorId); if (centers.length < 1) return; indexes.forEach((index, i) => { const start = index.row * vectorItemSize; const end = start + vectorItemSize; const source = vector.slice(start, end); const center: Point = centers[i] as Point; const pointsSource = source.slice(2); const points: Point[] = []; for (let j = 0; j < pointsSource.length; j += 2) { const x = (pointsSource[j] as number) / quadrantsDim; const y = (pointsSource[j + 1] as number) / quadrantsDim; points.push({ x: (x + (center.x + 0.5)) * frameWidth, y: (y + (center.y + 0.5)) * frameHeight, }); } const probs = source.slice(0, 2).map(sigmoidF); predictions.push({ prob: probs[0] as number, probOutFromFrame: probs[1] as number, points: points, }); }); }); return predictions; };