/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * filename: m-ecat64.c * * * * UTIL C-source: Medical Image Conversion Utility * * * * purpose : Read and Write ECAT 6.4 files * * * * project : (X)MedCon by Erik Nolf * * * * Functions : MdcCheckECAT6() - Check for ECAT 6.4 format * * MdcReadECAT6() - Read ECAT 6.4 file * * MdcWriteECAT6() - Write ECAT 6.4 file * * MdcGetSliceLocation() - Get slice location * * MdcGetFilterCode() - Get code number of filter * * MdcFillMainHeader() - Fill in Main Header * * MdcFillImageSubHeader() - Fill in Image SubHeader * * MdcPrintEcatInfoDB() - Print ECAT database info * * * * * * Notes : source needs m-matrix.h & m-matrix.c * * * * Credits : - CTI coders - for creating the basic code * * - Johan Keppens - getting it to work initially * * - Sakari Alenius - reading using a matrix list * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* $Id: m-ecat64.c,v 1.83 2007/06/18 22:25:21 enlf Exp $ */ /* Copyright (C) 1997-2007 by Erik Nolf This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Place - Suite 330, Boston, MA 02111-1307, USA. */ /**************************************************************************** H E A D E R S ****************************************************************************/ #include "m-depend.h" #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STRINGS_H #ifndef _WIN32 #include #endif #endif #include "medcon.h" /**************************************************************************** D E F I N E S ****************************************************************************/ #define MDC_NUM_BEDS_TWEAK MDC_YES /* check on zero/one based num_beds */ #ifdef _WIN32 #define MDC_ECAT6_RESTRICT_DIMS MDC_YES /* only square dim and max 256 (safe) */ #else #define MDC_ECAT6_RESTRICT_DIMS MDC_NO /* no square dim and no max (danger) */ #endif static Uint32 saved_mwidth; static Uint32 saved_mheight; static char MdcEcatDataTypes [MDC_MAX_ECATDATATYPES][MDC_MAX_ECATDATATYPES_SIZE]= {"Unknown","ByteData","VAX Int16","VAX Int32", "VAX float","IEEE float","SUN Int16","SUN Int32"}; static char MdcEcatFileTypes [MDC_MAX_ECATFILETYPES][MDC_MAX_ECATFILETYPES_SIZE]= {"Unknown","Sinogram","PetImage","Attenuation", "Normalization","Smooth File"}; static char MdcEcatAcquisitionTypes [MDC_MAX_ECATACQTYPES][MDC_MAX_ECATACQTYPES_SIZE]= {"Undefined","Blank","Transmission", "Static Emission","Dynamic Emission","Gated Emission", "Transmission Rectilinear","Emission Rectilinear", "Whole Body Transmission","Whole Body Static"}; static char MdcEcatFilterTypes [MDC_MAX_ECATFLTRTYPES][MDC_MAX_ECATFLTRTYPES_SIZE]= {"None","Ramp","Butter","Hann", "Hamm","Parzen","Shepp","Unknown"}; static char MdcEcatQuantificationUnits [MDC_MAX_ECATQUANTTYPES][MDC_MAX_ECATQUANTTYPES_SIZE]= {"Total Counts","Undefined", "ECAT counts/second/pixel","uCi/ml [1uCi = 37Bq]", "LMRGlu","LMRGlu umol/min/100g","LMRGlu mg/min/100g", "nCi/ml","Well counts","Becquerels","ml/min/100g", "ml/min/g"}; static Int16 MdcEcatSystemTypes[MDC_MAX_ECATSYSTEMTYPES]= {831, 911, 931, 933, 951, 953}; /**************************************************************************** F U N C T I O N S ****************************************************************************/ int MdcCheckECAT6(FILEINFO *fi) { Mdc_Main_header mh; int i; if (mdc_mat_read_main_header(fi->ifp,&mh)) return MDC_BAD_READ; if (mh.system_type == MDC_ECAT6_SYST_TYPE) return MDC_FRMT_ECAT6; for (i=0; i < MDC_MAX_ECATSYSTEMTYPES; i++) { if (mh.system_type == MdcEcatSystemTypes[i]) return MDC_FRMT_ECAT6; } return MDC_FRMT_NONE; } const char *MdcReadECAT6(FILEINFO *fi) { FILE *fp = fi->ifp; IMG_DATA *id=NULL; DYNAMIC_DATA *dd=NULL; int i, error; const char *err; char *str; Uint32 bytes, img=0, found=0, number; Mdc_Main_header mh; Mdc_Image_subheader ish; Mdc_Scan_subheader ssh; Mdc_Attn_subheader ash; Mdc_Norm_subheader nsh; struct Mdc_MatDir entry, matrix_list[MDC_ECAT6_MAX_MATRICES]; struct Mdc_Matval matval; Int16 data_type=BIT16_S; int bed,gate,frame,plane,data=0,nb,ng,nf,np,nd; int matnum, startblk, endblk, num_matrices; float slice_position; if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Reading ECAT6:"); if (MDC_VERBOSE) MdcPrntMesg("ECAT6 Reading <%s> ...",fi->ifname); /* put some defaults we use */ fi->endian=MDC_FILE_ENDIAN=MDC_LITTLE_ENDIAN; fi->modality = M_PT; /* read the main header */ error = mdc_mat_read_main_header(fp, &mh); if (error) return("ECAT6 Bad read main header"); /* if (MDC_INFO_DB) { MdcPrintEcatInfoDB(&mh); return NULL; } */ if (MDC_INFO || MDC_INFO_DB) { MdcPrntScrn("Main Header (%d bytes)\n",MH_64_SIZE); MdcPrintLine('-',MDC_HALF_LENGTH); MdcPrntScrn("Original Filename : "); MdcPrintStr(mh.original_file_name); MdcPrntScrn("Software Version : %d\n",mh.sw_version); MdcPrntScrn("Data Type : %d ",mh.data_type); if ((mh.data_type > -1) && (mh.data_type < 8)) MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[mh.data_type]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("System Type : %d\n",mh.system_type); MdcPrntScrn("File Type : %d ",mh.file_type); if ((mh.file_type > -1) && (mh.file_type < 5)) MdcPrntScrn("(= %s)\n",MdcEcatFileTypes[mh.file_type]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("Node Id : "); MdcPrintStr(mh.node_id); MdcPrntScrn("Scan Date - Day : %d\n",mh.scan_start_day); MdcPrntScrn(" - Month : %d\n",mh.scan_start_month); MdcPrntScrn(" - Year : %d\n",mh.scan_start_year); MdcPrntScrn(" - Hour : %d\n",mh.scan_start_hour); MdcPrntScrn(" - Minute : %d\n",mh.scan_start_minute); MdcPrntScrn(" - Second : %d\n",mh.scan_start_second); MdcPrntScrn("Isotope Code : "); MdcPrintStr(mh.isotope_code); MdcPrntScrn("Isotope Halflife : %f [sec]\n",mh.isotope_halflife); MdcPrntScrn("Radiopharmaceutical : "); MdcPrintStr(mh.radiopharmaceutical); MdcPrntScrn("Gantry Tilt : %f [degrees]\n",mh.gantry_tilt); MdcPrntScrn("Gantry Rotation : %f [degrees]\n" ,mh.gantry_rotation); MdcPrntScrn("Bed Elevation : %f [cm]\n",mh.bed_elevation); MdcPrntScrn("Rotating Source Speed : %d [revolutions/minute]\n" ,mh.rot_source_speed); MdcPrntScrn("Wobble Control Speed : %d [revolutions/minute]\n" ,mh.wobble_speed); MdcPrntScrn("Transmission Source : %d\n",mh.transm_source_type); MdcPrntScrn("Axial Field of View : %f [cm]\n",mh.axial_fov); MdcPrntScrn("Transaxial Field of View : %f [cm]\n",mh.transaxial_fov); MdcPrntScrn("Transaxial Sampling Mode : %d\n",mh.transaxial_samp_mode); MdcPrntScrn("Coincidence Sampling Mode: %d\n",mh.coin_samp_mode); MdcPrntScrn("Axial Sampling Mode : %d\n",mh.axial_samp_mode); MdcPrntScrn("Calibration Factor : %f\n",mh.calibration_factor); MdcPrntScrn("Calibration Units : %d ",mh.calibration_units); if ((mh.calibration_units > -1) && (mh.calibration_units < 12)) MdcPrntScrn("(= %s)\n" ,MdcEcatQuantificationUnits[mh.calibration_units]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("Compression Code : %d\n",mh.compression_code); MdcPrntScrn("Study Name : "); MdcPrintStr(mh.study_name); MdcPrntScrn("Patient Id : "); MdcPrintStr(mh.patient_id); MdcPrntScrn("Patient Name : "); MdcPrintStr(mh.patient_name); MdcPrntScrn("Patient Sex : "); MdcPrintChar(mh.patient_sex); MdcPrntScrn("\n"); MdcPrntScrn("Patient Age : "); MdcPrintStr(mh.patient_age); MdcPrntScrn("Patient Height : "); MdcPrintStr(mh.patient_height); MdcPrntScrn("Patient Weight : "); MdcPrintStr(mh.patient_weight); MdcPrntScrn("Patient Dexterity : "); MdcPrintChar(mh.patient_dexterity); MdcPrntScrn("\n"); MdcPrntScrn("Physician Name : "); MdcPrintStr(mh.physician_name); MdcPrntScrn("Operator Name : "); MdcPrintStr(mh.operator_name); MdcPrntScrn("Study Description : "); MdcPrintStr(mh.study_description); MdcPrntScrn("Acquisition Type : %d ",mh.acquisition_type); if ((mh.acquisition_type > -1) && (mh.acquisition_type <= 9)) MdcPrntScrn("(= %s)\n",MdcEcatAcquisitionTypes[mh.acquisition_type]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("Bed Type : %d\n",mh.bed_type); MdcPrntScrn("Septa Type : %d\n",mh.septa_type); MdcPrntScrn("Facility Name : "); MdcPrintStr(mh.facility_name); MdcPrntScrn("Number of Planes : %d\n",mh.num_planes); MdcPrntScrn("Number of Frames : %d\n",mh.num_frames); MdcPrntScrn("Number of Gates : %d\n",mh.num_gates); MdcPrntScrn("Number of Bed Positions : %d\n",mh.num_bed_pos); MdcPrntScrn("Initial Bed Position : %f [cm]\n",mh.init_bed_position); for (i=0; i<15; i++) MdcPrntScrn("Bed Offset[%02d] : %f [cm]\n",i+1 ,mh.bed_offset[i]); MdcPrntScrn("Plane Separation : %f [cm]\n",mh.plane_separation); MdcPrntScrn("Lower Scatter Treshold : %d [KeV]\n",mh.lwr_sctr_thres); MdcPrntScrn("Lower True Treshold : %d [KeV]\n",mh.lwr_true_thres); MdcPrntScrn("Upper True Treshold : %d [KeV]\n",mh.upr_true_thres); MdcPrntScrn("Collimator : %6.0f\n",mh.collimator); MdcPrntScrn("User Process Code : "); MdcPrintStr(mh.user_process_code); MdcPrntScrn("Acquisition Mode : %d\n",mh.acquisition_mode); } if (MDC_INFO_DB) return(NULL); /* just needed db info */ if ((mh.file_type!=MDC_ECAT6_SCAN_FILE) && (mh.file_type!=MDC_ECAT6_IMAGE_FILE) && (mh.file_type!=MDC_ECAT6_ATTN_FILE) && (mh.file_type!=MDC_ECAT6_NORM_FILE) ) return("ECAT6 Unsupported file type"); if (mh.num_frames <= 0 ) mh.num_frames = 1; if (mh.num_gates <= 0 ) mh.num_gates = 1; if (mh.num_bed_pos < 0 ) mh.num_bed_pos = 0; /* fill in global FILEINFO data */ fi->dim[0]= 6; fi->dim[3]= mh.num_planes; fi->dim[4]= mh.num_frames; fi->dim[5]= mh.num_gates; fi->dim[6]= mh.num_bed_pos + 1; /* must be 1-based */ #if MDC_NUM_BEDS_TWEAK /* double check num_bed_pos value due to */ /* inconsistent use as zero or one based */ bed = mh.num_bed_pos; while (!mdc_mat_lookup(fp,mdc_mat_numcod(1,1,1,0,bed),&entry) && (bed > 0)) { bed--; } fi->dim[6] = bed + 1; #endif /* check for unsupported bed overlap */ if (fi->dim[6] > 1) { float axial_width, bed_offset=mh.bed_offset[0]; if (bed_offset < 0) bed_offset = -bed_offset; axial_width = mh.plane_separation * (float)fi->dim[3]; if ((axial_width - bed_offset) >= 1.0) { MdcPrntWarn("ECAT6 Bed overlaps unsupported"); } } for (i=3, number=1; i<=6; i++) number*=fi->dim[i]; if (number == 0) return("ECAT6 No valid images specified"); /* fill in orientation information */ fi->pat_slice_orient = MDC_SUPINE_HEADFIRST_TRANSAXIAL; /* default! */ str = MdcGetStrPatPos(fi->pat_slice_orient); MdcStringCopy(fi->pat_pos,str,strlen(str)); if ( (strncmp(mh.user_process_code,"COR",10)==0) || (strncmp(mh.user_process_code,"SAG",10)==0)) { /* CORONAL SLICES or SAGITTAL SLICES The images Ecat 6.4 software writes are useless: 128x128 images with the small coronal/sagittal slice in it ... This means their pixel_xsize & pixel_ysize doesn't quite fit the real world dimensions any more !! Therefore we don't even try to attempt writing the proper orientation information. */ }else{ /* "TRA" or nothing TRANSAXIAL SLICES (Transverse) Writing the proper orientation information See man-page `m-acr.4' for more info (!) */ str = MdcGetStrPatOrient(fi->pat_slice_orient); MdcStringCopy(fi->pat_orient,str,strlen(str)); } /* fill in patient study related information */ fi->patient_sex[0] = mh.patient_sex; fi->patient_sex[1]='\0'; MdcStringCopy(fi->patient_name,mh.patient_name,32); MdcStringCopy(fi->patient_id,mh.patient_id,16); fi->patient_weight = (float)atof(mh.patient_weight); fi->patient_height = (float)atof(mh.patient_height); fi->study_date_day = mh.scan_start_day; fi->study_date_month = mh.scan_start_month; fi->study_date_year = mh.scan_start_year; fi->study_time_hour = mh.scan_start_hour; fi->study_time_minute= mh.scan_start_minute; fi->study_time_second= mh.scan_start_second; if ((mh.file_type==MDC_ECAT6_SCAN_FILE) || (mh.file_type==MDC_ECAT6_IMAGE_FILE)) { switch (mh.acquisition_type) { case MDC_ECAT6_ACQTYPE_UNKNOWN : fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break; case MDC_ECAT6_ACQTYPE_BLANK : fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break; case MDC_ECAT6_ACQTYPE_TRANSMISSION : fi->acquisition_type = MDC_ACQUISITION_TOMO; break; case MDC_ECAT6_ACQTYPE_STATIC_EMISSION : fi->acquisition_type = MDC_ACQUISITION_TOMO; break; case MDC_ECAT6_ACQTYPE_DYNAMIC_EMISSION : fi->acquisition_type = MDC_ACQUISITION_DYNAMIC; break; case MDC_ECAT6_ACQTYPE_GATED_EMISSION : fi->acquisition_type = MDC_ACQUISITION_GSPECT; break; case MDC_ECAT6_ACQTYPE_TRANSMISSION_RECT : fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break; case MDC_ECAT6_ACQTYPE_EMISSION_RECT : fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break; case MDC_ECAT6_ACQTYPE_WHOLE_BODY_TRANSM : fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break; case MDC_ECAT6_ACQTYPE_WHOLE_BODY_STATIC : fi->acquisition_type = MDC_ACQUISITION_TOMO; break; default: fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; } }else{ fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; } sprintf(mdcbufr,"ECAT%hd",mh.system_type); MdcStringCopy(fi->manufacturer,mdcbufr,strlen(mdcbufr)); MdcStringCopy(fi->operator_name,mh.operator_name,32); MdcStringCopy(fi->study_descr,mh.study_description,32); MdcStringCopy(fi->study_id,mh.study_name,12); MdcStringCopy(fi->institution,mh.facility_name,20); MdcStringCopy(fi->radiopharma,mh.radiopharmaceutical,32); MdcStringCopy(fi->isotope_code,mh.isotope_code,8); fi->isotope_halflife = mh.isotope_halflife; fi->gantry_tilt = mh.gantry_tilt; if (MDC_ECHO_ALIAS == MDC_YES) { MdcEchoAliasName(fi); return(NULL); } if (!MdcGetStructID(fi,number)) return("ECAT6 Bad malloc IMG_DATA structs"); /* always malloc dyndata structs */ if (!MdcGetStructDD(fi,(Uint32)fi->dim[4]*fi->dim[5]*fi->dim[6])) return("ECAT6 Couldn't malloc DYNAMIC_DATA structs"); /* always malloc beddata structs */ if (!MdcGetStructBD(fi,(unsigned)fi->dim[6])) return("ECAT6 Couldn't malloc BED_DATA structs"); /* fill in BED_DATA struct */ fi->beddata[0].hoffset = mh.init_bed_position * 10.; /* mm */ fi->beddata[0].voffset = mh.bed_elevation * 10.; /* mm */ for (i=1; ibednr; i++) { fi->beddata[i].hoffset = mh.init_bed_position + mh.bed_offset[i-1]; fi->beddata[i].hoffset *= 10.; /* mm */ fi->beddata[i].voffset = mh.bed_elevation * 10.; /* mm */ } /* ECAT6: matrices for each slice */ num_matrices = mdc_mat_list(fp, matrix_list, MDC_ECAT6_MAX_MATRICES); if (num_matrices == 0) return("ECAT6 No matrices found"); if ((Uint32)num_matrices > fi->number) return("ECAT6 Too many matrices found"); /* sort matrices */ if ( num_matrices > 1) { switch (MDC_ECAT6_SORT) { case MDC_ANATOMICAL: /* anatomical */ bed = fi->dim[6]; /* one based */ if (fi->dim[4] > 1) { mdc_plane_sort(matrix_list, num_matrices); }else{ mdc_anatomical_sort(matrix_list, num_matrices, &mh, bed); } break; case MDC_BYFRAME : /* by frame */ mdc_matnum_sort(matrix_list, num_matrices); break; } } for (bed=0; beddim[6]; bed++) for (gate=1; gate<=fi->dim[5]; gate++) for (frame=1; frame<=fi->dim[4]; frame++) for (plane=1; plane<=fi->dim[3]; plane++) { if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,1./(float)fi->number,NULL); if (img == (Uint32)num_matrices) break; if (fi->dynnr > 0) dd = &fi->dyndata[(fi->dim[4]*bed) + (frame-1)]; mdc_mat_numdoc(matrix_list[img].matnum,&matval); nf = matval.frame; np = matval.plane; ng = matval.gate; nb = matval.bed; nd = matval.data; matnum = mdc_mat_numcod(nf,np,ng,nd,nb); if (!mdc_mat_lookup(fp, matnum, &entry)) continue; startblk = entry.strtblk + 1; endblk = entry.endblk - entry.strtblk; switch (mh.file_type) { case MDC_ECAT6_SCAN_FILE: error = mdc_mat_read_scan_subheader(fp, startblk-1, &ssh); if (error) return("ECAT6 Bad read scan subheader"); if (MDC_INFO) { MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("SINOGRAM SUBHEADER %05d: ",img+1); MdcPrntScrn("Frame: %d Plane: %d Gate: %d Data: %d Bed: %d\n" ,frame,plane,gate,data,bed); MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("Data Type : %d ",ssh.data_type); if ((ssh.data_type > -1) && (ssh.data_type < 8)) MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[ssh.data_type]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("Number of Elements : %d (width)\n",ssh.dimension_1); MdcPrntScrn("Number of Views : %d (height)\n",ssh.dimension_2); MdcPrntScrn("Smoothing : %d ",ssh.smoothing); switch (ssh.smoothing) { case 0: MdcPrntScrn("(= Not Smoothed)\n"); break; case 1: MdcPrntScrn("(= 9x9 Smoothing)\n"); break; default: MdcPrntScrn("(= Unknown)\n"); } MdcPrntScrn("Processing Code : %d\n",ssh.processing_code); MdcPrntScrn("Sample Distance : %f [cm]\n",ssh.sample_distance); MdcPrntScrn("Isotope Halflife : %f [sec]\n",ssh.isotope_halflife); MdcPrntScrn("Frame Duration (sec): %d [sec]\n" ,ssh.frame_duration_sec); MdcPrntScrn("Gate Duration : %d [ms]\n",ssh.gate_duration); MdcPrntScrn("R-Wave Offset : %d [ms]\n",ssh.r_wave_offset); MdcPrntScrn("Scale factor : %f\n",ssh.scale_factor); MdcPrntScrn("Minimum Scan Value : %d\n",ssh.scan_min); MdcPrntScrn("Maximum Scan Value : %d\n",ssh.scan_max); MdcPrntScrn("Total Prompts : %d\n",ssh.prompts); MdcPrntScrn("Total Delayed Events: %d\n",ssh.delayed); MdcPrntScrn("Total Multiples : %d\n",ssh.multiples); MdcPrntScrn("Total Net Trues : %d (Prompts - Random)\n" ,ssh.net_trues); for (i=0; i<16; i++) MdcPrntScrn("Corrected Singles [%2d] : %f\n",i+1 ,ssh.cor_singles[i]); for (i=0; i<16; i++) MdcPrntScrn("Uncorrected Singles [%2d] : %f\n",i+1 ,ssh.uncor_singles[i]); MdcPrntScrn("Total Average Corrected Singles: %f\n" ,ssh.tot_avg_cor); MdcPrntScrn("Total Average Uncorrected Singles: %f\n" ,ssh.tot_avg_uncor); MdcPrntScrn("Total Coincidence Rage : %d (from IPCP)\n" ,ssh.total_coin_rate); MdcPrntScrn("Frame Start Time : %d [ms]\n" ,ssh.frame_start_time); MdcPrntScrn("Frame Duration : %d [ms]\n" ,ssh.frame_duration); MdcPrntScrn("Loss Correction Factor : %f\n" ,ssh.loss_correction_fctr); for (i=0; i<8; i++) MdcPrntScrn("Phy_Planes [%d] : %d\n",i+1 ,ssh.phy_planes[i]); } /* fill in DYNAMIC_DATA struct */ if ((dd != NULL) && (plane == (fi->dim[3]/2))) { /* take values from a plane halfway */ dd->nr_of_slices = fi->dim[3]; dd->time_frame_start = (float)ssh.frame_start_time; dd->time_frame_duration = (float)ssh.frame_duration; } /* fill in IMG_DATA struct */ id = &fi->image[img]; id->width = (Uint32)ssh.dimension_1; id->height = (Uint32)ssh.dimension_2; id->quant_units = 1; id->quant_scale = 1; id->calibr_units= 1; id->calibr_fctr = 1; id->quant_scale = ssh.scale_factor; id->pixel_xsize = id->pixel_ysize = ssh.sample_distance * 10.;/* mm */ data_type = ssh.data_type; switch( data_type ) { case BYTE_TYPE: id->bits = 8; id->type = BIT8_U; break; case M68K_I2 : /* case SUN_I2 : */ case VAX_I2 : id->bits =16; id->type = BIT16_S; break; case M68K_I4 : /* case SUN_I4 : */ case VAX_I4 : id->bits =32; id->type = BIT32_S; break; case IEEE_R4 : /* case SUN_R4 : */ case VAX_R4 : id->bits =32; id->type = FLT32; break; } break; case MDC_ECAT6_IMAGE_FILE: error = mdc_mat_read_image_subheader(fp, startblk-1, &ish); if (error) return("ECAT6 Bad read image subheader"); if (MDC_INFO) { MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("IMAGE SUBHEADER %05d: ",img+1); MdcPrntScrn("Frame: %d Plane: %d Gate: %d Data: %d Bed: %d\n" ,frame,plane,gate,data,bed); MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("Data Type : %d ",ish.data_type); if ((ish.data_type > -1) && (ish.data_type < 8)) MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[ish.data_type]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("Number of Dimensions : %d\n",ish.num_dimensions); MdcPrntScrn("X Dimension : %d\n",ish.dimension_1); MdcPrntScrn("Y Dimension : %d\n",ish.dimension_2); MdcPrntScrn("X Offset : %f [cm]\n",ish.x_origin); MdcPrntScrn("Y Offset : %f [cm]\n",ish.y_origin); MdcPrntScrn("Recon Magnification Factor : %f\n",ish.recon_scale); MdcPrntScrn("Quantification Scale Factor : %e\n",ish.quant_scale); MdcPrntScrn("Image Minimum Pixel Value : %d\n",ish.image_min); MdcPrntScrn("Image Maximum Pixel Value : %d\n",ish.image_max); MdcPrntScrn("Pixel Size : %f [cm]\n",ish.pixel_size); MdcPrntScrn("Slice Width : %f [cm]\n",ish.slice_width); MdcPrntScrn("Frame Duration : %d [ms]\n",ish.frame_duration); MdcPrntScrn("Frame Start Time : %d [ms]\n",ish.frame_start_time); MdcPrntScrn("Slice Location : %d [cm]\n",ish.slice_location); MdcPrntScrn("Recon Start Hour : %d\n",ish.recon_start_hour); MdcPrntScrn("Recon Start Minute : %d\n",ish.recon_start_minute); MdcPrntScrn("Recon Start Second : %d\n",ish.recon_start_sec); MdcPrntScrn("Gate Duration : %d [ms]\n",ish.gate_duration); MdcPrntScrn("Filter code : %d ",ish.filter_code); ish.filter_code = abs(ish.filter_code); if ((ish.filter_code > -1) && (ish.filter_code < 7)) MdcPrntScrn("(= %s)\n",MdcEcatFilterTypes[ish.filter_code]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("Scan Matrix Number : %d\n",ish.scan_matrix_num); MdcPrntScrn("Normalization Matrix Number : %d\n",ish.norm_matrix_num); MdcPrntScrn("Attenuation Matrix Number : %d\n" ,ish.atten_cor_matrix_num); MdcPrntScrn("Image Rotation : %f [degrees]\n" ,ish.image_rotation); MdcPrntScrn("Plane Efficiency Correction Factor: %f\n" ,ish.plane_eff_corr_fctr); MdcPrntScrn("Decay Correction Factor : %f\n",ish.decay_corr_fctr); MdcPrntScrn("Loss Correction Factor : %f\n",ish.loss_corr_fctr); MdcPrntScrn("Processing Code : %d\n",ish.processing_code); MdcPrntScrn("Quantification Units : %d ",ish.quant_units); if ((ish.quant_units > -1) && (ish.quant_units < 13)) MdcPrntScrn("(= %s)\n",MdcEcatQuantificationUnits[ish.quant_units]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("Reconstruction Start Day : %d\n",ish.recon_start_day); MdcPrntScrn("Reconstruction Start Month : %d\n" ,ish.recon_start_month); MdcPrntScrn("Reconstruction Start Year : %d\n" ,ish.recon_start_year); MdcPrntScrn("Ecat Calibration Factor : %f\n" ,ish.ecat_calibration_fctr); MdcPrntScrn("Well Counter Calibribration Factor : %f\n" ,ish.well_counter_cal_fctr); MdcPrntScrn("Filter Params - Cutoff Frequency : %f\n" ,ish.filter_params[0]); MdcPrntScrn("Filter Params - DC Component : %f\n" ,ish.filter_params[1]); MdcPrntScrn("Filter Params - Ramp Slope : %f\n" ,ish.filter_params[2]); MdcPrntScrn("Filter Params - (4) : %f\n" ,ish.filter_params[3]); MdcPrntScrn("Filter Params - Scatter Comp 1 : %f\n" ,ish.filter_params[4]); MdcPrntScrn("Filter Params - Scatter Comp 2 : %f\n" ,ish.filter_params[5]); MdcPrntScrn("Annotation : "); MdcPrintStr(ish.annotation); } /* fill in DYNAMIC_DATA struct */ if ((dd != NULL) && (plane == (fi->dim[3]/2))) { /* take values from a plane halfway */ dd->nr_of_slices = fi->dim[3]; dd->time_frame_start = (float)ish.frame_start_time; dd->time_frame_duration = (float)ish.frame_duration; } /* fill in IMG_DATA struct */ id = &fi->image[img]; id->width = (Uint32)ish.dimension_1; id->height = (Uint32)ish.dimension_2; id->recon_scale = ish.recon_scale; id->quant_units = ish.quant_units; id->quant_scale = ish.quant_scale; id->calibr_units= mh.calibration_units; id->calibr_fctr = ish.ecat_calibration_fctr; id->pixel_xsize = id->pixel_ysize = ish.pixel_size * 10.; /* in mm */ id->slice_width = ish.slice_width * 10.; /* in mm */ data_type = ish.data_type; switch( data_type ) { case BYTE_TYPE: id->bits = 8; id->type = BIT8_U; break; case M68K_I2 : /* case SUN_I2 : */ case VAX_I2 : id->bits =16; id->type = BIT16_S; break; case M68K_I4 : /* case SUN_I4 : */ case VAX_I4 : id->bits =32; id->type = BIT32_S; break; case IEEE_R4 : /* case SUN_R4 : */ case VAX_R4 : id->bits =32; id->type = FLT32; break; } id->slice_spacing = mh.plane_separation*10.; /* separation in mm */ if ( (strncmp(mh.user_process_code,"COR",10)==0) || (strncmp(mh.user_process_code,"SAG",10)==0)) { /* CORONAL SLICES or SAGITTAL SLICES The images Ecat 6.4 software writes are useless: 128x128 images with the small coronal/sagittal slice in it ... This means their pixel_xsize & pixel_ysize doesn't quite fit the real world dimensions any more !! Therefore we don't even try to attempt writing the proper Acr/Nema variables ... */ }else{ /* "TRA" or nothing */ /* TRANSAXIAL SLICES (Transverse) */ /* Writing the proper Acr/Nema variables ... */ /* See man-page `m-acr.4' for more info (!) */ /* slice position with bed offset (mm) */ if (bed == 0) { slice_position = mh.init_bed_position; }else{ slice_position = mh.init_bed_position + mh.bed_offset[bed-1]; } slice_position *= 10.; /* mm */ MdcFillImgPos(fi,img,(Uint32)(plane-1),slice_position); MdcFillImgOrient(fi,img); } break; case MDC_ECAT6_ATTN_FILE: error = mdc_mat_read_attn_subheader(fp, startblk-1, &ash); if (error) return("ECAT6 Bad read attenuation subheader"); if (MDC_INFO) { MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("ATTENUATION SUBHEADER %05d: ",img+1); MdcPrntScrn("Frame: %d Plane: %d Gate: %d Data: %d Bed: %d\n" ,frame,plane,gate,data,bed); MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("Data Type : %d ",ash.data_type); if ((ash.data_type > -1) && (ash.data_type < 8)) MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[ash.data_type]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("Attenuation Correction Method : %d\n" ,ash.attenuation_type); MdcPrntScrn("Number of Elements : %d (width)\n" ,ash.dimension_1); MdcPrntScrn("Number of Views : %d (height)\n" ,ash.dimension_2); MdcPrntScrn("Attenuation Scale Factor : %f\n",ash.scale_factor); MdcPrntScrn("Ellipse X Offset : %f [cm]\n" ,ash.x_origin); MdcPrntScrn("Ellipse Y Offset : %f [cm]\n" ,ash.y_origin); MdcPrntScrn("Ellipse X Radius : %f [cm]\n" ,ash.x_radius); MdcPrntScrn("Ellipse Y Radius : %f [cm]\n" ,ash.y_radius); MdcPrntScrn("Ellipse Tilt Angle : %f [degrees]\n" ,ash.tilt_angle); MdcPrntScrn("Mu-Absorption Coefficient : %f [1/cm]\n" ,ash.attenuation_coeff); MdcPrntScrn("Sample Distance : %f [cm]\n" ,ash.sample_distance); } /* fill in IMG_DATA struct */ id = &fi->image[img]; id->width = (Uint32)ash.dimension_1; id->height = (Uint32)ash.dimension_2; id->quant_units = 1; id->quant_scale = 1; id->calibr_units= 1; id->calibr_fctr = 1; id->quant_scale = ash.scale_factor; data_type = ash.data_type; switch( data_type ) { case BYTE_TYPE: id->bits = 8; id->type = BIT8_U; break; case M68K_I2 : /* case SUN_I2 : */ case VAX_I2 : id->bits =16; id->type = BIT16_S; break; case M68K_I4 : /* case SUN_I4 : */ case VAX_I4 : id->bits =32; id->type = BIT32_S; break; case IEEE_R4 : /* case SUN_R4 : */ case VAX_R4 : id->bits =32; id->type = FLT32; break; } break; case MDC_ECAT6_NORM_FILE: error = mdc_mat_read_norm_subheader(fp, startblk-1, &nsh); if (error) return("ECAT6 Bad read normalization subheader"); if (MDC_INFO) { MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("NORMALIZATION SUBHEADER %05d: ",img+1); MdcPrntScrn("Frame: %d Plane: %d Gate: %d Data: %d Bed: %d\n" ,frame,plane,gate,data,bed); MdcPrintLine('-',MDC_FULL_LENGTH); MdcPrntScrn("Data Type : %d ",nsh.data_type); if ((nsh.data_type > -1) && (nsh.data_type < 8)) MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[nsh.data_type]); else MdcPrntScrn("(= Unknown)\n"); MdcPrntScrn("Number of Elements : %d (width)\n",nsh.dimension_1); MdcPrntScrn("Number of Views : %d (height)\n",nsh.dimension_2); MdcPrntScrn("Normalization Scale Factor : %f\n",nsh.scale_factor); MdcPrntScrn("Normalization Start Hour : %d\n",nsh.norm_hour); MdcPrntScrn("Normalization Start Minute : %d\n",nsh.norm_minute); MdcPrntScrn("Normalization Start Second : %d\n",nsh.norm_second); MdcPrntScrn("Normalization Start Day : %d\n",nsh.norm_day); MdcPrntScrn("Normalization Start Month : %d\n",nsh.norm_month); MdcPrntScrn("Normalization Start Year : %d\n",nsh.norm_year); MdcPrntScrn("Field of View Source Width : %f [cm]\n" ,nsh.fov_source_width); MdcPrntScrn("Ecat Calibration Factor : %f\n" ,nsh.ecat_calib_factor); } /* fill in IMG_DATA struct */ id = &fi->image[img]; id->width = (Uint32)nsh.dimension_1; id->height = (Uint32)nsh.dimension_2; id->quant_units = 1; id->quant_scale = nsh.scale_factor; id->calibr_units= mh.calibration_units; id->calibr_fctr = nsh.ecat_calib_factor; data_type = nsh.data_type; switch( data_type ) { case BYTE_TYPE: id->bits = 8; id->type = BIT8_U; break; case M68K_I2 : /* case SUN_I2 : */ case VAX_I2 : id->bits =16; id->type = BIT16_S; break; case M68K_I4 : /* case SUN_I4 : */ case VAX_I4 : id->bits =32; id->type = BIT32_S; break; case IEEE_R4 : /* case SUN_R4 : */ case VAX_R4 : id->bits =32; id->type = FLT32; break; } break; } bytes = id->width*id->height*MdcType2Bytes(id->type); bytes = MdcMatrixBlocks(bytes); id->buf = MdcGetImgBuffer(bytes); if (id->buf == NULL) return("ECAT6 Bad malloc image buffer"); error = mdc_mat_read_matrix_data(fp,startblk,endblk,(Int16 *)id->buf); if (error) { MdcPrntWarn("ECAT6 Bad read matrix data"); err=MdcHandleTruncated(fi,img+1,MDC_YES); if(err != NULL) return(err); } if (fi->truncated) break; img+=1; } /* check the images really found */ if (num_matrices < fi->number) { found = (Uint32)num_matrices; }else if (img < fi->number) { found = img; }else { found = fi->number; } if (found != fi->number) { err=MdcHandleTruncated(fi,found,MDC_YES); if (err != NULL) return(err); } /* fill in other FILEINFO variables */ id = &fi->image[0]; /* first image */ fi->dim[1] = id->width; fi->dim[2] = id->height; fi->bits = id->bits; fi->type = id->type; fi->pixdim[0]=3; fi->pixdim[1]=id->pixel_xsize; fi->pixdim[2]=id->pixel_ysize; fi->pixdim[3]=id->slice_width; if (mh.file_type == MDC_ECAT6_IMAGE_FILE) { MdcStringCopy(fi->filter_type,MdcEcatFilterTypes[abs(ish.filter_code)], MDC_MAX_ECATFLTRTYPES_SIZE); fi->reconstructed = MDC_YES; if (ish.decay_corr_fctr > 1.0 ) fi->decay_corrected = MDC_YES; MdcStringCopy(fi->recon_method,ish.annotation,strlen(ish.annotation)); if (strcmp(fi->recon_method,MDC_ECAT6_RECON_METHOD) == 0 ) { strcpy(fi->recon_method,"Filtered Backprojection"); } }else{ fi->reconstructed = MDC_NO; strcpy(fi->recon_method,"None"); } switch( data_type ) { case BYTE_TYPE: MDC_FILE_ENDIAN = MDC_HOST_ENDIAN; break; /* case SUN_I2 : */ case M68K_I2 : MDC_FILE_ENDIAN = MDC_BIG_ENDIAN; break; case VAX_I2 : MDC_FILE_ENDIAN = MDC_HOST_ENDIAN; break; /* case SUN_I4 : */ case M68K_I4 : MDC_FILE_ENDIAN = MDC_BIG_ENDIAN; break; case VAX_I4 : MDC_FILE_ENDIAN = MDC_HOST_ENDIAN; break; /* case SUN_R4 : */ case IEEE_R4 : MDC_FILE_ENDIAN = MDC_BIG_ENDIAN; break; case VAX_R4 : MDC_FILE_ENDIAN = MDC_HOST_ENDIAN; break; } MdcCloseFile(fi->ifp); if (fi->truncated) return("ECAT6 Truncated image file"); return NULL; } float MdcGetSliceLocation(FILEINFO *fi, Int32 img) { int orient; float locat=0.; orient = MdcGetIntSliceOrient((int)fi->pat_slice_orient); switch (orient) { case MDC_TRANSAXIAL: /* z-coord */ locat = fi->image[img].image_pos_pat[2]; break; case MDC_SAGITTAL : /* x-coord */ locat = fi->image[img].image_pos_pat[0]; break; case MDC_CORONAL : /* y-coord */ locat = fi->image[img].image_pos_pat[1]; break; } if (locat < 0.) locat = -locat; return(locat / 10.); /* cm */ } int MdcGetFilterCode(char *string) { int i = 0; for (i=0; iimage[0]; int i; float init_bed_position=0.; /* memset(mh,0,MH_64_SIZE); */ memset(mh,0,sizeof(Mdc_Main_header)); sprintf(mh->original_file_name,"%.19s",fi->ofname); mh->sw_version = 6; mh->system_type= 951; mh->file_type = 2; mh->data_type = 2; /* ECAT 6 only reads VAX Int16 */ sprintf(mh->isotope_code,"%.8s",fi->isotope_code); mh->isotope_halflife = fi->isotope_halflife; sprintf(mh->radiopharmaceutical,"%.31s",fi->radiopharma); mh->calibration_units = fi->image[0].calibr_units; if (fi->pixdim[0] >= 3.) /* only valid for TRANSVERSE slices */ mh->axial_fov = ((float)fi->dim[3] + 1.) * fi->pixdim[3] / 10.; mh->scan_start_day = fi->study_date_day; mh->scan_start_month = fi->study_date_month; mh->scan_start_year = fi->study_date_year; mh->scan_start_hour = fi->study_time_hour; mh->scan_start_minute= fi->study_time_minute; mh->scan_start_second= fi->study_time_second; mh->plane_separation = fi->image[0].slice_spacing/10.; /* in cm */ sprintf(mh->study_name,"%.11s",fi->study_id); mh->gantry_tilt = fi->gantry_tilt; sprintf(mh->patient_id,"%.15s",fi->patient_id); if (fi->patient_height == 0.) { mh->patient_height[0] = '\0'; }else{ sprintf(mh->patient_height,"%.2f",fi->patient_height); } if (fi->patient_weight == 0.) { mh->patient_weight[0] = '\0'; }else{ sprintf(mh->patient_weight,"%.2f",fi->patient_weight); } sprintf(mh->patient_name,"%.31s",fi->patient_name); mh->patient_sex = fi->patient_sex[0]; sprintf(mh->operator_name,"%.31s",fi->operator_name); sprintf(mh->study_description,"%.31s",fi->study_descr); switch (fi->acquisition_type ) { case MDC_ACQUISITION_STATIC : mh->acquisition_type = MDC_ECAT6_ACQTYPE_STATIC_EMISSION; break; case MDC_ACQUISITION_TOMO : mh->acquisition_type = MDC_ECAT6_ACQTYPE_STATIC_EMISSION; break; case MDC_ACQUISITION_DYNAMIC: mh->acquisition_type = MDC_ECAT6_ACQTYPE_DYNAMIC_EMISSION; break; case MDC_ACQUISITION_GSPECT : mh->acquisition_type = MDC_ECAT6_ACQTYPE_GATED_EMISSION; break; default : mh->acquisition_type = MDC_ECAT6_ACQTYPE_UNKNOWN; } sprintf(mh->facility_name,"%.19s",fi->institution); sprintf(mh->user_process_code,"%.10s",MDC_PRGR); mh->num_planes = mh->num_frames = mh->num_gates = 1; mh->num_bed_pos = 1; for ( i=3; i<=fi->dim[0]; i++) { switch (i) { case 3: mh->num_planes = fi->dim[i]; break; case 4: mh->num_frames = fi->dim[i]; break; case 5: mh->num_gates = fi->dim[i]; break; case 6: mh->num_bed_pos = fi->dim[i]; break; case 7: mh->num_bed_pos*= fi->dim[i]; break; } } mh->num_bed_pos -= 1; /* zero-based */ /* bed positions */ if ((fi->bednr > 0) && (fi->beddata != NULL)) { /* use preserved bed offsets */ mh->init_bed_position = fi->beddata[0].hoffset / 10.; /* cm */ mh->bed_elevation = fi->beddata[0].voffset / 10.; /* cm */ for (i=1; ibednr; i++) { if ( i==16 ) { MdcPrntWarn("ECAT6 Unsupported number of bed positions"); break; } mh->bed_offset[i-1] = fi->beddata[i].hoffset - fi->beddata[0].hoffset; mh->bed_offset[i-1] /= 10.; /* cm */ } }else{ /* guess bad offsets, assume adjacent bed positions */ switch (MdcGetIntSliceOrient(fi->pat_slice_orient)) { case MDC_TRANSAXIAL: init_bed_position = dd0->image_pos_pat[2]; /* x */ break; case MDC_CORONAL : init_bed_position = dd0->image_pos_pat[1]; /* y */ break; case MDC_SAGITTAL : init_bed_position = dd0->image_pos_pat[0]; /* z */ break; } if (init_bed_position < 0.) init_bed_position *= -1.; if (init_bed_position > dd0->slice_width) init_bed_position -= dd0->slice_width; mh->init_bed_position = init_bed_position / 10.; /* cm */ for (i=1; idim[6]; i++) { mh->bed_offset[i-1] = dd0->slice_width * (float)(fi->dim[3] * i / 10); } } } void MdcFillImageSubHeader(FILEINFO *fi,Mdc_Image_subheader *ish ,int type,Int32 img, Int32 matnum, Uint32 NEWSIZE) { IMG_DATA *id = &fi->image[img]; Uint32 fnr; Int32 fstart=0, fduration=0; /* memset(ish,0,ISH_64_SIZE); */ memset(ish,0,sizeof(Mdc_Image_subheader)); fnr = id->frame_number; if ((fi->dynnr > 0) && (fnr > 0)) { fstart = (Int32)fi->dyndata[fnr-1].time_frame_start; fduration = (Int32)fi->dyndata[fnr-1].time_frame_duration; }else{ fstart = 0; fduration = 0; } ish->data_type = 2; /* ECAT 6 only reads VAX Int16 */ ish->num_dimensions = 2; if (fi->diff_size || NEWSIZE) { ish->dimension_1 = fi->mwidth; ish->dimension_2 = fi->mheight; }else{ ish->dimension_1 = id->width; ish->dimension_2 = id->height; } ish->recon_scale = id->recon_scale; if (ish->data_type == 1 || ish->data_type == 2) { if (id->rescaled) { ish->image_min = (Int16) id->rescaled_min; ish->image_max = (Int16) id->rescaled_max; }else{ ish->image_min = (Int16) id->min; ish->image_max = (Int16) id->max; } }else{ /* data types too big for an Int16 */ ish->image_min = 0; ish->image_max = 0; } ish->pixel_size = ((id->pixel_xsize + id->pixel_ysize)/2.) / 10.; ish->slice_width = id->slice_width / 10.; #ifdef MDC_USE_SLICE_SPACING if (fi->number > 1) ish->slice_width = id->slice_spacing / 10.; #endif ish->frame_duration = fduration; ish->frame_start_time = fstart; ish->slice_location = (Int16)MdcGetSliceLocation(fi,img); ish->filter_code = -(MdcGetFilterCode(fi->filter_type)); ish->scan_matrix_num = matnum; ish->norm_matrix_num = matnum; ish->atten_cor_matrix_num = matnum; ish->quant_units = id->quant_units; if (id->rescaled) { ish->quant_scale = id->rescaled_fctr; ish->ecat_calibration_fctr = 1.; }else{ ish->quant_scale = id->quant_scale; ish->ecat_calibration_fctr = id->calibr_fctr; } if (strcmp(fi->recon_method,"Filtered Backprojection") == 0 ) { sprintf(ish->annotation,"%.40s",MDC_ECAT6_RECON_METHOD); }else{ sprintf(ish->annotation,"%.40s",fi->recon_method); } } static void MdcResetSizes(FILEINFO *fi) { fi->mwidth = saved_mwidth; fi->mheight= saved_mheight; } const char *MdcWriteECAT6(FILEINFO *fi) { IMG_DATA *id; Mdc_Main_header mh; Mdc_Image_subheader ish; Uint8 *buf, *maxbuf; Uint16 type, FREE; Int32 matnum, data=0, bed, gate, frame, plane, img=0; Uint32 size, NEWSIZE=0; if (MDC_FILE_STDOUT == MDC_YES) return("ECAT6 Writing to stdout unsupported for this format"); MDC_WRITE_ENDIAN = MDC_LITTLE_ENDIAN; /* always (VAX) */ if (XMDC_GUI == MDC_NO) { MdcDefaultName(fi,MDC_FRMT_ECAT6,fi->ofname,fi->ifname); } if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Writing ECAT6:"); if (MDC_VERBOSE) MdcPrntMesg("ECAT6 Writing <%s> ...",fi->ofname); /* check for colored files */ if (fi->map == MDC_MAP_PRESENT) return("ECAT6 Colored files unsupported"); if (MdcKeepFile(fi->ofname)) { return("ECAT6 File exists!!"); } if (MDC_FORCE_INT != MDC_NO) { if (MDC_FORCE_INT != BIT16_S) { MdcPrntWarn("ECAT6 Only Int16 pixels supported"); } } /* check some integrities */ /* check integrity of planes, frames, gates, beds */ if (fi->dim[3] > MDC_ECAT6_MAX_PLANES) return("ECAT6 number of planes too big (1024)"); if (fi->dim[4] > MDC_ECAT6_MAX_FRAMES) return("ECAT6 number of frames too big (512)"); if (fi->dim[5] > MDC_ECAT6_MAX_GATES) return("ECAT6 number of gates too big (64)"); if ((fi->dim[6]*fi->dim[7]) > MDC_ECAT6_MAX_BEDS) return("ECAT6 number of beds too big (16)"); #if MDC_ECAT6_RESTRICT_DIMS /* check dimensions (ECAT only 64, 128, 256) */ /* we don't do downsaling */ if (fi->mwidth > MDC_ECAT6_MAX_DIMS || fi->mheight > MDC_ECAT6_MAX_DIMS) return("ECAT6 dimensions too big (256)"); #endif /* get maximum dimension */ if (fi->mwidth > fi->mheight) size = fi->mwidth; else size = fi->mheight; #if MDC_ECAT6_RESTRICT_DIMS /* allow only 64, 128, 256 */ if (size <= 64) NEWSIZE=64; else if (size <= 128) NEWSIZE=128; else if (size <= 256) NEWSIZE=256; #endif /* save the original dimensions anyway */ saved_mwidth = fi->mwidth; saved_mheight= fi->mheight; /* change to new dimensions */ if (NEWSIZE) { fi->mwidth = NEWSIZE; fi->mheight= NEWSIZE; } MdcFillMainHeader(fi,&mh); if ( (fi->ofp = mdc_mat_create(fi->ofname,&mh)) == NULL) { MdcResetSizes(fi); return("Couldn't create file"); } for (bed=0; bed <= mh.num_bed_pos; bed++) for (gate=1; gate <= mh.num_gates; gate++) for (frame=1; frame <= mh.num_frames; frame++) for (plane=1; plane <= mh.num_planes; plane++) { if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,1./(float)fi->number,NULL); id = &fi->image[img]; if ((id->type != BIT16_S) || MDC_QUANTIFY || MDC_CALIBRATE) { buf = MdcGetImgBIT16_S(fi, (Uint32)img); FREE=MDC_YES; type=BIT16_S; }else{ buf = id->buf; FREE=MDC_NO; type=id->type; } matnum = mdc_mat_numcod(frame,plane,gate,data,bed); MdcFillImageSubHeader(fi,&ish,type,img,matnum,NEWSIZE); if (fi->diff_size || NEWSIZE) { size = fi->mwidth * fi->mheight * MdcType2Bytes(type); maxbuf = MdcGetResizedImage(fi, buf, type, (Uint32)img); if (maxbuf == NULL) { MdcResetSizes(fi); return("ECAT6 Bad malloc maxbuf"); } if (FREE) MdcFree(buf); FREE=MDC_YES; }else{ /* NEWSIZE is normally always set */ size = id->width * id->height * MdcType2Bytes(type); maxbuf = buf; } matnum = mdc_mat_numcod(frame,plane,gate,data,bed); if (mdc_mat_write_image(fi->ofp,matnum,&ish,(Uint16 *)maxbuf,(Int32)size)) { MdcResetSizes(fi); return("ECAT6 Bad write image matrix"); } img+=1; if (FREE) MdcFree(maxbuf); } MdcCheckQuantitation(fi); MdcCloseFile(fi->ofp); MdcResetSizes(fi); return NULL; } void MdcPrintEcatInfoDB(Mdc_Main_header *mh) { char Unknown[8]="Unknown"; Uint32 i, patient_strlen, study_strlen; patient_strlen = strlen(mh->patient_name); study_strlen = strlen(mh->study_name); /* remove # from strings, because it is used as field separator! */ for (i=0; ipatient_name[i] == '#' ) { mh->patient_name[i]='$'; } } /* print database info: study_name */ if (study_strlen != 6) { MdcPrntScrn("%s",Unknown); }else{ MdcPrntScrn("%s",mh->study_name); } MdcPrntScrn("# "); /* print database info: patient_name */ if (patient_strlen == 0) { MdcPrntScrn("%-35s",Unknown); }else{ MdcPrntScrn("%-35s",mh->patient_name); } MdcPrntScrn("#"); /* print database info: scan date */ MdcPrntScrn("%02d-",mh->scan_start_day); switch (mh->scan_start_month) { case 1: MdcPrntScrn("Jan"); break; case 2: MdcPrntScrn("Feb"); break; case 3: MdcPrntScrn("Mar"); break; case 4: MdcPrntScrn("Apr"); break; case 5: MdcPrntScrn("May"); break; case 6: MdcPrntScrn("Jun"); break; case 7: MdcPrntScrn("Jul"); break; case 8: MdcPrntScrn("Aug"); break; case 9: MdcPrntScrn("Sep"); break; case 10: MdcPrntScrn("Oct"); break; case 11: MdcPrntScrn("Nov"); break; case 12: MdcPrntScrn("Dec"); break; } MdcPrntScrn("-%4d",mh->scan_start_year); MdcPrntScrn("\n"); }