Diff of /v3/index.html [000000] .. [b86468]

Switch to unified view

a b/v3/index.html
1
<!--
2
  =========================================================
3
* Brainchop - v3.0.0
4
=========================================================
5
6
* Discription:  A user interface for whole brain segmentation
7
*               Input shape : [1, D, H, W, 1] e.g. [1, 256, 256, 256, 1]                
8
*               Model : Meshnet or similar lightweigth models    
9
*
10
* Authors:  Mohamed Masoud  and Sergey Plis  - 2023
11
=========================================================
12
13
14
15
=========================================================
16
           Brainchop for 3D Brain Segmentation
17
=========================================================
18
--> 
19
<!DOCTYPE html>
20
<html>
21
<head>
22
  <meta charset="utf-8">
23
  <title>Brainchop </title>
24
  <link rel="apple-touch-icon" sizes="180x180" href="css/favicon_io/apple-touch-icon.png">
25
  <link rel="icon" type="image/png" sizes="32x32" href="css/favicon_io/favicon-32x32.png">
26
  <link rel="icon" type="image/png" sizes="16x16" href="css/favicon_io/favicon-16x16.png">
27
  <link rel="manifest" href="css/favicon_io/site.webmanifest">
28
29
  <link rel="stylesheet" href="css/w3.css">
30
  <link rel="stylesheet" href="css/font-awesome-4.7.0/css/font-awesome.min.css">   
31
  <link rel="stylesheet" type="text/css" href="js/libs/webix/codebase/webix.css"> 
32
  <link rel="stylesheet" type="text/css" href="js/libs/papaya_lib/papaya.css">
33
  <link rel="stylesheet" type="text/css" href="css/style.css"> 
34
  <link rel="stylesheet" type="text/css" href="css/brainchop.css">
35
36
37
38
  <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
39
40
41
  <script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
42
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" type="text/javascript"></script>
43
  <script src="https://docs.opencv.org/3.4.0/opencv.js"></script> 
44
  <script src="https://www.lactame.com/lib/image-js/0.21.2/image.min.js"></script>  
45
46
  <script src="js/libs/webix/codebase/webix.js"></script>  
47
  <script src="js/libs/tf.min.js"></script> 
48
  <!--   <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.18.0/dist/tf.min.js"> </script>
49
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgpu/dist/tf-backend-webgpu.js"></script>   -->
50
  <script src="js/libs/float16.js"></script>  
51
52
  <script type="text/javascript" src="js/libs/papaya_lib/papaya.js"></script>
53
  
54
  <script type="text/javascript" src="js/libs/nifti-reader.js"></script>
55
  <script type="text/javascript" src="js/libs/nifti-reader-min.js"></script>
56
  <script type="text/javascript" src="js/brainchop/connectedComponents3DAll.js"></script>
57
58
  <script type="module">
59
  import { Niivue } from './js/brainchop/Niivue.js';
60
  window.Niivue = Niivue;
61
</script>
62
63
  <script type="text/javascript" src="js/brainchop/mainParameters.js"></script>  
64
  <script type="text/javascript" src="js/brainchop/mainMeshNetFunctions.js"></script>
65
  <script type="text/javascript" src="js/brainchop/mainNiftiReadingFunctions.js"></script> 
66
  <script type="text/javascript" src="js/brainchop/mainTypes.js"></script> 
67
68
  
69
  <script type="text/javascript" src="js/brainchop/checkCompatibility.js"></script> 
70
71
  <script type="text/javascript" src="js/libs/utilities.js"></script>  
72
  <script type="text/javascript"  src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js"></script>
73
  <script type="text/javascript" src="js/brainchop/mri_convert.js"></script>  
74
75
  <script type="text/javascript" src="https://cdn.webix.com/components/edge/highcharts/hcharts.js"></script>
76
77
  <script type="text/javascript" charset="UTF-8" src="js/libs/three/three.js"></script>
78
  <script type="text/javascript" charset="UTF-8" src="js/libs/three/controls/TrackballControls.js"></script>
79
  <script type="text/javascript" charset="UTF-8" src="js/libs/three/controls/OrbitControls.js"></script>
80
  <script type="text/javascript" src="js/libs/util/Stats.js"></script>
81
  <script type="text/javascript" src="js/libs/util/dat.gui.min.js"></script>
82
83
  <script type="text/javascript" src="js/libs/util.js"></script>
84
  <script type="text/javascript" src="js/brainchop/BCBrain3D.js"></script>
85
  <link rel="stylesheet" href="css/default.css">
86
87
  <style> 
88
    /*For Testing*/
89
  </style>
90
91
      
92
</head>
93
   
94
<body>
95
 
96
 <script type="text/javascript">
