Skip to content

Commit

Permalink
Include StudyInstanceUID and SeriesInstanceUID in filename
Browse files Browse the repository at this point in the history
  • Loading branch information
neurolabusc committed Apr 24, 2017
1 parent b536b6b commit 3f2d477
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 7 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ This software is open source. The bulk of the code is covered by the BSD license
- Options -1..-9 to control [gz compression level](https://github.com/rordenlab/dcm2niix/issues/90).
- Includes some [PET details in the BIDS JSON sidecar](https://github.com/rordenlab/dcm2niix/issues/87).
- Better detection of image order for Philips 4D DICOM (reported by Jason McMorrow and Stephen Wilson).
- [Include StudyInstanceUID and SeriesInstanceUID in filename](https://github.com/rordenlab/dcm2niix/issues/94)

7-Feb-2017
- Can be compiled to use either Philips [Float or Display](http://www.nitrc.org/forum/message.php?msg_id=20213) intensity intercept and slope values.
Expand Down
1 change: 1 addition & 0 deletions console/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ add_executable(dcm2niix
jpg_0XC3.cpp
ujpeg.cpp
nifti1_io_core.cpp
nii_foreign.cpp
nii_ortho.cpp
nii_dicom_batch.cpp)

Expand Down
9 changes: 6 additions & 3 deletions console/main_console.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,14 @@ void showHelp(const char * argv[], struct TDCMopts opts) {
printf("usage: %s [options] <in_folder>\n", cstr);
printf(" Options :\n");
printf(" -1..-9 : gz compression level (1=fastest, 9=smallest)\n");
printf(" -b : BIDS sidecar (y/n, default n)\n");
char bidsCh = 'n';
if (opts.isCreateBIDS) bidsCh = 'y';
printf(" -b : BIDS sidecar (y/n, default %c)\n", bidsCh);
#ifdef mySegmentByAcq
printf(" -f : filename (%%a=antenna (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%q=sequence number, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);

printf(" -f : filename (%%a=antenna (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%j seriesInstanceUID, %%k studyInstanceUID, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%q=sequence number, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
#else
printf(" -f : filename (%%a=antenna (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
printf(" -f : filename (%%a=antenna (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%j seriesInstanceUID, %%k studyInstanceUID, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
#endif
printf(" -h : show help\n");
printf(" -i : ignore derived, localizer and 2D images (y/n, default n)\n");
Expand Down
2 changes: 1 addition & 1 deletion console/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ ifneq ($(OS),Windows_NT)
endif
endif
all:
g++ $(CFLAGS) -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
g++ $(CFLAGS) -O3 -I. main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
11 changes: 11 additions & 0 deletions console/nii_dicom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,8 @@ struct TDICOMdata clear_dicom_data() {
strcpy(d.studyTime, "11:11:11");
strcpy(d.manufacturersModelName, "N/A");
strcpy(d.procedureStepDescription, "");
strcpy(d.seriesInstanceUID, "");
strcpy(d.studyInstanceUID, "");
strcpy(d.bodyPartExamined,"");
d.dateTime = (double)19770703150928.0;
d.acquisitionTime = 0.0f;
Expand Down Expand Up @@ -2566,6 +2568,8 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
#define kDiffusionDirectionGEX 0x0019+(0x10BB<< 16 ) //DS
#define kDiffusionDirectionGEY 0x0019+(0x10BC<< 16 ) //DS
#define kDiffusionDirectionGEZ 0x0019+(0x10BD<< 16 ) //DS
#define kStudyInstanceUID 0x0020+(0x000D << 16 )
#define kSeriesInstanceUID 0x0020+(0x000E << 16 )
#define kPatientPosition 0x0020+(0x0032 << 16 )
#define kSeriesNum 0x0020+(0x0011 << 16 )
#define kAcquNum 0x0020+(0x0012 << 16 )
Expand Down Expand Up @@ -2878,6 +2882,13 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
d.CSA.numDti = 1;
}
break;
case kStudyInstanceUID :
dcmStr (lLength, &buffer[lPos], d.studyInstanceUID);
break;

case kSeriesInstanceUID :
dcmStr (lLength, &buffer[lPos], d.seriesInstanceUID);
break;
case kPatientPosition :
if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (is2005140FSQ)) {
if (!is2005140FSQwarned)
Expand Down
2 changes: 1 addition & 1 deletion console/nii_dicom.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ static const int kCompress50 = 3; //obsolete JPEG lossy
double dateTime, acquisitionTime, acquisitionDate;
bool isXRay, isSlicesSpatiallySequentialPhilips, isNonImage, isValid, is3DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase,isHasMagnitude,isHasMixed, isFloat, isResampled;
char phaseEncodingRC;
char bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageType[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr],seriesDescription[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], birthDate[kDICOMStr], gender[kDICOMStr], age[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr], imageComments[kDICOMStr];
char seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageType[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr],seriesDescription[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], birthDate[kDICOMStr], gender[kDICOMStr], age[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr], imageComments[kDICOMStr];
struct TCSAdata CSA;
};

Expand Down
18 changes: 16 additions & 2 deletions console/nii_dicom_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#endif

#if defined(__APPLE__) && defined(__MACH__)
//#include "nii_foreign.h"
#endif
#ifndef myDisableZLib
#ifdef MiniZ
Expand All @@ -32,6 +31,7 @@
#include "nifti1.h"
#endif
#include "nii_dicom_batch.h"
#include "nii_foreign.h"
#include "nii_dicom.h"
#include <ctype.h> //toupper
#include <float.h>
Expand Down Expand Up @@ -361,6 +361,11 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
break;
};
fprintf(fp, "\t\"ManufacturersModelName\": \"%s\",\n", d.manufacturersModelName );
//CR 4/2017: are seriesInstanceUID and studyInstanceUID personal details?
//if (strlen(d.seriesInstanceUID) > 0)
// fprintf(fp, "\t\"SeriesInstanceUID\": \"%s\",\n", d.seriesInstanceUID );
//if (strlen(d.studyInstanceUID) > 0)
// fprintf(fp, "\t\"StudyInstanceUID\": \"%s\",\n", d.studyInstanceUID );
if (strlen(d.procedureStepDescription) > 0)
fprintf(fp, "\t\"ProcedureStepDescription\": \"%s\",\n", d.procedureStepDescription );
if (strlen(d.scanningSequence) > 0)
Expand Down Expand Up @@ -784,6 +789,10 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
strcat (outname,opts.indirParent);
if (f == 'I')
strcat (outname,dcm.patientID);
if (f == 'J')
strcat (outname,dcm.seriesInstanceUID);
if (f == 'K')
strcat (outname,dcm.studyInstanceUID);
if (f == 'L') //"L"ocal Institution-generated description or classification of the Procedure Step that was performed.
strcat (outname,dcm.procedureStepDescription);
if (f == 'M') {
Expand Down Expand Up @@ -2095,6 +2104,11 @@ int nii_loadDir(struct TDCMopts* opts) {
return convert_foreign(*opts);
}*/
getFileName(opts->indirParent, opts->indir);
if (isFile && ( (isExt(indir, ".v"))) ) {
//open_foreign(opts->indir);
return open_foreign (indir);

}
if (isFile && ( (isExt(indir, ".par")) || (isExt(indir, ".rec"))) ) {
char pname[512], rname[512];
strcpy(pname,indir);
Expand Down Expand Up @@ -2336,7 +2350,7 @@ void setDefaultOpts (struct TDCMopts *opts, const char * argv[]) { //either "set
opts->gzLevel = -1;
opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates
opts->isRGBplanar = false;
opts->isCreateBIDS = false;
opts->isCreateBIDS = true;
opts->isCreateText = false;
#ifdef myDebug
opts->isVerbose = true;
Expand Down
214 changes: 214 additions & 0 deletions console/nii_foreign.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
#include "nii_foreign.h"
#include "nifti1.h"
#include "nifti1_io_core.h"

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "print.h"

#ifndef Float32
#define Float32 float
#endif
#ifndef uint32
#define uint32 uint32_t
#endif



/*nii_readEcat7 2017 by Chris Rorden, BSD license
http://www.turkupetcentre.net/software/libdoc/libtpcimgio/ecat7_8h_source.html#l00060
http://www.turkupetcentre.net/software/libdoc/libtpcimgio/ecat7r_8c_source.html#l00717
http://xmedcon.sourcearchive.com/documentation/0.10.7-1build1/ecat7_8h_source.html
https://github.com/BIC-MNI/minc-tools/tree/master/conversion/ecattominc
https://github.com/nipy/nibabel/blob/ec4567fb09b4472c5a4bb9a13dbcc9eb0a63d875/nibabel/ecat.py
*/

//TO DO:
// currently only reads first 3D volume: for multiple volumes we need to handle num_frames num_bed_pos
// SForm and QForm copy SPM and do not account for slice angulation or orientation
int nii_readEcat7(const char * fname, struct nifti_1_header *nhdr, bool * swapEndian) {
//data type
#define ECAT7_BYTE 1
#define ECAT7_VAXI2 2
#define ECAT7_VAXI4 3
#define ECAT7_VAXR4 4
#define ECAT7_IEEER4 5
#define ECAT7_SUNI2 6
#define ECAT7_SUNI4 7
//file types
//#define ECAT7_UNKNOWN 0
#define ECAT7_2DSCAN 1
#define ECAT7_IMAGE16 2
#define ECAT7_ATTEN 3
#define ECAT7_2DNORM 4
#define ECAT7_POLARMAP 5
#define ECAT7_VOLUME8 6
#define ECAT7_VOLUME16 7
#define ECAT7_PROJ 8
#define ECAT7_PROJ16 9
#define ECAT7_IMAGE8 10
#define ECAT7_3DSCAN 11
#define ECAT7_3DSCAN8 12
#define ECAT7_3DNORM 13
#define ECAT7_3DSCANFIT 14
typedef struct __attribute__((packed)) {
char magic[14],original_filename[32];
uint16_t sw_version, system_type, file_type;
char serial_number[10];
uint32 scan_start_time;
char isotope_name[8];
Float32 isotope_halflife;
char radiopharmaceutical[32];
Float32 gantry_tilt, gantry_rotation, bed_elevation, intrinsic_tilt;
int16_t wobble_speed, transm_source_type;
Float32 distance_scanned, transaxial_fov;
uint16_t angular_compression, coin_samp_mode, axial_samp_mode;
Float32 ecat_calibration_factor;
uint16_t calibration_unitS, calibration_units_type, compression_code;
char study_type[12], patient_id[16], patient_name[32], patient_sex, patient_dexterity;
Float32 patient_age, patient_height, patient_weight;
uint32 patient_birth_date;
char physician_name[32], operator_name[32], study_description[32];
uint16_t acquisition_type, patient_orientation;
char facility_name[20];
uint16_t num_planes, num_frames, num_gates, num_bed_pos;
Float32 init_bed_position;
Float32 bed_position[15];
Float32 plane_separation;
uint16_t lwr_sctr_thres, lwr_true_thres, upr_true_thres;
char user_process_code[10];
uint16_t acquisition_mode;
Float32 bin_size, branching_fraction;
uint32 dose_start_time;
Float32 dosage, well_counter_corr_factor;
char data_units[32];
uint16_t septa_state;
char fill[12];
} ecat_main_hdr;
typedef struct __attribute__((packed)) {
int16_t data_type, num_dimensions, x_dimension, y_dimension, z_dimension;
Float32 x_offset, y_offset, z_offset, recon_zoom, scale_factor;
int16_t image_min, image_max;
Float32 x_pixel_size, y_pixel_size, z_pixel_size;
int32_t frame_duration, frame_start_time;
int16_t filter_code;
Float32 x_resolution, y_resolution, z_resolution, num_r_elements, num_angles, z_rotation_angle, decay_corr_fctr;
int32_t processing_code, gate_duration, r_wave_offset, num_accepted_beats;
Float32 filter_cutoff_frequenc, filter_resolution, filter_ramp_slope;
int16_t filter_order;
Float32 filter_scatter_fraction, filter_scatter_slope;
char annotation[40];
Float32 mtx[9], rfilter_cutoff, rfilter_resolution;
int16_t rfilter_code, rfilter_order;
Float32 zfilter_cutoff, zfilter_resolution;
int16_t zfilter_code, zfilter_order;
Float32 mtx_1_4, mtx_2_4, mtx_3_4;
int16_t scatter_type, recon_type, recon_views, fill_cti[87], fill_user[49];
} ecat_img_hdr;
typedef struct __attribute__((packed)) {
int32_t hdr[4],
r01[4],r02[4],r03[4],r04[4],r05[4],r06[4],r07[4],r08[4],r09[4],r10[4],
r11[4],r12[4],r13[4],r14[4],r15[4],r16[4],r17[4],r18[4],r19[4],r20[4],
r21[4],r22[4],r23[4],r24[4],r25[4],r26[4],r27[4],r28[4],r29[4],r30[4],
r31[4];
} ecat_list_hdr;
* swapEndian = false;
size_t n;
FILE *f;
ecat_main_hdr mhdr;
f = fopen(fname, "rb");
if (f)
n = fread(&mhdr, sizeof(mhdr), 1, f);
if(!f || n!=1) {
printf("Problem reading ECAT7 file!\n");
fclose(f);
return EXIT_FAILURE;
}
if ((mhdr.magic[0] != 'M') || (mhdr.magic[1] != 'A') || (mhdr.magic[2] != 'T')
|| (mhdr.magic[3] != 'R') || (mhdr.magic[4] != 'I') || (mhdr.magic[5] != 'X') ) {
printf("Signature not 'MATRIX' (ECAT7)\n");
fclose(f);
return EXIT_FAILURE;
}
* swapEndian = mhdr.file_type > 255;
if (*swapEndian) {
nifti_swap_2bytes(1, &mhdr.sw_version);
nifti_swap_2bytes(1, &mhdr.file_type);
nifti_swap_2bytes(1, &mhdr.num_frames);
nifti_swap_4bytes(1, &mhdr.ecat_calibration_factor);
}
if ((mhdr.file_type < ECAT7_2DSCAN) || (mhdr.file_type > ECAT7_3DSCANFIT)) {
printf("Unknown ECAT file type %d\n", mhdr.file_type);
fclose(f);
return EXIT_FAILURE;
}
if ((mhdr.num_frames > 1) || (mhdr.num_bed_pos > 1)) //to do: multi-volume files
printf("Only reading first volume from ECAT file with %d frames and %d bed positions\n", mhdr.num_frames, mhdr.num_bed_pos);
//read list matrix
fseek(f, 512, SEEK_SET);
ecat_list_hdr lhdr;
fread(&lhdr, sizeof(lhdr), 1, f);
if (*swapEndian) {
nifti_swap_4bytes(128, &lhdr.hdr[0]);
}
//offset to first image
int img1_StartBytes = lhdr.r01[1] * 512;
//load image header for first image
fseek(f, img1_StartBytes - 512, SEEK_SET); //image header is block immediately before image
ecat_img_hdr ihdr;
fread(&ihdr, sizeof(ihdr), 1, f);
if (*swapEndian) {
nifti_swap_2bytes(5, &ihdr.data_type);
nifti_swap_4bytes(7, &ihdr.x_resolution);
nifti_swap_4bytes(3, &ihdr.x_pixel_size);
}
if ((ihdr.data_type != ECAT7_BYTE) && (ihdr.data_type != ECAT7_SUNI2) && (ihdr.data_type != ECAT7_SUNI4)) {
printf("Unknown or unsupported ECAT data type %d\n", ihdr.data_type);
fclose(f);
return EXIT_FAILURE;
}
//cm -> mm
ihdr.x_pixel_size *= 10.0;
ihdr.y_pixel_size *= 10.0;
ihdr.z_pixel_size *= 10.0;
fclose(f);
nhdr->dim[0]=3;//3D
nhdr->dim[1]=ihdr.x_dimension;
nhdr->dim[2]=ihdr.y_dimension;
nhdr->dim[3]=ihdr.z_dimension; //slices per volume
nhdr->dim[4]=1; //volumes
nhdr->pixdim[1]=ihdr.x_pixel_size;
nhdr->pixdim[2]=ihdr.y_pixel_size;
nhdr->pixdim[3]=ihdr.z_pixel_size; //TO DO: slice gap?
nhdr->datatype = DT_INT16;
if (ihdr.data_type == ECAT7_BYTE) nhdr->datatype = DT_UINT8;
if (ihdr.data_type == ECAT7_SUNI4) nhdr->datatype = DT_INT32;
nhdr->vox_offset = img1_StartBytes;
nhdr->sform_code = NIFTI_XFORM_UNKNOWN; //NIFTI_XFORM_SCANNER_ANAT;
nhdr->scl_slope = ihdr.scale_factor * mhdr.ecat_calibration_factor;
//direct clone of spm_ecat2nifti: seems off by one (indexed from zero)
vec3 start = setVec3((ihdr.x_dimension-2.0)/2.0*ihdr.x_pixel_size, (ihdr.y_dimension-2.0)/2.0*ihdr.y_pixel_size, (ihdr.z_dimension-2.0)/2.0*ihdr.z_pixel_size);
nhdr->srow_x[0]=-nhdr->pixdim[1];nhdr->srow_x[1]=0.0f;nhdr->srow_x[2]=0.0f;nhdr->srow_x[3]=start.v[0];
nhdr->srow_y[0]=0.0f;nhdr->srow_y[1]=-nhdr->pixdim[2];nhdr->srow_y[2]=0.0f;nhdr->srow_y[3]=start.v[1];
nhdr->srow_z[0]=0.0f;nhdr->srow_z[1]=0.0f;nhdr->srow_z[2]=-nhdr->pixdim[3];nhdr->srow_z[3]=start.v[2];
fclose(f);
//convertForeignToNifti(nhdr);
return EXIT_SUCCESS;
}



int open_foreign (const char *fn){
printf("--> %s\n", fn);
struct nifti_1_header nhdr;
bool swapEndian;
int ret = nii_readEcat7(fn, &nhdr, &swapEndian);
if (ret == EXIT_SUCCESS)
printf("!\n");

return ret;
}// open_foreign()

16 changes: 16 additions & 0 deletions console/nii_foreign.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//Attempt to open non-DICOM image

#ifndef _NII_FOREIGN_
#define _NII_FOREIGN_

#ifdef __cplusplus
extern "C" {
#endif

int open_foreign (const char *fn);

#ifdef __cplusplus
}
#endif

#endif

0 comments on commit 3f2d477

Please sign in to comment.