UNPKG

@arcgis/core

Version:

ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API

3 lines (2 loc) • 11.2 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.19/LICENSE.txt */ import{assertIsSome as t}from"../../../core/maybe.js";import{getPixelValueRange as n}from"../formats/pixelRangeUtils.js";import e from"../../support/PixelBlock.js";import{stretchTypeFunctionEnum as s}from"../../../renderers/support/stretchRendererUtils.js";const i=1,o=[.299,.587,.114];function a(t){if(null==t)return!1;const{max:n,min:e,size:s}=t;return(n-e)/s===1&&e>=-.5&&.5===Math.abs(e%1)&&n<=255.5}function r(t,n=256){const e=255;n=Math.min(n,e+1);const{size:s,counts:i}=t,o=new Float64Array(s);o[0]=i[0]/n;for(let a=1;a<s;a++)o[a]=o[a-1]+i[a]/n;const r=new Uint8Array(n),l=n/o[o.length-1];if(s<=n){const i=a(t)?Math.max(0,Math.round(t.min+.5)):0;for(let t=i;t<n;t++)r[t]=t>=s+i?e:Math.min(e,Math.trunc(o[t-i]*l))}else{const t=o.map(t=>Math.min(255,Math.trunc(t*l)));for(let e=0;e<n;e++)r[e]=t[Math.round(s/n)];r[n-1]=255}return r}function l(t){const{minCutOff:n,maxCutOff:e,gamma:s,pixelType:i,rounding:o}=t;if(!["u8","u16","s8","s16"].includes(i))return null;const a=n.length,r="s8"===i?-127:"s16"===i?-32767:0,l=["u16","s16"].includes(i)?65536:256,c=[],m=[],{minOutput:f=0,maxOutput:h=255}=t,d=h-f;for(let u=0;u<a;u++)m[u]=e[u]-n[u],c[u]=0===m[u]?0:d/m[u];const g=[];if(s&&s.length>=a){const t=w(a,s);for(let i=0;i<a;i++){const a=[];for(let u=0;u<l;u++){if(0===m[i]){a[u]=f;continue}const l=u+r,c=(l-n[i])/m[i];let g=1;if(s[i]>1&&(g-=(1/d)**(c*t[i])),l<e[i]&&l>n[i]){const t=g*d*c**(1/s[i])+f;a[u]="floor"===o?Math.floor(t):"round"===o?Math.round(t):t}else l>=e[i]?a[u]=h:a[u]=f}g[i]=a}}else for(let u=0;u<a;u++){const t=[];for(let s=0;s<l;s++){const i=s+r;if(i<=n[u])t[s]=f;else if(i>=e[u])t[s]=h;else{const e=(i-n[u])*c[u]+f;t[s]="floor"===o?Math.floor(e):"round"===o?Math.round(e):e}}g[u]=t}if(null!=t.contrastOffset){const n=u(t.contrastOffset,t.brightnessOffset);for(let t=0;t<a;t++){const e=g[t];for(let t=0;t<l;t++)e[t]=n[e[t]]}}return{lut:g,offset:r}}function u(t,n){const e=Math.min(Math.max(t,-100),100),s=Math.min(Math.max(n??0,-100),100),i=255,o=128,a=new Uint8Array(256);for(let r=0;r<256;r++){let t=0;e>0&&e<100?t=(200*r-100*i+2*i*s)/(2*(100-e))+o:e<=0&&e>-100?t=(200*r-100*i+2*i*s)*(100+e)/2e4+o:100===e?(t=200*r-100*i+(i+1)*(100-e)+2*i*s,t=t>0?i:0):-100===e&&(t=o),a[r]=t>i?i:t<0?0:t}return a}function c(t,n,e){const s=[];for(let i=0;i<n.length;i++){let a=0,r=0,l=0;"min"in n[i]?({min:a,max:r,avg:l}=n[i]):[a,r,l]=n[i];let u=l??0;"u8"!==t&&(u=255*(u-a)/(r-a)),e&&(u*=o[i]),s.push(m(u))}return s}function m(t){if(t<=0||t>=255)return i;const n=255,e=t+(150===t?0:t<150?45*Math.cos(.01047*t):17*Math.sin(.021*t)),s=Math.log(t/n),o=Math.log(e/n);if(0===o)return i;const a=s/o;return isNaN(a)?i:Math.min(9.9,Math.max(.01,a))}function f(t,n,e){let{min:s,max:i,count:o,avg:a,mode:r,median:l}=d(t,n,!0);if(0===o)return{statistics:{min:0,max:0,avg:0,stddev:0,count:o,mode:0,median:0},histogram:null};const u={min:s,max:i,avg:a,mode:r,count:o,median:l},c=M(t,n,a,!e.includeSkewnessKurtosis);c&&(u.stddev=c.stddev,e.includeSkewnessKurtosis&&(u.skewness=c.skewness,u.kurtosis=c.kurtosis));const m=e?.histogramSize??256,f=!!e?.pixelType&&["u8","s8","u4","u2","u1"].includes(e.pixelType),g=!e?.skipHistograms;if(null==l){const e=h({band:t,mask:n,min:s,max:i,size:1e4,use8BitHistogram:f});if(l=x(t,n,e,50),g&&f&&256===m)return{statistics:u,histogram:e}}if(!g)return{statistics:u,histogram:null};return{statistics:u,histogram:h({band:t,mask:n,min:s,max:i,size:m,use8BitHistogram:f&&256===m})}}function h(t){const{band:n,mask:e,min:s,max:i,use8BitHistogram:o}=t;if(o){const t=i-s+1,o=new Uint32Array(t);for(let i=0;i<n.length;i++)e&&!e[i]||o[n[i]-s]++;return{min:s-.5,max:i+.5,size:t,counts:o}}const a=t.size??256,r=new Uint32Array(a),l=(i-s)/a;if(0===l){const t=e?e.reduce((t,n)=>t+(n?1:0),0):n.length;return{min:s,max:i,size:1,counts:new Uint32Array(1).fill(t)}}const u=new Uint32Array(a+1),c=1/l;for(let m=0;m<n.length;m++)e&&!e[m]||u[Math.floor((n[m]-s)*c)]++;for(let m=0;m<a-1;m++)r[m]=u[m];return r[a-1]=u[a-1]+u[a],{min:s,max:i,size:a,counts:r}}function d(t,n,e=!1,s=!1){let i=1/0,o=-1/0,a=0,r=0,l=0,u=0;const c=t.length,m=new Map;for(let p=0;p<c;p++){const e=t[p];if(!n||n[p]){const t=(m.get(e)??0)+1;m.set(e,t),t>u&&(u=t,l=e),i=e<i?e:i,o=e>o?e:o,a+=e,r++}}const f=r>0?a/r:0;let h=i===o?i:void 0,d=i===o?i:void 0,g=i===o?i:void 0;if(e&&r>0&&r<2**24){let e=t.slice();if(n){let s=0;for(let i=0;i<c;i++){const o=t[i];n[i]&&(e[s++]=o)}}e=e.slice(0,r),e.sort((t,n)=>t-n);const i=r>>>1;h=r%2?e[Math.floor(i)]:(e[i-1]+e[i])/2,s&&(d=e[Math.floor(.25*r)],g=e[Math.floor(.75*r)])}return{min:i,max:o,count:r,avg:f,mode:l,median:h,quartile1:d,quartile3:g}}function g(t,n,e){let{min:s,max:i,avg:o,median:a,quartile1:r,quartile3:l}=d(t,n,!0,!0);if(null==a||null==r||null==l){const o=h({band:t,mask:n,min:s,max:i,size:1e4,use8BitHistogram:["u8","s8","u4","u2","u1"].includes(e)});a=x(t,n,o,50),r=x(t,n,o,25),l=x(t,n,o,75)}return{min:s,max:i,avg:o,median:a,quartile1:r,quartile3:l}}function p(t){const{pixels:n,mask:e,bandMasks:s,pixelType:i}=t;return n.map((t,n)=>g(t,s?.[n]??e,i))}function x(t,n,e,s=50){const{counts:i,min:o,max:a,size:r}=e,l=i.reduce((t,n)=>t+n)*(s/=100);let u=0,c=0;for(;c<i.length&&(u+=i[c],!(u>=l+1));c++);const m=(a-o)/r,f=o+m*c,h=o+m*(c+1);if(1===m&&.5===Math.abs(o%1))return f+.5;const d=t.slice(0,i[c]).fill(0);for(let p=0,x=0;p<t.length;p++)if(!n||n[p]){const n=t[p];n>=f&&n<h&&(d[x++]=n)}d.sort((t,n)=>t-n);const g=u-Math.floor(l)-1;return d[Math.max(0,d.length-1-g)]}function M(t,n,e,s=!1,i=!1){let o=0,a=0,r=0,l=0;for(let c=0;c<t.length;c++)if(!n||n[c]){const n=t[c]-e,i=n*n;if(o+=i,!s){const t=i*n;a+=t;r+=t*n}l++}if(l<2)return null;o/=l-1;const u=Math.sqrt(o);if(s)return{stddev:u};r/=l;return{stddev:u,skewness:i?a*(l/((l-1)*(l-2)))/o**1.5:a/l/o**1.5,kurtosis:r/(o*o)}}function k(t,n){const{pixels:e,mask:s,pixelType:i,bandMasks:o}=t,a=e.map((t,e)=>f(t,o?.[e]??s,{pixelType:i,...n}));return{statistics:a.map(({statistics:t})=>t),histograms:a.map(({histogram:t})=>t)}}function y(t){if(!t?.pixels?.length)return null;const{pixels:n,mask:e,bandMasks:s,pixelType:i}=t,o=t.width*t.height,a=n.length,r=[],l=[],u=256;let c,m;for(let f=0;f<a;f++){let a=new Uint32Array(u);const h=n[f],d=s?.[f]??e;if("u8"===i){if(c=255,m=0,d){for(let t=0;t<o;t++)if(d[t]){const n=h[t];c=n<c?n:c,m=n>m?n:m,a[n]++}}else for(let t=0;t<o;t++){const n=h[t];c=n<c?n:c,m=n>m?n:m,a[n]++}a=a.slice(c,m+1)}else{let n=!1;t.statistics||(t.updateStatistics(),n=!0);const e=t.statistics;c=e[f].minValue,m=e[f].maxValue;const s=(m-c)/u;if(0===s){!e||t.validPixelCount||n||t.updateStatistics();const s=(t.validPixelCount||t.width*t.height)/u;for(let t=0;t<u;t++)a[t]=Math.round(s*(t+1))-Math.round(s*t)}else{const t=new Uint32Array(u+1);for(let n=0;n<o;n++)d&&!d[n]||t[Math.floor((h[n]-c)/s)]++;for(let n=0;n<u-1;n++)a[n]=t[n];a[u-1]=t[u-1]+t[u]}}const g="u8"===i?c-.5:c,p="u8"===i?m+.5:m;r.push({min:g,max:p,size:a.length,counts:a});let x=0,M=0,k=0;for(let t=0;t<a.length;t++)x+=a[t],M+=t*a[t];const y=M/x;for(let t=0;t<a.length;t++)k+=a[t]*(t-y)**2;const v=Math.sqrt(k/(x-1)),b=(p-g)/a.length,O=(y+("u8"===i?0:.5))*b+c,w=v*b;l.push({min:c,max:m,avg:O,stddev:w})}return{statistics:l,histograms:r}}function v(t){const n=[];for(let e=0;e<t.length;e++){const{min:s,max:i,size:o,counts:a}=t[e];let r=0,l=0;for(let t=0;t<o;t++)r+=a[t],l+=t*a[t];const u=l/r;let c=0;for(let t=0;t<o;t++)c+=a[t]*(t-u)**2;const m=(i-s)/o,f=(u+.5)*m+s,h=Math.sqrt(c/(r-1))*m;n.push({min:s,max:i,avg:f,stddev:h})}return n}function b(e,i){const{pixelBlock:o,bandIds:a,returnHistogramLut:l,rasterInfo:u}=i;let c=null,m=null,f=e.stretchType;if("number"==typeof f&&(f=s[f]),e.dra)if("minMax"===f&&o?.statistics)c=o.statistics.map(t=>[t.minValue,t.maxValue,0,0]);else{const t=y(o);c=null!=t?t.statistics:null,m=null!=t?t.histograms:null}else c=e.statistics?.length?e.statistics:u.statistics,m="histograms"in e?e.histograms:void 0,m||(m=u.histograms);"percentClip"!==f&&"histogramEqualization"!==f||m?.length||(f="minMax");const h=c?.length||m?.length||u.bandCount,d=[],g=[];c&&!Array.isArray(c[0])&&(c=c.map(t=>[t.min,t.max,t.avg,t.stddev]));const[p,x]=n(u.pixelType);if(!c?.length){c=[];for(let t=0;t<h;t++)c.push([p,x,1,1]);"standardDeviation"===f&&(f="minMax")}switch(f){case"none":for(let t=0;t<h;t++)d[t]=p,g[t]=x;break;case"minMax":for(let t=0;t<h;t++){const n=c[t];d[t]=n[0],g[t]=n[1]}break;case"standardDeviation":{const{numberOfStandardDeviations:t=2}=e;for(let n=0;n<h;n++){const e=c[n];d[n]=e[2]-t*e[3],g[n]=e[2]+t*e[3],d[n]<e[0]&&(d[n]=e[0]),g[n]>e[1]&&(g[n]=e[1])}}break;case"histogramEqualization":t(m);for(let t=0;t<h;t++)d[t]=m[t].min,g[t]=m[t].max;break;case"percentClip":t(m);for(let t=0;t<m.length;t++){const n=m[t],s=new Uint32Array(n.size),i=[...n.counts];i.length>=20&&(i[0]=i[1]=i[2]=i[i.length-1]=i[i.length-2]=0);let o=0;const a=(n.max-n.min)/n.size,r=-.5===n.min&&1===a?.5:0;for(let t=0;t<n.size;t++)o+=i[t],s[t]=o;let l=(e.minPercent||0)*o/100;d[t]=n.min+r;for(let e=0;e<n.size;e++)if(s[e]>l){d[t]=n.min+a*(e+r);break}l=(1-(e.maxPercent||0)/100)*o,g[t]=n.max+r;for(let e=n.size-2;e>=0;e--)if(s[e]<l){g[t]=n.min+a*(e+2-r);break}if(g[t]<d[t]){const n=d[t];d[t]=g[t],g[t]=n}}break;default:for(let t=0;t<h;t++){const n=c[t];d[t]=n[0],g[t]=n[1]}}let M,k,v;"histogramEqualization"===f?(t(m),k=m[0].size||256,M=0,l&&(v=m.map(t=>r(t)))):(k=e.max||255,M=e.min||0);return O({minCutOff:d,maxCutOff:g,maxOutput:k,minOutput:M,histogramLut:v},a)}function O(t,n){if(null==n||0===n.length)return t;const e=Math.max.apply(null,n),{minCutOff:s,maxCutOff:i,minOutput:o,maxOutput:a,histogramLut:r}=t;return s.length===n.length||s.length<=e?t:{minCutOff:n.map(t=>s[t]),maxCutOff:n.map(t=>i[t]),histogramLut:r?n.map(t=>r[t]):null,minOutput:o,maxOutput:a}}function w(t,n){const e=new Float32Array(t);for(let s=0;s<t;s++)n[s]>1?n[s]>2?e[s]=6.5+(n[s]-2)**2.5:e[s]=6.5+100*(2-n[s])**4:e[s]=1;return e}function z(t,n){if(!t?.pixels?.length)return t;const{mask:s,bandMasks:i,width:o,height:a,pixels:r}=t,{minCutOff:l,maxCutOff:u,minOutput:c,maxOutput:m,gamma:f}=n,h=o*a,d=n.outputPixelType||"u8",g=t.pixels.map(()=>e.createEmptyBand(d,h)),p=g.length,x=m-c,M=[],k=[];for(let e=0;e<p;e++)k[e]=u[e]-l[e],M[e]=0===k[e]?0:x/k[e];const y=d.startsWith("u")||d.startsWith("s"),v=f&&f.length>=p,b=!!n.isRenderer;if(v){const t=w(p,f);for(let n=0;n<p;n++){const e=i?.[n]??s;for(let s=0;s<h;s++)if(null==e||e[s]){if(0===k[n]){g[n][s]=c;continue}const e=r[n][s],i=(e-l[n])/k[n];let o=1;if(f[n]>1&&(o-=(1/x)**(i*t[n])),e<u[n]&&e>l[n]){const t=o*x*i**(1/f[n])+c;g[n][s]=b?Math.floor(t):y?Math.round(t):t}else e>=u[n]?g[n][s]=m:g[n][s]=c}}}else for(let e=0;e<p;e++){const t=i?.[e]??s;for(let n=0;n<h;n++)if(null==t||t[n]){const t=r[e][n];if(t<u[e]&&t>l[e]){const s=(t-l[e])*M[e]+c;g[e][n]=b?Math.floor(s):y?Math.round(s):s}else t>=u[e]?g[e][n]=m:g[e][n]=c}}const O=new e({width:o,height:a,mask:s,bandMasks:i,pixels:g,pixelType:d});return O.updateStatistics(),O}export{p as computeBoxStatistics,w as computeGammaCorrection,c as computeGammaValues,k as computeStatisticsHistograms,u as createContrastBrightnessLUT,r as createHistogramEqualizationLUT,l as createStretchLUT,v as estimateStatisticsFromHistograms,y as estimateStatisticsHistograms,b as getStretchCutoff,a as isStandardU8Histogram,z as stretch};