Ask Your Question
1

how can i load 4d medical image of type nifti?

asked 2017-11-26 08:13:24 -0600

AhmedSh3ban gravatar image

how can i load 3d or 4d medical image of type nifti (.nii) in c++??

edit retag flag offensive close merge delete

Comments

Have you try to search on github ?

LBerger gravatar imageLBerger ( 2017-11-26 10:29:14 -0600 )edit

Can you spare some sample .nii files?

Here's another link found on GitHub: https://github.com/glaivesoft/biomedi...

It doesn't help when the government's implementation and sample files are offline for some unknown reason: https://nifti.nimh.nih.gov/nifti-1

I found these things by googling for "nifti nii".

sjhalayka gravatar imagesjhalayka ( 2017-11-26 15:21:50 -0600 )edit

This is a great URL, describing the .nii format: https://brainder.org/2012/09/23/the-n...

Do a search on that page for "Image dimensions". That part of the file header documentation tells you how many dimensions, as well as the size of each dimension in terms of pixels/voxels. That's all you need to know in order to successfully read in a .nii file. Please provide a sample .nii file, and I'll come up with a sample code on how to read and write a 3D or 4D .nii file.

sjhalayka gravatar imagesjhalayka ( 2017-11-27 18:57:07 -0600 )edit

Thanks for your effor i will check this out. and for sample this is 3d https://www.mediafire.com/file/bfxx0k...

AhmedSh3ban gravatar imageAhmedSh3ban ( 2017-11-27 22:21:41 -0600 )edit

Yes, I see now that it's 3D data, where the x and y size is 64 and the z size is 21, and the data are 32-bit floats. Thanks for the sample. I'll be working on it today.

sjhalayka gravatar imagesjhalayka ( 2017-11-28 09:17:21 -0600 )edit

@AhmedSh3ban Have you considered using something like Marching Cubes to convert the 3D data's isosurface into a triangle mesh?

sjhalayka gravatar imagesjhalayka ( 2017-11-29 10:14:56 -0600 )edit

One thing I noticed about the sample .nii files gotten from https://nifti.nimh.nih.gov/nifti-1 is that they use a different endianness than that used by Intel CPUs running Windows or Mac OS X. This causes havoc when you try to read them using the code given in the answer below on Windows or Mac OS X. One could go through the process of converting everything from one endianness to the other endianness, but it would be a royal pain. I figured this all out by opening the sample .nii files in a hex editor -- the first four bytes should encode the value 348 (I used Windows calculator in programmer mode to do the conversion from hexadecimal to decimal), but instead the bytes are reversed in order, and so encodes some unacceptably gigantic number.

sjhalayka gravatar imagesjhalayka ( 2017-11-29 16:32:16 -0600 )edit

1 answer

Sort by ยป oldest newest most voted
2

answered 2017-11-28 13:51:10 -0600

sjhalayka gravatar image

updated 2017-11-28 16:04:23 -0600

The following code reads the .nii header and then the .nii body. It writes PNG images. Make sure that you mark this answer as correct if you find that it works for you. It handles input files of dimensions up to 7.

#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <strstream>
using namespace std;

#include <opencv2/opencv.hpp>
using namespace cv;
#pragma comment(lib, "opencv_world331.lib")

