From e8bc3dc0167deeb0f429b9d4bbb1a7486a466613 Mon Sep 17 00:00:00 2001 From: xororz Date: Mon, 15 Apr 2024 04:15:21 -0400 Subject: [PATCH] optimize performance --- src/App.vue | 90 ++++++++++++++++++---------- src/image.ts | 8 ++- src/imghelper.js | 2 +- src/imghelper.wasm | Bin 7215 -> 7404 bytes src/upscale.ts | 44 +++++++------- src/worker.js | 146 ++++----------------------------------------- 6 files changed, 101 insertions(+), 189 deletions(-) diff --git a/src/App.vue b/src/App.vue index 3dafbe2..0150c57 100644 --- a/src/App.vue +++ b/src/App.vue @@ -208,6 +208,7 @@ export default { worker: new Worker(new URL("./worker.js", import.meta.url), { type: "module", }), + wasmModule: null, }; }, watch: { @@ -271,19 +272,19 @@ export default { this.drawLine = true; let wasmModule = await Module(); + this.wasmModule = wasmModule; const imgCanvas = this.$refs.imgCanvas; imgCanvas.width = this.img.width; imgCanvas.height = this.img.height; const imgCtx = imgCanvas.getContext("2d"); imgCtx.drawImage(this.img, 0, 0); - this.input = new Img(this.img.width, this.img.height); let data = imgCtx.getImageData( 0, 0, this.img.width, this.img.height ).data; - this.input.data = new Uint8Array(data); + this.input = new Img(this.img.width, this.img.height, data); const numPixels = this.input.width * this.input.height; const bytesPerImage = numPixels * 4; let sourcePtr = wasmModule._malloc(bytesPerImage); @@ -292,7 +293,6 @@ export default { this.hasAlpha = wasmModule._check_alpha(sourcePtr, numPixels); if (this.hasAlpha) { this.inputAlpha = new Img(this.img.width, this.img.height); - this.inputAlpha.data = new Uint8Array(bytesPerImage); wasmModule._copy_alpha_to_rgb(sourcePtr, targetPtr, numPixels); this.inputAlpha.data.set( wasmModule.HEAPU8.subarray(targetPtr, targetPtr + bytesPerImage) @@ -610,28 +610,46 @@ export default { this.progress = progress; if (done) { if (!this.hasAlpha || (this.hasAlpha && this.inputAlpha)) { - this.output = output; + let factor = this.modelzoo[this.model].factor; + this.output = new Img( + factor * this.input.width, + factor * this.input.height, + new Uint8ClampedArray(output) + ); } this.info = "Processing Image..."; if (this.inputAlpha) { - worker.postMessage({ - input: this.inputAlpha.data, - fixed: this.modelzoo[this.model].fixed, - factor: this.modelzoo[this.model].factor, - width: this.inputAlpha.width, - height: this.inputAlpha.height, - model: this.model, - backend: this.backend, - hasAlpha: true, - }); + worker.postMessage( + { + input: this.inputAlpha.data.buffer, + fixed: this.modelzoo[this.model].fixed, + factor: this.modelzoo[this.model].factor, + width: this.inputAlpha.width, + height: this.inputAlpha.height, + model: this.model, + backend: this.backend, + hasAlpha: true, + }, + [this.inputAlpha.data.buffer] + ); this.inputAlpha = null; return; } if (this.hasAlpha) { - for (let i = 0; i < output.data.length; i += 4) { - if (output.data[i] < 128) this.output.data[i + 3] = 0; - else this.output.data[i + 3] = 255; - } + let outputArray = new Uint8Array(output); + let wasmModule = this.wasmModule; + let sourcePtr = wasmModule._malloc(outputArray.length); + let targetPtr = wasmModule._malloc(outputArray.length); + let numPixels = outputArray.length / 4; + wasmModule.HEAPU8.set(outputArray, sourcePtr); + wasmModule.HEAPU8.set(this.output.data, targetPtr); + wasmModule._copy_alpha_channel(sourcePtr, targetPtr, numPixels); + this.output.data.set( + wasmModule.HEAPU8.subarray( + targetPtr, + targetPtr + outputArray.length + ) + ); } const imgCanvas = this.$refs.imgCanvas; @@ -639,13 +657,22 @@ export default { imgCtx.clearRect(0, 0, imgCanvas.width, imgCanvas.height); imgCanvas.width = this.output.width; imgCanvas.height = this.output.height; - let outImg = imgCtx.createImageData(output.width, output.height); + let outImg = imgCtx.createImageData( + this.output.width, + this.output.height + ); outImg.data.set(this.output.data); imgCtx.putImageData(outImg, 0, 0); let type = "image/jpeg"; let quality = 0.92; if (this.hasAlpha) type = "image/png"; - this.processedImg.src = imgCanvas.toDataURL(type, quality); + this.processedImg.src = imgCanvas.toBlob( + (blob) => { + this.processedImg.src = URL.createObjectURL(blob); + }, + type, + quality + ); this.processedImg.onload = () => { this.linePosition = this.$refs.canvas.width * 0.5; this.$refs.dragLine.style.left = @@ -658,16 +685,19 @@ export default { worker.terminate(); } }); - worker.postMessage({ - input: this.input.data, - fixed: this.modelzoo[this.model].fixed, - factor: this.modelzoo[this.model].factor, - width: this.input.width, - height: this.input.height, - model: this.model, - backend: this.backend, - hasAlpha: false, - }); + worker.postMessage( + { + input: this.input.data.buffer, + fixed: this.modelzoo[this.model].fixed, + factor: this.modelzoo[this.model].factor, + width: this.input.width, + height: this.input.height, + model: this.model, + backend: this.backend, + hasAlpha: false, + }, + [this.input.data.buffer] + ); }, saveImage() { const a = document.createElement("a"); diff --git a/src/image.ts b/src/image.ts index 6cc9b4e..3231184 100644 --- a/src/image.ts +++ b/src/image.ts @@ -2,10 +2,14 @@ export default class Image { width: number; height: number; data: Uint8Array; - constructor(width: number, height: number) { + constructor( + width: number, + height: number, + data = new Uint8Array(width * height * 4) + ) { this.width = width; this.height = height; - this.data = new Uint8Array(width * height * 4); + this.data = data; } getImageCrop( x: number, diff --git a/src/imghelper.js b/src/imghelper.js index 12ea416..258dfe6 100644 --- a/src/imghelper.js +++ b/src/imghelper.js @@ -5,7 +5,7 @@ var Module = (() => { return ( async function(moduleArg = {}) { -var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary;if(ENVIRONMENT_IS_NODE){const{createRequire:createRequire}=await import("module");var require=createRequire(import.meta.url);var fs=require("fs");var nodePath=require("path");if(ENVIRONMENT_IS_WORKER){scriptDirectory=nodePath.dirname(scriptDirectory)+"/"}else{scriptDirectory=require("url").fileURLToPath(new URL("./",import.meta.url))}read_=(filename,binary)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=(filename,onload,onerror,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)onerror(err);else onload(binary?data.buffer:data)})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=(url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");var wasmBinaryFile;if(Module["locateFile"]){wasmBinaryFile="imghelper.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}}else{wasmBinaryFile=new URL("imghelper.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"&&!isFileURI(binaryFile)){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{if(!response["ok"]){throw`failed to load wasm binary file at '${binaryFile}'`}return response["arrayBuffer"]()}).catch(()=>getBinarySync(binaryFile))}else if(readAsync){return new Promise((resolve,reject)=>{readAsync(binaryFile,response=>resolve(new Uint8Array(response)),reject)})}}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function createWasm(){var info={"env":wasmImports,"wasi_snapshot_preview1":wasmImports};function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["memory"];updateMemoryViews();addOnInit(wasmExports["__wasm_call_ctors"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var noExitRuntime=Module["noExitRuntime"]||true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var getHeapMax=()=>2147483648;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf8"):undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={"string":str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},"array":arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var wasmImports={emscripten_resize_heap:_emscripten_resize_heap};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["__wasm_call_ctors"])();var _check_alpha=Module["_check_alpha"]=(a0,a1)=>(_check_alpha=Module["_check_alpha"]=wasmExports["check_alpha"])(a0,a1);var _copy_alpha_to_rgb=Module["_copy_alpha_to_rgb"]=(a0,a1,a2)=>(_copy_alpha_to_rgb=Module["_copy_alpha_to_rgb"]=wasmExports["copy_alpha_to_rgb"])(a0,a1,a2);var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["malloc"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["free"])(a0);var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["_emscripten_stack_restore"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["_emscripten_stack_alloc"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["emscripten_stack_get_current"])();Module["ccall"]=ccall;Module["cwrap"]=cwrap;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); +var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary;if(ENVIRONMENT_IS_NODE){const{createRequire:createRequire}=await import("module");var require=createRequire(import.meta.url);var fs=require("fs");var nodePath=require("path");if(ENVIRONMENT_IS_WORKER){scriptDirectory=nodePath.dirname(scriptDirectory)+"/"}else{scriptDirectory=require("url").fileURLToPath(new URL("./",import.meta.url))}read_=(filename,binary)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=(filename,onload,onerror,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)onerror(err);else onload(binary?data.buffer:data)})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=(url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");var wasmBinaryFile;if(Module["locateFile"]){wasmBinaryFile="imghelper.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}}else{wasmBinaryFile=new URL("imghelper.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"&&!isFileURI(binaryFile)){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{if(!response["ok"]){throw`failed to load wasm binary file at '${binaryFile}'`}return response["arrayBuffer"]()}).catch(()=>getBinarySync(binaryFile))}else if(readAsync){return new Promise((resolve,reject)=>{readAsync(binaryFile,response=>resolve(new Uint8Array(response)),reject)})}}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function createWasm(){var info={"env":wasmImports,"wasi_snapshot_preview1":wasmImports};function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["memory"];updateMemoryViews();addOnInit(wasmExports["__wasm_call_ctors"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var noExitRuntime=Module["noExitRuntime"]||true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var getHeapMax=()=>2147483648;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf8"):undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={"string":str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},"array":arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var wasmImports={emscripten_resize_heap:_emscripten_resize_heap};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["__wasm_call_ctors"])();var _check_alpha=Module["_check_alpha"]=(a0,a1)=>(_check_alpha=Module["_check_alpha"]=wasmExports["check_alpha"])(a0,a1);var _copy_alpha_to_rgb=Module["_copy_alpha_to_rgb"]=(a0,a1,a2)=>(_copy_alpha_to_rgb=Module["_copy_alpha_to_rgb"]=wasmExports["copy_alpha_to_rgb"])(a0,a1,a2);var _copy_alpha_channel=Module["_copy_alpha_channel"]=(a0,a1,a2)=>(_copy_alpha_channel=Module["_copy_alpha_channel"]=wasmExports["copy_alpha_channel"])(a0,a1,a2);var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["malloc"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["free"])(a0);var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["_emscripten_stack_restore"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["_emscripten_stack_alloc"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["emscripten_stack_get_current"])();Module["ccall"]=ccall;Module["cwrap"]=cwrap;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); return readyPromise diff --git a/src/imghelper.wasm b/src/imghelper.wasm index 698b440267536ffb71a845caf6f9bb8dd4a779b7..dc52f56c8d152c1f8f0fc91384282d9863ec8629 100755 GIT binary patch delta 399 zcmXYpzfQtX6o=2be|j$zB4IJaXl_ebOms797ipqlG4T;fttEh^{1FG^aEY@^!{Bb> z?0^s8;0w4o`3O!9P9}H(r|-+j`F+m^Ux!r)uX+doxM?*}qi#~IH5i2^K$B>5%Br<( zJ5L!R0S-VI#{nX8fp|wI6Jkj-^e6Ys79VJ5!=qX`>JM&$Fd4MNAZmwc8YjT4UYI2P z2y(17iet!UuN%m-&k9=E&z2)9LC{GrI-@um2d(Qg8h83>Fb>a?7$N_#pP%mGqkclP zG^s1%!)Xx==+NN??2Ijrzw`NA3>``93KjLq*kxUpC{PA$2fzcWh%&}J5G#`&*efe| zux3WA>rf>OT%ly62)3>GNfDMwciALeQzYI09`$jtunb&F@!30mTPUY*GFX=`DB37u gft!Y+mr4v>UXoKUcWgd%ZdXW$`cRUym)vdn51;K(H~;_u delta 219 zcmaE3x!yvVA+b1@k%57MQI;)%v7WIWNH8QYFeEV5gE-9f_4N!Bl~pylIGLDP7#UcB z3Rzeg3m6$0S=kvGo0u9JnAkXg`Wzd2Sh(2_GIC8UvUg+4P0Y#3PiEj?Nh?ZCW#E*I zPt7e(F3Kz@NzIEdE=f$zjxS0rF3B%SWndA { const result = tf.tidy(() => { const tensor = img2tensor(image); - const result = model.predict(tensor) as tf.Tensor; + let result = model.predict(tensor) as tf.Tensor; + if (alpha) { + result = tf.greater(result, 0.5); + } return result; }); const resultImage = await tensor2img(result); @@ -16,32 +20,26 @@ export default async function upscale( } function img2tensor(image: Image): tf.Tensor { - let arr = new Float32Array(image.width * image.height * 3); - for (let i = 0; i < image.width * image.height; i++) { - arr[i * 3] = image.data[i * 4] / 255; - arr[i * 3 + 1] = image.data[i * 4 + 1] / 255; - arr[i * 3 + 2] = image.data[i * 4 + 2] / 255; - } - let tensor = tf.tensor4d(arr, [1, image.height, image.width, 3]); + let imgdata = new ImageData(image.width, image.height); + imgdata.data.set(image.data); + let tensor = tf.browser.fromPixels(imgdata).div(255).toFloat().expandDims(); return tensor; } async function tensor2img(tensor: tf.Tensor): Promise { let [_, height, width, __] = tensor.shape; - let arr = await tensor.data(); - tensor.dispose(); - let clipped = new Uint8Array( - arr.map((x) => { - x = Math.min(1, Math.max(0, x)); - return Math.floor(x * 255); - }) + + let clipped = tf.tidy(() => + tensor + .reshape([height, width, 3]) + .mul(255) + .cast("int32") + .clipByValue(0, 255) ); - let image = new Image(width, height); - for (let i = 0; i < width * height; i++) { - image.data[i * 4] = clipped[i * 3]; - image.data[i * 4 + 1] = clipped[i * 3 + 1]; - image.data[i * 4 + 2] = clipped[i * 3 + 2]; - image.data[i * 4 + 3] = 255; - } + tensor.dispose(); + let data = await tf.browser.toPixels(clipped as tf.Tensor3D); + clipped.dispose(); + let image = new Image(width, height, data as unknown as Uint8Array); + return image; } diff --git a/src/worker.js b/src/worker.js index 51b52bf..f53b74a 100644 --- a/src/worker.js +++ b/src/worker.js @@ -41,8 +41,7 @@ self.addEventListener("message", async (e) => { if (!model) { return; } - const input = new Img(data.width, data.height); - input.data = data.input; + const input = new Img(data.width, data.height, new Uint8Array(data.input)); let hasAlpha = data.hasAlpha; function sendprogress(progress) { if (hasAlpha) { @@ -57,126 +56,6 @@ self.addEventListener("message", async (e) => { info: `Processing ${progress.toFixed(2)}%`, }); } - async function enlargeImage( - model, - inputImg, - factor = 4, - tilesize = 32, - padsize = 8 - ) { - if (hasAlpha) { - tilesize = 16; - padsize = 4; - } - const width = inputImg.width; - const height = inputImg.height; - const output = new Img(width * factor, height * factor); - const total = Math.ceil(width / tilesize) * Math.ceil(height / tilesize); - let current = 0; - let useModel = new Array(total).fill(false); - if (hasAlpha) { - for (let i = 0; i < width; i += tilesize) { - for (let j = 0; j < height; j += tilesize) { - const x1 = Math.max(i, 0); - const y1 = Math.max(j, 0); - const x2 = Math.min(i + tilesize, width); - const y2 = Math.min(j + tilesize, height); - const tile = new Img(x2 - x1, y2 - y1); - tile.getImageCrop(0, 0, input, x1, y1, x2, y2); - for (let k = 4; k < tile.data.length; k += 4) { - if (tile.data[k + 3] !== tile.data[3]) { - useModel[current] = true; - break; - } - } - if (useModel[current]) { - current++; - continue; - } - let scaled = new Img(tile.width * factor, tile.height * factor); - for (let k = 0; k < scaled.data.length; k += 4) { - scaled.data[k] = tile.data[3]; - scaled.data[k + 1] = tile.data[3]; - scaled.data[k + 2] = tile.data[3]; - } - output.getImageCrop( - i * factor, - j * factor, - scaled, - 0, - 0, - scaled.width, - scaled.height - ); - current++; - } - } - current = 0; - for (let i = 0; i < width; i += tilesize) { - for (let j = 0; j < height; j += tilesize) { - if (!useModel[current]) { - current++; - let progress = (current / total) * 100; - sendprogress(progress); - continue; - } - const x1 = Math.max(i - padsize, 0); - const y1 = Math.max(j - padsize, 0); - const x2 = Math.min(i + tilesize + padsize, width); - const y2 = Math.min(j + tilesize + padsize, height); - const pad_left = i - x1; - const pad_top = j - y1; - const pad_right = Math.max(0, x2 - (i + tilesize)); - const pad_bottom = Math.max(0, y2 - (j + tilesize)); - const tile = new Img(x2 - x1, y2 - y1); - tile.getImageCrop(0, 0, input, x1, y1, x2, y2); - let scaled = await upscale(tile, model); - output.getImageCrop( - i * factor, - j * factor, - scaled, - pad_left * factor, - pad_top * factor, - scaled.width - pad_right * factor, - scaled.height - pad_bottom * factor - ); - // console.log(i, j, x2 - x1, y2 - y1); - current++; - let progress = (current / total) * 100; - sendprogress(progress); - } - } - } else { - for (let i = 0; i < width; i += tilesize) { - for (let j = 0; j < height; j += tilesize) { - const x1 = Math.max(i - padsize, 0); - const y1 = Math.max(j - padsize, 0); - const x2 = Math.min(i + tilesize + padsize, width); - const y2 = Math.min(j + tilesize + padsize, height); - const pad_left = i - x1; - const pad_top = j - y1; - const pad_right = Math.max(0, x2 - (i + tilesize)); - const pad_bottom = Math.max(0, y2 - (j + tilesize)); - const tile = new Img(x2 - x1, y2 - y1); - tile.getImageCrop(0, 0, input, x1, y1, x2, y2); - let scaled = await upscale(tile, model); - output.getImageCrop( - i * factor, - j * factor, - scaled, - pad_left * factor, - pad_top * factor, - scaled.width - pad_right * factor, - scaled.height - pad_bottom * factor - ); - current++; - let progress = (current / total) * 100; - sendprogress(progress); - } - } - } - return output; - } async function enlargeImageWithFixedInput( model, inputImg, @@ -273,7 +152,6 @@ self.addEventListener("message", async (e) => { scaled.width - pad_right[i] * factor, scaled.height - pad_bottom[j] * factor ); - // console.log(i, j, x2 - x1, y2 - y1); current++; } } @@ -292,7 +170,7 @@ self.addEventListener("message", async (e) => { const y2 = locs_y[j] + input_size; const tile = new Img(input_size, input_size); tile.getImageCrop(0, 0, inputImg, x1, y1, x2, y2); - let scaled = await upscale(tile, model); + let scaled = await upscale(tile, model, true); output.getImageCrop( (x1 + pad_left[i]) * factor, (y1 + pad_top[j]) * factor, @@ -302,7 +180,6 @@ self.addEventListener("message", async (e) => { scaled.width - pad_right[i] * factor, scaled.height - pad_bottom[j] * factor ); - // console.log(i, j, x2 - x1, y2 - y1); current++; let progress = (current / total) * 100; sendprogress(progress); @@ -327,7 +204,6 @@ self.addEventListener("message", async (e) => { scaled.width - pad_right[i] * factor, scaled.height - pad_bottom[j] * factor ); - // console.log(i, j, x2 - x1, y2 - y1); current++; let progress = (current / total) * 100; sendprogress(progress); @@ -340,21 +216,25 @@ self.addEventListener("message", async (e) => { const start = Date.now(); let output; try { - // if (data?.fixed) { - // output = await enlargeImageWithFixedInput(model, input, factor); - // } else { - // output = await enlargeImage(model, input, factor); - // } output = await enlargeImageWithFixedInput(model, input, factor); } catch (e) { postMessage({ alertmsg: e.toString() }); } const end = Date.now(); console.log("Time:", end - start); + await new Promise((resolve) => setTimeout(resolve, 10)); postMessage({ progress: 100, - done: true, - output: output, info: `Processing image...`, }); + postMessage( + { + progress: 100, + done: true, + output: output.data.buffer, + info: `Processing image...`, + }, + [output.data.buffer] + ); + console.log("output"); });