NRRD data interpretation (not header interpretation) question

Hello friends

I am looking at the nrrd threejs example and i see data like this in the data portion of the file. I do understand the header part but my question is regading what does each byte of the array mean. Here is a sample of the data array:
Float32Array(308880)

  1. [0 … 9999]
  2. [0 … 99]
    0: 2130
    1: 2284
    2: 2320
    3: 2462
    4: 2366
    5: 2532
    6: 2488
    7: 2726
    8: 2841
    9: 2945
    10: 2699
    11: 2933
    12: 3034
    13: 3019
    14: 3078
    15: 3287
    16: 3359
    17: 3447
    18: 3172

As you can see element zero of the array has the value 2130 and element 1 has the value 2284. My question is what does 2130 represent. Does this have to do with XYZ coordinate or is it RGB data. Can some one break this down for me.

I did look up NRRD data format on various sites, but non of them zoom into the binary data interpretation. They all talk about the header format for the most part. And the fact that the data could be int, float , byte and e.t.c. But I want to understand how a value like 2284 correlate to screen coordinates.
For example does array element zero represent X, and element1 represent Y and element 2 represent Z coordinates? How about element 3?
I am assuming element 4 through 8 again represent the same thing. Anyway appreciate any insight and clarifications on my questions.

Thanks in advance

Additionally in that same NRRD file in volumeSlice.js there is a piece of code that converts the NRRD data to image data to add it to the canvas element. Here is the code fragment:

const imgData = ctx.getImageData( 0, 0, iLength, jLength );
const data = imgData.data;
const volumeData = volume.data;
const upperThreshold = volume.upperThreshold;
const lowerThreshold = volume.lowerThreshold;
const windowLow = volume.windowLow;
const windowHigh = volume.windowHigh;
 
for ( let j = 0; j < jLength; j ++ ) {

        for ( let i = 0; i < iLength; i ++ ) {

          let value = volumeData[ sliceAccess( i, j ) ]; <= original byte such as 2130 from NRRD data array
          let alpha = 0xff;
          //apply threshold
          alpha = upperThreshold >= value ? ( lowerThreshold <= value ? alpha : 0 ) : 0;
          //apply window level
          value = Math.floor( 255 * ( value - windowLow ) / ( windowHigh - windowLow ) );
          value = value > 255 ? 255 : ( value < 0 ? 0 : value | 0 );

         //at this point the value 2130 changes to a value between 0 to 255. Let say value of 9. And this value is added to the new canvas.imageData array which has all zero initial values 

          data[ 4 * pixelCount ] = value;       <== 9
          data[ 4 * pixelCount + 1 ] = value;  <== 9
          data[ 4 * pixelCount + 2 ] = value; <== 9
          data[ 4 * pixelCount + 3 ] = alpha;  <== 255
          pixelCount ++;

        }

      }

    ctx.putImageData( imgData, 0, 0 );
    this.ctx.drawImage( canvas, 0, 0, iLength, jLength, 0, 0, this.canvas.width, this.canvas.height );

So as you can see values like 2130 or 2244 from NRRD file will be converted to values like 9 or 10 or 27 and so forth. And that value is set to canvas image data array. So elements zero through three will have value 9. element 4 will have 255. Elements 5 through 7 could have value of 10 and element 8 again is 255 and this process repeats it self. So i need to also understand this conversion from 2130 for instance to 9. And what does that mean and where is the xyz coordinate in this picture for each value. Since i need to add small cubes on top of the image based on their coordinates which i get in xyz and i want to see how could i match this to the coordinates on the image.

Thanks

I’m just guessing, as I have never worked with NRRD:

  • there are no coordinates in the value data, most likely it is just a sequence of values. Imagine a 2D picture β†’ you have the overall size, and then you have a sequence of colors for each pixel, there is no need to provide coordinates for each pixel, their colors just come one after another
  • as for how 2130 is transformed into 9, according to the code it is linear mapping from the input range to a byte range, so 2130 inside [windowLow,windowHight] is where 9 is inside [0,255]

Thanks @PavelBoytchev. That helps a lot explaining the range conversion. So to peel the onion some more: I have the NRRD file which describes the image for a given cell, lets say cell one. I also get another csv file with coordinates of certain targets on this image. That file gives me XYZ coordinates for each target and I am supposed to draw a little square on the NRRD cell image using those coordinates.
So my challenge is how to transpose those cell coordinates on that image on the screen. I have added three little squares on the image at arbitrary points just as an example below. But I need to find the actual locations on this cell to draw the dots. So that is why I wanted to match the value 2130 for example to an XYZ coordinate so I know where a given square should be drawn.
I will see if there are any more insights on this from our friends here.
Thanks again @PavelBoytchev

Screenshot 2024-02-05 081008

Just a guess:

In the code snippet that you posted:

  • i appears to be the local X coordinate
  • j appears to be the local Y coordinate
  • the local Z coordinate depends on the slice number (not shown in the snipper)

To convert to global coordinates, you need to know the position and the size of the large box (the one with the yellow frame).

Thanks again @PavelBoytchev. This clarifies the positions in this file. I will play with coordinates and let you know if i had any questions. Much appreciate your quick responses.

Hi @PavelBoytchev and other friends

following our conversation in this topic i did some playing around and used the following code to convert from lcoal to world points:

 let localPoint = new Vector3(x,y,z)
 let worldPoint = mesh.localToWorld(localPoint);
 mesh.position.set(worldPoint.x, worldPoint.y, worldPoint.z)

But the points are not inside the box. please see atached screens:

you mentioned earlier:
β€œTo convert to global coordinates, you need to know the position and the size of the large box (the one with the yellow frame).”

Can you give e an example of what you were talking about above. What am i missing here?
I did some raycasting and when i point to the cloud mesh inside the box i do get different coordinates than the ones i have, and i can draw little circles based on the intersect[0].point coordinates. But obviously i want this to happen without user clicking on the cloud or better said the cell mesh we are looking at.

Her is the code that creates the box:

              const geometry = new THREE.BoxGeometry( volume.xLength, volume.yLength, volume.zLength );
             const material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.BackSide, wireframe: true, opacity: 0.1  } );
            
              let cube = new THREE.Mesh( geometry, material );
              const box = new THREE.BoxHelper( cube );
              scene.add( box );
              box.applyMatrix4( volume.matrix );
              scene.add( cube );

Thanks in advance for the help

Honestly, I feel like trying to fix the engine of a car by giving instructions over the phone.

The snapshots might indicate just an offset to all color dots – like the dots coordinates start from (0,0,0) and go up; while the box is centered around (0,0,0).

Can you try this: instead of drawing a point at (x,y,z), draw it at (x - volume.xLength/2, y - volume.yLength/2, z - volume.zLength/2 ), i.e. shift the coordinates down by half of the size of the yellow box.

1 Like

Thanks so much @PavelBoytchev. It seems like your great suggestion did the trick or at least it looks a hell of a lot better. I really appreciate your help and guidance here (your intelligence and expertise came through even with inadequate info :slight_smile:) . Sorry about the inadequate info (as you said over the phone) :slight_smile:
I hundred percent agree with you on that. My problem is i cant put the whole app in jfiddle as it is a big react app and not a simple html page. But please suggest how i can be more complete in the future and what other pieces of info would help.

Thanks again