int main(void)
{
    // https://brainder.org/2012/09/23/the-nifti-file-format/
    ifstream infile("example_func.nii", std::ios::binary);

    if (infile.fail())
    {
        cout << "Could not read file example_func.nii" << endl;
        return 0;
    }

    size_t bytes_read = 0;

    cout << "Reading header" << endl;

    int sizeof_header;
    infile.read(reinterpret_cast<char*>(&sizeof_header), sizeof(sizeof_header));
    bytes_read += infile.gcount();

    if (sizeof_header != 348)
    {
        cout << "Invalid header size: should be 348 bytes" << endl;
        return -1;
    }

    char data_type[10];
    infile.read(reinterpret_cast<char*>(&data_type), sizeof(data_type));
    bytes_read += infile.gcount();

    char db_name[18];
    infile.read(reinterpret_cast<char*>(&db_name), sizeof(db_name));
    bytes_read += infile.gcount();

    int extents;
    infile.read(reinterpret_cast<char*>(&extents), sizeof(extents));
    bytes_read += infile.gcount();

    short session_error;
    infile.read(reinterpret_cast<char*>(&session_error), sizeof(session_error));
    bytes_read += infile.gcount();

    char regular;
    infile.read(reinterpret_cast<char*>(&regular), sizeof(regular));
    bytes_read += infile.gcount();

    char dim_info;
    infile.read(reinterpret_cast<char*>(&dim_info), sizeof(dim_info));
    bytes_read += infile.gcount();

    short dim[8];
    infile.read(reinterpret_cast<char*>(&dim), sizeof(dim));
    bytes_read += infile.gcount();

    cout << dim[0] << " dimensions" << endl;
    cout << "Dim 1: " << dim[1] << endl;
    cout << "Dim 2: " << dim[2] << endl;
    cout << "Dim 3: " << dim[3] << endl;
    cout << "Dim 4: " << dim[4] << endl;
    cout << "Dim 5: " << dim[5] << endl;
    cout << "Dim 6: " << dim[6] << endl;
    cout << "Dim 7: " << dim[7] << endl;

    float intent_p1;
    infile.read(reinterpret_cast<char*>(&intent_p1), sizeof(intent_p1));
    bytes_read += infile.gcount();

    float intent_p2;
    infile.read(reinterpret_cast<char*>(&intent_p2), sizeof(intent_p2));
    bytes_read += infile.gcount();

    float intent_p3;
    infile.read(reinterpret_cast<char*>(&intent_p3), sizeof(intent_p3));
    bytes_read += infile.gcount();

    short intent_code;
    infile.read(reinterpret_cast<char*>(&intent_code), sizeof(intent_code));
    bytes_read += infile.gcount();

    short datatype;
    infile.read(reinterpret_cast<char*>(&datatype), sizeof(datatype));
    bytes_read += infile.gcount();

    if (datatype != 16)
    {
        cout << "Data type must be float" << endl;
        return -1;
    }
    else
    {
        cout << "Data type: float" << endl;
    }

    short bitpix;
    infile.read(reinterpret_cast<char*>(&bitpix), sizeof(bitpix));
    bytes_read += infile.gcount();

    if (bitpix != 32)
    {
        cout << "Bits per pixel must be 32-bit" << endl;
        return -1;
    }
    else
    {
        cout << "Bits per pixel: " << bitpix << endl;
    }

    short slice_start;
    infile.read(reinterpret_cast<char*>(&slice_start), sizeof(slice_start));
    bytes_read += infile.gcount();

    float pixdim[8];
    infile.read(reinterpret_cast<char*>(&pixdim), sizeof(pixdim));
    bytes_read += infile.gcount();

    float vox_offset;
    infile.read(reinterpret_cast<char*>(&vox_offset), sizeof(vox_offset));
    bytes_read += infile.gcount();

    float scl_slope;
    infile.read(reinterpret_cast<char*>(&scl_slope), sizeof(scl_slope));
    bytes_read += infile.gcount();

    float scl_inter;
    infile.read(reinterpret_cast<char*>(&scl_inter), sizeof(scl_inter));
    bytes_read += infile.gcount();

    short slice_end;
    infile.read(reinterpret_cast<char*>(&slice_end), sizeof(slice_end));
    bytes_read += infile.gcount();

    char slice_code;
    infile.read(reinterpret_cast<char*>(&slice_code), sizeof(slice_code));
    bytes_read += infile.gcount();

    char xyzt_units ...
(more)
edit flag offensive delete link more

Question Tools

1 follower

Stats

Asked: 2017-11-26 08:13:24 -0600

Seen: 2,209 times

Last updated: Nov 28 '17