phase 4
This commit is contained in:
121
ui/detection-app/src/cv/wasmBridge.ts
Normal file
121
ui/detection-app/src/cv/wasmBridge.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* WASM Worker Bridge — runs OpenCV operations in a Web Worker.
|
||||
*
|
||||
* The worker loads opencv.js (~10MB) in its own thread.
|
||||
* Main thread stays responsive during WASM compilation.
|
||||
*
|
||||
* Lazy-creates the worker on first call. Sends ImageData +
|
||||
* params, gets back results via transferable buffers.
|
||||
*/
|
||||
|
||||
import type { EdgeDetectionParams, EdgeDetectionResult, EdgeDetectionDebugResult } from './edges'
|
||||
import type { SegmentationParams, SegmentationDebugResult } from './segmentation'
|
||||
|
||||
let worker: Worker | null = null
|
||||
let messageId = 0
|
||||
const pending = new Map<number, { resolve: (v: any) => void; reject: (e: Error) => void }>()
|
||||
let initPromise: Promise<boolean> | null = null
|
||||
let ready = false
|
||||
let failed = false
|
||||
|
||||
function getWorker(): Worker {
|
||||
if (!worker) {
|
||||
worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' })
|
||||
worker.onmessage = (event) => {
|
||||
const { id, type, error: errMsg, ...data } = event.data
|
||||
const handler = pending.get(id)
|
||||
if (!handler) return
|
||||
pending.delete(id)
|
||||
|
||||
if (type === 'error') {
|
||||
handler.reject(new Error(errMsg ?? 'Worker error'))
|
||||
} else {
|
||||
handler.resolve(data)
|
||||
}
|
||||
}
|
||||
worker.onerror = (event) => {
|
||||
// Reject all pending
|
||||
for (const [, handler] of pending) {
|
||||
handler.reject(new Error(event.message ?? 'Worker crashed'))
|
||||
}
|
||||
pending.clear()
|
||||
}
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
function postMessage(type: string, imageData: ImageData, params: Record<string, unknown>): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = ++messageId
|
||||
pending.set(id, { resolve, reject })
|
||||
getWorker().postMessage({ id, type, imageData, params }, [imageData.data.buffer])
|
||||
})
|
||||
}
|
||||
|
||||
/** Initialize the worker + load WASM. Returns true if ready. */
|
||||
export async function initWasm(): Promise<boolean> {
|
||||
if (ready) return true
|
||||
if (failed) return false
|
||||
if (initPromise) return initPromise
|
||||
|
||||
initPromise = (async () => {
|
||||
try {
|
||||
// Send a ping — the worker will load opencv.js on first message
|
||||
const result = await postMessage('ping', new ImageData(1, 1), {})
|
||||
ready = true
|
||||
return true
|
||||
} catch {
|
||||
failed = true
|
||||
return false
|
||||
}
|
||||
})()
|
||||
|
||||
return initPromise
|
||||
}
|
||||
|
||||
export function isWasmReady(): boolean {
|
||||
return ready
|
||||
}
|
||||
|
||||
export function isWasmFailed(): boolean {
|
||||
return failed
|
||||
}
|
||||
|
||||
// --- Edge detection ---
|
||||
|
||||
export async function detectEdgesWasm(
|
||||
imageData: ImageData,
|
||||
params: Partial<EdgeDetectionParams>,
|
||||
): Promise<EdgeDetectionResult> {
|
||||
const data = await postMessage('detect_edges', imageData, params as Record<string, unknown>)
|
||||
return { regions: data.regions }
|
||||
}
|
||||
|
||||
export async function detectEdgesWasmDebug(
|
||||
imageData: ImageData,
|
||||
params: Partial<EdgeDetectionParams>,
|
||||
): Promise<EdgeDetectionDebugResult> {
|
||||
const data = await postMessage('detect_edges_debug', imageData, params as Record<string, unknown>)
|
||||
return {
|
||||
regions: data.regions,
|
||||
edgeImageData: data.edgeImageData,
|
||||
linesImageData: data.linesImageData,
|
||||
horizontalCount: data.horizontalCount,
|
||||
pairCount: data.pairCount,
|
||||
}
|
||||
}
|
||||
|
||||
// --- Segmentation ---
|
||||
|
||||
export async function segmentFieldWasmDebug(
|
||||
imageData: ImageData,
|
||||
params: Partial<SegmentationParams>,
|
||||
): Promise<SegmentationDebugResult> {
|
||||
const data = await postMessage('segment_field_debug', imageData, params as Record<string, unknown>)
|
||||
return {
|
||||
boundary: data.boundary,
|
||||
coverage: data.coverage,
|
||||
maskImageData: data.maskImageData,
|
||||
overlayImageData: data.overlayImageData,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user