UNPKG

imgtotext

Version:

ImgToText is a concise library that converts any image/video into customizable text!

3 lines (2 loc) 11.7 kB
// ImgToText ESM - v1.2.3 import{CDEUtils,FPSCounter,CanvasUtils,Color,_HasColor,GridAssets,TypingDevice,Mouse,Render,TextStyles,RenderStyles,Canvas,Anim,_BaseObj,AudioDisplay,ImageDisplay,TextDisplay,_DynamicColor,Pattern,_Obj,Shape,Gradient,FilledShape,Grid,Dot}from"cdejs";export class ImageToTextConverter{static DEFAULT_CHARACTER_SETS={VERY_LOW:[" ",".",":"],VERY_LOW_REVERSED:[":","."," "],LOW:[" ",".",":","-","~","=","+","o","O","X","H","M"],LOW_LARGE:[" "," ."," :"," -"," ~"," ="," +"," o"," O"," X"," H"," M"],LOW_REVERSED:["M","H","X","O","o","+","=","~","-",":","."," "],MIDDLE:[" ",".",":","-","~","=","+","o","O","X","H","M","B","8","$","W","%","@","#"],MIDDLE_REVERSED:["#","@","%","W","$","8","B","M","H","X","O","o","+","=","~","-",":","."," "],MIDDLE_PATCHY:[" ",".",":","-","~","=","+","o","O","X","H","M","B","8","$","W","%","@","#","�","�","�","�"],MIDDLE_PATCHY_REVERSED:["�","�","�","�","#","@","%","W","$","8","B","M","H","X","O","o","+","=","~","-",":","."," "],HIGH:[" ",".",",","-","~","=",":",";","+","c","o","O","X","H","M","B","$","?","#","@","�","�","�","�"],HIGH_REVERSED:["�","�","�","�","@","#","?","$","B","M","H","X","O","o","c","+",";",":","=","~","-",",","."," "],HIGH_CENTRAL_SHADING:[,"�","�","�","@","#","?","$","B","M","H","X","O","o","c","+",";",":","=","~","-",",","."," ",".",",","-","~","=",":",";","+","c","o","O","X","H","M","B","$","?","#","@","�","�","�","�"],HIGH_OUTER_SHADING:[" ",".",",","-","~","=",":",";","+","c","o","O","X","H","M","B","$","?","#","@","�","�","�","�","�","�","�","@","#","?","$","B","M","H","X","O","o","c","+",";",":","=","~","-",",","."," "],ASCII:[" ","�","�","�","�"],ASCII_REVERSED:["�","�","�","�"," "],BRIGHT_ASCII:["�","�","�","�"],BRIGHT_ASCII_REVERSED:["�","�","�","�"],LINES:[" ","-","?","-","�","�","?","+","+","?","�"],LINES_REVERSED:["�","?","+","+","?","�","�","-","?","-"," "],LINE_SEMI_CENTRAL_SHADING:[" ","-","?","�","?","+","?","-","�","+","�"],BINARY:[" ","1","0"],BINARY_REVERSED:["0","1"," "],BINARY_EXPANDED:[" ",".","�","�","1","0"],BINARY_EXPANDED_REVERSED:["0","1","�","�","."," "],DOTS:[" ",".","�","'",":"],SIMPLE:[" ",".",":","-","~","=","?","#"],SYMBOLS:[" ",".","*","+","8","%","#","&","�","?","@","�"],WAVY_BOXES:[" ",".",":","=","=","?","?","?","�"]};static DEFAULT_CHARACTER_SET=ImageToTextConverter.DEFAULT_CHARACTER_SETS.LOW;static DEFAULT_CVS_SIZE=[...ImageDisplay.RESOLUTIONS.MAX];static DEFAULT_MEDIA_SIZE=["92%","45%"];static DEFAULT_TEXT_SCALE=[2,1.25];static DEFAULT_MEDIA_ERROR_CALLBACK=(errorCode,media)=>console.warn("Error while loading media:",ImageDisplay.getErrorFromCode(errorCode),"("+media+")");static OUTPUT_FORMATS={NONE:0,MARKDOWN_COLORLESS:1,HTML:2,UNICODE_MONOSPACE:3,NON_BREAKING_SPACES:4};static DEFAULT_OUTPUT_FORMAT=ImageToTextConverter.OUTPUT_FORMATS.NONE;static DEFAULT_COLOR_OPTIMISATION_LEVEL=12;static UNICODE_MONOSPACE_CONVERTIONS={a:"??",b:"??",c:"??",d:"??",e:"??",f:"??",g:"??",h:"??",i:"??",j:"??",k:"??",l:"??",m:"??",n:"??",o:"??",p:"??",q:"??",r:"??",s:"??",t:"??",u:"??",v:"??",w:"??",x:"??",y:"??",z:"??",A:"??",B:"??",C:"??",D:"??",E:"??",F:"??",G:"??",H:"??",I:"??",J:"??",K:"??",L:"??",M:"??",N:"??",O:"??",P:"??",Q:"??",R:"??",S:"??",T:"??",U:"??",V:"??",W:"??",X:"??",Y:"??",Z:"??",0:"??",1:"??",2:"??",3:"??",4:"??",5:"??",6:"??",7:"??",8:"??",9:"??"};#cachedRangeDivision=null;constructor(resultCB,sourceMedia,pxGroupingSize=5,charSet,useColors=!1,maxMediaInputSize=ImageToTextConverter.DEFAULT_CVS_SIZE,maxRefreshRate=30){this._CVS=this.#createCVS(maxMediaInputSize,maxRefreshRate),this._resultCB=resultCB,this._pxGroupingSize=pxGroupingSize||5,this._charSet=charSet??ImageToTextConverter.DEFAULT_CHARACTER_SET,this._useColors=useColors||!1,this._colorOptimizationLevel=ImageToTextConverter.DEFAULT_COLOR_OPTIMISATION_LEVEL,this.#updateCachedRangeDivision(),this._media=null,"string"!=typeof sourceMedia||sourceMedia.match(/\..{1,4}$/gi)?sourceMedia&&this.loadMedia(sourceMedia):this.createBigText(sourceMedia)}#createCVS(sizeOrCanvas,maxRefreshRate){let canvas=null;canvas=Array.isArray(sizeOrCanvas)?new OffscreenCanvas(sizeOrCanvas[0],sizeOrCanvas[1]):sizeOrCanvas instanceof Canvas?sizeOrCanvas.cvs:sizeOrCanvas instanceof HTMLCanvasElement||sizeOrCanvas instanceof OffscreenCanvas?sizeOrCanvas:new OffscreenCanvas(...ImageToTextConverter.DEFAULT_CVS_SIZE);return new Canvas(canvas,()=>{this._media?.initialized&&this._resultCB(this.#getText(this.#mapPixels(),this._useColors))},maxRefreshRate,null,null,null,!0)}#updateCachedRangeDivision(){this.#cachedRangeDivision=1/(255/this._charSet.length)}#mapPixels(pxGroupingSize=this._pxGroupingSize,useColors=this._useColors){let data,x,atI,i,adjust,CVS=this._CVS,mediaSize=this._media.trueSize,width=mediaSize[0]>CVS.width?CVS.width:mediaSize[0]|0,height=mediaSize[1]>CVS.height?CVS.height:mediaSize[1]|0,pxGroupingCount=pxGroupingSize*pxGroupingSize*4,bigPxCountX=width/pxGroupingSize,bigPxCountY=height/pxGroupingSize,round=Math.round,pxGS4=bigPxCountX*pxGroupingSize*4,GCCX=bigPxCountY*pxGroupingCount*bigPxCountX,pxLength=4*width*pxGroupingSize,bigPixels=new Uint16Array(Math.ceil(bigPxCountX)*Math.ceil(bigPxCountY)*(useColors?5:2)),bigPixelsI=0;try{data=CVS.ctx.getImageData(0,0,width,height).data}catch(e){const src=this._media.source.src;console.warn("Media unavailable due to cross-origin, width/height or loading issues."+(src?" \n("+src+")":"")+"\n\n'"+e.message.split(":")[1].trim()+"'"),data=[]}for(let y=0;y<height;y+=pxGroupingSize){const offsetY=round(y/height*GCCX);for(x=0;x<width;x+=pxGroupingSize){let offsetX=round(x/width*pxGS4),pxTotal=0,total=0,nullCount=0,totalR=0,totalG=0,totalB=0;for(i=0,adjust=0;i<pxGroupingCount;i+=4){const i2=(i>>2)%pxGroupingSize;if(i&&!i2&&(adjust=pxLength*(i/pxGroupingCount)-i),atI=offsetX+offsetY+i+adjust,!data[atI+3])continue;const r=data[atI],g=data[atI+1],b=data[atI+2],pxAvg=null==r||x+i2>=width?null:(r+g+b)/3;null==pxAvg?nullCount++:useColors&&(totalR+=r,totalB+=b,totalG+=g),total+=pxAvg,pxTotal++}const adjustedCount=pxTotal-nullCount||0;bigPixels[bigPixelsI++]=y,bigPixels[bigPixelsI++]=total/adjustedCount,useColors&&(bigPixels[bigPixelsI++]=totalR/adjustedCount,bigPixels[bigPixelsI++]=totalG/adjustedCount,bigPixels[bigPixelsI++]=totalB/adjustedCount)}}return bigPixels}#getText(pixelMappingResults,useColors){let streaks,rangeDivision=this.#cachedRangeDivision,tolerance=this._colorOptimizationLevel,isColorOptimized=useColors&&tolerance,chars=this._charSet,p_ll=pixelMappingResults.length,step=useColors?5:2,textResults="",lastY=0,s_ll=0;for(let i=0;i<p_ll;i+=step){const y=pixelMappingResults[i],char=chars[Math.min((pixelMappingResults[i+1]||0)*rangeDivision|0,chars.length-1)],r=pixelMappingResults[i+2],g=pixelMappingResults[i+3],b=pixelMappingResults[i+4];if(isColorOptimized)if(i){const streak=streaks[s_ll],sr=streak[1],sg=streak[2],sb=streak[3];y!=lastY&&(streak[0]+="<br>"),r<=sr+tolerance&&r>=sr-tolerance&&g<=sg+tolerance&&g>=sg-tolerance&&b<=sb+tolerance&&b>=sb-tolerance?streak[0]+=char:s_ll=streaks.push([char,r,g,b])-1}else streaks=[[char,r,g,b]];else y!=lastY&&(textResults+=useColors?"<br>":"\n"),textResults+=useColors?r+g+b?`<c style="color:rgb(${r},${g},${b});">${char}</c>`:`<c>${char}</c>`:char;lastY=y}if(isColorOptimized){s_ll++;for(let i=0;i<s_ll;i++){const streak=streaks[i],sr=streak[1],sg=streak[2],sb=streak[3];textResults+=sr+sg+sb?`<c style="color:rgb(${sr},${sg},${sb})">${streak[0]}</c>`:`<c>${streak[0]}</c>`}}return textResults}loadMedia(sourceMedia,size=null,croppingPositions=null,errorCB=ImageToTextConverter.DEFAULT_MEDIA_ERROR_CALLBACK,readyCB=null){this.clear(),this._media=new ImageDisplay(sourceMedia,[0,0],size||[...ImageToTextConverter.DEFAULT_MEDIA_SIZE],errorCB,img=>{img.isDynamic?this._CVS.start():(this._CVS.stop(),this.generate()),CDEUtils.isFunction(readyCB)&&readyCB(this)},null,null,!0),this._media.sourceCroppingPositions=croppingPositions,this._CVS.add(this._media)}createBigText(text,font=null,scale=[...ImageToTextConverter.DEFAULT_TEXT_SCALE],color=null,isFilled=!0,letterSpacing=8,wordSpacing=-20,fontVariantCaps=null,direction=null,fontStretch=null,textRendering=TextStyles.RENDERINGS.LEGIBLE){font??="normal 54px monospace",scale??=[...ImageToTextConverter.DEFAULT_TEXT_SCALE],this._CVS.stop(),this.clear(),this._media=new TextDisplay(text,[0,0],color,render=>render.textProfile1.update(font,letterSpacing,wordSpacing,fontVariantCaps,direction,fontStretch,null,TextStyles.ALIGNMENTS.START,TextStyles.BASELINES.TOP,textRendering),isFilled?Render.DRAW_METHODS.FILL:Render.DRAW_METHODS.STROKE,null,null,null,null,!0),this._CVS.add(this._media),this._media.scale=scale,this.generate()}createHTMLFileInput(id=null,size=null,onInputCB=null){const usesOldInput=id instanceof HTMLInputElement,input=usesOldInput?id:document.createElement("input");return input.type="file",id&&!usesOldInput&&(input.id=id),input.accept=ImageDisplay.getSupportedHTMLAcceptValue(),input.oninput=()=>{const file=input.files[0];if(CDEUtils.isFunction(onInputCB)&&onInputCB(file,this),file)if(ImageDisplay.isVideoFormatSupported(file))this.loadMedia(ImageDisplay.loadVideo(file));else if(ImageDisplay.isImageFormatSupported(file)){const fileReader=new FileReader;fileReader.onload=e=>this.loadMedia(ImageDisplay.loadImage(e.target.result,size,!0)),fileReader.readAsDataURL(file)}},input}static formatText(text,outputFormatingMethod=ImageToTextConverter.DEFAULT_OUTPUT_FORMAT){const outputFormats=ImageToTextConverter.OUTPUT_FORMATS;if(outputFormatingMethod==outputFormats.MARKDOWN_COLORLESS)return"```\n"+text+"\n```";if(outputFormatingMethod==outputFormats.HTML)return`<div style="white-space: pre !important;font-family: monospace !important;font-size: 16px;letter-spacing: 0px;line-height: 18px;">${text}</div>`;if(outputFormatingMethod==outputFormats.NON_BREAKING_SPACES)return text.replaceAll(" ","�");if(outputFormatingMethod==outputFormats.UNICODE_MONOSPACE){const unicodeMonospaceConversions=ImageToTextConverter.UNICODE_MONOSPACE_CONVERTIONS;return text.replaceAll(/./g,char=>unicodeMonospaceConversions[char]||char)}return text}generate(){this._CVS.drawSingleFrame()}clear(){this._media&&this._media.remove(),this._CVS.clear()}updatePxGroupingSize(pxGroupingSize=5){return this._pxGroupingSize=pxGroupingSize,this.generate(),this._pxGroupingSize}updateCharSet(charSet=ImageToTextConverter.DEFAULT_CHARACTER_SETS[CDEUtils.random(0,ImageToTextConverter.DEFAULT_CHARACTER_SETS.length)]){return this.charSet=charSet,this.generate(),this.charSet}updateUseColors(useColors=!1){return this._useColors=useColors,this.generate(),this._useColors}updateColorOptimizationLevel(colorOptimizationLevel=ImageToTextConverter.DEFAULT_COLOR_OPTIMISATION_LEVEL){return this._colorOptimizationLevel=colorOptimizationLevel,this.generate(),this._colorOptimizationLevel}get CVS(){return this._CVS}get cvs(){return this._CVS.cvs}get size(){return this._CVS.size}get charSet(){return this._charSet}get media(){return this._media}get useColors(){return this._useColors}get colorOptimizationLevel(){return this._colorOptimizationLevel}get pxGroupingSize(){return this._pxGroupingSize}get resultCB(){return this._resultCB}get maxRefreshRate(){return this._CVS.fpsLimit}set size(size){this._CVS.width=size[0],this._CVS.height=size[1]}set charSet(charSet){this._charSet="string"==typeof charSet?[...charSet]:charSet,this.#updateCachedRangeDivision()}set colorOptimizationLevel(level){this._colorOptimizationLevel=level}set useColors(useColors){this._useColors=useColors}set pxGroupingSize(pxGroupingSize){this._pxGroupingSize=pxGroupingSize}set maxRefreshRate(maxRefreshRate){this._CVS.fpsLimit=maxRefreshRate}set resultCB(resultCB){this._resultCB=resultCB}}