From 450d4f70909445d1250b39ebbf13c1bc2d528798 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Sat, 13 Sep 2025 21:36:59 +0100 Subject: [PATCH] emblemscanner: worker wip --- scanner/pages/emblemscanner/emblemscanner.js | 103 ++++++++++++++++++- scanner/pages/emblemscanner/worker/index.js | 73 +++++++++++++ 2 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 scanner/pages/emblemscanner/worker/index.js diff --git a/scanner/pages/emblemscanner/emblemscanner.js b/scanner/pages/emblemscanner/emblemscanner.js index 1e10b91..aa44ca0 100644 --- a/scanner/pages/emblemscanner/emblemscanner.js +++ b/scanner/pages/emblemscanner/emblemscanner.js @@ -91,6 +91,7 @@ Page({ rule_zoom: -1, camera_rule: null, use_web_view: false, + use_worker: false, emblem_camera_url: null, // State machine: loading_rules -> loading_qrtool -> init_camera -> scanning -> verifying -> result app_state: 'loading_rules', // 'loading_rules', 'loading_qrtool', 'init_camera', 'scanning', 'webview_scanning', 'verifying', 'result' @@ -154,15 +155,89 @@ Page({ initializeSystem(enable_debug) { const systemInfo = get_system_info(); const phone_model = systemInfo.model; + const use_worker = phone_model.toLowerCase().includes("iphone"); this.setData({ enable_debug, phone_model, window_width: systemInfo.windowWidth, - window_height: systemInfo.windowHeight + window_height: systemInfo.windowHeight, + use_worker: use_worker }); - console.log(`Phone model: ${phone_model}, Using native camera mode`); + console.log(`Phone model: ${phone_model}, Use worker: ${use_worker}`); + + // Set up worker if needed (like camera.js) + if (use_worker) { + this.setupWorker(); + } + }, + + /** + * Set up worker for iPhone processing like camera.js + */ + setupWorker() { + // Create worker with local worker file + this.worker = wx.createWorker('/pages/emblemscanner/worker/index.js'); + + this.worker.onMessage((msg) => { + console.log('Worker message:', msg.type); + + if (msg.type === "result") { + const result = msg.res; + const processingTime = msg.processing_time; + + // Update statistics + const newFramesProcessed = this.data.frames_processed + 1; + const newTotalTime = this.data.total_processing_time + processingTime; + const newAvgTime = Math.round(newTotalTime / newFramesProcessed); + + this.setData({ + frames_processed: newFramesProcessed, + total_processing_time: newTotalTime, + avg_processing_time_ms: newAvgTime, + last_frame_time_ms: Math.round(processingTime), + debug_last_result: result + }); + + if (result) { + // For worker, we need to trigger image collection when we find a good QR + if (result.ok && result.qrcode && result.valid_pattern && is_emblem_qr_pattern(result.qrcode)) { + this.addDebugMessage(`Worker QR detected: ${result.qrcode}`); + + // Trigger zoom-in if function is set up + if (this.on_qr_found && !this.data.qr_found) { + this.on_qr_found(); + this.setData({ qr_found: true }); + } + + // Request worker to submit image data + this.worker.postMessage({ type: "ready_to_submit" }); + } + + this.handleQRResult(result, this.lastWorkerFrame); + } + } else if (msg.type === "submit") { + // Handle submit message from worker (image data for upload) + const result = msg.res; + const imageData = msg.image_data; + if (imageData) { + const dataUrl = data_url_from_frame(imageData.width, imageData.height, new Uint8ClampedArray(imageData.data)); + this.image_data_urls.push(dataUrl); + + if (this.image_data_urls.length >= 3) { + this.addDebugMessage('3 good images collected via worker, starting verification'); + this.startVerifying(); + this.submitImageForVerification(this.image_data_urls, result.qrcode); + this.image_data_urls = []; // Reset for next scan + } else { + this.addDebugMessage(`Collected ${this.image_data_urls.length}/3 worker images`); + } + } + } + }); + + this.addDebugMessage('Worker set up for iPhone processing'); }, loadCameraRules(max_zoom = null) { @@ -371,7 +446,7 @@ Page({ * Camera frame callback - receives live camera frames */ onCameraFrame(frame) { - // Only process frames when in camera scanning state and QRTool is ready + // Only process frames when in camera scanning state if (this.data.app_state !== 'scanning') { return; } @@ -386,10 +461,28 @@ Page({ } this.lastFrameTime = now; - // Start timing frame processing + // Use worker for iPhone, direct processing for other devices + if (this.data.use_worker && this.worker) { + // Worker processing (iPhone) + this.lastWorkerFrame = frame; // Store for handleQRResult + this.worker.postMessage({ + type: 'frame', + width: frame.width, + height: frame.height, + camera_sensitivity: this.data.camera_sensitivity + }); + } else { + // Direct processing (other devices) + this.processFrameDirect(frame); + } + }, + + /** + * Process frame directly (non-iPhone devices) + */ + processFrameDirect(frame) { const processStart = Date.now(); - // Process frame data directly try { const result = process_frame(frame.width, frame.height, frame.data, this.data.camera_sensitivity, this.data.enable_debug); diff --git a/scanner/pages/emblemscanner/worker/index.js b/scanner/pages/emblemscanner/worker/index.js new file mode 100644 index 0000000..ca8816e --- /dev/null +++ b/scanner/pages/emblemscanner/worker/index.js @@ -0,0 +1,73 @@ +console.log("hello from emblemscanner worker"); + +let qrtool = require('../qrtool.wx.js'); + +var qrtool_ready = false; + +qrtool.onRuntimeInitialized = () => { + console.log("emblemscanner qrtool ready"); + qrtool_ready = true; + worker.postMessage({ + type: "ready", + }); +} + +var submit_message = null; + +function handle_frame(data, width, height, camera_sensitivity) { + if (!qrtool_ready) { + console.log("qrtool not ready"); + worker.postMessage({ + type: "result", + res: { + ok: false, + err: "qrtool not ready", + }, + processing_time: Date.now() - begin, + }); + return; + } + const begin = Date.now(); + var uca = new Uint8ClampedArray(data); + var buf = qrtool._malloc(uca.length * uca.BYTES_PER_ELEMENT); + qrtool.HEAPU8.set(uca, buf); + var r = qrtool.ccall('qrtool_angle', 'string', ['number', 'number', 'number', 'number', 'number'], [buf, width, height, 0, camera_sensitivity]); + qrtool._free(buf); + console.log("emblemscanner worker r:", r); + var res = JSON.parse(r); + worker.postMessage({ + type: "result", + res, + processing_time: Date.now() - begin, + }); + if (res.ok) { + // Since image_data takes seconds to serialize, send it in a separate + // message to the UI can update with the good news + submit_message = { + type: "submit", + res, + image_data: { data, width, height }, + }; + } +} + +worker.onMessage((msg) => { + console.log("emblemscanner worker got message", msg.type); + switch (msg.type) { + case "frame": + try { + const data = worker.getCameraFrameData(); + if (data) { + handle_frame(data, msg.width, msg.height, msg.camera_sensitivity); + } + } catch (e) { + console.log(e); + } + break; + case "ready_to_submit": + worker.postMessage(submit_message); + break; + default: + console.log("Unknown message type:", msg.data.type); + } +}); \ No newline at end of file