client-side-ocr
Version:
High-performance client-side OCR with ONNX Runtime, RapidOCR and PPU PaddleOCR integration. 100+ language support. Process text from images entirely in the browser with state-of-the-art accuracy and complete privacy.
5 lines (4 loc) • 29.5 kB
JavaScript
const O={global:{text_score:.5,use_det:!0,use_cls:!0,use_rec:!0,min_height:30,width_height_ratio:8,max_side_len:2e3,min_side_len:30,return_word_box:!1,return_single_char_box:!1},det:{engine_type:"onnxruntime",lang_type:"en",model_type:"mobile",ocr_version:"PP-OCRv4",limit_side_len:736,limit_type:"min",std:[.5,.5,.5],mean:[.5,.5,.5],scale:.00392156862745098,thresh:.3,box_thresh:.5,max_candidates:1e3,unclip_ratio:1.6,use_dilation:!0,score_mode:"fast",padding_vertical:.4,padding_horizontal:.6,minimum_area_threshold:20},cls:{engine_type:"onnxruntime",lang_type:"en",model_type:"mobile",ocr_version:"PP-OCRv4",cls_image_shape:[3,48,192],cls_batch_num:6,cls_thresh:.9,label_list:["0","180"]},rec:{engine_type:"onnxruntime",lang_type:"en",model_type:"mobile",ocr_version:"PP-OCRv4",rec_img_shape:[3,48,320],rec_batch_num:6}},y={ch:{det:{lang_type:"ch"},cls:{lang_type:"ch"},rec:{lang_type:"ch"}},en:{det:{lang_type:"en"},cls:{lang_type:"en"},rec:{lang_type:"en"}},ja:{det:{lang_type:"ja"},cls:{lang_type:"ja"},rec:{lang_type:"ja"}},ko:{det:{lang_type:"ko"},cls:{lang_type:"ko"},rec:{lang_type:"ko"}},arabic:{det:{lang_type:"arabic"},cls:{lang_type:"arabic"},rec:{lang_type:"arabic"}},chinese_cht:{det:{lang_type:"chinese_cht"},cls:{lang_type:"chinese_cht"},rec:{lang_type:"chinese_cht"}},cyrillic:{det:{lang_type:"cyrillic"},cls:{lang_type:"cyrillic"},rec:{lang_type:"cyrillic"}},devanagari:{det:{lang_type:"devanagari"},cls:{lang_type:"devanagari"},rec:{lang_type:"devanagari"}},latin:{det:{lang_type:"latin"},cls:{lang_type:"latin"},rec:{lang_type:"latin"}},ta:{det:{lang_type:"ta"},cls:{lang_type:"ta"},rec:{lang_type:"ta"}},te:{det:{lang_type:"te"},cls:{lang_type:"te"},rec:{lang_type:"te"}},eslav:{det:{lang_type:"eslav"},cls:{lang_type:"eslav"},rec:{lang_type:"eslav"}},ka:{det:{lang_type:"ka"},cls:{lang_type:"ka"},rec:{lang_type:"ka"}}};function w(h,e){return{global:h.global,det:{...h.det,...e.det||{}},cls:{...h.cls,...e.cls||{}},rec:{...h.rec,...e.rec||{}}}}function x(h){const e=y[h]||{};return w(O,e)}const n="https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/onnx",R="https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/master/onnx",g={ch:{name:"中文",code:"ch",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/ch_PP-OCRv4_det_infer.onnx`,sha256:"d2a7720d45a54257208b1e13e36a8479894cb74155a5efe29462512d42f49da9"},server:{url:`${n}/PP-OCRv4/det/ch_PP-OCRv4_det_server_infer.onnx`,sha256:"cfa39a3f298f6d3fc71789834d15da36d11a6c59b489fc16ea4733728012f786"}},"PP-OCRv5":{mobile:{url:`${R}/PP-OCRv5/det/ch_PP-OCRv5_mobile_det.onnx`,sha256:"4d97c44a20d30a81aad087d6a396b08f786c4635742afc391f6621f5c6ae78ae"},server:{url:`${R}/PP-OCRv5/det/ch_PP-OCRv5_server_det.onnx`,sha256:"0f8846b1d4bba223a2a2f9d9b44022fbc22cc019051a602b41a7fda9667e4cad"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/ch_PP-OCRv4_rec_infer.onnx`,sha256:"48fc40f24f6d2a207a2b1091d3437eb3cc3eb6b676dc3ef9c37384005483683b",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/ch_PP-OCRv4_rec_infer/ppocr_keys_v1.txt"},server:{url:`${n}/PP-OCRv4/rec/ch_PP-OCRv4_rec_server_infer.onnx`,sha256:"6a2676219be9907c7fc9cf61ebaa843bf2898777def567925b78886fcd90c07a",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/ch_PP-OCRv4_rec_infer/ppocr_keys_v1.txt"}},"PP-OCRv5":{mobile:{url:`${R}/PP-OCRv5/rec/ch_PP-OCRv5_rec_mobile_infer.onnx`,sha256:"5825fc7ebf84ae7a412be049820b4d86d77620f204a041697b0494669b1742c5",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv5/rec/ch_PP-OCRv5_rec_mobile_infer/ppocrv5_dict.txt"},server:{url:`${R}/PP-OCRv5/rec/ch_PP-OCRv5_rec_server_infer.onnx`,sha256:"e09385400eaaaef34ceff54aeb7c4f0f1fe014c27fa8b9905d4709b65746562a",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv5/rec/ch_PP-OCRv5_rec_server_infer/ppocrv5_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"ppocr_keys_v1.txt","PP-OCRv5":"ppocrv5_dict.txt"}},fonts:["FZYTK.TTF"]},en:{name:"English",code:"en",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/en_PP-OCRv3_det_infer.onnx`,sha256:"ea07c15d38ac40cd69da3c493444ec75b44ff23840553ff8ba102c1219ed39c2"}},"PP-OCRv5":{mobile:{url:`${n}/PP-OCRv4/det/en_PP-OCRv3_det_infer.onnx`,sha256:"ea07c15d38ac40cd69da3c493444ec75b44ff23840553ff8ba102c1219ed39c2"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/en_PP-OCRv4_rec_infer.onnx`,sha256:"e8770c967605983d1570cdf5352041dfb68fa0c21664f49f47b155abd3e0e318",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/en_PP-OCRv4_rec_infer/en_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"en_dict.txt"}}},ja:{name:"日本語",code:"ja",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/japan_PP-OCRv4_rec_infer.onnx`,sha256:"e1075a67dba758ecfc7ebc78a10ae61c95ac8fb66a9c86fab5541e33f085cb7a",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/japan_PP-OCRv4_rec_infer/japan_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"japan_dict.txt"}},fonts:["japan.ttc"]},ko:{name:"한국어",code:"ko",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}},"PP-OCRv5":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/korean_PP-OCRv4_rec_infer.onnx`,sha256:"ab151ba9065eccd98f884cf4d927db091be86137276392072edd4f9d43ad7426",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/korean_PP-OCRv4_rec_infer/korean_dict.txt"}},"PP-OCRv5":{mobile:{url:`${R}/PP-OCRv5/rec/korean_PP-OCRv5_rec_mobile_infer.onnx`,sha256:"cd6e2ea50f6943ca7271eb8c56a877a5a90720b7047fe9c41a2e541a25773c9b",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv5/rec/korean_PP-OCRv5_rec_mobile_infer/ppocrv5_korean_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"korean_dict.txt","PP-OCRv5":"ppocrv5_korean_dict.txt"}},fonts:["korean.ttf"]},arabic:{name:"العربية",code:"arabic",direction:"rtl",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/arabic_PP-OCRv4_rec_infer.onnx`,sha256:"4a9011bef71687bb84288dc86ad2471bd5d37b717ddf672dd156f9e7a5601bac",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/arabic_PP-OCRv4_rec_infer/arabic_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"arabic_dict.txt"}},fonts:["arabic.ttf"]},chinese_cht:{name:"繁體中文",code:"chinese_cht",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/ch_PP-OCRv4_det_infer.onnx`,sha256:"d2a7720d45a54257208b1e13e36a8479894cb74155a5efe29462512d42f49da9"},server:{url:`${n}/PP-OCRv4/det/ch_PP-OCRv4_det_server_infer.onnx`,sha256:"cfa39a3f298f6d3fc71789834d15da36d11a6c59b489fc16ea4733728012f786"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/chinese_cht_PP-OCRv3_rec_infer.onnx`,sha256:"779656d044ce388045e02ea9244724616194e63928606436cdfc6dc3c9528cc6",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/chinese_cht_PP-OCRv3_rec_infer/chinese_cht_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"chinese_cht_dict.txt"}},fonts:["chinese_cht.ttf"]},cyrillic:{name:"Кириллица",code:"cyrillic",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/cyrillic_PP-OCRv3_rec_infer.onnx`,sha256:"1efb65bdc460af1c0e8733d005b20952b17ca5aac10ddb56c968333791c5eaa3",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/cyrillic_PP-OCRv3_rec_infer/cyrillic_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"cyrillic_dict.txt"}},fonts:["cyrillic.ttf"]},devanagari:{name:"देवनागरी",code:"devanagari",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/devanagari_PP-OCRv4_rec_infer.onnx`,sha256:"a62b6148303187907aa0b0d3a0125bdc62557d07966468cab9056949e36035e8",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/devanagari_PP-OCRv4_rec_infer/devanagari_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"devanagari_dict.txt"}},fonts:["devanagari_Martel-Regular.ttf"]},latin:{name:"Latin",code:"latin",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}},"PP-OCRv5":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/latin_PP-OCRv3_rec_infer.onnx`,sha256:"e9d7a33667e8aaa702862975186adf2012e3f390cc0f9422865957125f8071cf",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/latin_PP-OCRv3_rec_infer/latin_dict.txt"}},"PP-OCRv5":{mobile:{url:`${R}/PP-OCRv5/rec/latin_PP-OCRv5_rec_mobile_infer.onnx`,sha256:"b20bd37c168a570f583afbc8cd7925603890efbcdc000a59e22c269d160b5f5a",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv5/rec/latin_PP-OCRv5_rec_mobile_infer/ppocrv5_latin_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"latin_dict.txt","PP-OCRv5":"ppocrv5_latin_dict.txt"}},fonts:["latin.ttf"]},ta:{name:"தமிழ்",code:"ta",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/ta_PP-OCRv4_rec_infer.onnx`,sha256:"f78d752148873c5fa6e4294002bfd162dbba54236e406a39665ebbda766161b5",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/ta_PP-OCRv4_rec_infer/ta_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"ta_dict.txt"}},fonts:["tamil.ttf"]},te:{name:"తెలుగు",code:"te",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/te_PP-OCRv4_rec_infer.onnx`,sha256:"e608c3be00c8a9ea2f5c667d90f379403e2568bd5c8183308a49ca093def8eff",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/te_PP-OCRv4_rec_infer/te_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"te_dict.txt"}},fonts:["telugu.ttf"]},ka:{name:"ಕನ್ನಡ",code:"ka",models:{det:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/rec/ka_PP-OCRv4_rec_infer.onnx`,sha256:"9c1e186ea1d13cf6c853e57b42d382c3961fdd6acc2409e0d0dc44defc9f152b",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv4/rec/kannada_PP-OCRv4_rec_infer/ka_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv4":"ka_dict.txt"}},fonts:["kannada.ttf"]},eslav:{name:"Eastern Slavic",code:"eslav",models:{det:{"PP-OCRv5":{mobile:{url:`${n}/PP-OCRv4/det/Multilingual_PP-OCRv3_det_infer.onnx`,sha256:"5475c6c7f4d84a6c4f32241b487435d59f126a40c023387af99732258844cdc3"}}},rec:{"PP-OCRv5":{mobile:{url:`${R}/PP-OCRv5/rec/eslav_PP-OCRv5_rec_mobile_infer.onnx`,sha256:"08705d6721849b1347d26187f15a5e362c431963a2a62bfff4feac578c489aab",dictUrl:"https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v3.3.0/paddle/PP-OCRv5/rec/eslav_PP-OCRv5_rec_mobile_infer/ppocrv5_eslav_dict.txt"}}},cls:{"PP-OCRv4":{mobile:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}},dict:{"PP-OCRv5":"ppocrv5_eslav_dict.txt"}},fonts:["cyrillic.ttf"]}};function u(h,e="PP-OCRv4",t="mobile"){if(h==="en"&&e==="PP-OCRv4"&&t==="mobile"){const a="https://siva-sub.github.io/client-ocr/models/ppu-paddle-ocr";return{det:{url:`${a}/PP-OCRv5_mobile_det_infer.onnx`,sha256:void 0},rec:{url:`${a}/en_PP-OCRv4_mobile_rec_infer.onnx`,dictUrl:`${a}/en_dict.txt`,sha256:void 0},cls:{url:`${n}/PP-OCRv4/cls/ch_ppocr_mobile_v2.0_cls_infer.onnx`,sha256:"e47acedf663230f8863ff1ab0e64dd2d82b838fceb5957146dab185a89d6215c"}}}const o=g[h];if(!o)throw new Error(`Language ${h} not supported`);return{det:o.models.det[e]?.[t],rec:o.models.rec[e]?.[t],cls:o.models.cls[e]?.[t],dict:o.models.dict?.[e]}}function v(h){return typeof h=="string"?{url:`/models/${h}`,sha256:void 0,dictUrl:void 0,isMetaOnnx:h.includes(".meta.onnx")}:h}class L{cache=new Map;downloadQueue=new Map;onProgress;constructor(){this.initializeCache()}async initializeCache(){if("indexedDB"in window)try{const o=(await this.openDB()).transaction(["models"],"readonly").objectStore("models"),a=await new Promise((s,c)=>{const r=o.getAllKeys();r.onsuccess=()=>s(r.result),r.onerror=()=>c(r.error)});for(const s of a){const c=await new Promise((r,i)=>{const l=o.get(s);l.onsuccess=()=>r(l.result),l.onerror=()=>i(l.error)});c&&this.cache.set(s,c)}}catch(e){console.warn("Failed to load model cache:",e)}}async openDB(){return new Promise((e,t)=>{const o=indexedDB.open("rapidocr-models",1);o.onerror=()=>t(o.error),o.onsuccess=()=>e(o.result),o.onupgradeneeded=a=>{const s=a.target.result;s.objectStoreNames.contains("models")||s.createObjectStore("models")}})}async saveToCache(e,t){if(this.cache.set(e,t),"indexedDB"in window)try{const s=(await this.openDB()).transaction(["models"],"readwrite").objectStore("models");await new Promise((c,r)=>{const i=s.put(t,e);i.onsuccess=()=>c(),i.onerror=()=>r(i.error)})}catch(o){console.warn("Failed to save model to cache:",o)}}async downloadModel(e){if(this.cache.has(e.localPath))return this.cache.get(e.localPath);if(this.downloadQueue.has(e.localPath))return this.downloadQueue.get(e.localPath);const t=this.performDownload(e);this.downloadQueue.set(e.localPath,t);try{const o=await t;return await this.saveToCache(e.localPath,o),o}finally{this.downloadQueue.delete(e.localPath)}}async performDownload(e){const t=e.localPath.split("/").pop()||"model";try{this.reportProgress({modelName:t,progress:0,total:e.size||0,status:"downloading"});const o=await fetch(e.url);if(!o.ok)throw new Error(`HTTP ${o.status}: ${o.statusText}`);const a=o.headers.get("content-length"),s=a?parseInt(a,10):e.size||0,c=o.body?.getReader();if(!c)throw new Error("No response body");const r=[];let i=0;for(;;){const{done:f,value:P}=await c.read();if(f)break;r.push(P),i+=P.length,this.reportProgress({modelName:t,progress:i,total:s,status:"downloading"})}const l=new Uint8Array(i);let d=0;for(const f of r)l.set(f,d),d+=f.length;if(e.sha256&&(this.reportProgress({modelName:t,progress:i,total:s,status:"verifying"}),!await this.verifySHA256(l,e.sha256)))throw new Error("SHA256 verification failed");return this.reportProgress({modelName:t,progress:i,total:s,status:"complete"}),l.buffer}catch(o){throw this.reportProgress({modelName:t,progress:0,total:0,status:"error"}),o}}async verifySHA256(e,t){const o=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(o)).map(c=>c.toString(16).padStart(2,"0")).join("")===t.toLowerCase()}setProgressCallback(e){this.onProgress=e}reportProgress(e){this.onProgress&&this.onProgress(e)}async getModelUrls(e,t,o){const a=u(e,t,o),s={};if(a.det){const c=v(a.det);s.det={url:c.url,sha256:c.sha256,localPath:c.url.split("/").pop()||"det.onnx",size:this.estimateModelSize("det",o)}}if(a.rec){const c=v(a.rec);s.rec={url:c.url,sha256:c.sha256,dictUrl:c.dictUrl,localPath:c.url.split("/").pop()||"rec.onnx",size:this.estimateModelSize("rec",o)}}if(a.cls){const c=v(a.cls);s.cls={url:c.url,sha256:c.sha256,localPath:c.url.split("/").pop()||"cls.onnx",size:this.estimateModelSize("cls",o)}}if(a.dict){const c=a.rec?v(a.rec):null;c?.dictUrl&&(s.dict={url:c.dictUrl,localPath:c.dictUrl.split("/").pop()||"dict.txt",size:100*1024})}return s}estimateModelSize(e,t){return{det:{mobile:3145728,server:49283072},rec:{mobile:12582912,server:104857600},cls:{mobile:1572864,server:1572864}}[e]?.[t]||10*1024*1024}async areModelsCached(e,t,o){const a=u(e,t,o),s=[a.det,a.rec,a.cls].filter(c=>c!==void 0);for(const c of s){const i=v(c).url;if(!this.cache.has(i))return!1}return!0}async clearCache(){if(this.cache.clear(),"indexedDB"in window)try{const o=(await this.openDB()).transaction(["models"],"readwrite").objectStore("models");await new Promise((a,s)=>{const c=o.clear();c.onsuccess=()=>a(),c.onerror=()=>s(c.error)})}catch(e){console.warn("Failed to clear cache:",e)}}async getCacheSize(){let e=0;for(const t of this.cache.values())e+=t.byteLength;return e}async downloadDictionary(e){try{const t=await fetch(e);if(!t.ok)throw new Error(`Failed to download dictionary: ${t.status}`);return(await t.text()).split(`
`).filter(a=>a.length>0)}catch(t){return console.error("Failed to download dictionary:",t),[]}}async downloadLanguageModels(e,t="PP-OCRv4",o="mobile",a){a&&this.setProgressCallback(a);const s=await this.getModelUrls(e,t,o),c={},r=[];return s.det&&r.push(this.downloadModel(s.det).then(i=>{c.det=i})),s.rec&&r.push(this.downloadModel(s.rec).then(i=>{c.rec=i})),s.cls&&r.push(this.downloadModel(s.cls).then(i=>{c.cls=i})),s.dict&&r.push(this.downloadDictionary(s.dict.url).then(i=>{c.dict=i})),await Promise.all(r),c}}class C{static getWordBoxes(e,t,o){if(!e||t.length===0)return[];const a=[],s=e.split("");let c="",r=[],i=0;for(let l=0;l<s.length;l++){const d=s[l];d===" "||d===" "||d===`
`?c.length>0&&r.length>0&&(a.push({text:c,confidence:o,box:this.mergeBoxes(r),charBoxes:[...r]}),c="",r=[]):(c+=d,i<t.length&&r.push(t[i]),i++)}return c.length>0&&r.length>0&&a.push({text:c,confidence:o,box:this.mergeBoxes(r),charBoxes:[...r]}),a}static mergeBoxes(e){if(e.length===0)throw new Error("Cannot merge empty boxes array");if(e.length===1)return e[0];let t=1/0,o=-1/0,a=1/0,s=-1/0;for(const c of e)t=Math.min(t,c.topLeft.x,c.bottomLeft.x),o=Math.max(o,c.topRight.x,c.bottomRight.x),a=Math.min(a,c.topLeft.y,c.topRight.y),s=Math.max(s,c.bottomLeft.y,c.bottomRight.y);return{topLeft:{x:t,y:a},topRight:{x:o,y:a},bottomRight:{x:o,y:s},bottomLeft:{x:t,y:s}}}static approximateCharBoxes(e,t){const o=e.split("");if(o.length===0)return[];const s=Math.sqrt(Math.pow(t.topRight.x-t.topLeft.x,2)+Math.pow(t.topRight.y-t.topLeft.y,2)),c=Math.atan2(t.topRight.y-t.topLeft.y,t.topRight.x-t.topLeft.x),r=o.filter(P=>P!==" ").length,i=o.filter(P=>P===" ").length,l=s/(r+i*1.5),d=[];let f=0;for(const P of o){const _=P===" "?l*1.5:l,p={topLeft:{x:f,y:0},topRight:{x:f+_,y:0},bottomRight:{x:f+_,y:t.bottomLeft.y-t.topLeft.y},bottomLeft:{x:f,y:t.bottomLeft.y-t.topLeft.y}},m={topLeft:this.rotateAndTranslate(p.topLeft,c,t.topLeft),topRight:this.rotateAndTranslate(p.topRight,c,t.topLeft),bottomRight:this.rotateAndTranslate(p.bottomRight,c,t.topLeft),bottomLeft:this.rotateAndTranslate(p.bottomLeft,c,t.topLeft)};d.push(m),f+=_}return d}static rotateAndTranslate(e,t,o){const a=Math.cos(t),s=Math.sin(t);return{x:o.x+e.x*a-e.y*s,y:o.y+e.x*s+e.y*a}}static splitIntoLines(e,t){const o=e.split(`
`);if(o.length<=1)return[{text:e,box:t}];const s=Math.max(Math.abs(t.bottomLeft.y-t.topLeft.y),Math.abs(t.bottomRight.y-t.topRight.y))/o.length,c=[];for(let r=0;r<o.length;r++){const i=r*s;c.push({text:o[r],box:{topLeft:{x:t.topLeft.x,y:t.topLeft.y+i},topRight:{x:t.topRight.x,y:t.topRight.y+i},bottomRight:{x:t.bottomRight.x,y:t.topRight.y+i+s},bottomLeft:{x:t.bottomLeft.x,y:t.topLeft.y+i+s}}})}return c}}class M{config;workers={};modelPaths;onProgress;modelDownloader;enableWordBoxes;constructor(e={}){const{lang:t="en",version:o="PP-OCRv4",modelType:a="mobile",config:s={},modelBasePath:c="/models",enableWordBoxes:r=!1}=e,i=x(t);this.config={global:{...i.global,...s.global},det:{...i.det,...s.det,ocr_version:o,model_type:a},cls:{...i.cls,...s.cls,ocr_version:o,model_type:a},rec:{...i.rec,...s.rec,ocr_version:o,model_type:a}},this.modelPaths=u(t,o,a);const l=this.modelPaths.rec;if(this.modelPaths.det){const d=typeof this.modelPaths.det=="string"?this.modelPaths.det:this.modelPaths.det.url;this.modelPaths.det=d.startsWith("http")?d:`${c}/${d}`}if(this.modelPaths.rec){const d=typeof this.modelPaths.rec=="string"?this.modelPaths.rec:this.modelPaths.rec.url;this.modelPaths.rec=d.startsWith("http")?d:`${c}/${d}`}if(this.modelPaths.cls){const d=typeof this.modelPaths.cls=="string"?this.modelPaths.cls:this.modelPaths.cls.url;this.modelPaths.cls=d.startsWith("http")?d:`${c}/${d}`}l&&typeof l=="object"&&l.dictUrl?this.modelPaths.dict=l.dictUrl:this.modelPaths.dict&&(this.modelPaths.dict=`${c}/${this.modelPaths.dict}`),this.modelDownloader=new L,this.enableWordBoxes=r}async initialize(){const e=[];this.config.global.use_det&&this.modelPaths.det&&(this.workers.detection=new Worker(new URL("/client-ocr/assets/detection.worker.v2-DKtXKk48.js",import.meta.url),{type:"module"}),e.push(this.initWorker(this.workers.detection,{type:"INIT",data:{modelPath:this.modelPaths.det,config:this.config}}))),this.config.global.use_cls&&this.modelPaths.cls&&(this.workers.classification=new Worker(new URL("/client-ocr/assets/classification.worker.v2-Cqw-REfv.js",import.meta.url),{type:"module"}),e.push(this.initWorker(this.workers.classification,{type:"INIT",data:{modelPath:this.modelPaths.cls,config:this.config}}))),this.config.global.use_rec&&this.modelPaths.rec&&(this.workers.recognition=new Worker(new URL("/client-ocr/assets/recognition.worker.v2-VgAdGtRA.js",import.meta.url),{type:"module"}),e.push(this.initWorker(this.workers.recognition,{type:"INIT",data:{modelPath:this.modelPaths.rec,dictPath:this.modelPaths.dict,config:this.config}}))),await Promise.all(e)}async process(e,t,o){const a=[];let s=[];if(this.config.global.use_det&&this.workers.detection?(this.reportProgress("detection",0),s=await this.detect(e,t,o),this.reportProgress("detection",1)):s=[{topLeft:{x:0,y:0},topRight:{x:t,y:0},bottomRight:{x:t,y:o},bottomLeft:{x:0,y:o}}],s.length===0)return a;console.log("Detection boxes:",s.map(r=>({x:Math.min(r.topLeft.x,r.bottomLeft.x),y:Math.min(r.topLeft.y,r.topRight.y),w:Math.max(r.topRight.x,r.bottomRight.x)-Math.min(r.topLeft.x,r.bottomLeft.x),h:Math.max(r.bottomLeft.y,r.bottomRight.y)-Math.min(r.topLeft.y,r.topRight.y)})));const c=new Array(s.length).fill(0);if(this.config.global.use_cls&&this.workers.classification){this.reportProgress("classification",0);const r=s.map(l=>this.cropImage(e,t,o,l));(await this.classify(r)).forEach((l,d)=>{c[d]=l.angle}),this.reportProgress("classification",1)}if(this.config.global.use_rec&&this.workers.recognition){this.reportProgress("recognition",0),console.log(`Sending to recognition: image ${t}x${o}, boxes: ${s.length}`);let r=0;for(let l=0;l<Math.min(4e3,e.length);l+=4)(e[l]>0||e[l+1]>0||e[l+2]>0)&&r++;console.log(`Original image has ${r} non-zero pixels in first 1000 pixels`);const i=await this.recognize(e,t,o,s);for(let l=0;l<s.length;l++){const{text:d,confidence:f}=i[l];if(f>=this.config.global.text_score){const P={text:d,confidence:f,box:s[l],angle:c[l]};if(this.enableWordBoxes&&d.includes(" ")){const _=C.approximateCharBoxes(d,s[l]),p=C.getWordBoxes(d,_,f);P.words=p.map(m=>({text:m.text,confidence:m.confidence,box:m.box}))}a.push(P)}}this.reportProgress("recognition",1)}return this.sortResults(a)}setProgressCallback(e){this.onProgress=e}setDownloadProgressCallback(e){this.modelDownloader.setProgressCallback(e)}async areModelsAvailable(){const e=this.config.det.lang_type,t=this.config.det.ocr_version,o=this.config.det.model_type;return this.modelDownloader.areModelsCached(e,t,o)}async downloadModels(){const e=this.config.det.lang_type,t=this.config.det.ocr_version,o=this.config.det.model_type,a=await this.modelDownloader.getModelUrls(e,t,o),s=[];this.config.global.use_det&&a.det&&s.push(this.modelDownloader.downloadModel(a.det).then(()=>{})),this.config.global.use_cls&&a.cls&&s.push(this.modelDownloader.downloadModel(a.cls).then(()=>{})),this.config.global.use_rec&&a.rec&&s.push(this.modelDownloader.downloadModel(a.rec).then(()=>{})),a.dict&&typeof this.modelPaths.rec=="string"&&!this.modelPaths.rec.includes("meta.onnx")&&s.push(this.modelDownloader.downloadModel(a.dict).then(()=>{})),await Promise.all(s)}dispose(){Object.values(this.workers).forEach(e=>{e?.terminate()}),this.workers={}}async initWorker(e,t){return new Promise((o,a)=>{const s=c=>{c.data.type==="RESULT"?(e.removeEventListener("message",s),o()):c.data.type==="ERROR"&&(e.removeEventListener("message",s),a(new Error(c.data.error)))};e.addEventListener("message",s),e.postMessage(t)})}async detect(e,t,o){return this.workers.detection?new Promise((a,s)=>{const c=r=>{r.data.type==="RESULT"?(this.workers.detection.removeEventListener("message",c),a(r.data.data.boxes||[])):r.data.type==="ERROR"&&(this.workers.detection.removeEventListener("message",c),s(new Error(r.data.error)))};this.workers.detection.addEventListener("message",c),this.workers.detection.postMessage({type:"DETECT",data:{imageData:e,width:t,height:o}})}):[]}async classify(e){return this.workers.classification?new Promise((t,o)=>{const a=s=>{s.data.type==="RESULT"?(this.workers.classification.removeEventListener("message",a),t(s.data.data.results||[])):s.data.type==="ERROR"?(this.workers.classification.removeEventListener("message",a),o(new Error(s.data.error))):s.data.type==="PROGRESS"&&this.reportProgress("classification",s.data.progress)};this.workers.classification.addEventListener("message",a),this.workers.classification.postMessage({type:"CLASSIFY",data:{images:e}})}):e.map(()=>({angle:0,confidence:1,shouldRotate:!1}))}async recognize(e,t,o,a){return this.workers.recognition?new Promise((s,c)=>{const r=i=>{if(i.data.type==="RESULT"){this.workers.recognition.removeEventListener("message",r);const l=i.data.data.results||[];s(l.map(d=>({text:d.text,confidence:d.confidence})))}else i.data.type==="ERROR"?(this.workers.recognition.removeEventListener("message",r),c(new Error(i.data.error))):i.data.type==="PROGRESS"&&this.reportProgress("recognition",i.data.progress)};this.workers.recognition.addEventListener("message",r),this.workers.recognition.postMessage({type:"PROCESS",data:{imageData:e,width:t,height:o,boxes:a}})}):a.map(()=>({text:"",confidence:0}))}cropImage(e,t,o,a){const s=Math.min(a.topLeft.x,a.bottomLeft.x),c=Math.max(a.topRight.x,a.bottomRight.x),r=Math.min(a.topLeft.y,a.topRight.y),i=Math.max(a.bottomLeft.y,a.bottomRight.y),l=Math.round(c-s),d=Math.round(i-r),f=new Uint8ClampedArray(l*d*4);for(let P=0;P<d;P++)for(let _=0;_<l;_++){const p=((Math.round(r)+P)*t+Math.round(s)+_)*4,m=(P*l+_)*4;for(let b=0;b<4;b++)f[m+b]=e[p+b]}return{data:f,width:l,height:d}}sortResults(e){return e.sort((t,o)=>{const a=Math.min(t.box.topLeft.y,t.box.topRight.y),s=Math.min(o.box.topLeft.y,o.box.topRight.y);if(Math.abs(a-s)<10){const c=Math.min(t.box.topLeft.x,t.box.bottomLeft.x),r=Math.min(o.box.topLeft.x,o.box.bottomLeft.x);return c-r}return a-s})}reportProgress(e,t){this.onProgress&&this.onProgress({stage:e,progress:t})}static getAvailableLanguages(){return Object.entries(g).map(([e,t])=>({code:e,name:t.name}))}static isLanguageSupported(e,t="PP-OCRv4"){const o=g[e];return o&&o.models.rec[t]!==void 0}}export{M as R};