97
98
99
  webix.ready(function () {
100
101
102
      // About Window
103
      let aboutWindowForm = {view: "form", id: "aboutWindowFormId", elements: [ 
104
105
              {  cols: [
106
                    {   view: "template", template: "Brainchop is an in-browser  automatic segmentation setup for T1 MRI volumes. The tool is an open source and designed to enable users to segment T1 images into regions of interest with a simple interactive interface. The input must be a T1 brain volume in the Nifti format. The output can also be saved as a Nifti file. <br><br> <br><b>Authors:</b> Mohamed Masoud, Farfalla Hu and Sergey Plis (2024).<br> <b>Version:</b> 3.4.0 <br> <b>Funded by:</b>  NIH RF1MH121885. <br><br> <b>Special thanks</b> to Kevin Wang and Alex Fedorov for discussions and pre-trained Meshnet models.<br><br> <b>Affiliation: </b> Center for Translational Research in Neuroimaging and Data Science (<a href='https://trendscenter.org/'>TReNDS</a>) <br><center><img src='css/logo/TReNDS_logo.jpg' width='180' height='60'></img></center>" 
107
                        
108
                    }                 
109
110
                ]  
111
                
112
             },  
113
       
114
115
              {  cols: [
116
                        {}, 
117
118
                        { view:"button", value:"Ok", css:"bt_1", width:100,  
119
                                align:"center",
120
                                click: function() {
121
                                     $$("aboutWindow").hide();
122
                               }
123
                        },
124
125
                        {}
126
                       ]
127
              } 
128
129
          ]
130
       }
131
132
133
134
135
136
      // About Window to select local model
137
      webix.ui({
138
                  view:"window",
139
                  id: "aboutWindow",
140
                  height:550,
141
                  width:600,
142
143
                  head:{
144
                      view:"toolbar", css: "toolbarclass", elements:[
145
                          { type:"header", template:"About", css:"windowtitleclass" },
146
                          { view:"icon", icon:"wxi-close", click:function(){
147
                              $$("aboutWindow").hide();
148
                          } }
149
                      ]
150
                  },                  
151
152
                  position:"center",
153
                  body: aboutWindowForm
154
155
      })
156
157
      
158
      //-- let about = "Demo of 3D MRI Segmentation. <br>By<br> Mohamed Masoud, Sergey Plis (2023) <br> Grant:  NIH RF1MH121885 ";
159
       
160
      aboutClick = () => {
161
            $$("aboutWindow").show()
162
      }
163
164
165
166
167
      let  gl2Temp = document.createElement('canvas').getContext('webgl2') ? 
168
                  document.createElement('canvas').getContext('webgl2') : null;
169
      let maxTextureSize =   gl2Temp ?  gl2Temp.getParameter(gl2Temp.MAX_TEXTURE_SIZE) :  "N/A";         
170
171
      // Browser Resources Window
172
      let browserResourcesWindowForm = {view: "form", id: "browserResourcesWindowFormId", elements: [ 
173
174
              {  cols: [
175
                    {   view: "template", 
176
                        template: " <table>" +
177
                                      // "<tr>" +
178
                                      //   "<th>Model : </th>" +
179
                                      //   "<td>" + inferenceModelsList[$$("selectModel").getValue() - 1]["modelName"] + "</td>" +
180
                                      // "</tr>" +
181
182
                                      "<tr>" +
183
                                        "<th style='text-align:left'>Browser : </th>" +
184
                                        "<td>" + detectBrowser() + "</td>" +
185
                                      "</tr>" +
186
187
                                      "<tr>" +
188
                                        "<th style='text-align:left'>Browser Ver : </th>" +
189
                                        "<td>" + detectBrowserVersion() + "</td>" +
190
                                      "</tr>" +  
191
192
                                      "<tr>" +
193
                                        "<th style='text-align:left'>Operating Sys. : </th>" +
194
                                        "<td>" + detectOperatingSys() + "</td>" +
195
                                      "</tr>" +
196
197
                                      "<tr>" +
198
                                        "<th style='text-align:left'>Texture Size. : </th>" +
199
                                        "<td>" + maxTextureSize + "</td>" +
200
                                      "</tr>" +
201
202
                                      "<tr>" +
203
                                        "<th style='text-align:left'>GPU : </th>" +
204
                                        "<td>" + detectGPUCardType() + "</td>" +
205
                                      "</tr>" +
206
207
                                      "<tr>" +
208
                                        "<th style='text-align:left'>CPU Cores : </th>" +
209
                                        "<td>" + getCPUNumCores()+ "</td>" +
210
                                      "</tr>" +
211
212
                                    "</table>" +
213
                                    "<br><br> - To see a List of tested S/W and H/W with each model please open this <a href='https://docs.google.com/spreadsheets/d/1PU4p6oN4aBfTbXZ8pO6n8fc890Y5Ck0agsIZbm_FO3I/edit#gid=0' target=”_blank”> link. </a> " + 
214
215
                                    "<br><br> - If the browser above can detect only the internal graphics chip (e.g. HD, UHD), please check the external GPU driver or the system settings to fix the problem and try again." 
216
217
218
                    }                 
219
220
                ]  
221
                
222
             },  
223
       
224
225
              {  cols: [
226
                        {}, 
227
228
                        { view:"button", value:"Ok", css:"bt_1", width:100,  
229
                                align:"center",
230
                                click: function() {
231
                                     $$("browserResourcesWindow").hide();
232
                               }
233
                        },
234
235
                        {}
236
                       ]
237
              } 
238
239
          ]
240
       }
241
242
243
      // Browser Resources Window to select local model
244
      webix.ui({
245
                  view:"window",
246
                  id: "browserResourcesWindow",
247
                  height:550,
248
                  width:600,
249
250
                  head:{
251
                      view:"toolbar", css: "toolbarclass", elements:[
252
                          { type:"header", template:"Browser Resources", css:"windowtitleclass" },
253
                          { view:"icon", icon:"wxi-close", click:function(){
254
                              $$("browserResourcesWindow").hide();
255
                          } }
256
                      ]
257
                  },                  
258
259
                  position:"center",
260
                  body: browserResourcesWindowForm
261
262
      })
263
264
      
265
      browserResourcesClick = () => {
266
            $$("browserResourcesWindow").show()
267
      }
268
269
270
271
      feedbackClick = () => {
272
                   window.open("https://forms.gle/WzUyVEzgCCY2bHo79");
273
      }
274
275
      githubClick = () => {
276
                   window.open("https://github.com/neuroneural/brainchop");
277
      }
278
279
280
281
      toolbar = {
282
          view: "toolbar",
283
          css: "toolbarclass",
284
          id: "myToolbar",
285
          rows:[{ cols: 
286
                  [
287
                   { view: "label", label: '<img src="css/svg/Artboard2.svg"/>', css: {"color":"black !important", "font-weight": "bold"}, width: 30 },                   
288
                   { view: "label", label: '<p>Brain<span>chop</span></p>', css: {"margin":"0!important"},  inputWidth: 100, align: "left" },
289
                   { view: "label", label: '<i id="resourcesHwSwId" class="fa fa-cogs"/>',
290
                   css: {"color":"black !important", "font-weight": "bold"},  width: 60, align:"right", click: browserResourcesClick, tooltip:"Browser Resources" },                   
291
                   { view: "label", label: '<i id="feedbackId" class="fa fa-pencil-square-o"/>',
292
                   css: {"color":"black !important", "font-weight": "bold"},  width: 60, align:"right", click: feedbackClick, tooltip:"Feedback" },
293
                   { view: "button", id: "About", value: "About", css:"bt_1",  width: 100, align: "right", click: aboutClick },
294
                   { view: "label", label: '',   css: {"color":"black !important", "font-weight": "bold"},  width: 80, align:"right", click: githubClick, tooltip:"GitHub" },                   
295
                  ]
296
                }
297
          ]
298
      }
299
300
     closeMriConvPrgBarWin = () => {
301
           $$("mriConvPrgBarWin").hide(); 
302
     } 
303
304
 
305
      // Progress bar for mri_convert.js
306
     webix.ui({
307
        view:"window",
308
        id: "mriConvPrgBarWin",
309
        height:50,
310
        width:500,
311
        position:"center",
312
        head:false,
313
        body:{
314
          template:"  <div class='w3-container' style='margin-top: 1vh;' id='mriConvertProgBarDiv' > <div class='w3-border' > <div class='w3-green' style='height:2vh;width:0%;' id='mriConvertProgBar'></div> </div> </div> "
315
        }
316
      });
317
318
319
320
      //--------------Preprocessing Options -------------//
321
322
323
      clearInputThreejsWin = () => {
324
          if(inputSceneRendered) {
325
              clearScene(inputScene);
326
              input_gui.domElement.style.display = "none"; 
327
              inputSceneRendered = false;
328
          }
329
330
          $$("inputThreejsWinId").hide();
331
      }  
332
333
334
      showInputPreprocessWindow = () => {
335
        var defer = $.Deferred();
336
        $$("inputThreejsWinId").show();  
337
338
        if(! inputSceneRendered) { // if false,  render 
339
            document.getElementById("inputLoadingIconDiv").style.display = "";
340
        } else {
341
            document.getElementById("inputLoadingIconDiv").style.display = "none";
342
        } 
343
344
        setTimeout(function() {
345
            defer.resolve(); // When this fires, the code in a().then(/..../); is executed.
346
        }, 100);  
347
348
        return defer;         
349
      }
350
351
352
      let inputOptions = {view: "form", css: {"border-radius": "20px"}, id: "inputOptionsId", elements: [ 
353
354
              {  cols: [
355
                          {   
356
                            view: "label", label: "Input 3D (Enhancement)",  
357
                            id: "preprocessId", 
358
                            align: "left"
359
                          },                 
360
361
                          { 
362
                            view: "label", 
363
                            id: "inputPrepIcon", 
364
                            label: '<i class="fa fa-cube fa-lg" aria-hidden="true" id="preprocessIcon" style="opacity:0.9;filter:alpha(opacity=90);"/>', 
365
                            css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
366
                            width: 25,
367
                            height: 25,
368
                            disabled: false,
369
                            click: function() {
370
                                     
371
                                showInputPreprocessWindow().then(res => {    
372
                                    if(! inputSceneRendered) { // if false, will render scene and gui
373
374
                                        // Convert niftiImage arrayBuffer to array
375
                                        //-- let imageDataFlat1D = new Uint8Array(niftiImage);
376
                                        //-- imageDataFlat1D = [...imageDataFlat1D];
377
378
                                        let imageDataFlat1D = arrayBuffer2Array(niftiImage, niftiHeader.datatypeCode );
379
380
                                        // -- tf.tensor(imageDataFlat1D).max().print()
381
                                        
382
                                        if( ! isNiftiFileVerified(niftiHeader)) { // To render also non-sampled MRI
383
                                            imageDataFlat1D = tf.tidy(() => {  
384
                                               //Round image data
385
                                               let imageDataTensor = tf.round(tf.tensor1d(imageDataFlat1D));
386
                                               //Normalize image data
387
                                               return tensor2Array( tf.round(tf.mul(normalizeVolumeData(imageDataTensor), tf.scalar(255)) ) );
388
                                            });
389
                                        }
390
391
                                        // convert to 3D                 // <<<<<<<<<<<<<<<<<<<<<<<<<<<  256 256 256
392
                                        let numSlices = niftiHeader.dims[3];
393
                                        let sliceHeight =  niftiHeader.dims[2];
394
                                        let sliceWidth = niftiHeader.dims[1]; 
395
396
                                        let input = tf.tensor(imageDataFlat1D, [numSlices, sliceHeight , sliceWidth]).reverse(1).arraySync(); 
397
398
                                        let colorLutObj = {};
399
400
                                        // Find voxel values frequency
401
                                        let labelsHistogramMap = arrValuesFreq(imageDataFlat1D);
402
403
                                        // Convert map to object 
404
                                        let labelsHistoObj = map2Object(labelsHistogramMap);
405
                                        delete labelsHistoObj['0'];
406
407
                                        Object.keys(labelsHistoObj).forEach((labelKey, idx) => {
408
                                            colorLutObj[labelKey] =  "rgb(" + labelKey + "," + labelKey + "," + labelKey + ")";
409
                                        }) 
410
411
                                        // clearScene(scene)
412
413
                                        initInput(input, "inputThreejsWinId", 'input-threejs-container', "input_gui_container", "inputLoadingIconDiv", rawNiftiData, niftiImage, colorLutObj );
414
415
                                        inputSceneRendered = true;
416
                                        
417
418
                                    } else { // if already rendered
419
                                         input_gui.domElement.style.display = "";
420
                                    }
421
                                })    
422
423
                            }                        
424
                          }                           
425
426
                      ]    
427
              },  
428
429
                          
430
          ]
431
       }
432
433
434
435
      // Welcome Window
436
      let inputThreejsWinForm = {view: "form", id: "inputThreejsWinFormId", elements: [ 
437
438
              {  cols: [
439
                    {   view: "template", template: "  <div id='input-threejs-container'> <div id='input_gui_container'></div> <div id='inputLoadingIconDiv' class ='panel' style='top: 50%; left: 50%; ' >  <i id='inputLoadingIcon' style='font-size:1.4vw; color: gray; background-color: rgba(0,0,0,0);'  class='w3-xxxlarge w3-spin fa fa-refresh' ></i> </div> </div> <div id='progressLine' class='progress-line'></div>",  
440
                        
441
                    }                 
442
443
                ]  
444
                
445
             },  
446
       
447
448
449
              {  cols: [
450
                        {}, 
451
452
                        { view:"button", id: "inputApplyId", value:"Apply", css:"bt_1", width:100,  
453
                                align:"center",
454
                                disabled: true
455
                        },
456
457
                        { view:"button", value:"Cancel", css:"bt_1", width:100,  
458
                                align:"center",
459
                                click: function() {
460
                                     $$("inputThreejsWinId").hide();
461
                               }
462
                        },  
463
464
                        { view:"button", id: "inputSaveId", value:"Save Copy", css:"bt_1", width:100,  
465
                                align:"center",
466
                                disabled: true
467
                        },                                                                     
468
469
                        {}
470
                       ]
471
              }             
472
473
          ]
474
       }
475
476
477
478
      webix.ui({
479
                  view:"window",
480
                  id: "inputThreejsWinId",
481
                  height: Math.round(window.innerHeight * 0.7),
482
                  width: Math.round(window.innerWidth * 0.7),
483
                  move: true,
484
                  resize: true,
485
                  //width:400,
486
                  head:{
487
                      view:"toolbar", css: "toolbarclass", elements:[
488
                          { type:"header", template: "Input Enhancement", css:"windowtitleclass" },
489
                          { view:"icon", icon:"wxi-close", click:function(){
490
                              $$("inputThreejsWinId").hide();
491
                              input_gui.domElement.style.display="none"; 
492
                          } }
493
                      ]
494
                  },                  
495
                  position:"center",
496
                  body: inputThreejsWinForm
497
                  // body:{
498
                  //      // create custom list with checkboxes for different ROI.
499
                  //      template:"  <div id='input-threejs-container'> <div id='input_gui_container'></div> <div id='inputLoadingIconDiv' class ='panel' style='top: 50%; left: 50%; ' >  <i id='inputLoadingIcon' style='font-size:1.4vw; color: gray; background-color: rgba(0,0,0,0);'  class='w3-xxxlarge w3-spin fa fa-refresh' ></i> </div> </div> "
500
                  // }
501
              }
502
      ) 
503
504
      //-----------------Output 3D Options------------------// 
505
506
      clearOutputThreejsWin = () => {
507
          if(outputSceneRendered) {
508
              clearScene(outputScene);
509
              output_gui.domElement.style.display = "none"; 
510
              document.getElementById('roiList').style.display = "none"; 
511
              document.getElementById('roiItems').innerHTML = '';
512
              outputSceneRendered = false;
513
          }
514
515
          $$("outputThreejsWinId").hide();
516
      }        
517
518
      webix.ui({
519
                  view:"window",
520
                  id: "outputThreejsWinId",
521
                  height: Math.round(window.innerHeight * 0.7),
522
                  width: Math.round(window.innerWidth * 0.7),
523
                  move: true,
524
                  resize: true,
525
                  //width:400,
526
                  head:{
527
                      view:"toolbar", css: "toolbarclass", elements:[
528
                          { type:"header", template: "3D Segmentation Regions", css:"windowtitleclass" },
529
                          { view:"icon", icon:"wxi-close", click:function(){
530
                              $$("outputThreejsWinId").hide();
531
                              output_gui.domElement.style.display="none"; 
532
                          } }
533
                      ]
534
                  },                  
535
536
                  position:"center",
537
  
538
539
                  body:{
540
                       //-- create custom list with checkboxes for different ROI.
541
                       template:"  <div id='output-threejs-container'> <div id='output_gui_container'></div>    <div id='roiList' class='dropdown-check-list'> <span class='anchor'><p style='color:white; font-size:0.6vw; margin-top: 0em; margin-bottom: 0em;'>Select_ROI</p></span>  <ul id='roiItems' class='items'></ul> </div>  <div id='loadingIconDiv' class ='panel' style='top: 50%; left: 50%; ' > <i id='loadingIcon' style='font-size:1.4vw; color: gray; background-color: rgba(0,0,0,0);'  class='w3-xxxlarge w3-spin fa fa-refresh' ></i> </div>    </div> "
542
                  }
543
      })  
544
545
546
      //-----------------Open Brain T1 MRI ----------------------------------//  
547
548
      let referenceImage = { view: "form", css: {"border-radius": "20px"},  id: "referenceImage", elements: [ 
549
              { type:"header", template:"Open Brain T1 MRI", 
550
                css: "headerclass"
551
              },
552
553
              { cols:[ 
554
                      {  view: "label", label: "Select NIfTI file",  
555
                          align: "left"
556
                      }, 
557
558
                      { 
559
                       view: "uploader" , value: "Browse", width: 100, id:"imageUploader", 
560
561
                       css:"bt_1",
562
                       align: "left",
563
                       accept: "image/nii, image/nii.gz",
564
                       autosend: false, 
565
                       multiple: false, 
566
                       on: {        
567
                              onBeforeFileAdd: function(upload) {        
568
                                     let file = upload.file;
569
                                     //--file{name: "test.nii", lastModified: 1548792883000, webkitRelativePath: "", size: 503010, type: ""}
570
                                     if(file["name"].search(".nii") > 0) {
571
572
                                          refFileName = file["name"];
573
574
                                          let reader = new FileReader();  
575
576
                                          var blob = makeSlice(file, 0, file.size);
577
578
                                          reader.onloadend = function(event) {
579
580
                                              if (event.target.readyState === FileReader.DONE) {
581
                                                  //--evt.target.result is :  ArrayBuffer { byteLength: 763810 }
582
583
                                                  // params_mri["files"] = [file]; 
584
                                                  // Or
585
                                                  params_mri["binaryImages"] = [event.target.result];
586
587
588
589
                                                  // Set 0 for MRI viewer 
590
                                                  papaya.Container.resetViewer(0, params_mri);
591
                                                  papaya.Container.atlas = null;
592
                                                  // Set 1 for label viewer, need this step to remove startup image
593
                                                  // papaya.Container.resetViewer(1);
594
                                                  papayaContainers[1].viewer.resetViewer();
595
                                                  papayaContainers[1].viewer.canvas.removeEventListener('mousemove', mouseMoveHandler);
596
                                                  papayaContainers[1].viewer.canvas.removeEventListener('mouseout', mouseOutHandler);
597
598
                                                  let startTime = performance.now(); 
599
                                                  // decompress/check uploaded Nifti data
600
                                                  rawNiftiData = getNiftiRawData(event.target.result);
601
602
                                                  if(rawNiftiData == null) {
603
                                                      let loadingErrorTxt = "This file not Nifti or corrupted. Please try again";
604
605
                                                      webix.alert({
606
                                                        type:"alert-error",
607
                                                         width:"35%",
608
                                                        text: loadingErrorTxt
609
                                                      });
610
                                                     
611
                                                     $$("segmentBtn").disable();
612
                                                     // Disable input 3d preprocess button
613
                                                     $$("inputPrepIcon").disable();
614
                                                     document.getElementById("preprocessIcon").style.opacity = 0.3;  
615
                                                     document.getElementById("preprocessIcon").style.filter = "alpha(opacity=30)";                                                     
616
                                                     return 0;
617
                                                  }
618
619
                                                  // If new MRI loaded, clear last 3D window if rendered before.
620
                                                  clearInputThreejsWin();
621
                                                  $$("inputPrepIcon").enable();
622
                                                  document.getElementById("preprocessIcon").style.opacity = 0.9;  
623
                                                  document.getElementById("preprocessIcon").style.filter = "alpha(opacity=90)";                                                   
624
625
                                                  niftiHeader = readNiftiHeader(rawNiftiData);
626
                                                  niftiImage = readNiftiImageData(niftiHeader, rawNiftiData); 
627
628
                                                  statData["Data_Load"] = ((performance.now() - startTime)/1000).toFixed(4);
629
                                                  statData["Resampled"] = null;
630
631
                                                  statData["File_Type"] =  nifti.isNIFTI1(rawNiftiData) ? "NIFTI-1" : nifti.isNIFTI2(rawNiftiData) ? "NIFTI-2" : "NOT-NIFTI"; 
632
                                                  statData["Img_Size"] = JSON.stringify([niftiHeader.dims[1], niftiHeader.dims[2], niftiHeader.dims[3]]);  
633
                                                  statData["Num_Bits_Per_Voxel"] = niftiHeader['numBitsPerVoxel'] ;
634
                                                  statData["Data_Type_Code"] = niftiHeader['datatypeCode'];
635
                                                  statData["Vox_Offset"] = niftiHeader['vox_offset'];
636
                                                  statData["Vox_1mm"] = isVoxelSize1mm(niftiHeader);
637
                                                  statData["File_Verified"] = isNiftiFileVerified(niftiHeader);                                                  
638
                                                  //-- Check if Nifti file is already converted and no need to mri_convert.js
639
                                                  if( ! isNiftiFileVerified(niftiHeader)) {
640
641
                                                      let reasonTxt = "", actionTxt = "", actionCounter = 0;
642
                                                      //-- Check  Nifti file shape
643
                                                      if( (niftiHeader.dims[1]!= 256) || (niftiHeader.dims[2]!= 256) || (niftiHeader.dims[3]!= 256) ) {
644
                                                           reasonTxt = " <br> Image shape should be 256x256x256.<br>";
645
                                                           actionTxt = "reshaped";
646
                                                           actionCounter += 1;
647
                                                           $$("segmentBtn").disable();
648
                                                      } 
649
                                                      //-- Check  Nifti file data type 
650
                                                      if ( (niftiHeader['numBitsPerVoxel'] != 8) || (niftiHeader['datatypeCode'] != 2) ) {
651
                                                           reasonTxt = reasonTxt + " <br> Data type should be integer in range 0-255.<br>";
652
                                                           if(actionCounter) { actionTxt += "/"  }
653
                                                           actionTxt += "scaled ";
654
                                                           actionCounter += 1;                                                           
655
                                                           $$("segmentBtn").disable();
656
                                                      }  
657
658
                                                      //-- Check  Nifti file data type 
659
                                                      if ( ! isVoxelSize1mm(niftiHeader)) {
660
                                                           reasonTxt = reasonTxt + " <br> Voxels should be 1x1x1 mm thickness.<br>";
661
                                                           if(actionCounter) { actionTxt += "/"  }
662
                                                           actionTxt += "resampled ";
663
                                                           actionCounter += 1;                                                           
664
                                                           $$("segmentBtn").disable();
665
                                                      } 
666
667
                                                      let alertTxt = "MRI needs to be " + actionTxt.bold() + " for proper results. <br>" + reasonTxt.fontcolor("red").bold()  +  " <br> For more information please refer to  <a href='https://github.com/neuroneural/brainchop/wiki/Input-Data' target='_blank'><b>Input Data</b></a> section.";
668
669
670
                                                      webix.confirm({
671
                                                        title:"",
672
                                                        ok:"Apply", 
673
                                                        cancel:"Cancel",
674
                                                        type: "confirm-error",
675
                                                        width: 500,
676
                                                        text: alertTxt
677
                                                      })
678
                                                        .then(() => {
679
                                                               //---
680
                                                            console.log("Starting mri_convert") 
681
                                                            let blob = new Blob( params_mri["binaryImages"] , {type: "application/octet-binary;charset=utf-8"});
682
                                                            let rawImgurl = window.URL.createObjectURL(blob);
683
                                                             
684
                                                            mri_convert(rawImgurl, rawNiftiData, file["name"]).then(function(res) {
685
                                                                rawNiftiData = res;
686
                                                                niftiHeader = readNiftiHeader(rawNiftiData);
687
                                                                niftiImage = readNiftiImageData(niftiHeader, rawNiftiData); 
688
689
                                                                params_mri["binaryImages"] = [rawNiftiData];
690
                                                                papaya.Container.resetViewer(0, params_mri);
691
                                                                
692
                                                                // If still not correctly converted
693
                                                                if(! isNiftiFileVerified(niftiHeader)) { 
694
                                                                      webix.alert(" File can not be resampled. <br> For more options please refer to  <a href='https://github.com/neuroneural/brainchop/wiki/Input-Data' target='_blank'><b>Input Data</b></a> section.");
695
696
                                                                      statData["Resampled"] = false;
697
                                                                      submitTiming2GoogleSheet(statData);
698
699
                                                                } else {
700
701
                                                                      statData["Resampled"] = true;  
702
                                                                }
703
                                                                // enable it for all cases 
704
                                                                $$("segmentBtn").enable(); 
705
                                                                  
706
707
                                                            });
708
709
                                                      })
710
                                                        .fail(() => {
711
                                                               //---
712
                                                               $$("segmentBtn").disable();
713
                                                               statData["Brainchop_Ver"] = "Cancelled"
714
                                                               submitTiming2GoogleSheet(statData);
715
                                                      });
716
717
                                                  } else { // Not varified file
718
719
                                                                                                                                                
720
                                                      if(checkGPU()) {
721
                                                         $$("segmentBtn").enable();
722
                                                      }
723
                                                  }
724
725
                                                  numOfOverlays = 0;   
726
                                                  $$("downloadBtn").disable(); 
727
                                                  $$("out3DIcon").disable(); 
728
                                                  $$("outChartIcon").disable();       
729
                                                  document.getElementById("out3D-1").style.opacity = 0.3;
730
                                                  document.getElementById("outChart-1").style.opacity = 0.3;  
731
                                                  document.getElementById("out3D-1").style.filter = "alpha(opacity=30)";
732
                                                  document.getElementById("outChart-1").style.filter = "alpha(opacity=30)";                                                     
733
                                              }
734
                                         };
735
736
                                         reader.readAsArrayBuffer(blob);
737
738
                                    } else {
739
                                      webix.message("Select Nifti file *.nii / *.nii.gz")
740
                                    
741
                                    }     
742
                                    
743
                                    return false;
744
745
                              }
746
                          }
747
                      }
748
                    ] 
749
               }
750
         ]
751
      }
752
753
     // function to get masking model from inferenceModelsList
754
     getMaskingModels = () => {
755
        return ["", "Full Brain GWM (light)", "Full Brain GWM (H Acc & H Crop)"];
756
     }
757
758
      let modelBrowsingForm = {view: "form", id: "modelBrowsingFormId", elements: [ 
759
              {  cols: [
760
                    { view: "label", label: "Model*", align: "left"},                 
761
                    { view:"icon", id: "modelIconId", icon:"", align: "left"},
762
                      { 
763
                       view: "uploader" , value: "Load", width: 100, id:"modelUploader", 
764
765
                       css:"bt_1",
766
                       align: "left",
767
                       accept: "application/json",
768
                       autosend: false, 
769
                       multiple: false, 
770
                       on: {        
771
                              onBeforeFileAdd: function(upload) {        
772
                                     let file = upload.file;
773
                                     modelFile = file;
774
                                     console.log("modelFile :", modelFile)
775
                                     //--file{name: "model.json", lastModified: 1548792883000, webkitRelativePath: "", size: 5030, type: ""}
776
                                     if(modelFile["name"].search(".json") > 0) {
777
                                          let reader = new FileReader();  
778
                                          let blob = makeSlice(file, 0, file.size);
779
                                          reader.onloadend = function(event) {
780
781
                                              if (event.target.readyState === FileReader.DONE) {
782
                                                  //--evt.target.result is :  ArrayBuffer { byteLength: 763810 }
783
                                                  $$("modelIconId").config.icon = "wxi-check";
784
                                                  $$("modelIconId").refresh();                                                  
785
                                              }
786
                                         };
787
788
                                         reader.readAsArrayBuffer(blob);
789
790
                                    } else {
791
                                      webix.message("Select model json file *.json")
792
                                    
793
                                    }     
794
                                    
795
                                    return false;
796
797
                              }
798
                          }
799
                      }
800
801
802
                ]    
803
             },             
804
805
              {  cols: [
806
                    { view: "label", label: "Weights*", align: "left"},                 
807
                    { view:"icon", id: "weightsIconId", icon:"", align: "left"},
808
                     { 
809
                       view: "uploader" , value: "Load", width: 100, id:"weightUploader", 
810
811
                       css:"bt_1",
812
                       align: "left",
813
                       accept: "application/bin",
814
                       autosend: false, 
815
                       multiple: false, 
816
                       on: {        
817
                              onBeforeFileAdd: function(upload) {        
818
                                      let file = upload.file;
819
                                      weightFile = file;
820
                                      console.log("weightFile :", weightFile)
821
                                      //--file{name: "weights.bin", lastModified: 1548792883000, webkitRelativePath: "", size: 503010, type: ""}
822
                                     if(weightFile["name"].search(".bin") > 0) {
823
                                          let reader = new FileReader();  
824
825
                                          var blob = makeSlice(file, 0, file.size);
826
827
                                          reader.onloadend = function(event) {
828
829
                                              if (event.target.readyState === FileReader.DONE) {
830
                                                  //evt.target.result is :  ArrayBuffer { byteLength: 763810 }
831
                                                  $$("weightsIconId").config.icon = "wxi-check";
832
                                                  $$("weightsIconId").refresh();
833
                                              }
834
                                         };
835
836
                                         reader.readAsArrayBuffer(blob);
837
838
                                    } else {
839
                                      webix.message("Select weights binary file *.bin")
840
                                    
841
                                    }     
842
                                    
843
                                    return false;
844
                              }
845
                          }
846
                      }
847
                ]    
848
             },
849
850
              {  cols: [
851
                    { view: "label", label: "Labels", align: "left"},                 
852
                    { view:"icon", id: "labelsIconId", icon:"", align: "left"},
853
                     { 
854
                       view: "uploader" , value: "Load", width: 100, id:"labelUploader", 
855
856
                       css:"bt_1",
857
                       align: "left",
858
                       accept: "application/json",
859
                       autosend: false, 
860
                       multiple: false, 
861
                       on: {        
862
                              onBeforeFileAdd: function(upload) {        
863
                                      let file = upload.file;
864
                                      labelFile = file;
865
                                      console.log("labelFile :", labelFile)
866
                                      //--file{name: "weights.bin", lastModified: 1548792883000, webkitRelativePath: "", size: 503010, type: ""}
867
                                     if(labelFile["name"].search(".json") > 0) {
868
                                          let reader = new FileReader();  
869
870
                                          var blob = makeSlice(file, 0, file.size);
871
872
                                          reader.onloadend = function(event) {
873
874
                                              if (event.target.readyState === FileReader.DONE) {
875
                                                  //evt.target.result is :  ArrayBuffer { byteLength: 763810 }
876
                                                  $$("labelsIconId").config.icon = "wxi-check";
877
                                                  $$("labelsIconId").refresh();
878
                                              }
879
                                         };
880
881
                                         reader.readAsArrayBuffer(blob);
882
883
                                    } else {
884
                                      webix.message("Select labels json file *.json")
885
                                    
886
                                    }     
887
                                    
888
                                    return false;
889
                              }
890
                          }
891
                      }
892
                ]    
893
             },
894
895
              {  cols: [
896
                    { view: "label", label: "Colors", align: "left"},                 
897
                    { view:"icon", id: "colorsIconId", icon:"", align: "left"},
898
                     { 
899
                       view: "uploader" , value: "Load", width: 100, id:"colorUploader", 
900
901
                       css:"bt_1",
902
                       align: "left",
903
                       accept: "application/json",
904
                       autosend: false, 
905
                       multiple: false, 
906
                       on: {        
907
                              onBeforeFileAdd: function(upload) {        
908
                                      let file = upload.file;
909
                                      colorFile = file;
910
                                      console.log("colorFile :", colorFile)
911
                                      //--file{name: "weights.bin", lastModified: 1548792883000, webkitRelativePath: "", size: 503010, type: ""}
912
                                     if(colorFile["name"].search(".json") > 0) {
913
                                          let reader = new FileReader();  
914
915
                                          var blob = makeSlice(file, 0, file.size);
916
917
                                          reader.onloadend = function(event) {
918
919
                                              if (event.target.readyState === FileReader.DONE) {
920
                                                  //--evt.target.result is :  ArrayBuffer { byteLength: 763810 }
921
                                                  $$("colorsIconId").config.icon = "wxi-check";
922
                                                  $$("colorsIconId").refresh();
923
924
                                              }
925
                                         };
926
927
                                         reader.readAsArrayBuffer(blob);
928
929
                                    } else {
930
                                      webix.message("Select colors json file *.json")
931
                                    
932
                                    }     
933
                                    return false;
934
                              }
935
                          }
936
                      }
937
                ]    
938
             },
939
940
              {  cols: [
941
                    {   view: "label", label: "Transpose Input",  
942
                        align: "left"
943
                    }, 
944
945
                    { view: "select", 
946
                      id: "transposeStatus", 
947
                      value: 1, 
948
                      options: ["true", "false"]
949
                    },
950
951
                    { 
952
                      view: "label", 
953
                      label: '<img src="css/svg/info-circle-solid.svg"/>', 
954
                      css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
955
                      width: 25,
956
                      height: 25,
957
                      click: function() {
958
                          let info = "Transpose 3D MRI input data axis for best inference input orientation."
959
                          $$("modelTooltip").show();
960
                          document.getElementById("tooltipDiv").innerHTML = 
961
                          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"
962
963
                      }                        
964
                    }                                                         
965
966
                ]    
967
             }, 
968
969
970
              {  cols: [
971
                    {   view: "label", label: "Input Quantile Norm",  
972
                        align: "left"
973
                    }, 
974
975
                    { view: "select", 
976
                      id: "quantileNormStatus", 
977
                      value: 1, 
978
                      options: ["false", "true"]
979
                    },
980
981
                    { 
982
                      view: "label", 
983
                      label: '<img src="css/svg/info-circle-solid.svg"/>', 
984
                      css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
985
                      width: 25,
986
                      height: 25,
987
                      click: function() {
988
                          let info = "Apply Quantile normalization to input MRI, if false then Min-Max normalization will be applied."
989
                          $$("modelTooltip").show();
990
                          document.getElementById("tooltipDiv").innerHTML = 
991
                          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"
992
993
                      }                        
994
                    }                                                         
995
996
                ]    
997
             }, 
998
999
1000
              {  cols: [
1001
                    {   view: "label", label: "Auto Threshold Input",  
1002
                        align: "left"
1003
                    }, 
1004
1005
                    {
1006
                      view: "select", 
1007
                      id: "autoThresholdStatus", 
1008
                      value: 1, 
1009
                      options: ["0", "0.1", "0.2"]
1010
                    },
1011
1012
                    { 
1013
                      view: "label", 
1014
                      label: '<img src="css/svg/info-circle-solid.svg"/>', 
1015
                      css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1016
                      width: 25,
1017
                      height: 25,
1018
                      click: function() {
1019
                          let info = "For speed-up the inference with limited browser memory, auto thresholding the brain from noisy voxels around it before cropping it and feeding the result to the inference model can lower memory use.";
1020
                          $$("modelTooltip").show();
1021
                          document.getElementById("tooltipDiv").innerHTML = 
1022
                          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"
1023
                      }                        
1024
                    } 
1025
1026
                ]    
1027
             },  
1028
1029
1030
1031
1032
1033
              {  cols: [
1034
                    {   view: "label", label: "Crop Input",  
1035
                        align: "left"
1036
                    }, 
1037
1038
                    {
1039
                      view: "select", 
1040
                      id: "cropStatus", 
1041
                      value: 1, 
1042
                      options: ["false", "true"],
1043
                      on: {
1044
                        onChange: function(newValue, oldValue, config) {
1045
                          if(newValue == "true") {
1046
                               $$("cropPadStatus").enable();
1047
                               $$("preModelStatus").enable();
1048
1049
                          } else {
1050
                               $$("cropPadStatus").disable();
1051
                               $$("cropPadStatus").setValue(0);
1052
                               $$("preModelStatus").setValue("");
1053
                               $$("filterByMaskStatus").setValue("false");
1054
                          }
1055
1056
                        }
1057
                      }                      
1058
                    },
1059
1060
                    { 
1061
                      view: "label", 
1062
                      label: '<img src="css/svg/info-circle-solid.svg"/>', 
1063
                      css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1064
                      width: 25,
1065
                      height: 25,
1066
                      click: function() {
1067
                          let info = "For speed-up the inference with limited browser memory, cropping brain from background before feeding the result to the inference model can lower memory use.";
1068
                          $$("modelTooltip").show();
1069
                          document.getElementById("tooltipDiv").innerHTML = 
1070
                          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"
1071
                      }                        
1072
                    } 
1073
1074
                ]    
1075
             },   
1076
1077
              {  cols: [
1078
                    {   view: "label", label: "Crop Padding",  
1079
                        align: "left"
1080
                    }, 
1081
1082
                    { view: "select", 
1083
                      id: "cropPadStatus", 
1084
                      value: 0, 
1085
                      disabled: true,
1086
                      options: ["0", "1", "2", "3"]
1087
                    },
1088
                    
1089
                    { 
1090
                      view: "label", 
1091
                      label: '<img src="css/svg/info-circle-solid.svg"/>', 
1092
                      css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1093
                      width: 25,
1094
                      height: 25,
1095
                      click: function() {
1096
                          let info = "Add Padding to cropped brain 3D image. ";
1097
                          $$("modelTooltip").show();
1098
                          document.getElementById("tooltipDiv").innerHTML = 
1099
                          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"
1100
                      }                        
1101
                    }                                                         
1102
1103
                ]    
1104
             }, 
1105
1106
              {  cols: [
1107
                    {   view: "label", label: "Crop by Pre-Model",  
1108
                        align: "left"
1109
                    }, 
1110
1111
                    { 
1112
                      view: "select", 
1113
                      id: "preModelStatus", 
1114
                      value: 1, 
1115
                      disabled: true,
1116
                      options:  getMaskingModels(),
1117
                      on: {
1118
                        onChange: function(newValue, oldValue, config) {
1119
                          if(newValue !== "") {
1120
                               $$("filterByMaskStatus").enable();
1121
                               $$("preModelPostprocessStatus").enable();
1122
            
1123
                          } else {
1124
                               $$("filterByMaskStatus").disable();
1125
                               $$("filterByMaskStatus").setValue("false");
1126
                               $$("preModelPostprocessStatus").disable();
1127
                               $$("preModelPostprocessStatus").setValue("false");                               
1128
                          }
1129
1130
                        }
1131
                      }  
1132
1133
                    },
1134
1135
                    { 
1136
                      view: "label", 
1137
                      label: '<img src="css/svg/info-circle-solid.svg"/>', 
1138
                      css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1139
                      width: 25,
1140
                      height: 25,
1141
                      click: function() {
1142
                          let info = "Select a masking model for cropping the brain.";
1143
                          $$("modelTooltip").show();
1144
                          document.getElementById("tooltipDiv").innerHTML = 
1145
                          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"
1146
                      }                        
1147
                    }                        
1148
1149
1150
                ]    
1151
             },   
1152
1153
1154
              {  cols: [
1155
                    {   view: "label", label: "Pre-Model Postprocess",  
1156
                        align: "left"
1157
                    }, 
1158
1159
                    { 
1160
                      view: "select", 
1161
                      id: "preModelPostprocessStatus", 
1162
                      value: 1, 
1163
                      disabled: true,
1164
                      options:  ["false", "true"]
1165
                    },
1166
1167
                    { 
1168
                      view: "label", 
1169
                      label: '<img src="css/svg/info-circle-solid.svg"/>', 
1170
                      css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1171
                      width: 25,
1172
                      height: 25,
1173
                      click: function() {
1174
                          let info = "Select if postprocessing is needed after preModel complete brain masking inference";
1175
                          $$("modelTooltip").show();
1176
                          document.getElementById("tooltipDiv").innerHTML = 
1177
                          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"
1178
                      }                        
1179
                    }                        
1180
1181
1182
                ]    
1183
             },                                               
1184
1185
              {  cols: [
1186
                    {   view: "label", label: "Filter Output by Mask",  
1187
                        align: "left"
1188
                    }, 
1189
1190
                    { 
1191
                      view: "select", 
1192
                      id: "filterByMaskStatus", 
1193
                      disabled: true,
1194
                      value: 1, 
1195
                      options: ["false", "true"]
1196
                    },
1197
1198
                    { 
1199
                      view: "label", 
1200
                      label: '<img src="css/svg/info-circle-solid.svg"/>', 
1201
                      css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1202
                      width: 25,
1203
                      height: 25,
1204
                      click: function() {
1205
                          let info = "This is a voxel-wise multiplication of resulted output and the mask resulted from the pre-model inference. This option can be used to clean any wrongly segmented regions (e.g. skull areas) but also it can result in removing some properly segmented regions."
1206
                          $$("modelTooltip").show();
1207
                          document.getElementById("tooltipDiv").innerHTML = 
1208
                          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"
1209
                      }                        
1210
                    }                                                          
1211
1212
                ]    
1213
             },
1214
1215
              {  cols: [
1216
                    {   view: "label", label: "Sequential Convolution",  
1217
                        align: "left"
1218
                    }, 
1219
1220
                    { 
1221
                      view: "select", 
1222
                      id: "seqConvStatus", 
1223
                      disabled: false,
1224
                      value: 1, 
1225
                      options: ["false", "true"]
1226
                    },
1227
1228
                    { 
1229
                      view: "label", 
1230
                      label: '<img src="css/svg/info-circle-solid.svg"/>', 
1231
                      css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1232
                      width: 25,
1233
                      height: 25,
1234
                      click: function() {
1235
                          let info = "This is enabling sequential convolution layer instead of the model last layer for segmenation. This option can be used to make large and atlas models run on limited resources browser but it takes longer time for inference."
1236
                          $$("modelTooltip").show();
1237
                          document.getElementById("tooltipDiv").innerHTML = 
1238
                          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"
1239
                      }                        
1240
                    }                                                          
1241
1242
                ]    
1243
             },
1244
1245
              {  cols: [
1246
                    {   view: "label", label: "Model Name*",  
1247
                        align: "left"
1248
                    },                 
1249
                    { view: "text", 
1250
                      placeholder: "New Model ",  
1251
                      id: "proposedModelName", 
1252
                      disabled: false,
1253
                      align: "left"
1254
                    }
1255
                ]    
1256
             },              
1257
              {  cols: [
1258
                        {}, 
1259
                          { view:"button", value:"Confirm", css:"bt_1", width:100,  
1260
                                align:"center",
1261
                                click: function() {
1262
1263
                                    if(modelFile && weightFile) {
1264
1265
                                           let newId = parseInt(inferenceModelsList.at(-1)["id"]) + 1;
1266
                                           newId = newId.toString();
1267
1268
                                           // Model list loaded from browsing
1269
                                           browserModelList.push({id: newId, modelFile: modelFile, weightFile: weightFile, colorFile: colorFile, labelFile: labelFile });
1270
1271
                                           let labelFileUrl = null;
1272
1273
                                           if(browserModelList.at(-1)["labelFile"]) {
1274
                                                labelFileUrl = URL.createObjectURL(browserModelList.at(-1)["labelFile"]);
1275
                                           } 
1276
1277
                                           let colorFileUrl = null;
1278
1279
                                           if(browserModelList.at(-1)["colorFile"]) {
1280
                                                colorFileUrl = URL.createObjectURL(browserModelList.at(-1)["colorFile"]);
1281
                                           } 
1282
1283
                                           let newModelName = "New Model " + newId;
1284
                                           if($$("proposedModelName").getValue().length && isLetter($$("proposedModelName").getValue()[0])){
1285
                                                newModelName =   $$("proposedModelName").getValue().trim();
1286
                                                // Check new imported model name possible redundancy
1287
                                                if ( inferenceModelsList.filter(entry => entry.modelName == newModelName).length ) {
1288
                                                      webix.message("Please select unique model name");
1289
                                                      return 0;
1290
                                                }
1291
1292
                                            } else if($$("proposedModelName").getValue().length) {
1293
                                                webix.message("Please select valid model name");
1294
                                                return 0;
1295
                                            } else if(isLetter($$("proposedModelName").getValue()[0])) {
1296
                                                webix.message("Please select model name");
1297
                                                return 0;
1298
                                            }
1299
1300
                                           let preModelIdVal = null;
1301
                                           if($$("preModelStatus").getValue().length ){
1302
                                                preModelIdVal =   inferenceModelsList.filter(entry => entry.modelName === $$("preModelStatus").getValue())[0].id;
1303
1304
                                            } 
1305
1306
1307
                                           inferenceModelsList.push({
1308
                                                 id: newId, 
1309
                                                 type: "Segmentation", 
1310
                                                 path: null, 
1311
                                                 modelName: newModelName, 
1312
                                                 labelsPath: labelFileUrl, 
1313
                                                 colorsPath: colorFileUrl, 
1314
                                                 preModelId: preModelIdVal,
1315
                                                 preModelPostProcess: ($$("preModelPostprocessStatus").getValue() === 'true'),
1316
                                                 isBatchOverlapEnable: false, 
1317
                                                 numOverlapBatches: 0, 
1318
                                                 enableTranspose:($$("transposeStatus").getValue() === 'true'),
1319
                                                 enableCrop: ($$("cropStatus").getValue() === 'true'), 
1320
                                                 cropPadding: parseInt($$("cropPadStatus").getValue()), 
1321
                                                 autoThreshold: parseInt($$("autoThresholdStatus").getValue()),
1322
                                                 enableQuantileNorm: ($$("quantileNormStatus").getValue() === 'true'),
1323
                                                 filterOutWithPreMask: ($$("filterByMaskStatus").getValue() === 'true'), 
1324
                                                 enableSeqConv: ($$("seqConvStatus").getValue() === 'true'), 
1325
                                                 textureSize: 0,
1326
                                                 warning: null, 
1327
                                                 inferenceDelay: 100,             
1328
                                                 description: ""                                            
1329
                                           })
1330
1331
1332
                                           selectModelOptions = getSegmentationFormOptions(inferenceModelsList);
1333
                                           $$("selectModel").config.options = selectModelOptions;
1334
                                           $$("selectModel").config.value = parseInt(newId);
1335
                                           $$("selectModel").refresh();
1336
                                           
1337
                                           $$("modelBrowsingWindow").hide(); 
1338
                                           $$("proposedModelName").setValue("");
1339
1340
1341
                                    } else if(weightFile) { 
1342
                                           webix.message("Please select the model json file");
1343
                                    
1344
                                    } else {
1345
                                           webix.message("Please select the model weights file");
1346
                                    }  
1347
                               }
1348
                           },
1349
                          {}
1350
                       ]
1351
              }      
1352
1353
          ]
1354
       }
1355
1356
1357
1358
      // Model Browsing Window to select local model
1359
      webix.ui({
1360
                  view:"window",
1361
                  id: "modelBrowsingWindow",
1362
                  height:770,
1363
                  width:400,
1364
                  head:{
1365
                      view:"toolbar", css: "toolbarclass", elements:[
1366
                          { type:"header", template:"Load tfjs Model", css:"windowtitleclass" },
1367
                          { view:"icon", icon:"wxi-close", click:function(){
1368
                              $$("modelBrowsingWindow").hide();
1369
                              $$("proposedModelName").setValue("");
1370
                          } }
1371
                      ]
1372
                  },                  
1373
1374
                  position:"center",
1375
                  body: modelBrowsingForm
1376
      })
1377
1378
      // Reset check icon
1379
      $$("modelBrowsingWindow").attachEvent("onShow", function(){
1380
          $$("weightsIconId").config.icon = "";
1381
          $$("modelIconId").config.icon = "";
1382
          $$("labelsIconId").config.icon = "";
1383
          $$("colorsIconId").config.icon = "";
1384
          $$("weightsIconId").refresh();
1385
          $$("modelIconId").refresh();
1386
          $$("labelsIconId").refresh();
1387
          $$("colorsIconId").refresh();  
1388
          $$("cropStatus").setValue("false"); 
1389
          $$("seqConvStatus").setValue("false");    
1390
          modelFile = null;
1391
          weightFile = null;
1392
          labelFile = null;
1393
          colorFile = null;
1394
      });
1395
1396
1397
      getSegmentationFormOptions = (modelsList) => {
1398
          let selectedOptions = [];
1399
1400
          modelsList.forEach( function(modelEntry) {
1401
             selectedOptions.push({id: modelEntry["id"], value: modelEntry["modelName"], path: modelEntry["path"] });
1402
          })
1403
1404
          selectedOptions.push({id: "Browse...", value: "Browse...", path: null });
1405
1406
          return selectedOptions
1407
      }
1408
1409
      // To load models dynamically from  inferenceModelsList
1410
      let selectModelOptions = getSegmentationFormOptions(inferenceModelsList);
1411
      
1412
      //-- Count total number of models excluding browsing models
1413
      numOfModelsWithoutBrowse =  inferenceModelsList.length;
1414
1415
1416
      onShowWarningCheck = (checkElem) => {
1417
           webix.storage.local.put("noShowWarning", document.getElementById("noShowWarningId").checked);
1418
      }
1419
1420
      fetchModelWarningStatus = () => {
1421
          return webix.storage.local.get("noShowWarning");
1422
      }       
1423
1424
      onShowTooltipCheck = (checkElem) => {
1425
          webix.storage.local.put("noShowTooltip", document.getElementById("noShowTooltipId").checked);
1426
1427
      }
1428
1429
      fetchModelTooltipStatus = () => {
1430
          return webix.storage.local.get("noShowTooltip");
1431
      } 
1432
1433
      getModelinfo = () => {
1434
          let modelDescription = inferenceModelsList[$$("selectModel").getValue() - 1]["description"];
1435
          let info = "No info for this model";
1436
          
1437
          if(modelDescription) {
1438
              info = modelDescription;
1439
          } 
1440
1441
          $$("modelTooltip").show();
1442
          document.getElementById("tooltipDiv").innerHTML = 
1443
          "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ info +" </font>"          
1444
      }
1445
1446
      resestDependency = () => {
1447
        //-- For future use
1448
        document.getElementById("out3D-1").style.opacity = 0.3;
1449
        document.getElementById("outChart-1").style.opacity = 0.3;  
1450
        document.getElementById("out3D-1").style.filter = "alpha(opacity=30)";
1451
        document.getElementById("outChart-1").style.filter = "alpha(opacity=30)";      
1452
        document.getElementById("progressBarChild").parentElement.style.visibility = "hidden";
1453
        document.getElementById("progressBarChild").style.width = 0;        
1454
        //-- Reset papaya MRI viewer overlay if exists
1455
        resetMriViewerOverlay(1);
1456
        //-- Reset Label Viewer        
1457
        resetLabelViewer();  
1458
        clearOutputThreejsWin();      
1459
        resetMainParameters();                 
1460
      }     
1461
1462
      disableControls = () => {
1463
         $$("out3DIcon").disable(); 
1464
         $$("outChartIcon").disable();   
1465
         $$("downloadBtn").disable(); 
1466
         $$("segmentBtn").disable(); 
1467
      }     
1468
1469
      newRunInferencePrepare = () => {
1470
        var defer = $.Deferred();
1471
        disableControls();
1472
        resestDependency();
1473
       
1474
        setTimeout(function() {
1475
            defer.resolve(); // When this fires, the code in a().then(/..../); is executed.
1476
        }, 100);  
1477
1478
        return defer;
1479
      }
1480
1481
      let Segmentation = {view: "form", css: {"border-radius": "20px"}, id: "Segmentation", elements: [ 
1482
              { type:"header", template:"Segmentation Options",
1483
                css: "headerclass"
1484
              },
1485
1486
              { cols: [
1487
1488
                      { view:"select", 
1489
                        label:"Models", 
1490
                        id: "selectModel",
1491
                        value:1, 
1492
                        options: selectModelOptions,
1493
                        on:{        
1494
                            onChange: function(newValue, oldValue, config) {   
1495
                               const selectedModelEntry = selectModelOptions[this.getValue() - 1]; 
1496
1497
                               if(newValue == "Browse...") {
1498
                                      console.log("Browse selected ..")
1499
                                      $$("modelBrowsingWindow").show();
1500
                                    
1501
                               } else if (selectedModelEntry["path"]) {
1502
                                             modelObject = {};
1503
                                             const modelEntry = selectModelOptions[this.getValue() - 1]; 
1504
1505
                                             let curModelEntry = inferenceModelsList.filter(entry => entry.id == this.getValue().toString())[0];
1506
1507
                                             let noShowWarningStatus = fetchModelWarningStatus();
1508
1509
                                             if(! noShowWarningStatus) {
1510
1511
                                                 if (curModelEntry["warning"] != null) {
1512
                                                        $$("modelTooltip").show();
1513
                                                        document.getElementById("tooltipDiv").innerHTML = 
1514
                                                        "<i style='font-size:1.4vw'  class='fa fa-exclamation-triangle' ></i> <font style='font-size:0.9vw' >&nbsp&nbsp"+ curModelEntry["warning"] +" </font><br><br><input type='checkbox' id='noShowWarningId' onclick='onShowWarningCheck(this)' name='noShowWarning'><label for='noShowWarning' style='color: black'> Don't show again</label>"
1515
                                               
1516
                                                 }
1517
                                             }    
1518
1519
1520
                                             let noShowTooltipStatus = fetchModelTooltipStatus();
1521
                                             
1522
                                             if(! noShowTooltipStatus) {                                     
1523
1524
                                                 if (curModelEntry["textureSize"] != 0) {
1525
                                                    //--Compare model needed texture size and browser max texture size 
1526
                                                    if(curModelEntry["textureSize"] > getMaxTextureSize()) {
1527
                                                        $$("modelTooltip").show();
1528
1529
                                                        let warningMessage = "Current browser may not able to run selected model. " + curModelEntry["warning"];
1530
                                                        // $$("modelTooltip").config.body.template = 
1531
                                                        document.getElementById("tooltipDiv").innerHTML = 
1532
                                                        "<i style='font-size:1.4vw'  class='fa fa-info-circle' ></i> <font style='font-size:0.77vw' >&nbsp&nbsp"+ warningMessage +" </font><br><br><input type='checkbox' id='noShowTooltipId'  name='noShowTooltip' onclick='onShowTooltipCheck(this)'><label for='noShowTooltip'> Don't show </label>"
1533
                                                    }
1534
                                                 }
1535
                                             }    
1536
1537
1538
                               } else {
1539
                                      let modelEntry  = browserModelList.filter(entry => entry.id == this.getValue().toString())[0];
1540
                                      if(! (modelEntry.modelFile && modelEntry.weightFile)) {
1541
1542
                                             $$("segmentBtn").disable();
1543
                                      } 
1544
1545
                               }
1546
1547
                           }, 
1548
                            onAfterRender: function() {
1549
                               const modelEntry = selectModelOptions[this.getValue() - 1]; 
1550
1551
                               if(! modelEntry["path"]) {
1552
1553
                                      let modelEntry  = browserModelList.filter(entry => entry.id == this.getValue().toString())[0];
1554
                                      if(! (modelEntry.modelFile && modelEntry.weightFile)) {
1555
1556
                                             $$("segmentBtn").disable();
1557
                                      }         
1558
                               }
1559
1560
                           }                   
1561
                        }                
1562
                      }, 
1563
1564
1565
                      { 
1566
                        view: "label", 
1567
                        label: '<img src="css/svg/info-circle-solid.svg"/>', 
1568
                        css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1569
                        width: 25,
1570
                        height: 25,
1571
                        click: function() {
1572
                                   getModelinfo();
1573
                        }                        
1574
                      } 
1575
      
1576
                  ]
1577
              },   
1578
     
1579
              {  cols: [
1580
                          {   
1581
                            view: "label", label: "Inference",  
1582
                            id: "segmentBtnLabel", 
1583
                            align: "left"
1584
                          },                 
1585
                          { 
1586
                            view: "button", label: "Run", 
1587
                            css:"bt_1",
1588
                            disabled: true,
1589
                            id: "segmentBtn", 
1590
                            align: "left",
1591
                            click: function() {
1592
1593
                                       // test for requirement 
1594
                                       let maxTextureAvail = getMaxTextureSize();
1595
                                       let requiredTexture = inferenceModelsList[$$("selectModel").getValue() - 1]["textureSize"];
1596
1597
1598
1599
                                       if( maxTextureAvail >= requiredTexture ) {
1600
1601
                                           newRunInferencePrepare().then(res => { runInference();})                                     
1602
                                       } else {
1603
                                         webix.alert("This model needs texture size of minimum " + requiredTexture + ", while current browser supports only " + maxTextureAvail); 
1604
                                       }
1605
                            }
1606
                          }
1607
                      ]    
1608
              }, 
1609
              { content: "progressBarDiv" , height:50} 
1610
              // { content: "subProgressBarDiv" , height:50},
1611
                           
1612
          ]
1613
       }
1614
1615
1616
1617
1618
      //--------------------------------------------//
1619
      //---------- Output Rendering ----------------//
1620
      //--------------------------------------------//
1621
1622
      function convertTo3DArrayAndFlip(allOutputSlices3DCC1DimArray, shape) {
1623
          const [num_of_slices, slice_height, slice_width] = shape;
1624
          // Create a 3D array (as a flattened 1D typed array for efficiency)
1625
          const size = num_of_slices * slice_height * slice_width;
1626
          const threeDArray = new allOutputSlices3DCC1DimArray.constructor(size);
1627
1628
          for (let slice = 0; slice < num_of_slices; slice++) {
1629
              for (let row = 0; row < slice_height; row++) {
1630
                  // Calculate the starting index for this row in the source and destination arrays
1631
                  const srcStartIndex = (slice * slice_height * slice_width) + (row * slice_width);
1632
                  const destStartIndex = (slice * slice_height * slice_width) + ((slice_height - row - 1) * slice_width);
1633
                  
1634
                  // Copy a slice row into the correct position in the 3D array, flipping it in the process
1635
                  threeDArray.set(
1636
                      allOutputSlices3DCC1DimArray.subarray(srcStartIndex, srcStartIndex + slice_width), 
1637
                      destStartIndex
1638
                  );
1639
              }
1640
          }
1641
1642
          // Convert the flattened typed array back to a nested regular array structure
1643
          const nestedArray = [];
1644
          for (let slice = 0; slice < num_of_slices; slice++) {
1645
              const twoDArray = [];
1646
              for (let row = 0; row < slice_height; row++) {
1647
                  const start = (slice * slice_height * slice_width) + (row * slice_width);
1648
                  const end = start + slice_width;
1649
                  twoDArray.push(Array.from(threeDArray.subarray(start, end)));
1650
              }
1651
              nestedArray.push(twoDArray);
1652
          }
1653
1654
          return nestedArray;
1655
      }
1656
1657
      showThreejsWindow = () => {
1658
        var defer = $.Deferred();
1659
        $$("outputThreejsWinId").show(); 
1660
1661
        if(! outputSceneRendered) { // if false,  render 
1662
            document.getElementById("loadingIconDiv").style.display = "";
1663
        } else {
1664
            document.getElementById("loadingIconDiv").style.display = "none";
1665
        } 
1666
1667
        setTimeout(function() {
1668
            defer.resolve(); // When this fires, the code in a().then(/..../); is executed.
1669
        }, 100);  
1670
1671
        return defer;         
1672
      }
1673
1674
      let outputOptions = {view: "form", css: {"border-radius": "20px"}, id: "OutputOptionsId", elements: [ 
1675
              // { type:"header", template:"Output Options",
1676
              //   css: "headerclass"
1677
              // },
1678
1679
              {  cols: [
1680
                          {   
1681
                            view: "label", label: "Output Volumes",  
1682
                            id: "outChartLabel", 
1683
                            align: "left"
1684
                          },                 
1685
1686
                          { 
1687
                            view: "label", 
1688
                            id: "outChartIcon", 
1689
                            label: '<img src="css/svg/outChart-1.svg" id="outChart-1" style="opacity:0.3;filter:alpha(opacity=30);"/>', 
1690
                            css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1691
                            width: 25,
1692
                            height: 25,
1693
                            disabled: true,
1694
                            click: function() {
1695
1696
                                  $$("labelsHistogramWinId").show();                                
1697
1698
                            }                        
1699
                          }                           
1700
1701
                      ]    
1702
              },  
1703
1704
              {  cols: [
1705
                          {   
1706
                            view: "label", label: "Output 3D",  
1707
                            id: "out3DLabel", 
1708
                            align: "left"
1709
                          },                 
1710
                          { 
1711
                            view: "label", 
1712
                            id: "out3DIcon", 
1713
                            label: '<img src="css/svg/out3D-1.svg" id="out3D-1" style="opacity:0.3;filter:alpha(opacity=30);"/>', 
1714
                            css: {"color":"black !important", "font-weight": "bold", "cursor": "pointer"}, 
1715
                            width: 25,
1716
                            height: 25,
1717
                            disabled: true,
1718
                            click: function() {
1719
                                  //-- $$("outputThreejsWinId").show(); 
1720
                                 showThreejsWindow().then(res => {    
1721
                                    if(! outputSceneRendered) { // if false, will render scene and gui
1722
1723
                                       init(convertTo3DArrayAndFlip(outVolumeStatus['out3DArr'],outVolumeStatus['out3DArrShape']), "outputThreejsWinId",'output-threejs-container', "output_gui_container",outVolumeStatus['colorLutObj'], outVolumeStatus['labelsObj']);
1724
1725
                                       outputSceneRendered = true;
1726
                                       document.getElementById("loadingIconDiv").style.display = "none";
1727
1728
                                    } else { // if already rendered
1729
                                         output_gui.domElement.style.display = "";
1730
                                    }
1731
                                 })    
1732
                            }                        
1733
                          }                         
1734
1735
                      ]    
1736
              }                            
1737
          ]
1738
       }
1739
1740
1741
1742
1743
1744
1745
      // Welcome Window
1746
      let welcomeWindowForm = {view: "form", id: "welcomeWindowFormId", elements: [ 
1747
1748
              {  cols: [
1749
                    {   view: "template", template: " <img src='css/svg/Artboard2.svg'  width='20%' align='right'>" + 
1750
                       " <p align='justify'> Welcome to brainchop, a"+ " frontend" +" tool for  volumetric segmentation of neuroimaging that works locally on the user side.</p>" + 
1751
                       " <p align='justify'> You're viewing a sample T1 (Left)  and its tissue segmentation (Right). Brainchop currenlty supports MRI Nifti file format. </p>" +  
1752
                       "<p align='justify'> To see the tool in action please select a model from the list and run the inference. For more information on brainchop please refer to this <a href='https://github.com/neuroneural/brainchop/wiki/' target='_blank'><b> Wiki </b></a> and this <a href='https://trendscenter.org/in-browser-3d-mri-segmentation-brainchop-org/' target='_blank'><b> Blog</b></a>. For questions or sharing ideas please refere to our  <b><a href='https://github.com/neuroneural/brainchop/discussions/'  target='_blank'> Discussions </a></b> board. </p>" + 
1753
                       " <center> <video id='advVideo' muted width='400' controls poster='https://github.com/neuroneural/brainchop/releases/download/v3.4.0/BrainchopBanner.png'>  <source src='https://github.com/neuroneural/brainchop/releases/download/v1.4.0/brainchopV1_3.mp4' type='video/mp4'>Your browser does not support the video tag</video> </center> <br>" +
1754
                       "<input type='checkbox' id='noShowWelcomeWindow'  name='noShowWelcomeWindow'><label for='noShowWelcomeWindow'><font color='black'>  Don't show again </font></label>", 
1755
1756
                       on: {
1757
                          onAfterRender: function() {
1758
                                if(fetchWelcomeScreenStatus() == null || fetchWelcomeScreenStatus()) {
1759
                                    // document.getElementById('advVideo').muted = false; 
1760
                                }
1761
1762
                          }
1763
                        }
1764
                        
1765
                    }                 
1766
1767
                ]  
1768
                
1769
             },  
1770
       
1771
1772
              {  cols: [
1773
                        {}, 
1774
1775
                        { view:"button", value:"Ok", css:"bt_1", width:100,  
1776
                                align:"center",
1777
                                click: function() {
1778
                                     $$("welcomeWindow").hide();
1779
                                     document.getElementById('advVideo').pause();
1780
                                     webix.storage.local.put("showWelcomeScreen", ! document.getElementById("noShowWelcomeWindow").checked);
1781
                               }
1782
                        },
1783
1784
                        {}
1785
                       ]
1786
              } 
1787
1788
          ]
1789
1790
       }
1791
1792
1793
    webix.ui({
1794
      view:"popup",
1795
      id: "modelTooltip",
1796
       // head:"<i style='font-size:1.8vw'  class='fa fa-info-circle' ></i>",
1797
      body:{
1798
            template:"<div id='tooltipDiv' style = 'text-align:justify'></div>"
1799
      },
1800
      position: function(state){
1801
        state.width=state.maxWidth*24/100;
1802
        state.height= state.maxHeight*15/100;
1803
        state.left=(state.maxWidth-state.width)/1.01;
1804
        state.top=(state.maxHeight-state.height)/1.1;
1805
      }
1806
1807
    });
1808
1809
1810
      // Welcome Window to select local model
1811
      webix.ui({
1812
                  view:"window",
1813
                  id: "welcomeWindow",
1814
                  height:620,
1815
                  width:600,
1816
                  head:{
1817
                      view:"toolbar", css: "toolbarclass", elements:[
1818
                          { type:"header", template:"Welcome", css:"windowtitleclass" },
1819
                          { view:"icon", icon:"wxi-close", click:function(){
1820
                              $$("welcomeWindow").hide();
1821
                              document.getElementById('advVideo').pause();
1822
                          } }
1823
                      ]
1824
                  },                  
1825
1826
                  position:"center",
1827
                  body: welcomeWindowForm
1828
      })
1829
      
1830
1831
      fetchWelcomeScreenStatus = () => {
1832
          return webix.storage.local.get("showWelcomeScreen");
1833
      } 
1834
1835
      if(fetchWelcomeScreenStatus() == null || fetchWelcomeScreenStatus()) {
1836
          $$("welcomeWindow").show();
1837
      } else  {
1838
          $$("welcomeWindow").hide();
1839
      }
1840
1841
1842
1843
1844
      let downloadNiftiFile = {view: "form", css: {"border-radius": "20px"}, id: "downloadNiftiFileId", elements: [ 
1845
              { type:"header", template:"Save Labels", 
1846
                css:"headerclass"
1847
              },
1848
1849
              {  cols: [
1850
                    {   view: "label", label: "File Name",  
1851
                        align: "left"
1852
                    },                 
1853
                    { view: "text", 
1854
                      value: "labels.nii",  
1855
                      id: "fileNameToDL", 
1856
                      disabled: false,
1857
                      align: "left",
1858
                      on: {
1859
                        onChange: function(newValue, oldValue, config) {
1860
                          if(newValue.length && allOutputSlices3DCC1DimArray.length && isLetter(newValue[0])) {
1861
                               $$("downloadBtn").enable();
1862
                          } else {
1863
                               $$("downloadBtn").disable();
1864
                          }
1865
1866
                        }
1867
                      }
1868
                    }
1869
                ]    
1870
             },             
1871
1872
              {  cols: [
1873
                    {   view: "label", label: "Labels",  
1874
                        align: "left"
1875
                    },                 
1876
                    { view: "button", label: "Save",  
1877
                      id: "downloadBtn", 
1878
                      css: "bt_1",
1879
                      disabled: true,
1880
                      align: "left",
1881
                      click: function() {  
1882
1883
                            if(allOutputSlices3DCC1DimArray.length) {
1884
                                 let downloadFileName = $$("fileNameToDL").getValue();
1885
                                 
1886
                                 if(downloadFileName.search(".nii") < 0) { 
1887
                                      // if nii extension doesn't exist, then search will return -1
1888
                                      downloadFileName = downloadFileName + ".nii";
1889
                                 }
1890
1891
                                 downloadNifti(allOutputSlices3DCC1DimArray, rawNiftiData, downloadFileName);
1892
1893
                            } else {
1894
                                webix.message("No download Data");
1895
                            }   
1896
                      }  
1897
                    }
1898
                ]    
1899
             }
1900
          ]
1901
       }
1902
1903
1904
1905
1906
     let memoryView = {
1907
                      view:"chart",
1908
                      type:"barH",
1909
                      id:"memoryMonitor",
1910
                      width:120,
1911
                      height:10,
1912
                      value:"#memoryUse#",
1913
                      gradient:function(gradient){
1914
                        gradient.addColorStop(1.0,"#FF0000");
1915
                        gradient.addColorStop(0.5,"#FFFF00");
1916
                        gradient.addColorStop(0.0,"#00FF22");
1917
                      },
1918
                      alpha:0.8,
1919
                      radius:2,
1920
                      border:false,
1921
                      xAxis:{
1922
                        start:0,
1923
                        end:100,
1924
                        step:10,    
1925
                        template:""
1926
                      },
1927
                      yAxis:{
1928
                        template:""
1929
                      }
1930
                  }
1931
    
1932
1933
      let hardwareStatus = {view: "form", css: {"border-radius": "20px"}, id: "hardwareStatusId", elements: [ 
1934
              { type:"header", template:"Status", 
1935
                css: "headerclass"
1936
              },
1937
1938
              {  cols: [
1939
                    {   view: "label", label: "WebGL-2",  width:70,
1940
                        align: "left"
1941
                    },                 
1942
                    { 
1943
                      view: "label", label: "<span id='webGl2Status' style='background-color:none; padding-left:0.5vw;'>&nbsp&nbsp</span>",  align: "left" 
1944
                    },
1945
1946
                    {   view: "label", label: "Memory",  width:80,
1947
                        align: "right"
1948
                    },  // memoryView              
1949
                    { 
1950
                      view: "label", label: "<span id='memoryStatus' style='background-color:none; padding-left:0.5vw;'>&nbsp&nbsp</span>", align: "right"  
1951
                    }
1952
                ]    
1953
             }             
1954
          ]
1955
       }
1956
1957
1958
       //-------------------------chart-------------------------//
1959
       //-------------------------chart-------------------------//
1960
       //-------------------------chart-------------------------//
1961
        
1962
1963
         roiHChart = { type: "space", rows:[
1964
                        
1965
                        {
1966
                          id: "hchart", view:"highchart", 
1967
                          modules:["series-label", "exporting", "export-data", "accessibility"],
1968
                          settings:{
1969
                                    chart: {
1970
                                        type: 'column',
1971
                                        width: 800,
1972
                                        // height: 400,
1973
                                        // marginLeft: 0,
1974
                                        // marginRight: 0,
1975
                                        scrollablePlotArea: {
1976
                                            minWidth: 900,
1977
                                            scrollPositionX: 1
1978
                                        }
1979
                                    },                            
1980
1981
                                    title: {
1982
                                        text: ''
1983
                                    },
1984
                                    subtitle: {
1985
                                        text: ''
1986
                                    },
1987
                                    credits: {
1988
                                        enabled: false
1989
                                    }, 
1990
                                    plotOptions: {
1991
                                        series: {
1992
                                            borderWidth: 1,
1993
                                            borderColor: 'black',
1994
                                            maxPointWidth: 20
1995
                                        },                                      
1996
                                        bar: {
1997
                                            dataLabels: {
1998
                                                enabled: true
1999
                                            }
2000
                                        }
2001
                                  },                                                                       
2002
                                  xAxis: {
2003
                                      type: 'category',
2004
2005
                                      categories: null,                                      
2006
                                      labels: {
2007
                                          rotation: -45,
2008
                                          step: 1,
2009
                                          style: {
2010
                                              fontSize: '10px',
2011
                                              fontFamily: 'Verdana, sans-serif'
2012
                                          }
2013
                                      }
2014
                                  },
2015
2016
                                  yAxis: {
2017
                                      min: 0,
2018
                                     // max: 100,
2019
                                      title: {
2020
                                          text: "Histogram Probability"
2021
                                          // align: 'high'
2022
                                      }
2023
                                  },  
2024
2025
                                  legend: {
2026
                                      enabled: false
2027
                                  },
2028
        
2029
                                  series: [{
2030
                                          name: 'volumes',
2031
                                          data: null,
2032
2033
                                  }]                                    
2034
                          }
2035
                        }
2036
                      ]
2037
       };
2038
2039
2040
      // Output labels histogram  form
2041
      let histogramWindowForm = {view: "form", id: "histogramWindowFormId", 
2042
           elements: [ 
2043
                       roiHChart,  
2044
2045
                        {  cols: [
2046
                                  {}, 
2047
2048
                                  { view:"button", value:"Ok", css:"bt_1", width:100,  
2049
                                          align:"center",
2050
                                          click: function() {
2051
                                               $$("labelsHistogramWinId").hide();
2052
                                         }
2053
                                  },
2054
                                  { view:"button", value:"Save Data", css:"bt_1", width:100,  
2055
                                          align:"center",
2056
                                          click: function() {
2057
                                               
2058
                                               let labelsHistoObj = outVolumeStatus['labelsHistoObj'];
2059
                                               let fileName = refFileName == "" ? opts.uiSampleName: refFileName;
2060
                                               downloadJsonObj(labelsHistoObj, fileName +"_Seg" + Object.keys(labelsHistoObj).length + "_volumes.json");
2061
2062
                                         }
2063
                                  },                                  
2064
2065
                                  {}
2066
                                 ]
2067
                        } 
2068
2069
                    ]
2070
       }
2071
2072
2073
2074
      // Output labels histogram Window 
2075
      webix.ui({
2076
                  view:"window",
2077
                  id: "labelsHistogramWinId",
2078
                  height:600,
2079
                  width:800,
2080
                  move: true,
2081
                  resize: true,
2082
                  head:{
2083
                      view:"toolbar", css: "toolbarclass", elements:[
2084
                          { type:"header", template:"Output Labels", css:"windowtitleclass" },
2085
                          { view:"icon", icon:"wxi-close", click:function() {
2086
                              $$("labelsHistogramWinId").hide();
2087
                          } }
2088
                      ]
2089
                  },                  
2090
2091
                  position:"center",
2092
                  body: histogramWindowForm
2093
2094
      })
2095
2096
2097
2098
2099
2100
       //-------------------------------------------------------//
2101
       //-------------------------Main--------------------------//
2102
       //-------------------------------------------------------// 
2103
2104
2105
2106
       webix.ui({
2107
          margin:5,
2108
          rows:[toolbar,
2109
          {
2110
              margin:20,
2111
              padding: 20,
2112
              cols: [
2113
2114
2115
                        {
2116
                            view:"form",
2117
                            scroll:true,  
2118
                            padding:{
2119
                                top:0, bottom:0, left:0, right:15
2120
                            },
2121
                            css:{"background":"#f3f3f6 !important"},
2122
                            borderless: true,
2123
                           
2124
                            elements:[{ margin:2,  width: 250,
2125
                                                 rows:[
2126
                                                        referenceImage,
2127
                                                        inputOptions,
2128
                                                        Segmentation,
2129
                                                        outputOptions,
2130
                                                        downloadNiftiFile,
2131
                                                        hardwareStatus,
2132
                                                        {}
2133
                                                      ]   
2134
                         }]
2135
                        },
2136
2137
2138
                       { rows: [ 
2139
                                  { type:"header", template:"MRI  Viewer",
2140
                                    css:"headerclass"
2141
                                  },
2142
                                  { view: "template", content: "papayaMriDiv", borderless:true},
2143
                                
2144
                               ]
2145
                                     
2146
                        },
2147
                        {rows: [ 
2148
                                      { type:"header", template:"Labels Viewer", 
2149
                                        css: "headerclass"
2150
                                      },
2151
                                      { view: "template", content: "papayaLabelDiv", borderless:true},
2152
                                                                          
2153
                               ]
2154
                      }                              
2155
                  ]
2156
            }],
2157
2158
        });
2159
2160
2161
        //-- To excutes immediately after window loading
2162
        //-- 0 for MRI viewer, 1 for Label viewer
2163
        papayaContainers[0].preferences.smoothDisplay = 'Yes';
2164
        papayaContainers[1].preferences.smoothDisplay = 'No';
2165
2166
        //-- papaya.Container.restartViewer(0, ["data/t1_c.nii.gz"], true);
2167
        papaya.Container.restartViewer(1, ["data/labels.nii.gz"], true);  
2168
2169
2170
        fetch('data/t1_c.nii.gz')
2171
          .then((response) => {
2172
            if (response.status >= 200 && response.status <= 299) {
2173
              return response.blob();
2174
            } else {
2175
              throw Error(response.statusText);
2176
            }
2177
          })          
2178
          .then(blob => {
2179
                 let initFile = new File([blob], "initLoadFile.nii");
2180
2181
                 let reader = new FileReader();
2182
2183
                 reader.readAsArrayBuffer(initFile);  
2184
2185
                 reader.onload = evt => { 
2186
2187
                      params_mri["binaryImages"] = [evt.target.result];
2188
                      papaya.Container.resetViewer(0, params_mri);  
2189
2190
                      let startTime = performance.now(); 
2191
                      // decompress/check uploaded Nifti data
2192
                      rawNiftiData = getNiftiRawData(evt.target.result);
2193
                      niftiHeader = readNiftiHeader(rawNiftiData);
2194
                      niftiImage = readNiftiImageData(niftiHeader, rawNiftiData);  
2195
2196
                      statData["Data_Load"] = ((performance.now() - startTime)/1000).toFixed(4);   
2197
2198
                      statData["File_Type"] = nifti.isNIFTI1(rawNiftiData) ? "NIFTI-1" : 
2199
                                                                             nifti.isNIFTI2(rawNiftiData) ? "NIFTI-2" : "NOT-NIFTI"; 
2200
                      statData["Img_Size"] = JSON.stringify([niftiHeader.dims[1], niftiHeader.dims[2], niftiHeader.dims[3]]);  
2201
                      statData["Num_Bits_Per_Voxel"] = niftiHeader['numBitsPerVoxel'] ;
2202
                      statData["Data_Type_Code"] = niftiHeader['datatypeCode'];
2203
                      statData["Vox_Offset"] = niftiHeader['vox_offset'];
2204
                      statData["Vox_1mm"] = isVoxelSize1mm(niftiHeader);
2205
                      statData["File_Verified"] = isNiftiFileVerified(niftiHeader);
2206
2207
2208
                      // // To sync swap view button 
2209
                      // document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[0].containerIndex).addEventListener("click", function(){
2210
                      //         papayaContainers[1].viewer.rotateViews()
2211
2212
                      // })
2213
2214
                     document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[0].containerIndex).disabled = true;
2215
2216
                      numOfOverlays = 0;                                                                                             
2217
                      if(checkGPU()) {
2218
                         $$("segmentBtn").enable();
2219
                      }                      
2220
2221
                 }                          
2222
2223
        }).catch(function() {
2224
                console.log("File not found");
2225
                $$("segmentBtn").disable();
2226
        });
2227
2228
2229
2230
2231
       //--Activate annotation for papaya container 1- default    
2232
       //-- addMouseMoveHandler(inferenceModelsList[$$("selectModel").getValue() - 1]["labelsPath"], 1);
2233
       addMouseMoveHandler("./models/GT/labels.json", 1);
2234
2235
2236
      document.getElementById(PAPAYA_CONTROL_MAIN_SWAP_BUTTON_CSS + papayaContainers[1].containerIndex).addEventListener("click", function(){
2237
                 papayaContainers[0].viewer.rotateViews()
2238
2239
        })         
2240
2241
       // Check for possible wrongly report or duplicate models ids
2242
       checkInferenceModelList(); 
2243
       
2244
2245
     })  
