@arcgis/core
Version:
ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API
6 lines (5 loc) • 9 kB
JavaScript
/*
All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://js.arcgis.com/4.32/esri/copyright.txt for details.
*/
import{assertIsSome as t}from"../../../core/maybe.js";import n from"../PixelBlock.js";import{getPixelValueRange as e}from"../rasterFormats/pixelRangeUtils.js";import{stretchTypeFunctionEnum as s}from"../../../renderers/support/stretchRendererUtils.js";const i=1,o=[.299,.587,.114];function r(t,n=256){n=Math.min(n,256);const{size:e,counts:s}=t,i=new Uint8Array(e),o=s.reduce(((t,e)=>t+e/n),0);let r=0,a=0,l=0,u=o;for(let f=0;f<e;f++)if(l+=s[f],!(f<e-1&&l+s[f+1]<u)){for(;r<n-1&&u<l;)r++,u+=o;for(let t=a;t<=f;t++)i[t]=r;a=f+1}for(let f=a;f<e;f++)i[f]=n-1;return i}function a(t){const{minCutOff:n,maxCutOff:e,gamma:s,pixelType:i,rounding:o}=t;if(!["u8","u16","s8","s16"].includes(i))return null;const r=n.length,a="s8"===i?-127:"s16"===i?-32767:0,u=["u16","s16"].includes(i)?65536:256,f=[],c=[],{minOutput:m=0,maxOutput:h=255}=t,p=h-m;for(let l=0;l<r;l++)c[l]=e[l]-n[l],f[l]=0===c[l]?0:p/c[l];const g=[];if(s&&s.length>=r){const t=x(r,s);for(let i=0;i<r;i++){const r=[];for(let l=0;l<u;l++){if(0===c[i]){r[l]=m;continue}const u=l+a,f=(u-n[i])/c[i];let g=1;if(s[i]>1&&(g-=(1/p)**(f*t[i])),u<e[i]&&u>n[i]){const t=g*p*f**(1/s[i])+m;r[l]="floor"===o?Math.floor(t):"round"===o?Math.round(t):t}else u>=e[i]?r[l]=h:r[l]=m}g[i]=r}}else for(let l=0;l<r;l++){const t=[];for(let s=0;s<u;s++){const i=s+a;if(i<=n[l])t[s]=m;else if(i>=e[l])t[s]=h;else{const e=(i-n[l])*f[l]+m;t[s]="floor"===o?Math.floor(e):"round"===o?Math.round(e):e}}g[l]=t}if(null!=t.contrastOffset){const n=l(t.contrastOffset,t.brightnessOffset);for(let t=0;t<r;t++){const e=g[t];for(let t=0;t<u;t++)e[t]=n[e[t]]}}return{lut:g,offset:a}}function l(t,n){const e=Math.min(Math.max(t,-100),100),s=Math.min(Math.max(n??0,-100),100),i=255,o=128,r=new Uint8Array(256);for(let a=0;a<256;a++){let t=0;e>0&&e<100?t=(200*a-100*i+2*i*s)/(2*(100-e))+o:e<=0&&e>-100?t=(200*a-100*i+2*i*s)*(100+e)/2e4+o:100===e?(t=200*a-100*i+(i+1)*(100-e)+2*i*s,t=t>0?i:0):-100===e&&(t=o),r[a]=t>i?i:t<0?0:t}return r}function u(t,n,e){const s=[];for(let i=0;i<n.length;i++){let r=0,a=0,l=0;"min"in n[i]?({min:r,max:a,avg:l}=n[i]):[r,a,l]=n[i];let u=l??0;"u8"!==t&&(u=255*(u-r)/(a-r)),e&&(u*=o[i]),s.push(f(u))}return s}function f(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 r=s/o;return isNaN(r)?i:Math.min(9.9,Math.max(.01,r))}function c(t,n,e,s){let i=1/0,o=-1/0,r=0,a=0,l=0,u=0;const f=t.length,c=new Map,m=[];for(let v=0;v<f;v++){const e=t[v];if(!n||n[v]){m.push(e);const t=(c.get(e)??0)+1;c.set(e,t),t>u&&(u=t,l=e),i=e<i?e:i,o=e>o?e:o,r+=e,a++}}if(0===a)return{statistics:{min:0,max:0,avg:0,stddev:0,mode:0,median:0},histogram:null};const h=r/a;let p=0;for(let v=0;v<f;v++)n&&!n[v]||(p+=(t[v]-h)**2);const g=n?n.filter((t=>t)).length:f,d=g<=1?0:Math.sqrt(p/(g-1));m.sort(((t,n)=>t-n));const x=a>>>1,M={min:i,max:o,avg:h,stddev:d,mode:l,median:a%2?m[Math.floor(x)]:(m[x-1]+m[x])/2};if(["u8","s8","u4","u2","u1"].includes(e)){const e=o-i+1,s=new Uint32Array(e);for(let o=0;o<f;o++)n&&!n[o]||s[t[o]-i]++;return{statistics:M,histogram:{min:i-.5,max:o+.5,size:e,counts:s}}}const O=256,y=new Uint32Array(O),b=(o-i)/O;if(0===b)return{statistics:M,histogram:{min:i,max:o,size:1,counts:new Uint32Array(1).fill(a)}};const k=new Uint32Array(O+1);for(let v=0;v<f;v++)n&&!n[v]||k[Math.floor((t[v]-i)/b)]++;for(let v=0;v<O-1;v++)y[v]=k[v];return y[O-1]=k[O-1]+k[O],{statistics:M,histogram:{min:i,max:o,size:O,counts:y}}}function m(t){const{pixels:n,mask:e,pixelType:s,bandMasks:i}=t,o=n.map(((t,n)=>c(t,i?.[n]??e,s)));return{statistics:o.map((({statistics:t})=>t)),histograms:o.map((({histogram:t})=>t))}}function h(t){if(!t?.pixels?.length)return null;const{pixels:n,mask:e,bandMasks:s,pixelType:i}=t,o=t.width*t.height,r=n.length,a=[],l=[],u=256;let f,c;for(let m=0;m<r;m++){let r=new Uint32Array(u);const h=n[m],p=s?.[m]??e;if("u8"===i){if(f=255,c=0,p){for(let t=0;t<o;t++)if(p[t]){const n=h[t];f=n<f?n:f,c=n>c?n:c,r[n]++}}else for(let t=0;t<o;t++){const n=h[t];f=n<f?n:f,c=n>c?n:c,r[n]++}r=r.slice(f,c+1)}else{let n=!1;t.statistics||(t.updateStatistics(),n=!0);const e=t.statistics;f=e[m].minValue,c=e[m].maxValue;const s=(c-f)/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++)r[t]=Math.round(s*(t+1))-Math.round(s*t)}else{const t=new Uint32Array(u+1);for(let n=0;n<o;n++)p&&!p[n]||t[Math.floor((h[n]-f)/s)]++;for(let n=0;n<u-1;n++)r[n]=t[n];r[u-1]=t[u-1]+t[u]}}const g="u8"===i?f-.5:f,d="u8"===i?c+.5:c;a.push({min:g,max:d,size:r.length,counts:r});let x=0,M=0,O=0;for(let t=0;t<r.length;t++)x+=r[t],M+=t*r[t];const y=M/x;for(let t=0;t<r.length;t++)O+=r[t]*(t-y)**2;const b=Math.sqrt(O/(x-1)),k=(d-g)/r.length,v=(y+("u8"===i?0:.5))*k+f,w=b*k;l.push({min:f,max:c,avg:v,stddev:w})}return{statistics:l,histograms:a}}function p(t){const n=[];for(let e=0;e<t.length;e++){const{min:s,max:i,size:o,counts:r}=t[e];let a=0,l=0;for(let t=0;t<o;t++)a+=r[t],l+=t*r[t];const u=l/a;let f=0;for(let t=0;t<o;t++)f+=r[t]*(t-u)**2;const c=(i-s)/o,m=(u+.5)*c+s,h=Math.sqrt(f/(a-1))*c;n.push({min:s,max:i,avg:m,stddev:h})}return n}function g(n,i){const{pixelBlock:o,bandIds:a,returnHistogramLut:l,rasterInfo:u}=i;let f=null,c=null,m=n.stretchType;if("number"==typeof m&&(m=s[m]),n.dra)if("minMax"===m&&o?.statistics)f=o.statistics.map((t=>[t.minValue,t.maxValue,0,0]));else{const t=h(o);f=null!=t?t.statistics:null,c=null!=t?t.histograms:null}else f=n.statistics?.length?n.statistics:u.statistics,c="histograms"in n?n.histograms:void 0,c||(c=u.histograms);"percentClip"!==m&&"histogramEqualization"!==m||c?.length||(m="minMax");const p=f?.length||c?.length||u.bandCount,g=[],x=[];f&&!Array.isArray(f[0])&&(f=f.map((t=>[t.min,t.max,t.avg,t.stddev])));const[M,O]=e(u.pixelType);if(!f?.length){f=[];for(let t=0;t<p;t++)f.push([M,O,1,1]);"standardDeviation"===m&&(m="minMax")}switch(m){case"none":for(let t=0;t<p;t++)g[t]=M,x[t]=O;break;case"minMax":for(let t=0;t<p;t++){const n=f[t];g[t]=n[0],x[t]=n[1]}break;case"standardDeviation":{const{numberOfStandardDeviations:t=2}=n;for(let n=0;n<p;n++){const e=f[n];g[n]=e[2]-t*e[3],x[n]=e[2]+t*e[3],g[n]<e[0]&&(g[n]=e[0]),x[n]>e[1]&&(x[n]=e[1])}}break;case"histogramEqualization":t(c);for(let t=0;t<p;t++)g[t]=c[t].min,x[t]=c[t].max;break;case"percentClip":t(c);for(let t=0;t<c.length;t++){const e=c[t],s=new Uint32Array(e.size),i=[...e.counts];i.length>=20&&(i[0]=i[1]=i[2]=i[i.length-1]=i[i.length-2]=0);let o=0;const r=(e.max-e.min)/e.size,a=-.5===e.min&&1===r?.5:0;for(let t=0;t<e.size;t++)o+=i[t],s[t]=o;let l=(n.minPercent||0)*o/100;g[t]=e.min+a;for(let n=0;n<e.size;n++)if(s[n]>l){g[t]=e.min+r*(n+a);break}l=(1-(n.maxPercent||0)/100)*o,x[t]=e.max+a;for(let n=e.size-2;n>=0;n--)if(s[n]<l){x[t]=e.min+r*(n+2-a);break}if(x[t]<g[t]){const n=g[t];g[t]=x[t],x[t]=n}}break;default:for(let t=0;t<p;t++){const n=f[t];g[t]=n[0],x[t]=n[1]}}let y,b,k;"histogramEqualization"===m?(t(c),b=c[0].size||256,y=0,l&&(k=c.map((t=>r(t))))):(b=n.max||255,y=n.min||0);return d({minCutOff:g,maxCutOff:x,maxOutput:b,minOutput:y,histogramLut:k},a)}function d(t,n){if(null==n||0===n.length)return t;const e=Math.max.apply(null,n),{minCutOff:s,maxCutOff:i,minOutput:o,maxOutput:r,histogramLut:a}=t;return s.length===n.length||s.length<=e?t:{minCutOff:n.map((t=>s[t])),maxCutOff:n.map((t=>i[t])),histogramLut:a?n.map((t=>a[t])):null,minOutput:o,maxOutput:r}}function x(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 M(t,e){if(!t?.pixels?.length)return t;const{mask:s,bandMasks:i,width:o,height:r,pixels:a}=t,{minCutOff:l,maxCutOff:u,minOutput:f,maxOutput:c,gamma:m}=e,h=o*r,p=e.outputPixelType||"u8",g=t.pixels.map((()=>n.createEmptyBand(p,h))),d=g.length,M=c-f,O=[],y=[];for(let n=0;n<d;n++)y[n]=u[n]-l[n],O[n]=0===y[n]?0:M/y[n];const b=p.startsWith("u")||p.startsWith("s"),k=m&&m.length>=d,v=!!e.isRenderer;if(k){const t=x(d,m);for(let n=0;n<d;n++){const e=i?.[n]??s;for(let s=0;s<h;s++)if(null==e||e[s]){if(0===y[n]){g[n][s]=f;continue}const e=a[n][s],i=(e-l[n])/y[n];let o=1;if(m[n]>1&&(o-=(1/M)**(i*t[n])),e<u[n]&&e>l[n]){const t=o*M*i**(1/m[n])+f;g[n][s]=v?Math.floor(t):b?Math.round(t):t}else e>=u[n]?g[n][s]=c:g[n][s]=f}}}else for(let n=0;n<d;n++){const t=i?.[n]??s;for(let e=0;e<h;e++)if(null==t||t[e]){const t=a[n][e];if(t<u[n]&&t>l[n]){const s=(t-l[n])*O[n]+f;g[n][e]=v?Math.floor(s):b?Math.round(s):s}else t>=u[n]?g[n][e]=c:g[n][e]=f}}const w=new n({width:o,height:r,mask:s,bandMasks:i,pixels:g,pixelType:p});return w.updateStatistics(),w}export{x as computeGammaCorrection,u as computeGammaValues,m as computeStatisticsHistograms,l as createContrastBrightnessLUT,r as createHistogramEqualizationLUT,a as createStretchLUT,p as estimateStatisticsFromHistograms,h as estimateStatisticsHistograms,g as getStretchCutoff,M as stretch};