Load vol file into 3D Slicer from Morita machine

Hi,

I have a volume file named *.vol from Morita machine. I am trying to convert it into a stl or obj file. It is not supported to be loaded by 3D Slicer right now. Is there any extension available? Or any other way to do it like a matlab script? Thanks in advance.

Is there an option to export the volume in DICOM format?
Can you share an example .vol file?

Hi Andras,

Thanks for your reply. The .vol file can be only opened by a software named OneVolumeViewer form Morita. I have uploaded the .vol file and the software.

https://drive.google.com/open?id=1iABWN3jzvWv7PNrMhFKZhQRxD9KyGqgV

I’m not sure it’s the same file type (.vol may be an overused file extension) but here’s some code to read a .vol file from a microCT machine:

This .vol file has a human-readable header. From that you can determine image geometry (size, spacing, origin) and write it to a header file. You can then open that file in Slicer.

For example, here is the header for the file you shared:

NRRD0004
# Complete NRRD file format specification at:
# http://teem.sourceforge.net/nrrd/format.html
type: short
dimension: 3
space: left-posterior-superior
sizes: 504 501 500
space directions: (0.08,0,0) (0,0.08,0) (0,0,0.08)
kinds: domain domain domain
endian: little
encoding: raw
space origin: (0,0,0)
byte skip: 800
data file: CT_0.vol

If you save the content above to a file named CT_0.nhdr (or download it from here) then you can open that file in Slicer.

2 Likes

Hi Andras,

This really helps! I do appreciate it.

Hi Andras,

I have successfully loaded the vol file, but the rendering result is different from yours. (It looks incorrectly) Is there any steps before volume rendering? Thanks for your help.

Voxels values of the scan are uncalibrated, it is not in Hounsfield units but in tens of thousands range. So, you need to adjust the transfer functions accordingly (you need to open Advanced section and drag points outside the current default range, which are specified for Hounsfield units).

Hi Andras,

Here is my result. I was trying but failed to achieve the result similar to yours.


Could you tell the details what configuration you were using for the above one you posted? Screenshots are the best if you could attach. Thank you for your time and effort.

Hi,

I am confusing about the Hounsfield units. I watched several tutorals but they skip the step. Where and how can I modify this Hounsfield units or transfer functions configuration? Thanks for the help.

I had to tune the transfer functions, which were not easy, because the points were way out of normal CT range. I would recommend to rescale voxel intensities using SimpleFilters module:

After this you can use built-in volume rendering presets, such as “CT-AAA2” or “CT-Cardiac3”, and move “Shift” slider to fine-tune what is displayed.

I have the same kind of problem importing data from a cbct volume from a morita cbct machine

This is the header from the file
I dont know how to proceed or what to do.

   JmVolumeVersion=1Ú  <?xml version='1.0' encoding='Shift_JIS' ?><JmVolume><Version value="1"/><Attribute><tfStatRadius value="40"/><tfStatZMax value="17.5"/><tfStatZMin value="-17.5"/><tfStatSkipLength value="0.5"/><tfStatSideLength value="2"/><tfStatIntervalLength value="2"/><tfXGridSize value="0.25"/><tfYGridSize value="0.25"/><tfZGridSize value="0.25"/><tfXLen value="100"/><tfYLen value="100"/><tfZLen value="50"/><strVolumeId value="0"/><tfInitialAngleInRadian value="2.26893"/><tfA value="0.00492927"/><tfB value="88.7966"/><tfXCenter value="0"/><tfYCenter value="0"/><tfZCenter value="0"/><iZMaxValid value="2147483647" FYI="2147483647 denotes uninitialized"/><iZMinValid value="2147483647" FYI="2147483647 denotes uninisizlized"/><strGuidsFromRootToThis value="{328CBBC4-3E54-4035-A6AF-6919B90EE07D}"/><STITCHID value=""/><STITCHNUMBER value=""/><STITCHCENTER value=""/><tfAntiAliasAngleInRadian value="0" FYI="This node is obsolete. See tfAntiAliasAngleInDegree instead"/><tfAntiAliasAngleInDegree value="45"/><tfSliceInterval value="0"/><tfSliceThickness value="0"/><iSliceMeanRange value="0"/><tfSystemV2HuSlope value="190"/><tfSystemV2HuIntercept value="0"/><tfUserV2HuSlope value="+1.0000000000000000e+000"/><tfUserV2HuIntercept value="+0.0000000000000000e+000"/><bIsHuAvailable value="0"/><bIsUserDefinedV2Hu value="0"/><CAdditionalVolumeInfo><bIsValid value="1"/><strReconstructionFilterId value="G_001"/><strAcquisitionModeId value="MCT1F17_V01_SL2_W100xH50_FR30_PR17"/><tfmaTubeCurrent value="5"/><tfkvTubeVoltage value="90"/></CAdditionalVolumeInfo></Attribute><FYI><XMin value="-200"/><XMax value="200"/><YMin value="-200"/><YMax value="200"/><ZMin value="-102"/><ZMax value="101"/><MinValue value="-72.7257"/><MaxValue value="250.314"/></FYI></JmVolume>   CArray3D8ÿÿÿÈ   8ÿÿÿÈ   šÿÿÿe