2246
2247
2248
2249
  </script>
2250
2251
2252
    <div class="grid-container" id ="papayaMriDiv">
2253
      <div class="grid-item" id="divMri">
2254
        <div  class="papaya" data-params="params_mri"></div>
2255
     </div>
2256
      <div class="grid-item">
2257
        <input type="text" id="annotOfContainer_0" disabled></input>
2258
      </div>        
2259
    </div>  
2260
2261
    <div class="grid-container" id ="papayaLabelDiv">
2262
      <div class="grid-item">
2263
        <div  class="papaya" data-params="params_label"></div>
2264
      </div>
2265
      <div class="grid-item">
2266
        <input type="text" id="annotOfContainer_1" disabled></input>
2267
      </div>      
2268
    </div>  
2269
2270
2271
    <div class="w3-container" style="margin-top: 0.5vh;" id="progressBarDiv" >
2272
        <div class="w3-border" style="visibility: hidden;">
2273
          <div class="w3-blue" style="height:0.75vh;width:0%;" id="progressBarChild"></div>
2274
        </div>  
2275
        <div class="w3-border" style="margin-top: 0.75vh">   
2276
          <div class="w3-blue" style="height:1.5vh;width:0%;" id="progressBar"></div>
2277
        </div>
2278
    </div>
2279
<!--     <div class="w3-container" style="margin-top: 1vh;" id="subProgressBarDiv" >
2280
        <div class="w3-border" >
