@@ -24,6 +24,7 @@ setMeshFiltersPipelinesUrl(pipelinesBaseUrl)
2424async function main ( ) {
2525 const niimath = new Niimath ( )
2626 await niimath . init ( )
27+ niimath . setOutputDataType ( 'input' ) // call before setting image since this is passed to the image constructor
2728 aboutBtn . onclick = function ( ) {
2829 const url = "https://github.com/niivue/brain2print"
2930 window . open ( url , "_blank" )
@@ -153,7 +154,7 @@ async function main() {
153154 let cmap = await fetchJSON ( modelEntry . colormapPath )
154155 overlayVolume . setColormapLabel ( cmap )
155156 // n.b. most models create indexed labels, but those without colormap mask scalar input
156- overlayVolume . hdr . intent_code = 1002 ; // NIFTI_INTENT_LABEL
157+ overlayVolume . hdr . intent_code = 1002 // NIFTI_INTENT_LABEL
157158 } else {
158159 let colormap = opts . atlasSelectedColorTable . toLowerCase ( )
159160 const cmaps = nv1 . colormaps ( )
@@ -204,7 +205,64 @@ async function main() {
204205 remeshDialog . show ( )
205206 }
206207 }
208+ qualitySelect . onchange = function ( ) {
209+ const isBetterQuality = Boolean ( Number ( qualitySelect . value ) )
210+ const opacity = 1.0 - ( 0.5 * Number ( isBetterQuality ) )
211+ largestCheck . disabled = isBetterQuality
212+ largestClusterGroup . style . opacity = opacity
213+ bubbleCheck . disabled = isBetterQuality
214+ bubbleGroup . style . opacity = opacity
215+ closeMM . disabled = isBetterQuality
216+ closeGroup . style . opacity = opacity
217+ }
207218 applyBtn . onclick = async function ( ) {
219+ const isBetterQuality = Boolean ( Number ( qualitySelect . value ) )
220+ const startTime = performance . now ( )
221+ if ( isBetterQuality )
222+ await applyQuality ( )
223+ else
224+ await applyFaster ( )
225+ console . log ( `Execution time: ${ Math . round ( performance . now ( ) - startTime ) } ms` )
226+ }
227+ async function applyFaster ( ) {
228+ const niiBuffer = await nv1 . saveImage ( { volumeByIndex : nv1 . volumes . length - 1 } ) . buffer
229+ const niiFile = new File ( [ niiBuffer ] , 'image.nii' )
230+ let processor = niimath . image ( niiFile )
231+ loadingCircle . classList . remove ( 'hidden' )
232+ //mesh with specified isosurface
233+ const isoValue = 0.5
234+ //const largestCheckValue = largestCheck.checked
235+ let reduce = Math . min ( Math . max ( Number ( shrinkPct . value ) / 100 , 0.01 ) , 1 )
236+ let hollowSz = Number ( hollowSelect . value )
237+ let closeSz = Number ( closeMM . value )
238+ const pixDim = Math . min ( Math . min ( nv1 . volumes [ 0 ] . hdr . pixDims [ 1 ] , nv1 . volumes [ 0 ] . hdr . pixDims [ 2 ] ) , nv1 . volumes [ 0 ] . hdr . pixDims [ 3 ] )
239+ if ( ( pixDim < 0.2 ) && ( ( hollowSz !== 0 ) || ( closeSz !== 0 ) ) ) {
240+ hollowSz *= pixDim
241+ closeSz *= pixDim
242+ console . log ( 'Very small pixels, scaling hollow and close values by ' , pixDim )
243+ }
244+ if ( hollowSz < 0 ) {
245+ processor = processor . hollow ( 0.5 , hollowSz )
246+ }
247+ if ( ( isFinite ( closeSz ) ) && ( closeSz > 0 ) ) {
248+ processor = processor . close ( isoValue , closeSz , 2 * closeSz )
249+ }
250+ processor = processor . mesh ( {
251+ i : isoValue ,
252+ l : largestCheck . checked ? 1 : 0 ,
253+ r : reduce ,
254+ b : bubbleCheck . checked ? 1 : 0
255+ } )
256+ console . log ( 'niimath operation' , processor . commands )
257+ const retBlob = await processor . run ( 'test.mz3' )
258+ const arrayBuffer = await retBlob . arrayBuffer ( )
259+ loadingCircle . classList . add ( 'hidden' )
260+ if ( nv1 . meshes . length > 0 )
261+ nv1 . removeMesh ( nv1 . meshes [ 0 ] )
262+ await nv1 . loadFromArrayBuffer ( arrayBuffer , 'test.mz3' )
263+ nv1 . reverseFaces ( 0 )
264+ }
265+ async function applyQuality ( ) {
208266 const volIdx = nv1 . volumes . length - 1
209267 let hdr = nv1 . volumes [ volIdx ] . hdr
210268 let img = nv1 . volumes [ volIdx ] . img
@@ -214,17 +272,13 @@ async function main() {
214272 const niiBuffer = await nv1 . saveImage ( { volumeByIndex : nv1 . volumes . length - 1 } ) . buffer
215273 const niiBlob = new Blob ( [ niiBuffer ] , { type : 'application/octet-stream' } )
216274 const niiFile = new File ( [ niiBlob ] , 'input.nii' )
217- // with niimath wasm ZLIB builds, isGz seems to be the default output type:
218- // see: https://github.com/rordenlab/niimath/blob/9f3a301be72c331b90ef5baecb7a0232e9b47ba4/src/core.c#L201
219- // also added new option to set outputDataType in niimath in version 0.3.0 (published 20 Dec 2024)
220275 niimath . setOutputDataType ( 'input' ) // call before setting image since this is passed to the image constructor
221276 let image = niimath . image ( niiFile )
277+ image = image . gz ( 0 )
278+ image = image . ras ( )
222279 image = image . hollow ( 0.5 , hollowInt )
223- // must use .gz extension because niimath will create .nii.gz by default, so
224- // wasm file system commands will look for this, not .nii.
225- // Error 44 will happen otherwise (file not found error)
226- const outBlob = await image . run ( 'output.nii.gz' )
227- let outFile = new File ( [ outBlob ] , 'hollow.nii.gz' )
280+ const outBlob = await image . run ( 'output.nii' )
281+ let outFile = new File ( [ outBlob ] , 'hollow.nii' )
228282 const outVol = await NVImage . loadFromFile ( {
229283 file : outFile ,
230284 name : outFile . name
@@ -236,7 +290,6 @@ async function main() {
236290 loadingCircle . classList . remove ( "hidden" )
237291 meshProcessingMsg . classList . remove ( "hidden" )
238292 meshProcessingMsg . textContent = "Generating mesh from segmentation"
239-
240293 const itkImage = nii2iwi ( hdr , img , false )
241294 itkImage . size = itkImage . size . map ( Number )
242295 const { mesh } = await antiAliasCuberille ( itkImage , { noClosing : true } )
@@ -254,6 +307,7 @@ async function main() {
254307 meshProcessingMsg . textContent = "Smoothing and remeshing"
255308 const smooth = parseInt ( smoothSlide . value )
256309 const shrink = parseFloat ( shrinkPct . value )
310+ console . log ( `smoothing iterations ${ smooth } shrink percent ${ shrink } ` )
257311 const { outputMesh : smoothedMesh } = await smoothRemesh ( largestOnly , { newtonIterations : smooth , numberPoints : shrink } )
258312 const { outputMesh : smoothedRepairedMesh } = await repair ( smoothedMesh , { maximumHoleArea : 50.0 } )
259313 const niiMesh = iwm2meshCore ( smoothedRepairedMesh )
@@ -302,13 +356,14 @@ async function main() {
302356 option . value = inferenceModelsList [ i ] . id . toString ( )
303357 modelSelect . appendChild ( option )
304358 }
359+ qualitySelect . onchange ( )
305360 nv1 . onImageLoaded = doLoadImage
306361 modelSelect . selectedIndex = - 1
307- workerCheck . checked = await isChrome ( ) ; //TODO: Safari does not yet support WebGL TFJS webworkers, test FireFox
362+ workerCheck . checked = await isChrome ( ) //TODO: Safari does not yet support WebGL TFJS webworkers, test FireFox
308363 console . log ( 'brain2print 20241218' )
309364 // uncomment next two lines to automatically run segmentation when web page is loaded
310- // modelSelect.selectedIndex = 11
311- // modelSelect.onchange()
365+ // modelSelect.selectedIndex = 11
366+ // modelSelect.onchange()
312367}
313368
314369main ( )
0 commit comments