how did you get the header

Hi Andras. Please tell me, I don’t understand how you saw that data in this data? I looked in his file CT_0.vol, there is the following information:
 JmVolumeVersion=1╙ <?xml version='1.0' encoding='Shift_JIS' ?> CArray3D ·  ·

For example, it doesn’t have sizes: 504 501 500
And many other.

Please Andras :pray: teach me to see these numbers.

Instruction to Create .nhdr file

Use the following template for your .nhdr file:

NRRD0004
# Complete NRRD file format specification at:
# http://teem.sourceforge.net/nrrd/format.html
type: signed short
dimension: 3
space: left-posterior-superior
sizes: Z Y X
space directions: (0,0,S) (S,0,0) (0,S,0)
kinds: domain domain domain
endian: little
encoding: raw
space origin: (0,0,0)
byte skip: B
data file: CT_0.vol

You need to fill values from your .vol header:
X, Y, Z, S, B

Voxel size (S)

Look for the grid size in the .vol XML header, e.g.:

<tfXGridSize value="0.125"/>
<tfYGridSize value="0.125"/>
<tfZGridSize value="0.125"/>

S = 0.125

Dimensions (X, Y, Z)

Check the minimum and maximum indices:

<FYI>
  <XMin value="-352"/>
  <XMax value="352"/>
  <YMin value="-352"/>
  <YMax value="352"/>
  <ZMin value="-162"/>
  <ZMax value="161"/>
  ..
</FYI>

X = XMax - XMin + 1 = 352 - (-352) + 1 = 705
Y = YMax - YMin + 1 = 352 - (-352) + 1 = 705
Z = ZMax - ZMin + 1 = 161 - (-162) + 1 = 324

Byte skip (B)

total voxels = X * Y * Z = 161036100
voxel size = 2 bytes (because data type in .vol is 16-bit integer)
raw data size = total voxels * voxel size = 322072200 (bytes)

Raw data located at the very end of the .vol file, so the byte skip parameter can be calculated as:
B = total .vol file size - raw data size

Adjust axes (if necessary)

If after import the axes look incorrect, modify the space directions line, for example:

space directions: (S,0,0) (0,S,0) (0,0,S)

Test different permutations until the orientation matches your viewer.

Data type consideration

Use signed short data type, not unsigned short, because in your .vol:

  • 0x8000 value corresponds to void
  • and all other values correspond to actual intensities

Python parser for reference

import struct, tempfile, subprocess
import numpy as np

def load_data (path: str, dtype = np.int16, extract_header: bool = False) -> np.ndarray:
    with open(path, 'rb') as f:
        n = struct.unpack("<I", f.read(4))[0]
        data = f.read(n)
        assert data.decode('ascii') == "JmVolumeVersion=1"

        # XML
        n = struct.unpack("<I", f.read(4))[0]
        data = f.read(n)
        if extract_header:
            with tempfile.NamedTemporaryFile() as fht:
                fht.write(data)
                fht.flush()
                subprocess.run(["xmllint", "--format", "--output", path + ".header.xml", fht.name])

        n = struct.unpack("<I", f.read(4))[0]
        data = f.read(n)
        assert data.decode('ascii') == "CArray3D"

        # X limits
        x_min, x_max = struct.unpack("<ii", f.read(8))
        X = x_max - x_min + 1

        # Y limits
        y_min, y_max = struct.unpack("<ii", f.read(8))
        Y = y_max - y_min + 1

        # Z limits
        z_min, z_max = struct.unpack("<ii", f.read(8))
        Z = z_max - z_min + 1

        print(f"X={X} Y={Y} Z={Z}")

        # voxels
        d = np.frombuffer(f.read(), dtype=dtype)
        print(len(d))
        assert len(d) == X * Y * Z
        d = d.reshape((X, Y, Z))
        return d
1 Like

I’ve created a small Python utility called vol2nrrd that converts 3D Morita .vol medical imaging files into NRRD (.nrrd/.nhdr) format.

1 Like