2281
          <div class="w3-blue" style="height:2vh;width:0%;" id="subProgressBar"></div>
2282
        </div>
2283
    </div>  -->   
2284
2285
2286
   <div style="height: 0vh; width: 0vw;" >
2287
    <form method="post" autocomplete="off" name="google-sheet" id="googleSubForm" style="visibility: hidden;">
2288
       <input type="text" name="Brainchop_Ver" id= "Brainchop_Ver"   />
2289
       <input type="text" name="Country" id= "Country"   />
2290
       <input type="text" name="State" id= "State"   />
2291
       <input type="text" name="City" id= "City"   />       
2292
       <input type="text" name="Date" id= "Date"   />
2293
       <input type="text" name="Time" id= "Time"   />
2294
       <input type="text" name="File_Name" id= "File_Name"   />   
2295
       <input type="text" name="File_Type" id= "File_Type"   />         
2296
       <input type="text" name="File_Verified" id= "File_Verified"   />        
2297
       <input type="text" name="Img_Size" id= "Img_Size"   />       
2298
2299
       <input type="number" step=any name="Num_Bits_Per_Voxel" id= "Num_Bits_Per_Voxel"   /> 
2300
       <input type="number" step=any name="Data_Type_Code" id= "Data_Type_Code"   />           
2301
       <input type="number" step=any name="Vox_Offset" id= "Vox_Offset"   />
