/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * filename: m-dicm.c * * * * UTIL C-source: Medical Image Conversion Utility * * * * purpose : Read DICOM files * * * * project : (X)MedCon by Erik Nolf * * * * Functions : MdcCheckDICM() - Check DICOM format * * MdcReadDICM() - Read DICOM format * * MdcWriteDICM() - Write DICOM format * * MdcCheckMosaic() - Check Mosaic file * * * * Notes : Source needs VT-DICOM-package written by Tony Voet * * * * Credits : - DICOM library - Tony Voet * * - Mosaic support - Roland Marcus Rutschmann * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* $Id: m-dicm.c,v 1.178 2007/06/07 21:50:45 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_STRING_H #include #endif #ifdef HAVE_STRINGS_H #ifndef _WIN32 #include #endif #endif #include "dicom.h" #include "medcon.h" /**************************************************************************** D E F I N E S ****************************************************************************/ #define MDC_STR_UID_CREATOR "777.777.0.0.0" #define UNDEFINED_LENGTH 0xffffffff #define MDC_DICM_FIX_TYPE MDC_YES /* fix wrong unsigned pixel type */ #define IROW 2 #define ICOL 1 /* extra stuff for reading DICOM */ static void MdcDicomInvert(IMG_DATA *id); static char *MdcDicomHandleImages(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom , IMAGE *image, Uint32 number); static char *MdcHandleMosaic(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom , IMAGE *image); static void MdcPrintDicomInfoDB(FILEINFO *fi); /* extra stuff for writing DICOM */ static Uint32 MdcDicomMakeUID(FILEINFO *fi, Int8 uid, char str[]); static void MdcDicomWriteInfoSeq(FILE *fp, Uint16 group, Uint16 element); static void MdcDicomWriteItem(FILE *fp); static void MdcDicomWriteItemDelItem(FILE *fp); static void MdcDicomWriteInfoSeqDelItem(FILE *fp); static char *MdcDicomWriteMetaHeader (FILEINFO *fi, MDC_DICOM_STUFF_T *dicom); static char *MdcDicomWriteSetModality(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom); static char *MdcDicomWriteG0008(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom); static char *MdcDicomWriteG0010(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom); static char *MdcDicomWriteG0018(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom); static char *MdcDicomWriteG0020(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom); static char *MdcDicomWriteG0028(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom); static char *MdcDicomWriteG0054(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom); static char *MdcDicomWriteG7FE0(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom); /* including the addapted dicom lib functions */ static int mdc_dicom_read(FILEINFO *fi, IMAGE **image, int *number); static void mdc_dicom_dumpinfo(FILEINFO *fi); static void mdc_dicom_printinfo(const ELEMENT *e,const char *description); static void mdc_dicom_getinfo(FILEINFO *fi); static void mdc_dicom_get_vr(ELEMENT *e); static Uint8 *mdc_dicom_handle_vr(ELEMENT *e, Uint8 *tdata); static int mdc_dicom_write_element(FILE *fp, Uint16 group, Uint16 element, Uint32 length, Uint8 *data); static int MDC_DICOM_VERBOSE = MDC_YES; static Uint32 MDC_REWRF_SLOPE; /* rewrite offset to slope tag */ static Uint32 MDC_REWRF_INTERCEPT; /* rewrite offset to intercept tag */ static Int32 mdc_prev_nr_series = -MDC_TYPE_UID_SERIES; static Int32 mdc_prev_series_uid = 0; static time_t mdc_sec, *mdc_psec=NULL; /* universal time */ static char mdc_dummy1[]="1"; static GATED_DATA *gd; static ACQ_DATA *acq; static DYNAMIC_DATA *dd; extern MDC_DICOM_STUFF_T mdc_dicom_stuff; /**************************************************************************** F U N C T I O N S ****************************************************************************/ int MdcCheckDICM(FILEINFO *fi) { char sig[5]; fseek(fi->ifp,128,SEEK_SET); fread(sig,1,4,fi->ifp); fseek(fi->ifp,0,SEEK_SET); if (ferror(fi->ifp)) return(MDC_BAD_READ); sig[4]='\0'; MdcLowStr(sig); if (strstr(sig,MDC_DICM_SIG) == NULL) return(MDC_FRMT_NONE); return(MDC_FRMT_DICM); } void MdcDicomInvert(IMG_DATA *id) { double pixvalue; double max=0., min=0.; Uint8 *pixel; Uint32 i, n; n = id->width * id->height; /* retrieve present max/min values */ for (pixel=id->buf, i=0; itype)) { pixvalue = MdcGetDoublePixel(pixel,id->type); if (i==0) { max = pixvalue; min = pixvalue; }else{ if ( pixvalue > max ) max = pixvalue; else if ( pixvalue < min ) min = pixvalue; } } /* invert pixel values */ for (pixel=id->buf, i=0; itype)) { pixvalue = MdcGetDoublePixel(pixel,id->type); pixvalue = max - pixvalue + min; MdcPutDoublePixel(pixel,pixvalue,id->type); } } char *MdcDicomHandleImages(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom, IMAGE *image, Uint32 number) { IMAGE *pimg; IMG_DATA *id; Uint32 img=0, i, f, bytes, t; Uint8 *pdata = NULL; for (img=0, i=0; inumber,NULL); id = &fi->image[img]; id->width = (Uint32)pimg->w; id->height = (Uint32)pimg->h; id->type = fi->type; id->bits = MdcType2Bits(id->type); if (id->type != COLRGB) { id->quant_scale = dicom->si_slope; id->intercept = dicom->si_intercept; } bytes = id->width*id->height*MdcType2Bytes(id->type); id->buf = MdcGetImgBuffer(bytes); if (id->buf == NULL) return("DICM Couldn't allocate image buffer"); if (fi->type == COLRGB) { pdata = (Uint8 *)pimg->data.rgb; }else{ pdata = (Uint8 *)pimg->data.gray; } pdata+=f*bytes; memcpy(id->buf,pdata,bytes); if (!((img == 0) && (f == 0))) { /* copy voxel size and orient values from the first image */ id->pixel_xsize = fi->image[0].pixel_xsize; id->pixel_ysize = fi->image[0].pixel_ysize; id->slice_width = fi->image[0].slice_width; id->slice_spacing = fi->image[0].slice_spacing; } /* image orient/position according to patient coordinate system */ if (id->image_orient_pat[0]==0.0 && id->image_orient_pat[1]==0.0 && id->image_orient_pat[4]==0.0 && id->image_orient_pat[5]==0.0 ) { /* no patient coordinate system defines, try pat_orient */ if ((img == 0) && (f == 0)) fi->pat_slice_orient = MdcTryPatSliceOrient(fi->pat_orient); if (fi->pat_slice_orient != MDC_UNKNOWN) { MdcFillImgPos(fi,img,img%fi->dim[3],0.0); MdcFillImgOrient(fi,img); } } /* image orient/position according to device (RETIRED)*/ if (id->image_orient_dev[0]==0.0 && id->image_orient_dev[1]==0.0 && id->image_orient_dev[4]==0.0 && id->image_orient_dev[5]==0.0 ) { /* no patient coordinate system defines */ switch (fi->pat_slice_orient) { case MDC_SUPINE_HEADFIRST_TRANSAXIAL: case MDC_SUPINE_HEADFIRST_SAGITTAL : case MDC_SUPINE_HEADFIRST_CORONAL : /* same coordinate system */ for (t=0; t<6; t++) id->image_orient_dev[t] = id->image_orient_pat[t]; break; } } if (id->image_pos_dev[0] == 0.0 && id->image_pos_dev[1] == 0.0 && id->image_pos_dev[2] == 0.0 ) { switch (fi->pat_slice_orient) { case MDC_SUPINE_HEADFIRST_TRANSAXIAL: case MDC_SUPINE_HEADFIRST_SAGITTAL : case MDC_SUPINE_HEADFIRST_CORONAL : /* same coordinate system */ for (t=0; t<3; t++) id->image_pos_dev[t] = id->image_pos_pat[t]; break; } } if (id->type != COLRGB) { if (dicom->INVERT == MDC_YES) MdcDicomInvert(id); } img+=1; } } return(NULL); } int MdcCheckMosaic(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { /* support enabled ? */ if (MDC_DICOM_MOSAIC_ENABLED == MDC_NO) return(MDC_NO); if (dicom->MOSAIC == MDC_NO) return(MDC_NO); if (MDC_DICOM_MOSAIC_FORCED == MDC_YES) { dicom->mosaic_width = mdc_mosaic_width; dicom->mosaic_height= mdc_mosaic_height; dicom->mosaic_number= mdc_mosaic_number; dicom->mosaic_interlaced = mdc_mosaic_interlaced; } /* do some sanity checks before handling as MOSAIC */ if ( fi->number == 1 && dicom->mosaic_number > 0 && dicom->mosaic_width > 0 && dicom->mosaic_height > 0 && fi->mwidth > dicom->mosaic_width && fi->mheight > dicom->mosaic_height && fi->mwidth % dicom->mosaic_width == 0 && fi->mheight % dicom->mosaic_height == 0 ) { return(MDC_YES); } return(MDC_NO); } char *MdcHandleMosaic(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom, IMAGE *image) { IMG_DATA *id=NULL; IMAGE *pimg = &image[0]; Uint32 width, height, number, bytes, size; Uint32 bytes_per_mosaic, bytes_per_old_img_line, bytes_per_y_plane; Uint32 nr_of_pics_per_line, sl, s, x, y, offset, yline, i; Uint8 *pmosaic, *p, *pdata; Int8 DO_VOXEL_FIX=MDC_NO; float f; /* in case we need to know */ MdcDebugPrint("handling image as MOSAIC"); /* allocate new block of memory */ width = dicom->mosaic_width; height = dicom->mosaic_height; number= dicom->mosaic_number; bytes = MdcType2Bytes(fi->type); size = bytes * width * height; pmosaic = malloc(size * number); if (pmosaic == NULL) return("DICM Bad malloc pmosaic buffer"); /* extract the stamps */ bytes_per_mosaic = bytes * width; bytes_per_old_img_line = bytes * fi->mwidth; nr_of_pics_per_line = bytes_per_old_img_line / bytes_per_mosaic; bytes_per_y_plane = height * bytes_per_old_img_line; for (sl=0; slmosaic_interlaced == MDC_YES) { if (sl%2 == 0) { s = sl/2; }else{ s = number/2 + (sl-1)/2; } }else{ s=sl; } x = s % nr_of_pics_per_line; y = s / nr_of_pics_per_line; offset = (y * bytes_per_y_plane) + (x * bytes_per_mosaic); for (yline=0; yline < height; yline++) { p = pmosaic + sl*size + yline*bytes_per_mosaic; pdata = (Uint8 *)pimg->data.gray + offset; memcpy(p,pdata,bytes_per_mosaic); offset += bytes_per_old_img_line; } } /* fake multi frame image */ MdcFree(pimg->data.gray); pimg->data.gray= (Uint16 *)pmosaic; image[0].frames = number; pimg->w = width; pimg->h = height; /* set FILEINFO appropriate */ fi->dim[3] = number; fi->mwidth = width; fi->mheight = height; if (!MdcGetStructID(fi,number)) { MdcFree(pmosaic); return("DICM Bad malloc IMG_DATA structs for mosaic"); } /* fake DYNAMIC_DATA later */ dicom->frameduration = 1.; /* no idea what tag to use */ /* set IMG_DATA appropriate */ id = &fi->image[0]; id->width = width; id->height = height; if (MDC_DICOM_MOSAIC_FORCED == MDC_YES) { /* mosaic forced: voxel fixing only when requested */ if (MDC_DICOM_MOSAIC_FIX_VOXEL == MDC_YES) { DO_VOXEL_FIX = MDC_YES; }else{ DO_VOXEL_FIX = MDC_NO; } }else{ /* mosaic autodetect: always do voxel fixing */ DO_VOXEL_FIX = MDC_YES; } if (DO_VOXEL_FIX == MDC_YES) { /* handle mosaic calculation of pixel size */ /* mosaic calculates pixsizefact by field of view/nr_of_all_pix not taking*/ /* into account that there are severall slices in the whole picture. */ /* eg 200x200mm˛ fov 8x8 pix each having 64x64 pix. The whole matrix has */ /* 512x512 pix so pixsize is calculated by 200/512 instead of 200/64 */ /* NOTE: apparently valid for MAGNETOM dialect, not valid for SONATA */ id->pixel_xsize *= (float)nr_of_pics_per_line; id->pixel_ysize *= (float)nr_of_pics_per_line; /* set globally as well */ fi->pixdim[1] = id->pixel_xsize; fi->pixdim[2] = id->pixel_ysize; } /* make orthogonal */ for (i=0; i<6; i++) { f = id->image_orient_dev[i]; id->image_orient_dev[i]=(float)MdcGetOrthogonalInt(f); } for (i=0; i<6; i++) { f = id->image_orient_pat[i]; id->image_orient_pat[i]=(float)MdcGetOrthogonalInt(f); } fi->pat_slice_orient = MdcGetPatSliceOrient(fi,0); MdcFillImgPos(fi,0,0,0); return(NULL); } static void MdcPrintDicomInfoDB(FILEINFO *fi) { /* make string */ sprintf(mdcbufr,"%s+%04d%02d%02d+%02d%02d%02d",fi->patient_name ,fi->study_date_year ,fi->study_date_month ,fi->study_date_day ,fi->study_time_hour ,fi->study_time_minute ,fi->study_time_second); /* print string */ MdcPrntScrn("%s\n",mdcbufr); } const char *MdcReadDICM(FILEINFO *fi) { IMAGE *image=NULL, *pimg=NULL; IMG_DATA *id=NULL; MDC_DICOM_STUFF_T *dicom=&mdc_dicom_stuff; Uint32 i, t, number=0, nrimages=0; int COLOR=MDC_NO; const char *msg=NULL; MDC_FILE_ENDIAN = MDC_HOST_ENDIAN; /* init dicom struct */ MdcDicomInitStuff(dicom); /* init MOD structs */ MdcGetStructMOD(fi); if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Reading DICOM:"); if (MDC_VERBOSE) MdcPrntMesg("DICM Reading <%s> ...",fi->ifname); if ((MDC_ECHO_ALIAS == MDC_YES) || (MDC_INFO_DB == MDC_YES)) { /* get one struct to fill */ MdcGetStructID(fi,1); /* retrieve info from file */ mdc_dicom_getinfo(fi); /* echo alias */ if (MDC_ECHO_ALIAS == MDC_YES) MdcEchoAliasName(fi); /* print db info */ if (MDC_INFO_DB == MDC_YES) MdcPrintDicomInfoDB(fi); /* leave */ return(NULL); } MdcMergePath(fi->ipath,fi->idir,fi->ifname); /* first pass: limit log level to errors */ if (MDC_BLOCK_MESSAGES == MDC_LEVEL_ALL) { dicom_log_level = EMERGENCY; }else{ dicom_log_level = ERROR; } /* reading file 1st time for info printout */ if (MDC_INFO) { MdcPrintLine('*',MDC_HALF_LENGTH); MdcPrntScrn("Pass #1: through DICOM reader\n"); MdcPrintLine('*',MDC_HALF_LENGTH); mdc_dicom_dumpinfo(fi); } if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,0.1,NULL); /* second pass: allow warnings */ if (MDC_BLOCK_MESSAGES == MDC_NO) dicom_log_level = NOTICE; /* reading file 2nd time for images */ if (mdc_dicom_read(fi,&image,(Int32 *)&nrimages)) { MdcSplitPath(fi->ipath,fi->idir,fi->ifname); dicom_free(image,(signed)nrimages); return("DICM Error reading file"); } if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,0.1,NULL); MdcSplitPath(fi->ipath,fi->idir,fi->ifname); /* number of images & flag color */ for (number=0, i=0; iframes; if (number == 0 ) { dicom_free(image,(signed)nrimages); return("DICM Bad number of images"); } if (pimg->rgb) COLOR = MDC_YES; /* MARK: all images must be identical */ } if (!MdcGetStructID(fi,number)) { dicom_free(image,(signed)nrimages); return("DICM Bad malloc IMG_DATA structs"); } /* third pass: limit log level back to errors */ if (MDC_BLOCK_MESSAGES == MDC_NO) dicom_log_level = ERROR; /* reading file 3rd time for info retrieving (through Acr/Nema reader) */ if (MDC_INFO) { MdcPrntScrn("\n\n"); MdcPrintLine('*',MDC_HALF_LENGTH); MdcPrntScrn("Pass #2: through Acr/Nema reader\n"); MdcPrintLine('*',MDC_HALF_LENGTH); } MdcMergePath(fi->ipath,fi->idir,fi->ifname); mdc_dicom_getinfo(fi); MdcSplitPath(fi->ipath,fi->idir,fi->ifname); /* init FILEINFO parameters */ if (COLOR == MDC_YES) { fi->map = MDC_MAP_PRESENT; fi->type= COLRGB; }else{ fi->map = MDC_MAP_GRAY; fi->type= (dicom->sign == 1) ? BIT16_S : BIT16_U; } fi->bits = MdcType2Bits(fi->type); fi->endian = MDC_HOST_ENDIAN; fi->dim[0] = 3; fi->pixdim[0] = 0.; /* fix for single image PT modality */ if (dicom->modality == M_PT) { fi->number = 1; for (t=3; t < MDC_MAX_DIMS; t++) fi->dim[t] = 1; } /* in case of mosaic, fake a multi frame image */ if (MdcCheckMosaic(fi,dicom) == MDC_YES) { msg = MdcHandleMosaic(fi,dicom,image); if (msg != NULL) { dicom_free(image,(signed)nrimages); return(msg); } } /* fill in the FILEINFO structs */ for (t=(MDC_MAX_DIMS - 1); t > 3; t--) if (fi->dim[t] > 1) break; fi->dim[0] = t; fi->pixdim[0] = t; fi->pixdim[1] = fi->image[0].pixel_xsize; fi->pixdim[2] = fi->image[0].pixel_ysize; fi->pixdim[3] = fi->image[0].slice_width; id = &fi->image[0]; if (MDC_TRUE_GAP == MDC_YES) id->slice_spacing += id->slice_width; /* fill in DYNAMIC_DATA structs */ if (fi->acquisition_type == MDC_ACQUISITION_TOMO || fi->acquisition_type == MDC_ACQUISITION_DYNAMIC) { if (dicom->frameduration > 0.) { if ((fi->dynnr > 0) && (fi->dyndata != NULL)) { /* preserve but do set frame_duration */ for (i=0; i < fi->dynnr; i++) { dd = &fi->dyndata[i]; if (fi->planar == MDC_YES) { /* planar: sum of all images + delays */ dd->time_frame_duration *= dd->nr_of_slices; dd->time_frame_duration += dd->delay_slices * (dd->nr_of_slices-1); }else{ dd->time_frame_start = dicom->framestart; dd->time_frame_duration = dicom->frameduration; } } }else{ if (!MdcGetStructDD(fi,1)) { dicom_free(image,(signed)nrimages); return("DICM Couldn't malloc DYNAMIC_DATA structs"); } fi->dyndata[0].nr_of_slices = fi->number; fi->dyndata[0].time_frame_start = dicom->framestart; fi->dyndata[0].time_frame_duration = dicom->frameduration; } } } /* fill in GATED_DATA structs */ if (fi->gatednr > 0 && fi->gdata != NULL) { gd = &fi->gdata[0]; gd->nr_projections = dicom->nrframes; gd->extent_rotation = dicom->scan_arc; gd->image_duration = dicom->frametime; gd->time_per_proj = dicom->frameduration; gd->study_duration = dicom->nrframes * dicom->frameduration; gd->cycles_acquired = dicom->intervals_acquired; gd->cycles_observed = dicom->intervals_acquired + dicom->intervals_rejected; gd->window_low = dicom->window_low; gd->window_high= dicom->window_high; } /* put images and info in IMG_DATA structs */ msg = MdcDicomHandleImages(fi,dicom,image,nrimages); if (msg != NULL) { dicom_free(image,(signed)nrimages); return(msg); } dicom_free(image,(signed)nrimages); MdcCloseFile(fi->ifp); return(NULL); } Uint32 MdcDicomMakeUID(FILEINFO *fi, Int8 uid, char str[]) { Int16 year, month, day; Int16 hour, minute, second; Uint32 utc, len, study_uid, series_uid, instance_uid; year = fi->study_date_year; month = fi->study_date_month; day = fi->study_date_day; hour = fi->study_time_hour; minute= fi->study_time_minute; second= fi->study_time_second; if (mdc_psec != NULL) { utc = (Uint32)*mdc_psec; }else{ utc = 777UL; } /* study_uid = hash(patient_name + study_date + study_time) */ sprintf(str,"%s%s%hd%02hd%02hd%02hd%02hd%02hd" ,fi->patient_name,fi->patient_id ,year,month,day,hour,minute,second); study_uid = MdcHashSDBM((unsigned char *)str); if (study_uid == 182208422) { /* Unknown000000000 case */ sprintf(str,"%u",utc); study_uid = MdcHashSDBM((unsigned char *)str); } /* series_uid = hash(input filename)*/ if (mdc_prev_nr_series == -MDC_TYPE_UID_SERIES) { mdc_prev_nr_series = fi->nr_series; series_uid = MdcHashSDBM((unsigned char *)fi->ifname); mdc_prev_series_uid = series_uid; } /* new series_uid for each input file */ if ((fi->nr_series != mdc_prev_nr_series) || (fi->nr_series <= 0)) { mdc_prev_nr_series = fi->nr_series; series_uid = MdcHashSDBM((unsigned char*)fi->ifname); mdc_prev_series_uid = series_uid; }else{ series_uid = mdc_prev_series_uid; } /* new instance_uid for each output file */ instance_uid = MdcHashSDBM((unsigned char *)fi->ofname); switch (uid) { case MDC_TYPE_UID_CREATOR: sprintf(str,"%s",MDC_STR_UID_CREATOR); break; case MDC_TYPE_UID_SOP_INSTANCE: case MDC_TYPE_UID_MEDIA_INSTANCE: sprintf(str,"%s.%u.%u.%u.%u" ,MDC_STR_UID_CREATOR,utc ,study_uid,series_uid,instance_uid); break; case MDC_TYPE_UID_FRAME: case MDC_TYPE_UID_STUDY: sprintf(str,"%s.%u.%u" ,MDC_STR_UID_CREATOR,utc ,study_uid); break; case MDC_TYPE_UID_SERIES: sprintf(str,"%s.%u.%u.%u" ,MDC_STR_UID_CREATOR,utc ,study_uid,series_uid); break; default: sprintf(str,"%s.%u.%u" ,MDC_STR_UID_CREATOR,utc ,study_uid); } len = (Uint32)strlen(str); if (len > MDC_UID_MAXSTR) { MdcPrntWarn("DICM Inappropriate UID length"); } return(len); } void MdcDicomWriteInfoSeq(FILE *fp, Uint16 group, Uint16 element) { mdc_dicom_write_element(fp,group,element,UNDEFINED_LENGTH,NULL); } void MdcDicomWriteItem(FILE *fp) { mdc_dicom_write_element(fp,0xfffe,0xe000,UNDEFINED_LENGTH,NULL); } void MdcDicomWriteItemDelItem(FILE *fp) { mdc_dicom_write_element(fp,0xfffe,0xe00d,0,NULL); } void MdcDicomWriteInfoSeqDelItem(FILE *fp) { mdc_dicom_write_element(fp,0xfffe,0xe0dd,0,NULL); } char *MdcDicomWriteMetaHeader(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { Uint32 REWRF, BEGIN, END, len; Int32 i32; FILE *ofp = fi->ofp; /* write the empty preamable */ memset(mdcbufr,0,128); fwrite(mdcbufr,1,128,ofp); /* write the signature */ strcpy(mdcbufr,"DICM"); fwrite(mdcbufr,1,4,ofp); /* group 0x0002 */ REWRF = ftell(ofp); i32=0; mdc_dicom_write_element(ofp,0x0002,0x0000,4,(Uint8 *)&i32); BEGIN = ftell(ofp); mdcbufr[0]=0x00; mdcbufr[1]=0x01; mdc_dicom_write_element(ofp,0x0002,0x0001,2,(Uint8 *)mdcbufr); switch (dicom->modality) { case M_PT: strcpy(mdcbufr,"1.2.840.10008.5.1.4.1.1.128"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0002,0x0002,len,(Uint8 *)mdcbufr); break; default : /* default to NM modality */ strcpy(mdcbufr,"1.2.840.10008.5.1.4.1.1.20"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0002,0x0002,len,(Uint8 *)mdcbufr); } /* strcpy(mdcbufr,"1.2.840.10008.Media.Storage.SOP.Instance"); */ len = MdcDicomMakeUID(fi,MDC_TYPE_UID_MEDIA_INSTANCE,mdcbufr); mdc_dicom_write_element(ofp,0x0002,0x0003,len,(Uint8 *)mdcbufr); /* transfer syntax */ if (MDC_DICOM_WRITE_IMPLICIT == MDC_YES) { strcpy(mdcbufr,"1.2.840.10008.1.2"); /* implicit VR little */ }else{ if (MDC_FILE_ENDIAN == MDC_LITTLE_ENDIAN) { strcpy(mdcbufr,"1.2.840.10008.1.2.1"); /* explicit VR little */ }else{ strcpy(mdcbufr,"1.2.840.10008.1.2.2"); /* explicit VR big */ } } len = strlen(mdcbufr); mdc_dicom_write_element(ofp,0x0002,0x0010,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"0.0.0.0"); len = strlen(mdcbufr); mdc_dicom_write_element(ofp,0x0002,0x0012,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"NOTSPECIFIED"); len = strlen(mdcbufr); mdc_dicom_write_element(ofp,0x0002,0x0013,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"NOTSPECIFIED"); len = strlen(mdcbufr); mdc_dicom_write_element(ofp,0x0002,0x0016,len,(Uint8 *)mdcbufr); END = ftell(ofp); /* rewrite group length */ fseek(ofp,(signed)REWRF,SEEK_SET); i32 = END - BEGIN; mdc_dicom_write_element(ofp,0x0002,0x0000,4,(Uint8 *)&i32); fseek(ofp,0,SEEK_END); if (ferror(ofp)) return("DICM Failure to write MetaHeader"); return(NULL); } /* char *MdcWritePatientModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteGeneralStudyModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteGeneralSeriesModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteNMSeriesModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteGeneralEquipmentModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteSOPCommonModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteGeneralImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteImagePixelModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteMultiFrameModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteNMImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteNMImagePixelModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteNMMultiFrameImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteNMIsotopeImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } char *MdcWriteNMDetectorImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { return(NULL); } */ char *MdcDicomWriteSetModality(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { /* currently only NM writing supported */ switch (fi->modality) { case M_PT: default : dicom->modality = M_NM; } return(NULL); } char *MdcDicomWriteG0008(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { Uint32 bytes, len; char *pdata; /* group 0x0008 */ strcpy(mdcbufr,"DERIVED\\PRIMARY"); switch (dicom->modality) { case M_PT: break; default : /* default to NM modality */ switch (fi->acquisition_type) { case MDC_ACQUISITION_TOMO: if (fi->reconstructed == MDC_YES) strcat(mdcbufr,"\\RECON TOMO"); else strcat(mdcbufr,"\\TOMO"); break; case MDC_ACQUISITION_DYNAMIC: strcat(mdcbufr,"\\DYNAMIC"); break; case MDC_ACQUISITION_GATED: strcat(mdcbufr,"\\GATED"); break; case MDC_ACQUISITION_GSPECT: if (fi->reconstructed == MDC_YES) strcat(mdcbufr,"\\RECON GATED TOMO"); else strcat(mdcbufr,"\\GATED TOMO"); break; case MDC_ACQUISITION_UNKNOWN: /* fake as static */ case MDC_ACQUISITION_STATIC: strcat(mdcbufr,"\\STATIC"); break; default: strcat(mdcbufr,"\\UNSPECIFIED"); } strcat(mdcbufr,"\\EMISSION"); /* MARK: no flag for transmission yet */ } len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0008,len,(Uint8 *)mdcbufr); strftime(mdcbufr,35,"%Y%m%d",localtime(mdc_psec)); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0012,len,(Uint8 *)mdcbufr); strftime(mdcbufr,35,"%H%M%S",localtime(mdc_psec)); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0013,len,(Uint8 *)mdcbufr); len = MdcDicomMakeUID(fi,MDC_TYPE_UID_CREATOR,mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0014,len,(Uint8 *)mdcbufr); switch (dicom->modality) { case M_PT: strcpy(mdcbufr,"1.2.840.10008.5.1.4.1.1.128"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0016,len,(Uint8 *)mdcbufr); break; default : /* default to NM modality */ strcpy(mdcbufr,"1.2.840.10008.5.1.4.1.1.20"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0016,len,(Uint8 *)mdcbufr); } len = MdcDicomMakeUID(fi,MDC_TYPE_UID_SOP_INSTANCE,mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0018,len,(Uint8 *)mdcbufr); /* date settings, make sure it is conform */ if (fi->mod != NULL) { pdata = fi->mod->gn_info.study_date; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0020,len,(Uint8 *)pdata); pdata = fi->mod->gn_info.series_date; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0021,len,(Uint8 *)pdata); pdata = fi->mod->gn_info.acquisition_date; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0022,len,(Uint8 *)pdata); pdata = fi->mod->gn_info.image_date; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0023,len,(Uint8 *)pdata); }else{ if (fi->study_date_year == 0) { pdata = NULL; bytes=0; }else{ sprintf(mdcbufr,"%04d%02d%02d",fi->study_date_year ,fi->study_date_month ,fi->study_date_day); pdata = mdcbufr; bytes = strlen(mdcbufr); } mdc_dicom_write_element(fi->ofp,0x0008,0x0020,bytes,(Uint8 *)pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0021,bytes,(Uint8 *)pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0022,bytes,(Uint8 *)pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0023,bytes,(Uint8 *)pdata); } /* time settings, can be full of zero's ... */ if (fi->mod != NULL) { pdata = fi->mod->gn_info.study_time; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0030,len,(Uint8 *)pdata); pdata = fi->mod->gn_info.series_time; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0031,len,(Uint8 *)pdata); pdata = fi->mod->gn_info.acquisition_time; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0032,len,(Uint8 *)pdata); pdata = fi->mod->gn_info.image_time; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0033,len,(Uint8 *)pdata); }else{ sprintf(mdcbufr,"%02d%02d%02d",fi->study_time_hour ,fi->study_time_minute ,fi->study_time_second); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0030,len,(Uint8 *)mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0031,len,(Uint8 *)mdcbufr); if (fi->image[0].sdata != NULL) { sprintf(mdcbufr,"%02d%02d%02d",fi->image[0].sdata->start_time_hour ,fi->image[0].sdata->start_time_minute ,fi->image[0].sdata->start_time_second); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0032,len,(Uint8 *)mdcbufr); }else{ mdc_dicom_write_element(fi->ofp,0x0008,0x0032,len,(Uint8 *)mdcbufr); } sprintf(mdcbufr,"%02d%02d%02d",fi->study_time_hour ,fi->study_time_minute ,fi->study_time_second); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0033,len,(Uint8 *)mdcbufr); } len = strlen(mdc_dummy1); mdc_dicom_write_element(fi->ofp,0x0008,0x0050,len,(Uint8 *)mdc_dummy1); pdata = MdcGetStrModality((signed)dicom->modality); /* already in mdcbufr */ len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0008,0x0060,len,(Uint8 *)pdata); strcpy(mdcbufr,fi->manufacturer); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0070,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,fi->institution); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0080,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"Unknown^^^^"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0090,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,fi->study_descr); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x1030,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,fi->series_descr); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x103E,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,fi->operator_name); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x1070,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,MDC_LIBVERS); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x2111,len,(Uint8 *)mdcbufr); return(NULL); } char *MdcDicomWriteG0010(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { Uint32 len; char *pdata; /* group 0x0010 */ sprintf(mdcbufr,"%.64s^^^^",fi->patient_name); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0010,0x0010,len,(Uint8 *)mdcbufr); pdata = fi->patient_id; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0010,0x0020,len,(Uint8 *)pdata); pdata = fi->patient_dob; len = strlen(pdata); if (len > 0 && pdata[0] != '0' ) { mdc_dicom_write_element(fi->ofp,0x0010,0x0030,len,(Uint8 *)pdata); }else{ mdc_dicom_write_element(fi->ofp,0x0010,0x0030,0,NULL); } mdc_dicom_write_element(fi->ofp,0x0010,0x0032,0,NULL); /* Pat Birth Time */ strcpy(mdcbufr,fi->patient_sex); MdcLowStr(mdcbufr); if (strchr(mdcbufr,'f') != NULL) { /* first check for fe-male */ strcpy(mdcbufr,"F"); }else if (strchr(mdcbufr,'m') != NULL) { /* now check for male */ strcpy(mdcbufr,"M"); }else { /* guess what? */ strcpy(mdcbufr,"O"); } len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0010,0x0040,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%.2f",fi->patient_height); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0010,0x1020,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%.2f",fi->patient_weight); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0010,0x1030,len,(Uint8 *)mdcbufr); return(NULL); } char *MdcDicomWriteG0018(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { Uint32 len; Uint16 ui16; char *pdata; /* 0x0018 */ strcpy(mdcbufr,fi->organ_code); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x0015,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%+e",fi->image[0].slice_width); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x0050,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"0"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x0070,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%+e",fi->image[0].slice_spacing); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x0088,len,(Uint8 *)mdcbufr); if (fi->gatednr > 0 && fi->gdata != NULL) { /* heart beat */ ui16 = (Uint16)MdcGetHeartRate(gd,MDC_HEART_RATE_OBSERVED); sprintf(mdcbufr,"%u",ui16); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1088,len,(Uint8 *)mdcbufr); } switch (dicom->modality) { case M_PT: break; default : /* default to NM modality */ if ((fi->acquisition_type == MDC_ACQUISITION_UNKNOWN) || (fi->acquisition_type == MDC_ACQUISITION_STATIC)) { /* normally whole body too ...*/ if (fi->image[0].sdata != NULL) { sprintf(mdcbufr,"%-12.0f",fi->image[0].sdata->image_duration); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1242,len,(Uint8 *)mdcbufr); } } } if (strcmp(fi->pat_pos,"Unknown") == 0) { pdata = NULL; len = 0; }else{ pdata = fi->pat_pos; len = strlen(pdata); } mdc_dicom_write_element(fi->ofp,0x0018,0x5100,len,(Uint8 *)pdata); return(NULL); } char *MdcDicomWriteG0020(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { Uint32 len; char *pdata; /* group 0x0020 */ len = MdcDicomMakeUID(fi,MDC_TYPE_UID_STUDY,mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x000D,len,(Uint8 *)mdcbufr); len = MdcDicomMakeUID(fi,MDC_TYPE_UID_SERIES,mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x000E,len,(Uint8 *)mdcbufr); pdata = fi->study_id; len = strlen(pdata); mdc_dicom_write_element(fi->ofp,0x0020,0x0010,len,(Uint8 *)pdata); if (fi->nr_series >= 0) sprintf(mdcbufr,"%d",fi->nr_series); else strcpy(mdcbufr,"0"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x0011,len,(Uint8 *)mdcbufr); if (fi->nr_acquisition >= 0) sprintf(mdcbufr,"%d",fi->nr_acquisition); else strcpy(mdcbufr,"0"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x0012,len,(Uint8 *)mdcbufr); if (fi->nr_instance >= 0) sprintf(mdcbufr,"%d",fi->nr_instance); else strcpy(mdcbufr,"0"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x0013,len,(Uint8 *)mdcbufr); len = MdcDicomMakeUID(fi,MDC_TYPE_UID_STUDY,mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x0052,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%u",fi->number); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x1002,len,(Uint8 *)mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x1040,0,NULL); strcpy(mdcbufr,"*** NOT APPROVED ***"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x4000,len,(Uint8 *)mdcbufr); return(NULL); } char *MdcDicomWriteG0028(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { Uint32 len, bytes; Uint16 ui16, *pui16, bits_allocated, bits_stored; float intercept=0., slope=1.; Int16 type = dicom->type; /* group 0x0028 */ ui16 = 1; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0028,0x0002,len,(Uint8 *)&ui16); strcpy(mdcbufr,"MONOCHROME2"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0028,0x0004,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%u",fi->number); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0028,0x0008,len,(Uint8 *)mdcbufr); bytes = 0; pui16 = NULL; switch (dicom->modality) { case M_PT: switch (fi->acquisition_type) { case MDC_ACQUISITION_DYNAMIC: bytes = 4 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc (PT/DYNAMIC) FrameIncrPointer"); pui16[0]=0x0054; pui16[1]=0x0080; /* slices */ pui16[2]=0x0054; pui16[3]=0x0100; /* frames */ dicom->VectDO[MDC_VECT_SLICE] = MDC_YES; dicom->VectDO[MDC_VECT_TIMESLICE] = MDC_YES; break; case MDC_ACQUISITION_TOMO: case MDC_ACQUISITION_STATIC: case MDC_ACQUISITION_UNKNOWN: default: bytes = 2 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc (PT/STATIC) FrameIncrPointer"); pui16[0]=0x0054; pui16[1]=0x0080; /* slices */ dicom->VectDO[MDC_VECT_SLICE] = MDC_YES; } mdc_dicom_write_element(fi->ofp,0x0028,0x0009,bytes,(Uint8 *)pui16); MdcFree(pui16); break; default : /* default to NM modality */ switch (fi->acquisition_type) { case MDC_ACQUISITION_TOMO: bytes = 2 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc (NM/TOMO) FrameIncrPointer"); pui16[0]=0x0054; pui16[1]=0x0080; /* slices */ dicom->VectDO[MDC_VECT_SLICE] = MDC_YES; break; case MDC_ACQUISITION_DYNAMIC: bytes = 8 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc (NM/DYNAMIC) FrameIncrPointer"); pui16[0]=0x0054; pui16[1]=0x0010; /* energy windows */ pui16[2]=0x0054; pui16[3]=0x0020; /* detectors */ pui16[4]=0x0054; pui16[5]=0x0030; /* phases */ pui16[6]=0x0054; pui16[7]=0x0100; /* time slices */ dicom->VectDO[MDC_VECT_ENERGYWINDOW] = MDC_YES; dicom->VectDO[MDC_VECT_DETECTOR] = MDC_YES; dicom->VectDO[MDC_VECT_PHASE] = MDC_YES; dicom->VectDO[MDC_VECT_TIMESLICE] = MDC_YES; break; case MDC_ACQUISITION_GATED: bytes = 8 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc (NM/GATED) FrameIncrPointer"); pui16[0]=0x0054; pui16[1]=0x0010; /* energy windows */ pui16[2]=0x0054; pui16[3]=0x0020; /* detectors */ pui16[4]=0x0054; pui16[5]=0x0060; /* RR-intervals */ pui16[6]=0x0054; pui16[7]=0x0070; /* time slots */ dicom->VectDO[MDC_VECT_ENERGYWINDOW] = MDC_YES; dicom->VectDO[MDC_VECT_DETECTOR] = MDC_YES; dicom->VectDO[MDC_VECT_RRINTERVAL] = MDC_YES; dicom->VectDO[MDC_VECT_TIMESLOT] = MDC_YES; break; case MDC_ACQUISITION_GSPECT: if (fi->reconstructed == MDC_YES) { bytes = 6 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc (NM/RECON GATED) FrameIncrPointer"); pui16[0]=0x0054; pui16[1]=0x0060; /* RR-intervals */ pui16[2]=0x0054; pui16[3]=0x0070; /* time slots */ pui16[4]=0x0054; pui16[5]=0x0080; /* slices */ dicom->VectDO[MDC_VECT_RRINTERVAL] = MDC_YES; dicom->VectDO[MDC_VECT_TIMESLOT] = MDC_YES; dicom->VectDO[MDC_VECT_SLICE] = MDC_YES; }else{ bytes = 12 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc (NM/GATED TOMO) FrameIncrPointer"); pui16[0]=0x0054; pui16[1]=0x0010; /* energy windows */ pui16[2]=0x0054; pui16[3]=0x0020; /* detectors */ pui16[4]=0x0054; pui16[5]=0x0050; /* rotations = 1 */ pui16[6]=0x0054; pui16[7]=0x0060; /* RR-intervals */ pui16[8]=0x0054; pui16[9]=0x0070; /* time slot */ pui16[10]=0x0054;pui16[11]=0x0090;/* angular views */ dicom->VectDO[MDC_VECT_ENERGYWINDOW] = MDC_YES; dicom->VectDO[MDC_VECT_DETECTOR] = MDC_YES; dicom->VectDO[MDC_VECT_ROTATION] = MDC_YES; dicom->VectDO[MDC_VECT_RRINTERVAL] = MDC_YES; dicom->VectDO[MDC_VECT_TIMESLOT] = MDC_YES; dicom->VectDO[MDC_VECT_ANGULARVIEW] = MDC_YES; } break; case MDC_ACQUISITION_UNKNOWN: /* fake as static */ case MDC_ACQUISITION_STATIC: bytes = 4 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc (NM/STATIC) FrameIncrPointer"); pui16[0]=0x0054; pui16[1]=0x0010; /* energy windows */ pui16[2]=0x0054; pui16[3]=0x0020; /* detectors */ dicom->VectDO[MDC_VECT_ENERGYWINDOW] = MDC_YES; dicom->VectDO[MDC_VECT_DETECTOR] = MDC_YES; break; } mdc_dicom_write_element(fi->ofp,0x0028,0x0009,bytes,(Uint8 *)pui16); MdcFree(pui16); } ui16 = (Uint16) fi->mheight; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0028,0x0010,len,(Uint8 *)&ui16); ui16 = (Uint16) fi->mwidth; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0028,0x0011,len,(Uint8 *)&ui16); sprintf(mdcbufr,"%+e\\%+e",fi->pixdim[IROW],fi->pixdim[ICOL]); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0028,0x0030,len,(Uint8 *)mdcbufr); mdcbufr[0]='\0'; if (fi->decay_corrected) strcat(mdcbufr,"DECY\\"); if (fi->flood_corrected) strcat(mdcbufr,"UNIF\\"); len = strlen(mdcbufr); /* if present, remove last redundant backslash */ if (len > 0) { len -= 1; mdcbufr[len] = '\0'; } mdc_dicom_write_element(fi->ofp,0x0028,0x0051,len,(Uint8 *)mdcbufr); bits_allocated = (Uint16)MdcType2Bits(type); if (MDC_FORCE_INT == BIT16_S) { bits_stored = MDC_INT16_BITS_USED; }else{ bits_stored = MdcType2Bits(type); } ui16 = bits_allocated; /* bits allocated */ len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0028,0x0100,len,(Uint8 *)&ui16); ui16 = bits_stored; /* bits stored */ len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0028,0x0101,len,(Uint8 *)&ui16); ui16 = bits_stored - 1; /* high bit */ len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0028,0x0102,len,(Uint8 *)&ui16); switch (type) { case BIT8_U: case BIT16_U: case BIT32_U: case BIT64_U: ui16 = 0; break; case BIT8_S: case BIT16_S: case BIT32_S: case BIT64_S: ui16 = 1; break; default: ui16 = 0; } if (type == BIT16_S && MDC_INT16_BITS_USED < 16) ui16 = 0; /* unsigned */ len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0028,0x0103,len,(Uint8 *)&ui16); /* need to rewrite the following tag after rescaling images */ MDC_REWRF_INTERCEPT = ftell(fi->ofp); sprintf(mdcbufr,"%+e",intercept); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0028,0x1052,len,(Uint8 *)mdcbufr); /* need to rewrite the following tag after rescaling images */ MDC_REWRF_SLOPE = ftell(fi->ofp); sprintf(mdcbufr,"%+e",slope); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0028,0x1053,len,(Uint8 *)mdcbufr); return(NULL); } /* write NM Image Information */ char *MdcDicomWriteG0054(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { ACQ_DATA tmp_acqdata, *acqdata=NULL; DYNAMIC_DATA *dd=NULL; Uint32 i, t, bytes, dim, vect, len, acqnr; Uint32 ph, ts, inr; Uint16 ui16, *pui16; float v; if (fi->acqnr > 0 && fi->acqdata != NULL) { acqnr = fi->acqnr; acqdata = (ACQ_DATA *)fi->acqdata; }else{ acqnr = 1; acqdata = (ACQ_DATA *)&tmp_acqdata; MdcInitAD(acqdata); if (gd->nr_projections > 0.) { acqdata->angle_step = gd->extent_rotation / gd->nr_projections; }else{ acqdata->angle_step = acqdata->scan_arc / (float)fi->dim[3]; } } /* group 0x0054 */ if (dicom->VectDO[MDC_VECT_ENERGYWINDOW] == MDC_YES) { /* MARK: window vectors */ if (fi->dim[7] == 0) return("DICM Bad zero value for fi->dim[7]"); if (fi->number % fi->dim[7]) return("DICM Garbled value for fi->dim[7]"); vect = fi->number / fi->dim[7]; bytes = fi->number * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc EnergyWindowVector"); for (i=0; inumber; i++) pui16[i] = (Uint16)((i/vect)+1); mdc_dicom_write_element(fi->ofp,0x0054,0x0010,bytes,(Uint8 *)pui16); MdcFree(pui16); } /* number of energy windows */ ui16 = fi->dim[7]; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0054,0x0011,len,(Uint8 *)&ui16); /* window information sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0012); /* item */ MdcDicomWriteItem(fi->ofp); /* window range sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0013); /* item */ MdcDicomWriteItem(fi->ofp); /* item delimitation item */ MdcDicomWriteItemDelItem(fi->ofp); /* sequence delimitation item*/ MdcDicomWriteInfoSeqDelItem(fi->ofp); /* item delimitation item */ MdcDicomWriteItemDelItem(fi->ofp); /* sequence delimitation item */ MdcDicomWriteInfoSeqDelItem(fi->ofp); /* radiopharmaceutical info sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0016); /* item */ MdcDicomWriteItem(fi->ofp); strcpy(mdcbufr,fi->radiopharma); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x0031,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"0.0"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1071,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%02d%02d%02d",fi->dose_time_hour ,fi->dose_time_minute ,fi->dose_time_second); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1072,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%g",fi->injected_dose); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1074,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%g",fi->isotope_halflife); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1075,len,(Uint8 *)mdcbufr); /* radionuclidecode info sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0300); /* item */ MdcDicomWriteItem(fi->ofp); strcpy(mdcbufr,fi->isotope_code); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0100,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"99SDM"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0102,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,fi->isotope_code); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0104,len,(Uint8 *)mdcbufr); /* item delimitation item */ MdcDicomWriteItemDelItem(fi->ofp); /* sequence delimiter */ MdcDicomWriteInfoSeqDelItem(fi->ofp); MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0304); /* item */ MdcDicomWriteItem(fi->ofp); strcpy(mdcbufr,fi->radiopharma); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0100,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"99SDM"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0102,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,fi->radiopharma); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0104,len,(Uint8 *)mdcbufr); /* item delimitation item */ MdcDicomWriteItemDelItem(fi->ofp); /* sequence delimiter */ MdcDicomWriteInfoSeqDelItem(fi->ofp); /* item delimitation item */ MdcDicomWriteItemDelItem(fi->ofp); /* sequence delimiter */ MdcDicomWriteInfoSeqDelItem(fi->ofp); if (dicom->VectDO[MDC_VECT_DETECTOR] == MDC_YES) { /* MARK: detector vectors */ if (fi->dim[6] == 0) return("DICM Bad zero value for fi->dim[6]"); if (fi->number % fi->dim[6]) return("DICM Garbled value for fi->dim[6]"); vect = fi->number / fi->dim[6]; bytes = fi->number * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc DetectorVector"); for (i=0; inumber; i++) pui16[i] = (Uint16)((i/vect)+1); mdc_dicom_write_element(fi->ofp,0x0054,0x0020,bytes,(Uint8 *)pui16); MdcFree(pui16); } /* number of detector heads */ ui16 = fi->dim[6]; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0054,0x0021,len,(Uint8 *)&ui16); /* detector info sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0022); /* item */ MdcDicomWriteItem(fi->ofp); /* colimator type */ mdc_dicom_write_element(fi->ofp,0x0018,0x1181,0,NULL); /* focal distance */ mdc_dicom_write_element(fi->ofp,0x0018,0x1182,0,NULL); /* image pos patient */ sprintf(mdcbufr,"%+e\\%+e\\%+e",fi->image[0].image_pos_pat[0] ,fi->image[0].image_pos_pat[1] ,fi->image[0].image_pos_pat[2]); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x0032,len,(Uint8 *)mdcbufr); /* image orient patient */ sprintf(mdcbufr,"%+e\\%+e\\%+e\\%+e\\%+e\\%+e" ,fi->image[0].image_orient_pat[0] ,fi->image[0].image_orient_pat[1] ,fi->image[0].image_orient_pat[2] ,fi->image[0].image_orient_pat[3] ,fi->image[0].image_orient_pat[4] ,fi->image[0].image_orient_pat[5]); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0020,0x0037,len,(Uint8 *)mdcbufr); /* static image label */ if (fi->image[0].sdata != NULL) { STATIC_DATA *sd = fi->image[0].sdata; if (fi->number > 1) { MdcPrntWarn("DICM static info lost; to prevent choose to split slices"); } /* view code sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0220); /* item */ MdcDicomWriteItem(fi->ofp); strcpy(mdcbufr,sd->label); len = strlen(sd->label); mdc_dicom_write_element(fi->ofp,0x0008,0x0100,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"99SDM"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0008,0x0102,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,sd->label); len = strlen(sd->label); mdc_dicom_write_element(fi->ofp,0x0008,0x0104,len,(Uint8 *)mdcbufr); /* item delimitation item */ MdcDicomWriteItemDelItem(fi->ofp); /* sequence delimitation item */ MdcDicomWriteInfoSeqDelItem(fi->ofp); }else{ /* start angle */ v = acqdata[0].angle_start; v = MdcRotateAngle(v,180.); sprintf(mdcbufr,"%g",v); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0054,0x0200,len,(Uint8 *)mdcbufr); } /* item delimitation item */ MdcDicomWriteItemDelItem(fi->ofp); /* sequence delimitation item */ MdcDicomWriteInfoSeqDelItem(fi->ofp); if (dicom->modality == M_PT) { switch (fi->acquisition_type) { /* PET MODALITY */ case MDC_ACQUISITION_DYNAMIC: /* MARK: slice vector */ if (dicom->VectDO[MDC_VECT_SLICE] == MDC_YES) { if (fi->dim[3] == 0) return("DICM Bad zero value for fi->dim[3] (PT)"); vect = fi->dim[3]; bytes = fi->number*sizeof(Uint16); pui16=(Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc SliceVector (PT)"); for (i=0; inumber; i++) pui16[i]=(Uint16)((i%vect)+1); mdc_dicom_write_element(fi->ofp,0x0054,0x0080,bytes,(Uint8 *)pui16); MdcFree(pui16); } ui16 = (Uint16) fi->dim[3]; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0054,0x0081,len,(Uint8 *)&ui16); /* MARK: time slice vector */ if (dicom->VectDO[MDC_VECT_TIMESLICE] == MDC_YES) { if (fi->dim[4] == 0) return("DICM Bad zero value for fi->dim[4] (PT)"); if (fi->number % fi->dim[4]) return("DICM Garbled value for fi->dim[4] (PT)"); vect = fi->number / fi->dim[4]; bytes = fi->number * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc TimeSlotVector (PT)"); for (i=0; inumber; i++) pui16[i] = (Uint16)((i/vect)+1); mdc_dicom_write_element(fi->ofp,0x0054,0x0100,bytes,(Uint8 *)pui16); MdcFree(pui16); } ui16 = (Uint16) fi->dim[4]; mdc_dicom_write_element(fi->ofp,0x0054,0x0101,len,(Uint8 *)&ui16); break; case MDC_ACQUISITION_STATIC : case MDC_ACQUISITION_TOMO : case MDC_ACQUISITION_UNKNOWN: default: if (fi->dim[4] > 1) return("DICM Unsupported dim[]-values (PT)"); /* MARK: slice vector */ if (dicom->VectDO[MDC_VECT_SLICE] == MDC_YES) { bytes = fi->number*sizeof(Uint16); pui16=(Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc slice vector buffer (PT)"); for (i=0; inumber; i++) pui16[i]=(Uint16)i+1; mdc_dicom_write_element(fi->ofp,0x0054,0x0080,bytes,(Uint8 *)pui16); MdcFree(pui16); } ui16 = (Uint16) fi->dim[3]; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0054,0x0081,len,(Uint8 *)&ui16); } switch (fi->acquisition_type) { case MDC_ACQUISITION_TOMO: strcpy(mdcbufr,"STATIC"); break; case MDC_ACQUISITION_DYNAMIC: strcpy(mdcbufr,"DYNAMIC"); break; case MDC_ACQUISITION_UNKNOWN: /* fake as static */ case MDC_ACQUISITION_STATIC : strcpy(mdcbufr,"STATIC"); break; default: strcpy(mdcbufr,"UNSPECIFIED"); } /* type of detector motion */ /* strcpy(mdcbufr,"UNDEFINED"); */ /* mdc_dicom_write_element(fi->ofp,0x0054,0x0202,strlen(mdcbufr) */ /* ,(Uint8 *)mdcbufr);*/ /* patient orientation code sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0410); /* sequence delimitation item */ MdcDicomWriteInfoSeqDelItem(fi->ofp); /* patient gantry relationship code sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0414); /* sequence delimitation item */ MdcDicomWriteInfoSeqDelItem(fi->ofp); if (fi->reconstructed == MDC_YES) strcat(mdcbufr,"\\IMAGE"); else strcat(mdcbufr,"\\REPROJECTION"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0054,0x1000,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"UNKNOWN"); /* units */ len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0054,0x1001,len,(Uint8 *)mdcbufr); strcpy(mdcbufr,"UNKNOWN"); /* counts source */ len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0054,0x1002,len,(Uint8 *)mdcbufr); if (fi->decay_corrected) { strcpy(mdcbufr,"ADMIN"); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0054,0x1102,len,(Uint8 *)mdcbufr); } }else{ /* NM-MODALITY */ /* MARK: phases vector*/ if (dicom->VectDO[MDC_VECT_PHASE] == MDC_YES) { if ((fi->dynnr == 0) || (fi->dyndata == NULL)) return("DICM Required DYNAMIC_DATA values missing"); bytes = fi->number * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc PhaseVector (NM)"); for (i=0; inumber; i++) pui16[i] = (Uint16)fi->image[i].frame_number; mdc_dicom_write_element(fi->ofp,0x0054,0x0030,bytes,(Uint8 *)pui16); MdcFree(pui16); /* number of phases */ ui16 = (Uint16)fi->dynnr; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0054,0x0031,len,(Uint8 *)&ui16); MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0032); for (i=0; idynnr; i++) { MdcDicomWriteItem(fi->ofp); dd = &fi->dyndata[i]; sprintf(mdcbufr,"%-12.0f",MdcSingleImageDuration(fi,i)); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1242,len,(Uint8 *)mdcbufr); /* number of slices in phase */ ui16 = (Uint16)dd->nr_of_slices; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0054,0x0033,len,(Uint8 *)&ui16); sprintf(mdcbufr,"%-12.0f",dd->time_frame_delay); len = strlen(mdcbufr); /* phase delay */ mdc_dicom_write_element(fi->ofp,0x0054,0x0036,len,(Uint8 *)mdcbufr); /* pause between frames */ sprintf(mdcbufr,"%-12.0f",dd->delay_slices); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0054,0x0038,len,(Uint8 *)mdcbufr); MdcDicomWriteItemDelItem(fi->ofp); } MdcDicomWriteInfoSeqDelItem(fi->ofp); } /* MARK: Rotation Vector: we always consider it as 1, */ /* we don't know how to map on InterFile tomographic */ if (dicom->VectDO[MDC_VECT_ROTATION] == MDC_YES) { vect = fi->number / 1; bytes = fi->number * sizeof(Uint16); pui16=(Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc RotationVector (NM)"); for (i=0; inumber; i++) pui16[i]=(Uint16)((i/vect)+1); mdc_dicom_write_element(fi->ofp,0x0054,0x0050,bytes,(Uint8 *)pui16); MdcFree(pui16); } /* Rotation sequence must be included for all TOMO types */ if ( fi->acquisition_type == MDC_ACQUISITION_TOMO || fi->acquisition_type == MDC_ACQUISITION_GSPECT ) { /* number of rotations */ ui16 = (Uint16)acqnr; len = sizeof(ui16); mdc_dicom_write_element(fi->ofp,0x0054,0x0051,len,(Uint8 *)&ui16); /* rotation info sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0052); for (i=0; iofp); /* MARK: distance source to detector: only transmission */ /* mdc_dicom_write_element(fi->ofp,0x0018,0x1110,0,NULL); */ switch (acq->rotation_direction) { case MDC_ROTATION_CW: strcpy(mdcbufr,"CW"); break; case MDC_ROTATION_CC: strcpy(mdcbufr,"CC"); break; default : mdcbufr[0] = '\0'; } len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1140,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%g",acq->radial_position); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1142,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%g",acq->scan_arc); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1143,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%g",acq->angle_step); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1144,len,(Uint8 *)mdcbufr); sprintf(mdcbufr,"%g",acq->rotation_offset); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1145,len,(Uint8 *)mdcbufr); if (fi->acquisition_type == MDC_ACQUISITION_GSPECT) { /* gspect: gd->time_per_proj */ v = gd->time_per_proj; }else{ /* tomo : dd->time_frame_duration */ if ((fi->dynnr > 0) && (fi->dyndata != NULL)) { v = fi->dyndata[0].time_frame_duration; }else{ v = 0.; } } sprintf(mdcbufr,"%-12.0f",v); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1242,len,(Uint8 *)mdcbufr); if (gd->nr_projections > 0.) { ui16 = (Uint16)gd->nr_projections; len = sizeof(ui16); }else{ ui16 = (Uint16)fi->dim[3]; len = sizeof(ui16); } mdc_dicom_write_element(fi->ofp,0x0054,0x0053,len,(Uint8 *)&ui16); v = acq->angle_start; v = MdcRotateAngle(v, 180.); sprintf(mdcbufr,"%g",v); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0054,0x0200,len,(Uint8 *)mdcbufr); switch (acq->detector_motion) { case MDC_MOTION_STEP: strcpy(mdcbufr,"STEP AND SHOOT"); break; case MDC_MOTION_CONT: strcpy(mdcbufr,"CONTINUOUS"); break; case MDC_MOTION_DRNG: strcpy(mdcbufr,"ACQ DURING STEP"); break; default : strcpy(mdcbufr,"UNDEFINED"); } len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0054,0x0202,len,(Uint8 *)mdcbufr); MdcDicomWriteItemDelItem(fi->ofp); } /* sequence delimitation item */ MdcDicomWriteInfoSeqDelItem(fi->ofp); } /* MARK: interval vectors */ if (dicom->VectDO[MDC_VECT_RRINTERVAL] == MDC_YES) { if (fi->dim[5] == 0) return("DICM Bad zero value for fi->dim[5] (NM)"); if (fi->number % fi->dim[5]) return("DICM Garbled value for fi->dim[5] (NM)"); vect = fi->number / fi->dim[5]; bytes = fi->number * sizeof(Uint16); pui16=(Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc RRIntervalVector (NM)"); for (i=0; inumber; i++) pui16[i]=(Uint16)((i/vect)+1); mdc_dicom_write_element(fi->ofp,0x0054,0x0060,bytes,(Uint8 *)pui16); MdcFree(pui16); /* number of intervals */ ui16 = (Uint16) fi->dim[5]; mdc_dicom_write_element(fi->ofp,0x0054,0x0061,sizeof(Uint16) ,(Uint8 *)&ui16); MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0062); for (i=0; idim[5]; i++) { MdcDicomWriteItem(fi->ofp); MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0063); MdcDicomWriteItem(fi->ofp); /* nominal interval*/ mdc_dicom_write_element(fi->ofp,0x0018,0x1062,0,NULL); /* frame time */ sprintf(mdcbufr,"%+e",gd->image_duration); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1063,len,(Uint8 *)mdcbufr); /* low RR value */ sprintf(mdcbufr,"%u",(Uint16)gd->window_low); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1081,len,(Uint8 *)mdcbufr); /* high RR value */ sprintf(mdcbufr,"%u",(Uint16)gd->window_high); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1082,len,(Uint8 *)mdcbufr); /* intervals acquired */ ui16 = (Uint16) gd->cycles_acquired; sprintf(mdcbufr,"%u",ui16); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1083,len,(Uint8 *)mdcbufr); /* intervals rejected */ ui16 = (Uint16) (gd->cycles_observed - gd->cycles_acquired); sprintf(mdcbufr,"%u",ui16); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0018,0x1084,len,(Uint8 *)mdcbufr); MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0072); for (t=0; tdim[4]; t++) { MdcDicomWriteItem(fi->ofp); v = (gd->cycles_acquired * gd->image_duration)/(float)fi->number; sprintf(mdcbufr,"%+e",v); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0054,0x0073 ,len,(Uint8 *)mdcbufr); MdcDicomWriteItemDelItem(fi->ofp); } MdcDicomWriteInfoSeqDelItem(fi->ofp); MdcDicomWriteItemDelItem(fi->ofp); MdcDicomWriteInfoSeqDelItem(fi->ofp); MdcDicomWriteItemDelItem(fi->ofp); } MdcDicomWriteInfoSeqDelItem(fi->ofp); } /* MARK: timeslot vectors */ if (dicom->VectDO[MDC_VECT_TIMESLOT] == MDC_YES) { if (fi->acquisition_type == MDC_ACQUISITION_GATED) { /* MARK: for gated, time slot is the last dimension !! */ dim = fi->dim[3]; }else{ dim = fi->dim[4]; } if (dim == 0) return("DICM Bad zero value for fi->dim[3|4] (NM)"); if (fi->number % dim) return("DICM Garbled value for fi->dim[3|4] (NM)"); vect = fi->number / dim; bytes = fi->number * sizeof(Uint16); pui16=(Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc TimeSlotVector (NM)"); for (i=0; inumber; i++) pui16[i]=(Uint16)((i/vect)+1); mdc_dicom_write_element(fi->ofp,0x0054,0x0070,bytes,(Uint8 *)pui16); MdcFree(pui16); /* number of intervals */ ui16 = (Uint16) dim; mdc_dicom_write_element(fi->ofp,0x0054,0x0071,sizeof(Uint16) ,(Uint8 *)&ui16); } /* MARK: slice vector */ if (dicom->VectDO[MDC_VECT_SLICE] == MDC_YES) { bytes = fi->number*sizeof(Uint16); pui16=(Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc SliceVector (NM)"); vect = fi->dim[3]; for (i=0; inumber; i++) pui16[i]=(Uint16)((i%vect)+1); mdc_dicom_write_element(fi->ofp,0x0054,0x0080,bytes,(Uint8 *)pui16); MdcFree(pui16); /* number of slices */ ui16 = (Uint16) fi->dim[3]; mdc_dicom_write_element(fi->ofp,0x0054,0x0081,sizeof(Uint16) ,(Uint8 *)&ui16); } /* MARK: angular view vector */ if (dicom->VectDO[MDC_VECT_ANGULARVIEW] == MDC_YES) { bytes = fi->number*sizeof(Uint16); pui16=(Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc AngularViewVector (NM)"); vect = fi->dim[3]; for (i=0; inumber; i++) pui16[i]=(Uint16)((i%vect)+1); mdc_dicom_write_element(fi->ofp,0x0054,0x0090,bytes,(Uint8 *)pui16); MdcFree(pui16); } /* MARK: time slice vector */ if (dicom->VectDO[MDC_VECT_TIMESLICE] == MDC_YES) { if ((fi->dynnr > 0) && (fi->dyndata != NULL)) { bytes = fi->number * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes); if (pui16 == NULL) return("DICM Bad malloc TimeSliceVector (NM)"); /* phases */ inr = 0; for (ph = 0; ph < fi->dynnr; ph++ ) { /* timeslices */ for (ts=0; ts < fi->dyndata[ph].nr_of_slices ; ts++) { pui16[inr++] = (Uint16)ts + 1; } } mdc_dicom_write_element(fi->ofp,0x0054,0x0100,bytes,(Uint8 *)pui16); MdcFree(pui16); }else{ return("DICM Missing dynamic data structs"); } } /* patient orientation code sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0410); /* sequence delimitation item */ MdcDicomWriteInfoSeqDelItem(fi->ofp); /* patient gantry relationship code sequence */ MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0414); /* sequence delimitation item */ MdcDicomWriteInfoSeqDelItem(fi->ofp); } return(NULL); } /* write Images */ char *MdcDicomWriteG7FE0(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom) { Uint32 i, bytes, pixels, len; Uint8 *newbuff, *buff; float slope, intercept; /* group 0x7FE0 - dump the images */ bytes = fi->number * fi->mwidth * fi->mheight * MdcType2Bytes(dicom->type); mdc_dicom_write_element(fi->ofp,0x7fe0,0x0010,bytes,(Uint8 *)&dicom->type); for (i=0; inumber; i++) { if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,1./(float)fi->number,NULL); switch (dicom->type) { case BIT8_U : newbuff = MdcGetImgBIT8_U(fi,i); break; case BIT16_S: newbuff = MdcGetImgBIT16_S(fi,i); break; default: newbuff = NULL; /* bad pixel type */ } if (newbuff == NULL) return("DICM Bad malloc newbuff image"); if (fi->diff_size == MDC_YES) { buff = MdcGetResizedImage(fi,newbuff,dicom->type,i); if (buff == NULL) return("DICM Bad malloc resized image"); MdcFree(newbuff); }else buff = newbuff; if (MDC_FILE_ENDIAN != MDC_HOST_ENDIAN) MdcMakeImgSwapped(buff, fi, i, fi->mwidth, fi->mheight, dicom->type); pixels = fi->mwidth * fi->mheight; bytes = MdcType2Bytes(dicom->type); if (fwrite(buff,bytes,pixels,fi->ofp) != pixels) return("DICM Bad writing of image"); MdcFree(buff); } if (MDC_QUANTIFY == MDC_YES || MDC_CALIBRATE == MDC_YES) { /* rewrite the true intercept value */ fseek(fi->ofp,(signed)MDC_REWRF_INTERCEPT,SEEK_SET); intercept = fi->image[0].rescaled_intercept; sprintf(mdcbufr,"%+e",intercept); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0028,0x1052,len,(Uint8 *)mdcbufr); /* rewrite the true slope value */ fseek(fi->ofp,(signed)MDC_REWRF_SLOPE,SEEK_SET); slope = fi->image[0].rescaled_slope; sprintf(mdcbufr,"%+e",slope); len = strlen(mdcbufr); mdc_dicom_write_element(fi->ofp,0x0028,0x1053,len,(Uint8 *)mdcbufr); } return(NULL); } const char *MdcWriteDICM(FILEINFO *fi) { GATED_DATA tmpgd; MDC_DICOM_STUFF_T *dicom=&mdc_dicom_stuff; const char *msg; if (MDC_DICOM_WRITE_IMPLICIT == MDC_YES) { MDC_FILE_ENDIAN = MDC_LITTLE_ENDIAN; }else{ MDC_FILE_ENDIAN = MDC_WRITE_ENDIAN; } if (fi->gatednr > 0 && fi->gdata != NULL) { gd = (GATED_DATA *)&fi->gdata[0]; }else{ gd = (GATED_DATA *)&tmpgd; MdcInitGD(gd); } /* no batch process in GUI, change UID's for each write */ if (XMDC_GUI == MDC_YES) mdc_psec = NULL; if (mdc_psec == NULL) { /* for first time, retrieve universal time (seconds) */ if ( time(&mdc_sec) == ((time_t)-1) ) { MdcPrntMesg("DICM Generating unique UID failed"); }else{ mdc_psec = &mdc_sec; } } if (XMDC_GUI == MDC_NO) { MdcDefaultName(fi,MDC_FRMT_DICM,fi->ofname,fi->ifname); } if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Writing DICOM:"); if (MDC_VERBOSE) MdcPrntMesg("DICM Writing <%s> ...",fi->ofname); /* check for colored files */ if (fi->map == MDC_MAP_PRESENT) return("DICM Colored files unsupported"); if (MDC_FILE_STDOUT == MDC_YES) { fi->ofp = stdout; }else{ if (MdcKeepFile(fi->ofname)) return("DICM File exists!!"); if ( (fi->ofp=fopen(fi->ofname,"wb")) == NULL) return("DICM Couldn't open file"); } /* init dicom struct */ MdcDicomInitStuff(dicom); /* set modality to write */ MdcDicomWriteSetModality(fi,dicom); /* check for requested pixel type */ if (MDC_FORCE_INT != MDC_NO) dicom->type = MDC_FORCE_INT; /* only Int16 or Uint8 type supported */ if ( (dicom->type != BIT16_S) && (dicom->type != BIT8_U) ) { dicom->type = BIT16_S; MdcPrntWarn("DICM Only Int16 or Uint8 pixels supported"); } /* NM dynamic inappropriate for non-planar */ if (dicom->modality == M_NM && fi->planar == MDC_NO && fi->acquisition_type == MDC_ACQUISITION_DYNAMIC) { MdcPrntWarn("DICM Inappropriate for non-planar dynamic studies (NM)"); } if (MDC_DICOM_WRITE_NOMETA == MDC_NO) { msg = MdcDicomWriteMetaHeader(fi,dicom); if (msg != NULL) return(msg); } msg = MdcDicomWriteG0008(fi,dicom); if (msg != NULL) return(msg); msg = MdcDicomWriteG0010(fi,dicom); if (msg != NULL) return(msg); msg = MdcDicomWriteG0018(fi,dicom); if (msg != NULL) return(msg); msg = MdcDicomWriteG0020(fi,dicom); if (msg != NULL) return(msg); msg = MdcDicomWriteG0028(fi,dicom); if (msg != NULL) return(msg); switch (dicom->modality) { case M_PT: default : /* default to NM modality */ msg = MdcDicomWriteG0054(fi,dicom); if (msg != NULL) return(msg); } msg = MdcDicomWriteG7FE0(fi,dicom); if (msg != NULL) return(msg); MdcCloseFile(fi->ofp); return NULL; } /********** * images * **********/ static int mdc_dicom_read(FILEINFO *fi, IMAGE **image, int *number) { int err; dicom_init(fi->ifp); /* last argument: parametric(=1) (see more in vtdicom file transform.c)*/ /* we need original values, preventing the rescaling to max/inverting */ /* for doing the things ourselves (width/center or slope/intercept) */ err = dicom_read(fi->ipath,image,number,1); return(err); } /******** * info * ********/ static void mdc_dicom_getinfo(FILEINFO *fi) { ELEMENT *e; DICTIONARY *d; MDC_ACR_TAG acrtag; MDC_SEQ_TAG seqtag, *seq; /* MARK Int8 saved_file_endian = MDC_FILE_ENDIAN; */ dicom_log(INFO,"dump_open()"); dicom_init(fi->ifp); if (dicom_open(fi->ipath)) return; for (;;) { e=dicom_element(); if (!e) return; d=dicom_query(e); if (e->vr==UN) e->vr=d->vr; if (mdc_dicom_load(e->vr)) return; acrtag.group = e->group; acrtag.element= e->element; acrtag.length = e->length; acrtag.data = (Uint8 *)e->value.UN; seqtag.group = e->sqtag.group; seqtag.element= e->sqtag.element; seq = (e->sequence) ? &seqtag : NULL; if (acrtag.data != NULL) { if (mdc_dicom_skip_sequence(e) == 0) MdcDoTag(seq,&acrtag,fi,0); MdcFree(e->value.UN); } } } /******** * open * ********/ static void mdc_dicom_dumpinfo(FILEINFO *fi) { ELEMENT *e; DICTIONARY *d; dicom_log(INFO,"dump_open()"); dicom_init(fi->ifp); if (dicom_open(fi->ipath)) return; for (;;) { e=dicom_element(); if (!e) return; d=dicom_query(e); if (e->vr==UN && d->vr!=ox) { /* replace, except for special tags */ e->vr=d->vr; } if (dicom_load(e->vr)) return; mdc_dicom_printinfo(e,d->description); MdcFree(e->value.UN); } } /********* * print * *********/ static void mdc_dicom_printinfo(const ELEMENT *e,const char *description) { U32 i, len; dicom_log(INFO,"dump_print()"); for (i=e->sequence; i; i--) MdcPrntScrn(" "); if (MDC_DICOM_VERBOSE) MdcPrntScrn("(%.4X,%.4X) %c%c[%u] " ,e->group,e->element ,e->vr>>8,e->vr&0xFF,e->vm); MdcPrntScrn("%s%s: ",e->encapsulated?"Encapsulated ":"",description); if (!e->vm) { puts("(no value)"); return; } if (e->length == UNDEFINED_LENGTH) { puts("(undefined length)"); return; } for (i=0; ivm; i++) switch(e->vr) { case US : MdcPrntScrn("%u ",e->value.US[i]); break; case SS : MdcPrntScrn("%d ",e->value.SS[i]); break; case UL : MdcPrntScrn("%u ",e->value.UL[i]); break; case SL : MdcPrntScrn("%d ",e->value.SL[i]); break; case AT : MdcPrntScrn("(%.4X,%.4X) ",e->value.AT[i].group,e->value.AT[i].element); break; case FL : MdcPrntScrn("%f ",e->value.FL[i]); break; case FD : MdcPrntScrn("%f ",e->value.FD[i]); break; case LT : case ST : if (e->length > 128) { strcpy(mdcbufr,"..."); }else{ MdcGetSafeString(mdcbufr,e->value.LT,e->length,MDC_2KB_OFFSET); } MdcPrntScrn("[%s] ",mdcbufr); break; case AE : case AS : case CS : case DA : case DS : case DT : case IS : case LO : case PN : case SH : case TM : case UI : len = strlen(e->value.AE[i]); if (len > 128) { strcpy(mdcbufr,"..."); }else{ MdcGetSafeString(mdcbufr,e->value.AE[i],len,MDC_2KB_OFFSET); } MdcPrntScrn("[%s] ",mdcbufr); break; default : MdcPrntScrn("(%u bytes)\n",e->length); return; } if (MDC_DICOM_VERBOSE) MdcPrntScrn("(%u bytes)",e->length); puts(""); } void mdc_dicom_get_vr(ELEMENT *e) { DICTIONARY *d; d = dicom_query(e); e->vr = d->vr; } Uint8 *mdc_dicom_handle_vr(ELEMENT *e, Uint8 *tdata) { switch (e->vr) { case ox: if ((e->group == 0x7fe0) && (e->element == 0x0010)) { Int16 type; memcpy(&type,tdata,2); switch (type) { case BIT8_U : e->vr = OB; return(NULL); case BIT16_S: e->vr = OW; return(NULL); } } /* else other handle code */ break; default: return(tdata); /* no special VR */ } /* error exit = unhandled special VR */ MdcPrntErr(MDC_BAD_CODE,"Internal ## Extra code required for tag %x:%x" ,e->group,e->element); return(tdata); } /********* * write * *********/ int mdc_dicom_write_element(FILE *fp, Uint16 group, Uint16 element, Uint32 length, Uint8 *data) { ELEMENT element_t, *e; Uint32 i, vr_w, length32_w; Uint16 length16_w; Int8 file_endian_saved=MDC_FILE_ENDIAN; Int8 MAKE_EVEN=0, DO_IMPLICIT=MDC_DICOM_WRITE_IMPLICIT; Uint8 *ndata=NULL; /* make even tags */ if ((length%2) && (length != UNDEFINED_LENGTH)) { MAKE_EVEN = 1; } /* fill in the values */ e = &element_t; e->group = group; e->element = element; e->length = length + MAKE_EVEN; length32_w = e->length; length16_w = (Uint16)e->length; /* default transfer = explicit */ if (DO_IMPLICIT == MDC_YES) { /* only implicit VR little */ MDC_FILE_ENDIAN = MDC_LITTLE_ENDIAN; } /* meta group must be explicit VR little */ if (e->group == 0x0002) { MDC_FILE_ENDIAN = MDC_LITTLE_ENDIAN; DO_IMPLICIT = MDC_NO; } /* fix endian of tag items to write */ MdcSWAP(group); MdcSWAP(element); MdcSWAP(length16_w); MdcSWAP(length32_w); /* write group */ fwrite((Uint8 *)&group,1,sizeof(e->group),fp); /* write element */ fwrite((Uint8 *)&element,1,sizeof(e->element),fp); /* write value representation & length */ mdc_dicom_get_vr(e); /* handle special VR values */ ndata = mdc_dicom_handle_vr(e,data); vr_w = e->vr; if (MdcHostBig()) vr_w = (vr_w << 16); else MdcForceSwap((Uint8 *)&vr_w,2); switch (e->vr) { case OB : case OW : case SQ : case UN : case UT : if (DO_IMPLICIT == MDC_YES) { fwrite((Uint8 *)&length32_w,1,4,fp); }else{ if (e->group != 0xfffe) fwrite((Uint8 *)&vr_w,1,4,fp); fwrite((Uint8 *)&length32_w,1,4,fp); } break; case AT : /* 2 bytes endian sensitive data */ case SS : case US : if (DO_IMPLICIT == MDC_YES) { fwrite((Uint8 *)&length32_w,1,4,fp); }else{ fwrite((Uint8 *)&vr_w,1,2,fp); fwrite((Uint8 *)&length16_w,1,2,fp); } e->vm = length >> 1; for (i=0; ivm; i++) MdcSwapBytes(ndata+(i<<1),2); break; case FL : /* 4 bytes endian sensitive data */ case SL : case UL : if (DO_IMPLICIT == MDC_YES) { fwrite((Uint8 *)&length32_w,1,4,fp); }else{ fwrite((Uint8 *)&vr_w,1,2,fp); fwrite((Uint8 *)&length16_w,1,2,fp); } e->vm = length >> 2; for (i=0; ivm; i++) MdcSwapBytes(ndata+(i<<2),4); break; case FD : /* 8 bytes endian sensitive data */ if (DO_IMPLICIT == MDC_YES) { fwrite((Uint8 *)&length32_w,1,4,fp); }else{ fwrite((Uint8 *)&vr_w,1,2,fp); fwrite((Uint8 *)&length16_w,1,2,fp); } e->vm = length >> 3; for (i=0; ivm; i++) MdcSwapBytes(ndata+(i<<3),8); break; default : if (DO_IMPLICIT == MDC_YES) { fwrite((Uint8 *)&length32_w,1,4,fp); }else{ fwrite((Uint8 *)&vr_w,1,2,fp); fwrite((Uint8 *)&length16_w,1,2,fp); } } /* write value data */ if ((ndata != NULL) && (length != 0) && (length != UNDEFINED_LENGTH)) { fwrite((Uint8 *)ndata,1,length,fp); if (MAKE_EVEN) fputc('\0',fp); } /* restore original output file endian */ MDC_FILE_ENDIAN = file_endian_saved; if (ferror(fp)) return(MDC_NO); return(MDC_YES); }