@@ -11,19 +11,25 @@ function isGeoTIFF(fileOrUrl) {
1111 ( typeof fileOrUrl === "string" && fileOrUrl . startsWith ( "data:image/tiff;base64," ) ) ;
1212}
1313
14+ const DEFAULT_SETTINGS = {
15+ maxMpx : - 1 ,
16+ colorSpace : COLOR_SPACE . RGB ,
17+ bands : [ 1 , 2 , 3 ] ,
18+ binCount : 256
19+ }
1420/**
1521 * Load image (regular or GeoTIFF)
1622 * @param {File|string } fileOrUrl - File object or URL
1723 * @param {string } slot - 'source' or 'target'
1824 * @param {number } maxMpx - Max megapixels for processing
1925 * @returns {Promise<void> }
2026 */
21- async function loadImage ( fileOrUrl , slot , maxMpx = - 1 ) {
27+ async function loadImage ( fileOrUrl , slot , settings = DEFAULT_SETTINGS ) {
2228 try {
2329 if ( isGeoTIFF ( fileOrUrl ) ) {
24- await loadGeoTIFF ( fileOrUrl , slot , maxMpx ) ;
30+ await loadGeoTIFF ( fileOrUrl , slot , settings ) ;
2531 } else {
26- await loadRegularImage ( fileOrUrl , slot , maxMpx ) ;
32+ await loadRegularImage ( fileOrUrl , slot , settings ) ;
2733 }
2834 } catch ( error ) {
2935 console . error ( `Failed to load ${ slot } image:` , error ) ;
@@ -38,7 +44,7 @@ async function loadImage(fileOrUrl, slot, maxMpx = -1) {
3844 * @param {number } maxMpx - Max megapixels for processing
3945 * @returns {Promise<void> }
4046 */
41- async function loadRegularImage ( fileOrUrl , slot , maxMpx ) {
47+ async function loadRegularImage ( fileOrUrl , slot , settings = DEFAULT_SETTINGS ) {
4248 return new Promise ( ( resolve , reject ) => {
4349 const img = new Image ( ) ;
4450 img . onload = async ( ) => {
@@ -53,7 +59,7 @@ async function loadRegularImage(fileOrUrl, slot, maxMpx) {
5359 document . getElementById ( slot + "-img-div" ) . replaceChildren ( img ) ;
5460
5561 if ( imageObjects . source && imageObjects . target ) {
56- await processMatching ( maxMpx ) ;
62+ await processMatching ( settings ) ;
5763 }
5864 resolve ( ) ;
5965 } catch ( error ) {
@@ -72,7 +78,7 @@ async function loadRegularImage(fileOrUrl, slot, maxMpx) {
7278 * @param {number } maxMpx - Max megapixels for processing
7379 * @returns {Promise<void> }
7480 */
75- async function loadGeoTIFF ( fileOrUrl , slot , maxMpx ) {
81+ async function loadGeoTIFF ( fileOrUrl , slot , settings = DEFAULT_SETTINGS ) {
7682 const { fromArrayBuffer } = await import ( "https://esm.sh/geotiff@2.1.4-beta.0" ) ;
7783
7884 let arrayBuffer ;
@@ -115,7 +121,7 @@ async function loadGeoTIFF(fileOrUrl, slot, maxMpx) {
115121 document . getElementById ( slot + "-img-div" ) . replaceChildren ( canvas ) ;
116122
117123 if ( imageObjects . source && imageObjects . target ) {
118- await processMatching ( maxMpx ) ;
124+ await processMatching ( settings ) ;
119125 }
120126}
121127
@@ -124,7 +130,7 @@ async function loadGeoTIFF(fileOrUrl, slot, maxMpx) {
124130 * @param {number } maxMpx - Max megapixels for processing
125131 * @returns {Promise<void> }
126132 */
127- async function processMatching ( maxMpx = - 1 ) {
133+ async function processMatching ( settings = DEFAULT_SETTINGS ) {
128134 const sourceCanvas = imageObjects . source . canvas ;
129135 const targetCanvas = imageObjects . target . canvas ;
130136 const sourceArr = imageObjects . source . ctx . getImageData ( 0 , 0 , sourceCanvas . width , sourceCanvas . height ) . data ;
@@ -134,18 +140,29 @@ async function processMatching(maxMpx = -1) {
134140 const start = Date . now ( ) ;
135141 // const matched = matchHistograms(sourceArr, targetArr, 4, 'MATCHED_DATA', maxMpx);
136142
137- const matched = matchHistogramsColorspaces (
143+ let matched
144+ if ( settings ) {
145+ matched = matchHistogramsRGB (
146+ sourceArr ,
147+ targetArr ,
148+ 4 ,
149+ 'MATCHED_DATA' , settings . maxMpx
150+ )
151+ } else {
152+ matched = matchHistogramsColorspaces (
138153 sourceArr ,
139154 targetArr ,
140155 4 ,
141156 {
142157 returnType : 'MATCHED_DATA' ,
143- maxMpx : 0 ,
144- colorSpace : COLOR_SPACE . LCH ,
145- bands : [ 1 , 2 , 3 ] ,
146- binCount : 256 ,
158+ ...settings
159+ // maxMpx: 0,
160+ // colorSpace: COLOR_SPACE.RGB,
161+ // bands: [1, 2, 3],
162+ // binCount: 256,
147163 }
148164 )
165+ }
149166
150167 const elapsed_ms = Date . now ( ) - start ;
151168
@@ -159,7 +176,7 @@ async function processMatching(maxMpx = -1) {
159176 ctxOut . putImageData ( outData , 0 , 0 ) ;
160177
161178 // Display results and handle exports
162- await displayResults ( outCanvas , matched , elapsed_ms , maxMpx ) ;
179+ await displayResults ( outCanvas , matched , elapsed_ms ) ;
163180}
164181
165182/**
@@ -169,7 +186,7 @@ async function processMatching(maxMpx = -1) {
169186 * @param {number } elapsed_ms - Processing time
170187 * @returns {Promise<void> }
171188 */
172- async function displayResults ( outCanvas , matched , elapsed_ms , maxMpx ) {
189+ async function displayResults ( outCanvas , matched , elapsed_ms ) {
173190 const container = document . getElementById ( 'matched-img-div' ) ;
174191
175192 // Make download Link/Button
@@ -195,7 +212,7 @@ async function displayResults(outCanvas, matched, elapsed_ms, maxMpx) {
195212 // statsDiv.style = 'text-align: left; padding: 20px;'
196213 const sourceMpix = ( imageObjects . source . canvas . width * imageObjects . source . canvas . height / 1e6 ) . toFixed ( 2 ) ;
197214 const targetMpix = ( imageObjects . target . canvas . width * imageObjects . target . canvas . height / 1e6 ) . toFixed ( 2 ) ;
198- statsDiv . textContent = `Histogram matching took ${ elapsed_ms } ms (maxMpx: ${ maxMpx <= 0 ? 'uncapped' : maxMpx + 'Mpx' } ) \nSource: ${ sourceMpix } Mpx (${ imageObjects . source . canvas . width } x ${ imageObjects . source . canvas . height } )\nTarget: ${ targetMpix } Mpx (${ imageObjects . target . canvas . width } x ${ imageObjects . target . canvas . height } )` ;
215+ statsDiv . textContent = `Histogram matching took ${ elapsed_ms } ms \nSource: ${ sourceMpix } Mpx (${ imageObjects . source . canvas . width } x ${ imageObjects . source . canvas . height } )\nTarget: ${ targetMpix } Mpx (${ imageObjects . target . canvas . width } x ${ imageObjects . target . canvas . height } )` ;
199216 // statsContainer.replaceChildren(statsDiv);
200217 } , "image/png" ) ;
201218
@@ -237,24 +254,37 @@ async function exportGeoTIFF(raster, geoMetadata) {
237254 * Get current maxMpx value from input
238255 * @returns {number } Current maxMpx value
239256 */
240- function getInputMaxMpx ( ) {
241- const input = document . getElementById ( 'max-mpx-input' ) ;
242- return input ? parseFloat ( input . value ) || 0 : 0 ;
257+ function getInputSettings ( ) {
258+ const maxMpxInput = document . getElementById ( "max-mpx-input" ) ;
259+ const maxMpx = parseFloat ( maxMpxInput . value ) || 0 ;
260+ const colorSpaceInput = document . getElementById ( "colorSpace-input" ) ;
261+ const colorSpace = colorSpaceInput . value ; // TODO MAP TO ENUM
262+ const bandsInput = document . getElementById ( "bands-input" ) ;
263+ const bands = bandsInput . value . trim ( ) . split ( ',' )
264+ const binCountInput = document . getElementById ( "binCount-input" ) ;
265+ const binCount = parseFloat ( binCountInput . value ) || 256 ;
266+
267+ return {
268+ maxMpx,
269+ colorSpace,
270+ bands,
271+ binCount
272+ }
243273}
244274
245275/**
246276 * Handle file input change
247277 * @param {Event } e - Input change event
248278 */
249279function handleFileInput ( e ) {
250- const maxMpx = getInputMaxMpx ( )
280+ const settings = getInputSettings ( )
251281 const file = e . target . files [ 0 ] ;
252282 if ( ! file ) return ;
253283
254284 const reader = new FileReader ( ) ;
255285 reader . onload = ( ev ) => {
256286 const slot = e . target . id . includes ( 'source' ) ? 'source' : 'target' ;
257- loadImage ( ev . target . result , slot , maxMpx ) ;
287+ loadImage ( ev . target . result , slot , settings ) ;
258288 } ;
259289 reader . readAsDataURL ( file ) ;
260290}
@@ -391,32 +421,46 @@ let imageObjects = {}
391421function setupEventListeners ( ) {
392422 const sourceInput = document . getElementById ( "source-input" ) ;
393423 const targetInput = document . getElementById ( "target-input" ) ;
394- const maxMpxInput = document . getElementById ( "max-mpx-input" ) ;
395424
396425 if ( sourceInput ) {
397426 sourceInput . addEventListener ( "change" , ( e ) => handleFileInput ( e ) ) ;
398427 }
399428 if ( targetInput ) {
400429 targetInput . addEventListener ( "change" , ( e ) => handleFileInput ( e ) ) ;
401430 }
402- if ( maxMpxInput ) {
403- maxMpxInput . addEventListener ( "change" , async ( e ) => {
404- const maxMpx = getInputMaxMpx ( )
405- await processMatching ( maxMpx )
406- } ) ;
431+
432+ // Add settings
433+ async function handleSettingsChange ( e ) {
434+ const settings = getInputSettings ( )
435+ await processMatching ( settings )
436+ }
437+ document . getElementById ( "max-mpx-input" ) . addEventListener ( "change" , handleSettingsChange ) ;
438+ document . getElementById ( "bands-input" ) . addEventListener ( "change" , handleSettingsChange ) ;
439+ document . getElementById ( "binCount-input" ) . addEventListener ( "change" , handleSettingsChange ) ;
440+
441+ // Add options to color-spaces select
442+ const colorSpaceInput = document . getElementById ( "colorSpace-input" )
443+ colorSpaceInput . addEventListener ( "change" , handleSettingsChange ) ;
444+ if ( colorSpaceInput . children . length == 0 ) {
445+ for ( const [ key , value ] of Object . entries ( COLOR_SPACE ) ) {
446+ const opt = document . createElement ( 'option' ) ;
447+ opt . value = value ;
448+ opt . innerHTML = key ;
449+ colorSpaceInput . appendChild ( opt ) ;
450+ }
407451 }
408452}
409453
410454/**
411455 * Load default images if available
412456 */
413457async function loadDefaultImages ( ) {
414- const maxMpx = getInputMaxMpx ( )
415- console . log ( 'maxMpx ' , maxMpx )
458+ const settings = getInputSettings ( )
459+ console . log ( 'settings ' , settings )
416460 try {
417461 await Promise . allSettled ( [
418- loadImage ( './source1.jpg' , "source" , maxMpx ) ,
419- loadImage ( './reference1.jpg' , "target" , maxMpx )
462+ loadImage ( './source1.jpg' , "source" , settings ) ,
463+ loadImage ( './reference1.jpg' , "target" , settings )
420464 ] ) ;
421465 } catch ( error ) {
422466 console . log ( 'Default images not available, waiting for user input' ) ;
0 commit comments