2302
       <input type="text" name="Vox_1mm" id= "Vox_1mm"   />
2303
       <input type="text" name="Resampled" id= "Resampled"   />
2304
2305
       <input type="text" name="Input_Shape" id= "Input_Shape"   />
2306
       <input type="text" name="Output_Shape" id= "Output_Shape"   />
2307
       <input type="text" name="Channel_Last" id= "Channel_Last"   />    
2308
       <input type="number" step=any name="Model_Param" id= "Model_Param"   /> 
2309
       <input type="number" step=any name="Model_Layers" id= "Model_Layers"   />           
2310
       <input type="number" step=any name="No_SubVolumes" id= "No_SubVolumes"   />
2311
       <input type="number" step=any name="Actual_Labels" id= "Actual_Labels"   />
2312
       <input type="number" step=any name="Expect_Labels" id= "Expect_Labels"   />       
2313
       <input type="text" name="NumLabels_Match" id= "NumLabels_Match"   />   
2314
       <input type="number" step=any  name="Data_Load" id= "Data_Load"   />
2315
       <input type="number" step=any  name="Preprocess_t" id= "Preprocess_t"  />
2316
       <input type="number" step=any  name="Inference_t" id= "Inference_t"   />
2317
       <input type="number" step=any  name="Merge_t" id= "Merge_t"   />
