From e4259d27f31ae0a05621ff254107158240b75264 Mon Sep 17 00:00:00 2001 From: mfwadejr <125498560+mfwadejr@users.noreply.github.com> Date: Mon, 21 Sep 2026 10:18:10 -0400 Subject: [PATCH] Read printed label identifiers with OCR --- public/app.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/app.js b/public/app.js index 5f16f5d..41fcd85 100644 --- a/public/app.js +++ b/public/app.js @@ -179,6 +179,8 @@ async function scanLabelPhotoFinal(form,help,file){let url;try{const {BrowserMul const photoFinalObserver=new MutationObserver(()=>{const input=$("#receiveForm .photo-input");if(input&&!input.dataset.final){input.dataset.final="true";input.onchange=()=>{if(input.files?.[0])scanLabelPhotoFinal($("#receiveForm"),$("#receiveForm .scan-help"),input.files[0]);input.value=""}}});photoFinalObserver.observe($("#modalBody"),{childList:true}); async function scanLabelPhotoPreprocessed(form,help,file){let url;try{const {BrowserMultiFormatReader}=await import("https://cdn.jsdelivr.net/npm/@zxing/browser@0.1.5/+esm"),reader=new BrowserMultiFormatReader();url=URL.createObjectURL(file);const image=new Image();image.src=url;await image.decode();const source=document.createElement("canvas"),sctx=source.getContext("2d");source.width=image.naturalWidth;source.height=image.naturalHeight;sctx.drawImage(image,0,0);const found=[],regions=[{y:0,h:1},{y:.15,h:.7},{y:.28,h:.24},{y:.48,h:.24},{y:.68,h:.24}];for(const region of regions){const sy=Math.floor(source.height*region.y),sh=Math.floor(source.height*region.h),canvas=document.createElement("canvas"),ctx=canvas.getContext("2d"),scale=2;canvas.width=source.width*scale;canvas.height=sh*scale;ctx.drawImage(source,0,sy,source.width,sh,0,0,canvas.width,canvas.height);const pixels=ctx.getImageData(0,0,canvas.width,canvas.height);for(let i=0;i175?255:gray<80?0:gray;pixels.data[i]=pixels.data[i+1]=pixels.data[i+2]=value}ctx.putImageData(pixels,0,0);const crop=new Image();crop.src=canvas.toDataURL("image/png");await crop.decode();try{const result=await reader.decodeFromImageElement(crop),value=String(result.getText()||"").trim().replace(/^\s*(UID|SN|MAC)\s*[:#]?\s*/i,"");if(value&&!found.includes(value))found.push(value)}catch{}}if(found.length===3){form.elements.uid.value=found[0];form.elements.sn.value=found[1];form.elements.mac.value=found[2]}else for(const value of found){if(/^([0-9a-f]{2}[:-]){5}[0-9a-f]{2}$/i.test(value)||/^[0-9a-f]{12}$/i.test(value))form.elements.mac.value=value;else if(/^V[0-9A-Z]/i.test(value))form.elements.sn.value=value;else if(!form.elements.uid.value)form.elements.uid.value=value;else if(!form.elements.sn.value)form.elements.sn.value=value;else if(!form.elements.mac.value)form.elements.mac.value=value}help.textContent=found.length?found.length+" barcode"+(found.length===1?"":"s")+" read after image enhancement. Review the fields before adding the device.":"No barcodes detected after image enhancement. Try a closer, brighter photo."}catch(error){help.textContent="The enhanced photo could not be decoded. Enter the identifiers manually."}finally{if(url)URL.revokeObjectURL(url)}} const photoPreprocessObserver=new MutationObserver(()=>{const input=$("#receiveForm .photo-input");if(input&&!input.dataset.preprocessed){input.dataset.preprocessed="true";input.onchange=()=>{if(input.files?.[0])scanLabelPhotoPreprocessed($("#receiveForm"),$("#receiveForm .scan-help"),input.files[0]);input.value=""}}});photoPreprocessObserver.observe($("#modalBody"),{childList:true}); +async function scanLabelPhotoOcr(form,help,file){let url,worker;try{const Tesseract=await import("https://cdn.jsdelivr.net/npm/tesseract.js@5/+esm");url=URL.createObjectURL(file);worker=await Tesseract.createWorker("eng");const result=await worker.recognize(url),text=String(result.data.text||"").replace(/\r/g," ");const uid=text.match(/UID\s*[:#]?\s*([A-Z0-9]+)/i)?.[1],sn=text.match(/SN\s*[:#]?\s*([A-Z0-9]+)/i)?.[1],mac=text.match(/MAC\s*[:#]?\s*((?:[A-F0-9]{2}[:\-]?){6})/i)?.[1];if(uid)form.elements.uid.value=uid;if(sn)form.elements.sn.value=sn;if(mac)form.elements.mac.value=mac.replace(/-/g,":").match(/.{2}/g)?.join(":")||mac;const count=[uid,sn,mac].filter(Boolean).length;help.textContent=count?count+" labeled value"+(count===1?"":"s")+" read from the photo. Review the fields before adding the device.":"No UID, SN, or MAC text was recognized. Retake the photo with the label filling most of the frame."}catch(error){help.textContent="The printed label text could not be recognized. Retake it closer and in brighter light, or enter the values manually."}finally{await worker?.terminate().catch(()=>{});if(url)URL.revokeObjectURL(url)}} +const photoOcrObserver=new MutationObserver(()=>{const input=$("#receiveForm .photo-input");if(input&&!input.dataset.ocr){input.dataset.ocr="true";input.onchange=()=>{if(input.files?.[0])scanLabelPhotoOcr($("#receiveForm"),$("#receiveForm .scan-help"),input.files[0]);input.value=""}}});photoOcrObserver.observe($("#modalBody"),{childList:true}); if(!("BarcodeDetector" in window))document.addEventListener("click",e=>{const button=e.target.closest(".scan-camera");if(button){e.stopPropagation();scanLabelWithLibrary(button.closest("form"),button.closest("form").querySelector(".scan-help"))}},true); applyTheme(); initialize();