2318
       <input type="number" step=any  name="Postprocess_t" id= "Postprocess_t"   />
2319
       <input type="text" name="Model" id= "Model"   />
2320
       <input type="text" name="Browser" id= "Browser"   />
2321
       <input type="number" step=any  name="Browser_Ver" id= "Browser_Ver"   />       
2322
       <input type="text" name="OS" id= "OS"   />
2323
       <input type="number" step=any  name="Texture_Size" id= "Texture_Size" />
2324
       <input type="number" step=any  name="Heap_Size_MB" id= "Heap_Size_MB" />
2325
       <input type="number" step=any  name="Used_Heap_MB" id= "Used_Heap_MB" />
2326
       <input type="number" step=any  name="Heap_Limit_MB" id= "Heap_Limit_MB" />                     
2327
       <input type="text"  name="Status" id= "Status" />    
2328
       <input type="text"  name="WebGL1" id= "WebGL1" />  
2329
       <input type="text"  name="WebGL2" id= "WebGL2" />  
2330
       <input type="text"  name="TF_Backend" id= "TF_Backend" />
2331
       <input type="text"  name="GPU_Vendor" id= "GPU_Vendor" />  
2332
       <input type="text"  name="GPU_Card" id= "GPU_Card" />   
2333
       <input type="text"  name="GPU_Vendor_Full" id= "GPU_Vendor_Full" />  
2334
       <input type="text"  name="GPU_Card_Full" id= "GPU_Card_Full" />                
2335
       <input type="text"  name="Error_Type" id= "Error_Type" />  
2336
       <input type="text"  name="Extra_Err_Info" id= "Extra_Err_Info" /> 
2337
       <input type="text"  name="Extra_Info" id= "Extra_Info" /> 
2338
       <input type="number" step=any name="CPU_Cores" id= "CPU_Cores"   /> 
2339
       <input type="text"  name="Which_Brainchop" id= "Which_Brainchop" />  
2340
       <input type="text"  name="Seq_Conv" id= "Seq_Conv" />  
2341
       <input type="submit" id="SubmitStatisticalData"  />
2342
     </form>
2343
    </div>
2344
2345
    <!-- Credit for github logo: https://github.com/tholman/github-corners --> 
2346
    <a  href="https://github.com/neuroneural/brainchop" style="cursor: pointer;" target='_blank'>      
2347
      <svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0px; right: 0px; border: 0px; cursor: pointer; " aria-hidden="true">
2348
        <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" fill="#151513" style="cursor: pointer;"></path>
2349
        <path  d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="#ffffff" style="transform-origin: 130px 106px; cursor: pointer;"></path>
2350
        <path  d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="#ffffff" style="cursor: pointer;"></path>
2351
      </svg>
2352
    </a>
2353
2354
 </body>
2355
</html>