home *** CD-ROM | disk | FTP | other *** search
Wrap
unit DICOM; // Limitations //- compiling for Pascal other than Delphi 2.0+: gDynStr gets VERY big //- does not extract encapsulated/compressed images //- write_dicom: currently only writes little endian, data should be little_endian //- chris.rorden@nottingham.ac.uk //- rev 7 has disk caching: speeds DCOM header reading //- rev 8 can read interfile format images //- rev 9 Siemens Magnetom, GELX //- rev 10 EC*T6/7, DICOM runlengthencoding[RLE] parameters // *NOTE: RLE compressed DICOM images no longer generate ImgFormatOK errors: // if your software can not read RLE images, then refuse to open images which // return DICOMdata.CompressOffset > 0 interface uses SysUtils,Dialogs,Controls; {$H+} //use long, dynamic strings type ByteRA = array [1..1] of byte; Bytep = ^ByteRA; WordRA = array [1..1] of Word; Wordp = ^WordRA; LongRA = array [1..1] of LongInt; Longp = ^LongRA; {SingleRA = array [1..1] of Single; Singlep = ^SingleRA;} DICOMdata = record XYZdim: array [1..4] of integer; //4=volume, eg time: some EC*T7 images XYZori: array [1..3] of integer; XYZmm: array [1..3] of double; Float,RunLengthEncoding,GenesisCpt,JPEGlosslessCpt,JPEGlossyCpt,ElscintCompress: boolean; IntenScale: single; CompressSz,CompressOffset,AcquNum,ImageNum,Monochrome,SamplesPerPixel,PlanarConfig,ImageStart,little_endian, Allocbits_per_pixel,Storedbits_per_pixel,ImageSz, WindowWidth,WindowCenter,GenesisPackHdr, NamePos,StudyDatePos: integer; end; int32 = LongInt; uint32 = Cardinal; int16 = SmallInt; uint16 = Word; int8 = ShortInt; uint8 = Byte; const kCR = chr (13);//PC EOLN kA = ord('A'); kB = ord('B'); kC = ord('C'); kD = ord('D'); kE = ord('E'); kF = ord('F'); kH = ord('H'); kI = ord('I'); kL = ord('L'); kM = ord('M'); kN = ord('N'); kO = ord('O'); kP = ord('P'); kQ = ord('Q'); kS = ord('S'); kT = ord('T'); kU = ord('U'); kW = ord('W'); procedure read_ecat_data(var lDICOMdata: DICOMdata;lVerboseRead,lReadECAToffsetTables:boolean; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;lFileName: string); {- if lReadECAToffsetTables is true, you will need to freemem gECAT_slice_table if it is filled: see example} {-for analysis, you should also take scaling and calibration factors into account!} procedure read_siemens_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;lFileName: string); procedure write_slc (lFileName: string; var pDICOMdata: DICOMdata;var lSz: integer; lDICOM3: boolean); procedure read_ge_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;lFileName: string); procedure read_interfile_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;var lFileName: string); procedure read_PAR_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;var lFileName: string); procedure read_picker_data(lVerboseRead: boolean; var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;lFileName: string); procedure write_dicom (lFileName: string; var pDICOMdata: DICOMdata;var lSz: integer; lDICOM3: boolean); procedure read_dicom_data(lReadJPEGtables,lVerboseRead,lAutoDECAT7,lReadECAToffsetTables,lAutodetectInterfile,lAutoDetectGenesis,lReadColorTables: boolean; var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;var lFileName: string); procedure clear_dicom_data (var lDicomdata:Dicomdata); {- if lReadECAToffsetTables is true, you will need to freemem gECAT_slice_table if it is filled: see example} {- if lReadColorTables is true, you will need to freemem red_table/green_table/blue_table if it is filled: see example} procedure write_interfile_hdr (lHdrName,lImgName: string; var pDICOMdata: DICOMdata); var gSizeMMWarningShown : boolean = false; gECATJPEG_table_entries: integer = 0; gECATJPEG_pos_table,gECATJPEG_size_table : LongP; red_table_size : Integer = 0; green_table_size : Integer = 0; blue_table_size : Integer = 0; red_table : ByteP; green_table : ByteP; blue_table : ByteP; implementation procedure write_interfile_hdr (lHdrName,lImgName: string; var pDICOMdata: DICOMdata); var lTextFile: textfile; //creates interfile text header "lHdrName" that points to the image "lImgName") //pass pDICOMdata that contains the relevant image details begin if (pDICOMdata.Allocbits_per_pixel <> 8) and (pDICOMdata.Allocbits_per_pixel <> 16) then begin showmessage('Can only create Interfile headers for 8 or 16 bit images.'); end; if fileexists(lHdrName) then begin showmessage('The file '+lHdrName+' already exists. Unable to create Interfile format header.'); exit; end; assignfile(lTextFile,lHdrName); rewrite(lTextFile); writeln(lTextFile,'!INTERFILE :='); writeln(lTextFile,'!imaging modality:=nucmed'); writeln(lTextFile,'!originating system:=MS-DOS'); writeln(lTextFile,'!version of keys:=3.3'); writeln(lTextFile,'conversion program:=DICOMxv'); writeln(lTextFile,'program author:=C. Rorden'); writeln(lTextFile,'!GENERAL DATA:='); writeln(lTextFile,'!data offset in bytes:='+inttostr(pDicomData.imagestart)); writeln(lTextFile,'!name of data file:='+extractfilename(lImgName)); writeln(lTextFile,'data compression:=none'); writeln(lTextFile,'data encode:=none'); writeln(lTextFile,'!GENERAL IMAGE DATA :='); if pDICOMdata.little_endian = 1 then writeln(lTextFile,'imagedata byte order := LITTLEENDIAN') else writeln(lTextFile,'imagedata byte order := BIGENDIAN'); writeln(lTextFile,'!matrix size [1] :='+inttostr(pDICOMdata.XYZdim[1])); writeln(lTextFile,'!matrix size [2] :='+inttostr(pDICOMdata.XYZdim[2])); writeln(lTextFile,'!matrix size [3] :='+inttostr(pDICOMdata.XYZdim[3])); if pDICOMdata.Allocbits_per_pixel = 8 then begin writeln(lTextFile,'!number format := unsigned integer'); writeln(lTextFile,'!number of bytes per pixel := 1'); end else begin writeln(lTextFile,'!number format := signed integer'); writeln(lTextFile,'!number of bytes per pixel := 2'); end; writeln(lTextFile,'scaling factor (mm/pixel) [1] :='+floattostrf(pDicomData.XYZmm[1],ffFixed,7,7)); writeln(lTextFile,'scaling factor (mm/pixel) [2] :='+floattostrf(pDicomData.XYZmm[2],ffFixed,7,7)); writeln(lTextFile,'scaling factor (mm/pixel) [3] :='+floattostrf(pDicomData.XYZmm[3],ffFixed,7,7)); writeln(lTextFile,'!number of slices :='+inttostr(pDICOMdata.XYZdim[3])); writeln(lTextFile,'slice thickness := '+floattostrf(pDicomData.XYZmm[3],ffFixed,7,7)); writeln(lTextFile,'!END OF INTERFILE:='); closefile(lTextFile); end; (**) procedure clear_dicom_data (var lDicomdata:Dicomdata); begin red_table_size := 0; green_table_size := 0; blue_table_size := 0; red_table := nil; green_table := nil; blue_table := nil; with lDicomData do begin ElscintCompress := false; Float := false; ImageNum := 0; IntenScale := 1; AcquNum := 0; PlanarConfig:= 1; //only used in RGB values runlengthencoding := false; CompressSz := 0; CompressOffset := 0; SamplesPerPixel := 1; WindowCenter := 0; WindowWidth := 0; monochrome := 2; {most common} XYZmm[1] := 1; XYZmm[2] := 1; XYZmm[3] := 1; XYZdim[1] := 1; XYZdim[2] := 1; XYZdim[3] := 1; XYZdim[4] := 1; ImageStart := 0; Little_Endian := 0; Allocbits_per_pixel := 16;//bits Storedbits_per_pixel:= Allocbits_per_pixel; GenesisCpt := false; JPEGlosslesscpt := false; JPEGlossycpt := false; GenesisPackHdr := 0; StudyDatePos := 0; NamePos := 0; end; end; procedure read_ecat_data(var lDICOMdata: DICOMdata;lVerboseRead,lReadECAToffsetTables:boolean; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;lFileName: string); label 121,539; const kMaxnSLices = 6000; kStrSz = 40; var lLongRA: Longp; lECAT7sigUpcase,lECAT7sig : array [0..6] of Char; lParse,lSPos,lFPos{,lScomplement},lF,lS,lYear,lFrames,lVox,lHlfVox,lJ,lPass,lVolume,lNextDirectory,lSlice,lSliceSz,lVoxelType,lPos,lEntry, lSlicePos,lLongRApos,lLongRAsz,{lSingleRApos,lSingleRAsz,}{lMatri,}lX,lY,lZ,lCacheSz,lImgSz,lTransferred,lSubHeadStart,lMatrixStart,lMatrixEnd,lInt,lInt2,lInt3,lINt4,n,filesz: LongInt; lPlanes,lGates,lAqcType,lFileType,lI,lWord, lWord22: word; lXmm,lYmm,lZmm,lCalibrationFactor, lQuantScale: real; FP: file; lCreateTable,lSwapBytes,lMR,lECAT6: boolean; function xWord(lPos: longint): word; var s: word; begin seek(fp,lPos); BlockRead(fp, s, 2, n); if lSwapBytes then result := swap(s) else result := s; //assign address of s to inguy end; function swap32i(lPos: longint): Longint; type swaptype = packed record case byte of 0:(Word1,Word2 : word); //word is 16 bit 1:(Long:LongInt); end; swaptypep = ^swaptype; var s : LongInt; inguy:swaptypep; outguy:swaptype; begin seek(fp,lPos); BlockRead(fp, s, 4, n); inguy := @s; //assign address of s to inguy if not lSwapBytes then begin result := inguy.long; exit; end; outguy.Word1 := swap(inguy^.Word2); outguy.Word2 := swap(inguy^.Word1); swap32i:=outguy.Long; end; function StrRead (lPos, lSz: longint) : string; var I: integer; tx : array [1..kStrSz] of Char; begin result := ''; if lSz > kStrSz then exit; seek(fp, lPos{-1}); BlockRead(fp, tx, lSz*SizeOf(Char), n); for I := 1 to (lSz-1) do begin if tx[I] in [' ','[',']','+','-','.','\','~','/', '0'..'9','a'..'z','A'..'Z'] then {if (tx[I] <> kCR) and (tx[I] <> UNIXeoln) then} result := result + tx[I]; end; end; function fswap4r (lPos: longint): single; type swaptype = packed record case byte of 0:(Word1,Word2 : word); //word is 16 bit 1:(float:single); end; swaptypep = ^swaptype; var s:single; inguy:swaptypep; outguy:swaptype; begin seek(fp,lPos); if not lSwapBytes then begin BlockRead(fp, result, 4, n); exit; end; BlockRead(fp, s, 4, n); inguy := @s; //assign address of s to inguy outguy.Word1 := swap(inguy^.Word2); outguy.Word2 := swap(inguy^.Word1); fswap4r:=outguy.float; end; function fvax4r (lPos: longint): single; type swaptype = packed record case byte of 0:(Word1,Word2 : word); //word is 16 bit 1:(float:single); end; swaptypep = ^swaptype; var s:single; lT1,lT2 : word; inguy:swaptypep; begin seek(fp,lPos); BlockRead(fp, s, 4, n); inguy := @s; if (inguy.Word1 =0) and (inguy.Word2 = 0) then begin result := 0; exit; end; lT1 := inguy.Word1 and $80FF; lT2 := ((inguy.Word1 and $7F00) +$FF00) and $7F00; inguy.Word1 := inguy.Word2; inguy.Word2 := (lt1+lT2); fvax4r:=inguy.float; end; begin Clear_Dicom_Data(lDicomData); if gECATJPEG_table_entries <> 0 then begin freemem (gECATJPEG_pos_table); freemem (gECATJPEG_size_table); gECATJPEG_table_entries := 0; end; lHdrOK:= false; lQuantScale:= 1; lCalibrationFactor := 1; lLongRASz := 0; lLongRAPos := 0; lImageFormatOK := false; lVolume := 1; if not fileexists(lFileName) then begin showmessage('Unable to find the image '+lFileName); exit; end; FileMode := 0; //set to readonly AssignFile(fp, lFileName); Reset(fp, 1); FIleSz := FileSize(fp); if filesz < (2048) then begin showmessage('This file is to small to be a ECAT format image.'); goto 539; end; seek(fp, 0); BlockRead(fp, lECAT7Sig, 6*SizeOf(Char){, n}); for lInt4 := 0 to (5) do begin if lECAT7Sig[lInt4] in ['a'..'z','A'..'Z'] then lECAT7SigUpCase[lInt4] := upcase(lECAT7Sig[lInt4]) else lECAT7SigUpCase[lInt4] := ' '; end; if (lECAT7SigUpCase[0]='M') and (lECAT7SigUpCase[1]='A') and (lECAT7SigUpCase[2]='T') and (lECAT7SigUpCase[3]='R') and (lECAT7SigUpCase[4]='I') and (lECAT7SigUpCase[5]='X') then lECAT6 := false else lECAT6 := true; if lEcat6 then begin lSwapBytes := false; lFileType := xWord(27*2); if lFileType > 255 then lSwapBytes := not lSwapBytes; lFileType := xWord(27*2); lAqcType := xWord(175*2); lPlanes := xWord(188*2); lFrames := xword(189*2); lGates := xWord(190*2); lYear := xWord(70); if (lPlanes < 1) or (lFrames < 1) or (lGates < 1) then begin case MessageDlg('Warning: one of the planes/frames/gates values is less than 1 ['+inttostr(lPlanes)+'/'+inttostr(lFrames)+'/'+inttostr(lGates)+']. Is this file really ECAT 6 format? Press abort to cancel conversion. ', mterror,[mbOK,mbAbort], 0) of mrAbort: goto 539; end; //case end else if (lYear < 1940) or (lYear > 3000) then begin case MessageDlg('Warning: the year value appears invlaid ['+inttostr(lYear)+']. Is this file really ECAT 6 format? Press abort to cancel conversion. ', mterror,[mbOK,mbAbort], 0) of mrAbort: goto 539; end; //case end; if lVerboseRead then begin lDynStr :='ECAT6 data'; lDynStr :=lDynStr+kCR+('Patient Name:'+StrRead(190,32)); lDynStr :=lDynStr+kCR+('Patient ID:'+StrRead(174,16)); lDynStr :=lDynStr+kCR+('Study Desc:'+StrRead(318,32)); lDynStr := lDynStr+kCR+('Facility: '+StrRead(356,20)); lDynStr := lDynStr+kCR+('Planes: '+inttostr(lPlanes)); lDynStr := lDynStr+kCR+('Frames: '+inttostr(lFrames)); lDynStr := lDynStr+kCR+('Gates: '+inttostr(lGates)); lDynStr := lDynStr+kCR+('Date DD/MM/YY: '+ inttostr(xWord(66))+'/'+inttostr(xWord(68))+'/'+inttostr(lYear)); end; {show summary} end else begin //NOT ECAT6 lSwapBytes := true; lFileType := xWord(50); if lFileType > 255 then lSwapBytes := not lSwapBytes; lFileType := xWord(50); lAqcType := xWord(328); lPlanes := xWord(352); lFrames := xWord(354); lGates := xWord(356); lCalibrationFactor := fswap4r(144); if {(true) or} (lPlanes < 1) or (lFrames < 1) or (lGates < 1) then begin case MessageDlg('Warning: on of the planes/frames/gates values is less than 1 ['+inttostr(lPlanes)+'/'+inttostr(lFrames)+'/'+inttostr(lGates)+']. Is this file really ECAT 7 format? Press abort to cancel conversion. ', mterror,[mbOK,mbAbort], 0) of mrAbort: goto 539; end; //case end; //error if lVerboseRead then begin lDynStr := 'ECAT 7 format'; lDynStr := lDynStr+kCR+('Serial Number:'+StrRead(52,10)); lDynStr := lDynStr+kCR+('Patient Name:'+StrRead(182,32)); lDynStr := lDynStr+kCR+('Patient ID:'+StrRead(166,16)); lDynStr := lDynStr+kCR+('Study Desc:'+StrRead(296,32)); lDynStr := lDynStr+kCR+('Facility: '+StrRead(332,20)); lDynStr := lDynStr+kCR+('Scanner: '+inttostr(xWord(48))); lDynStr := lDynStr+kCR+('Planes: '+inttostr(lPlanes)); lDynStr := lDynStr+kCR+('Frames: '+inttostr(lFrames)); lDynStr := lDynStr+kCR+('Gates: '+inttostr(lGates)); lDynStr := lDynStr+kCR+'Calibration: '+floattostr(lCalibrationFactor); end; {lShow Summary} end; //lECAT7 if not (lFileType in [1,2,3,4,7]) then begin Showmessage('This software does not recognize the ECAT file type. Selected filetype: '+inttostr(lFileType)); goto 539; end; lVoxelType := 2; if lFileType = 3 then lVoxelType := 4; if lVerboseRead then begin case lFileType of 1: lDynStr := lDynStr+kCR+('File type: Scan File'); 2: lDynStr := lDynStr+kCR+('File type: Image File'); //x 3: lDynStr := lDynStr+kCR+('File type: Attn File'); 4: lDynStr := lDynStr+kCR+('File type: Norm File'); 7: lDynStr := lDynStr+kCR+('File type: Volume 16'); //x end; //lfiletye case case lAqcType of 1:lDynStr := lDynStr+kCR+('Acquisition type: Blank'); 2:lDynStr := lDynStr+kCR+('Acquisition type: Transmission'); 3:lDynStr := lDynStr+kCR+('Acquisition type: Static Emission'); 4:lDynStr := lDynStr+kCR+('Acquisition type: Dynamic Emission'); 5:lDynStr := lDynStr+kCR+('Acquisition type: Gated Emission'); 6:lDynStr := lDynStr+kCR+('Acquisition type: Transmission Rect'); 7:lDynStr := lDynStr+kCR+('Acquisition type: Emission Rect'); 8:lDynStr := lDynStr+kCR+('Acquisition type: Whole Body Transm'); 9:lDynStr := lDynStr+kCR+('Acquisition type: Whole Body Static'); else lDynStr := lDynStr+kCR+('Acquisition type: Undefined'); end; //case AqcType end; //verbose read if ((lECAT6) and (lFiletype =2)) or ((not lECAT6) and (lFileType=7)) then else begin Showmessage('Unusual ECAT filetype. Please contact the author.'); goto 539; end; lHdrOK:= true; lImageFormatOK := true; lLongRASz := kMaxnSlices * sizeof(longint); getmem(lLongRA,lLongRAsz); lPos := 512; //lSingleRASz := kMaxnSlices * sizeof(single); //getmem(lSingleRA,lSingleRAsz); //lMatri := 0; lVolume := 1; lPass := 0; 121: lEntry := 1; lInt := swap32i(lPos); lInt2 := swap32i(lPos+4); lNextDirectory := lInt2; while true do begin inc(lEntry); lPos := lPos + 16; lInt := swap32i(lPos); lInt2 := swap32i(lPos+4); lInt3 := swap32i(lPos+8); lInt4 := swap32i(lPos+12); lInt2 := lInt2 - 1; lSubHeadStart := lINt2 *512; lMatrixStart := ((lInt2) * 512)+512 {add subhead sz}; lMatrixEnd := lInt3 * 512; if (lInt4 = 1) and (lMatrixStart < FileSz) and (lMatrixEnd <= FileSz) then begin if (lFileType= 7) {or (lFileType = 4) } or (lFileType = 2) then begin //Volume of 16-bit integers if lEcat6 then begin lX := xWord(lSubHeadStart+(66*2)); lY := xWord(lSubHeadStart+(67*2)); lZ := 1;//uxWord(lSubHeadStart+8); lXmm := 10*fvax4r(lSubHeadStart+(92*2));// fswap4r(lSubHeadStart+(92*2)); lYmm := lXmm;//read32r(lSubHeadStart+(94*2)); lZmm := 10 * fvax4r(lSubHeadStart+(94*2)); lCalibrationFactor := fvax4r(lSubHeadStart+(194*2)); lQuantScale := fvax4r(lSubHeadStart+(86*2)); if lVerboseRead then lDynStr := lDynStr+kCR+'Plane '+inttostr(lPass+1)+' Calibration/Scale Factor: '+floattostr(lCalibrationFactor)+'/'+floattostr(lQuantScale); end else begin //02 or 07 lX := xWord(lSubHeadStart+4); lY := xWord(lSubHeadStart+6); lZ := xWord(lSubHeadStart+8); //if lFileType <> 4 then begin lXmm := 10*fswap4r(lSubHeadStart+34); lYmm := 10*fswap4r(lSubHeadStart+38); lZmm := 10*fswap4r(lSubHeadStart+42); lQuantScale := fswap4r(lSubHeadStart+26); if lVerboseRead then lDynStr := lDynStr+kCR+'Volume: '+inttostr(lPass+1)+' Scale Factor: '+floattostr(lQuantScale); //end; //filetype <> 4 end; //ecat7 if true then begin //FileMode := 2; //set to read/write inc(lPass); lImgSz := lX * lY * lZ * lVoxelType; {2 bytes per voxel} lSliceSz := lX * lY * lVoxelType; if lZ < 1 then begin lHdrOK := false; goto 539; end; lSlicePos := lMatrixStart; if ((lECAT6) and (lPass = 1)) or ( (not lECAT6)) then begin lDICOMdata.XYZdim[1] := lX; lDICOMdata.XYZdim[2] := lY; lDICOMdata.XYZdim[3] := lZ; lDICOMdata.XYZmm[1] := lXmm; lDICOMdata.XYZmm[2] := lYmm; lDICOMdata.XYZmm[3] := lZmm; case lVoxelType of 1: begin Showmessage('Error: 8-bit data not supported [yet]. Please contact the author.'); lDicomData.Allocbits_per_pixel := 8; lHdrOK := false; goto 539; end; 4: begin Showmessage('Error: 32-bit data not supported [yet]. Please contact the author.'); lHdrOK := false; goto 539; end; else begin //16-bit integers lDicomData.Allocbits_per_pixel := 16; end; end; {case lVoxelType} end else begin //if lECAT6 if (lDICOMdata.XYZdim[1] <> lX) or (lDICOMdata.XYZdim[2] <> lY) or (lDICOMdata.XYZdim[3] <> lZ) then begin Showmessage('Error: different slices in this volume have different slice sizes. Please contact the author.'); lHdrOK := false; goto 539; end; //dimensions have changed //lSlicePos :=((lMatri-1)*lImgSz); end; //ECAT6 lVox := lSliceSz div 2; lHlfVox := lSliceSz div 4; for lSlice := 1 to lZ do begin if (not lECAT6) then lSlicePos := ((lSlice-1)*lSliceSz)+lMatrixStart; if lLongRAPos >= kMaxnSLices then begin lHdrOK := false; goto 539; end; inc(lLongRAPos); lLongRA[lLongRAPos] := lSlicePos; {inc(lSingleRAPos); if lCalibTableType = 1 then lSingleRA[lSingleRAPos] := lQuantScale else lSingleRA[lSingleRAPos] := lCalibrationFactor *lQuantScale;} end; //slice 1..lZ if not lECAT6 then inc(lVolume); end; //fileexistsex end; //correct filetype end; //matrix start/end within filesz if (lMatrixStart > FileSz) or (lMatrixEnd >= FileSz) then goto 539; if ((lEntry mod 32) = 0) then begin if ((lNextDirectory-1)*512) <= lPos then goto 539; //no more directories lPos := (lNextDirectory-1)*512; goto 121; end; //entry 32 end ; //while true 539: CloseFile(fp); FileMode := 2; //set to read/write lDicomData.XYZdim[3] := lLongRApos; lDicomData.XYZdim[4] :=(lVolume); if lSwapBytes then lDicomData.little_endian := 0 else lDicomData.little_endian := 1; if (lLongRApos > 0) and (lHdrOK) then begin lDicomData.ImageStart := lLongRA[1]; lCreateTable := false; if (lLongRApos > 1) then begin lFPos := lDICOMdata.ImageStart; for lS := 2 to lLongRApos do begin lFPos := lFPos + lSliceSz; if lFPos <> lLongRA[lS] then lCreateTable := true; end; if (lCreateTable) and (lReadECAToffsetTables) then begin gECATJPEG_table_entries := lLongRApos; getmem (gECATJPEG_pos_table, gECATJPEG_table_entries*sizeof(longint)); getmem (gECATJPEG_size_table, gECATJPEG_table_entries*sizeof(longint)); for lS := 1 to gECATJPEG_table_entries do gECATJPEG_pos_table[lS] := lLongRA[lS] end else if (lCreateTable) then lImageFormatOK := false; //slices are offset within this file end; if (lVerboseRead) and (lHdrOK) then begin lDynStr :=lDynStr+kCR+('XYZdim:'+inttostr(lX)+'/'+inttostr(lY)+'/'+inttostr(gECATJPEG_table_entries)); lDynStr :=lDynStr+kCR+('XYZmm: '+floattostrf(lDicomData.XYZmm[1],ffFixed,7,7)+'/'+floattostrf(lDicomData.XYZmm[2],ffFixed,7,7) +'/'+floattostrf(lDicomData.XYZmm[3],ffFixed,7,7)); lDynStr :=lDynStr+kCR+('Bits per voxel: '+inttostr(lDicomData.Storedbits_per_pixel)); lDynStr :=lDynStr+kCR+('Image Start: '+inttostr(lDicomData.ImageStart)); if lCreateTable then lDynStr :=lDynStr+kCR+('Note: staggered slice offsets'); end end; lDicomData.Storedbits_per_pixel:= lDicomData.Allocbits_per_pixel; if lLongRASz > 0 then freemem(lLongRA); (*if (lSingleRApos > 0) and (lHdrOK) and (lCalibTableType <> 0) then begin gECAT_scalefactor_entries := lSingleRApos; getmem (gECAT_scalefactor_table, gECAT_scalefactor_entries*sizeof(single)); for lS := 1 to gECAT_scalefactor_entries do gECAT_scalefactor_table[lS] := lSingleRA[lS]; end; if lSingleRASz > 0 then freemem(lSingleRA);*) end; procedure write_slc (lFileName: string; var pDICOMdata: DICOMdata;var lSz: integer; lDICOM3: boolean); const kMaxRA = 41; lXra: array [1..kMaxRA] of byte = (7,8,9,21,22,26,27, 35,36,44,45, 50,62,66,78, 81,95, 97,103,104,105,106,111, 113,123,127, 129,139,142, 146,147,148,149,155,156,157, 166,167,168,169,170); var fp: file; lX,lClr,lPos,lRApos: integer; lP: bytep; procedure WriteString(lStr: string; lCR: boolean); var n,lStrLen : Integer; begin lStrLen := length(lStr); for n := 1 to lstrlen do begin lPos := lPos + 1; lP[lPos] := ord(lStr[n]); end; if lCR then begin lPos := lPos + 1; lP[lPos] := ord(kCR); end; end; begin lSz := 0; getmem(lP,2048); lPos := 0; WriteString('11111',true); WriteString(inttostr(pDicomData.XYZdim[1])+' '+inttostr(pDicomData.XYZdim[2])+' '+inttostr(pDicomData.XYZdim[3])+' 8',true); WriteString(floattostrf(pDicomData.XYZmm[1],ffFixed,7,7)+' '+floattostrf(pDicomData.XYZmm[2],ffFixed,7,7)+' '+floattostrf(pDicomData.XYZmm[3],ffFixed,7,7),true); WriteString('1 1 0 0',true); //mmunits,MR,original,nocompress WriteString('16 12 X',false); //icon is 8x8 grid, so 64 bytes for red,green blue for lClr := 1 to 3 do begin lRApos := 1; for lX := 1 to 192 do begin inc(lPos); if (lRApos <= kMaxRA) and (lX = lXra[lRApos]) then begin inc(lRApos); lP[lPos] := 200; end else lP[lPos] := 0; end; {icongrid 1..192} end; {RGB} if lFileName <> '' then begin AssignFile(fp, lFileName); Rewrite(fp, 1); blockwrite(fp,lP^,lPos); close(fp); end; freemem(lP); lSz := lPos; end; procedure read_interfile_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;var lFileName: string); label 333; const UNIXeoln = chr(10); var lTmpStr,lInStr,lUpCaseStr: string; lHdrEnd,lFloat,lUnsigned: boolean; lPos,lLen,FileSz,linPos: integer; fp: file; lCharRA: bytep; function readInterFloat:real; var lStr: string; begin lStr := ''; While (lPos <= lLen) and (lInStr[lPos] <> ';') do begin if lInStr[lPos] in ['+','-','e','E','.','0'..'9'] then lStr := lStr+(linStr[lPos]); inc(lPos); end; try result := strtofloat(lStr); except on EConvertError do begin showmessage('Unable to convert the string '+lStr+' to a number'); result := 1; exit; end; end; {except} end; function readInterStr:string; var lStr: string; begin lStr := ''; While (lPos <= lLen) and (lInStr[lPos] = ' ') do begin inc(lPos); end; While (lPos <= lLen) and (lInStr[lPos] <> ';') do begin lStr := lStr+upcase(linStr[lPos]); //zebra upcase inc(lPos); end; result := lStr; end; //interstr func begin lHdrOK := false; lFloat := false; lUnsigned := false; lImageFormatOK := true; Clear_Dicom_Data(lDicomData); lDynStr := ''; FileMode := 0; //set to readonly AssignFile(fp, lFileName); Reset(fp, 1); FileSz := FileSize(fp); lHdrEnd := false; //lDicomData.ImageStart := FileSz; GetMem( lCharRA, FileSz+1 ); BlockRead(fp, lCharRA^, FileSz, linpos); if lInPos <> FileSz then showmessage('Disk error: Unable to read full input file.'); linPos := 1; CloseFile(fp); FileMode := 2; //set to read/write repeat linstr := ''; while (linPos < FileSz) and (lCharRA[linPos] <> ord(kCR)) and (lCharRA[linPos] <> ord(UNIXeoln)) do begin lInStr := lInstr + chr(lCharRA[linPos]); inc(linPos); end; inc(lInPos); //read EOLN lLen := length(lInStr); lPos := 1; lUpcaseStr := ''; While (lPos <= lLen) and (lInStr[lPos] <> ';') and (lInStr[lPos] <> '=') and (lUpCaseStr <>'INTERFILE') do begin if lInStr[lPos] in ['[',']','(',')','/','+','-',{' ',} '0'..'9','a'..'z','A'..'Z'] then lUpCaseStr := lUpCaseStr+upcase(linStr[lPos]); inc(lPos); end; inc(lPos); {read equal sign in := statement} if lUpCaseStr ='INTERFILE' then begin lHdrOK := true; lDicomData.little_endian := 0; end; if lUpCaseStr ='DATASTARTINGBLOCK'then lDicomData.ImageStart := 2048 * round(readInterFloat); if lUpCaseStr ='DATAOFFSETINBYTES'then lDicomData.ImageStart := round(readInterFloat); if (lUpCaseStr ='MATRIXSIZE[1]') or (lUpCaseStr ='MATRIXSIZE[X]') then lDicomData.XYZdim[1] := round(readInterFloat); if (lUpCaseStr ='MATRIXSIZE[2]')or (lUpCaseStr ='MATRIXSIZE[Y]')then lDicomData.XYZdim[2] := round(readInterFloat); if (lUpCaseStr ='MATRIXSIZE[3]')or (lUpCaseStr ='MATRIXSIZE[Z]') or (lUpCaseStr ='NUMBEROFSLICES') or (lUpCaseStr ='TOTALNUMBEROFIMAGES') then begin lDicomData.XYZdim[3] := round(readInterFloat); end; if lUpCaseStr ='IMAGEDATABYTEORDER' then begin if readInterStr = 'LITTLEENDIAN' then lDicomData.little_endian := 1; end; if lUpCaseStr ='NUMBERFORMAT' then begin lTmpStr := readInterStr; if (lTmpStr = 'ASCII') or (lTmpStr='BIT') then begin lHdrOK := false; showmessage('This software can not convert '+lTmpStr+' data type.'); goto 333; end; if lTmpStr = 'UNSIGNEDINTEGER' then lUnsigned := true; if (lTmpStr='FLOAT') or (lTmpStr='SHORTFLOAT') or (lTmpStr='LONGFLOAT') then begin lFloat := true; end; end; if lUpCaseStr ='NAMEOFDATAFILE' then lFileName := ExtractFilePath(lFileName)+readInterStr; if lUpCaseStr ='NUMBEROFBYTESPERPIXEL' then lDicomData.Allocbits_per_pixel := round(readInterFloat)*8; if (lUpCaseStr ='SCALINGFACTOR(MM/PIXEL)[1]') or (lUpCaseStr ='SCALINGFACTOR(MM/PIXEL)[X]') then lDicomData.XYZmm[1] := (readInterFloat); if (lUpCaseStr ='SCALINGFACTOR(MM/PIXEL)[2]') or (lUpCaseStr ='SCALINGFACTOR(MM/PIXEL)[Y]')then lDicomData.XYZmm[2] := (readInterFloat); if (lUpCaseStr ='SCALINGFACTOR(MM/PIXEL)[3]')or (lUpCaseStr ='SCALINGFACTOR(MM/PIXEL)[Z]')or (lUpCaseStr ='SLICETHICKNESS')then lDicomData.XYZmm[3] := (readInterFloat); if (lUpCaseStr ='ENDOFINTERFILE') then lHdrEnd := true; if not lHdrOK then goto 333; if lInStr <> '' then lDynStr := lDynStr + lInStr+kCr; lHdrOK := true; until (linPos >= FileSz) or (lHdrEnd){EOF(fp)}; lDicomData.Storedbits_per_pixel := lDicomData.Allocbits_per_pixel; lImageFormatOK := true; if (not lFLoat) and (lUnsigned) and ((lDicomData.Storedbits_per_pixel = 16)) then begin showmessage('Warning: this Interfile image uses UNSIGNED 16-bit data [values 0..65535]. Analyze specifies SIGNED 16-bit data [-32768..32767]. Some images may not transfer well. [Future versions of MRIcro should fix this].'); lImageFormatOK := false; end else if (not lFLoat) and (lDicomData.Storedbits_per_pixel > 16) then begin showmessage('WARNING: The image '+lFileName+' is a '+inttostr(lDicomData.Storedbits_per_pixel)+'-bit integer data type. This software may display this as SIGNED data. Bits per voxel: '+inttostr(lDicomData.Storedbits_per_pixel)); lImageFormatOK := false; end else if (lFloat) then begin //zebra change float check //showmessage('WARNING: The image '+lFileName+' uses floating point [real] numbers. The current software can only read integer data type Interfile images.'); lDicomData.Float := true; //lImageFormatOK := false; end; 333: FreeMem( lCharRA); end; (*procedure read_PAR_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;var lFileName: string); label 333; const UNIXeoln = chr(10); var lInStr,lUpCaseStr: string; lFirstSlope: boolean; lPos,lLen,FileSz,linPos,lInc{,lIndex,lIndexOld},lScanResX,lScanResY: integer; lSlope,lSlopeOld : single; fp: file; lCharRA: bytep; function readParFloat:single; var lStr: string; begin lStr := ''; While (lPos <= lLen) and ((lStr='') or(lInStr[lPos] <> ' ')) do begin if lInStr[lPos] in ['+','-','e','E','.','0'..'9'] then lStr := lStr+(linStr[lPos]); inc(lPos); end; try result := strtofloat(lStr); except on EConvertError do begin showmessage('Unable to convert the string '+lStr+' to a number'); result := 1; exit; end; end; {except} end; begin lHdrOK := false; lImageFormatOK := true; lFirstSlope := true; lScanResX := 0; lScanResY := 0; lSlopeOld := 0; //lIndexOld := -1; Clear_Dicom_Data(lDicomData); lDynStr := ''; FileMode := 0; //set to readonly AssignFile(fp, lFileName); Reset(fp, 1); FileSz := FileSize(fp); GetMem( lCharRA, FileSz+1 ); BlockRead(fp, lCharRA^, FileSz, linpos); if lInPos <> FileSz then showmessage('Disk error: Unable to read full input file.'); linPos := 1; CloseFile(fp); FileMode := 2; //set to read/write repeat linstr := ''; while (linPos < FileSz) and (lCharRA[linPos] <> ord(kCR)) and (lCharRA[linPos] <> ord(UNIXeoln)) do begin lInStr := lInstr + chr(lCharRA[linPos]); inc(linPos); end; inc(lInPos); //read EOLN lDynStr := lDynStr + lInStr; lDynStr := lDynStr+kCr; lLen := length(lInStr); lPos := 1; lUpcaseStr := ''; if lLen < 1 then else if (lInStr[1] = '#') and (lHdrOK) then //# => comment //ignore comment else if (lInStr[1] <> '.') and (lHdrOK) then begin // SliceIndexData for lInc := 1 to 6 do //1=slicenum,2=echonum,3=dynScanNum,4=CardiacPhase,5=ImageType,6=SequenceType readParFloat; readparfloat; //7=index {lIndex := trunc(readParFloat); if lIndex < lIndexOld then begin Showmessage('Warning: Par file images were not stored in sequential order. Conversion is being aborted.'); goto 333; end; lIndexOld := lIndex; } readParFloat; //8=intercept lSlope := readParFloat; //9=lslope //Showmessage(inttostr(lIndex)+':'+floattostr(lSlope)); if (not lFirstSlope) and (lSlope <> lSlopeOld) then begin Showmessage('Warning: Par file conversion is being aborted: scaling slope varies between slices.'); goto 333; end; lFirstSlope := false; lSlopeOld := lSlope; lDicomData.IntenScale := lSlope; end else begin // Signature not yet found or '.' starts all data except slice index While (lPos <= lLen) and (lInStr[lPos] <> ':') and ((not lHdrOK) or (lInStr[lPos] <> '#')) do begin if lInStr[lPos] in ['[',']','(',')','/','+','-',{' ',} '0'..'9','a'..'z','A'..'Z'] then lUpCaseStr := lUpCaseStr+upcase(linStr[lPos]); inc(lPos); end; inc(lPos); {read equal sign in := statement} if lUpCaseStr = ('DATADESCRIPTIONFILE') then begin //PAR file lHdrOK := true; lDicomData.little_endian := 1; lFileName := changefileext(lFilename,'.rec'); end; if (lUpCaseStr ='MAXNUMBEROFSLICES/LOCATIONS') then lDicomData.XYZdim[3] := round(readParFloat); if lUpCaseStr = 'FOV(APFHRL)[MM]' then begin lDicomData.XYZmm[1] := (readParFloat); lDicomData.XYZmm[3] := (readParFloat); lDicomData.XYZmm[2] := (readParFloat); end; if lUpCaseStr = 'SCANRESOLUTION(XY)' then begin lScanResX := round(readParFloat); lScanResY := round(readParFloat); end; if lUpCaseStr = 'RECONRESOLUTION(XY)' then begin lDicomData.XYZdim[1] := round(readParFloat); lDicomData.XYZdim[2] := round(readParFloat); end; if lUpCaseStr ='IMAGEPIXELSIZE[8OR16BITS]' then lDicomData.Allocbits_per_pixel := round(readPARFloat); if not lHdrOK then goto 333; lHdrOK := true; end; until (linPos >= FileSz) {or (lHdrEnd){EOF(fp)}; if (lScanResX > 0) then //Convert FOV to slice thickness lDicomData.XYZmm[1] := lDicomData.XYZmm[1]/lScanResX; if (lScanResY > 0) then //Convert FOV to slice thickness lDicomData.XYZmm[2] := lDicomData.XYZmm[2]/lScanResY; if (lDicomdata.XYZdim[1] > 0) then //Convert FOV to slice thickness lDicomData.XYZmm[3] := lDicomData.XYZmm[3]/lDicomdata.XYZdim[3]; lDicomData.Storedbits_per_pixel := lDicomData.Allocbits_per_pixel; lImageFormatOK := true; //if (lUnsigned) and ((lDicomData.Storedbits_per_pixel = 16)) then //showmessage('Warning: this Interfile image uses UNSIGNED 16-bit data [values 0..65535]. Analyze specifies SIGNED 16-bit data [-32768..32767]. Some images may not transfer well. [Future versions of MRIcro should fix this].'); //if (lDicomData.Storedbits_per_pixel > 16) then //showmessage('WARNING: The image '+lFileName+' is a '+inttostr(lDicomData.Storedbits_per_pixel)+'-bit UNSIGNED integer data type. This software may display this as SIGNED data. Bits per voxel: '+inttostr(lDicomData.Storedbits_per_pixel)); //if (lFloat) then begin // showmessage('WARNING: The image '+lFileName+' uses floating point [real] numbers. The current software can only read integer data type Interfile images.'); //lImageFormatOK := false; //end; 333: FreeMem( lCharRA); end;*) procedure read_PAR_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;var lFileName: string); label 333; const UNIXeoln = chr(10); var lInStr,lUpCaseStr: string; lFirstSlope: boolean; lPos,lLen,FileSz,linPos,lInc{,lIndex,lIndexOld},lScanResX,lScanResY: integer; lSlope,lSlopeOld : single; fp: file; lCharRA: bytep; function readParFloat:single; var lStr: string; begin lStr := ''; While (lPos <= lLen) and ((lStr='') or(lInStr[lPos] <> ' ')) do begin if lInStr[lPos] in ['+','-','e','E','.','0'..'9'] then lStr := lStr+(linStr[lPos]); inc(lPos); end; try result := strtofloat(lStr); except on EConvertError do begin showmessage('Unable to convert the string '+lStr+' to a number'); result := 1; exit; end; end; {except} end; begin lHdrOK := false; lImageFormatOK := true; lFirstSlope := true; lScanResX := 0; lScanResY := 0; lSlopeOld := 0; //lIndexOld := -1; Clear_Dicom_Data(lDicomData); lDynStr := ''; FileMode := 0; //set to readonly AssignFile(fp, lFileName); Reset(fp, 1); FileSz := FileSize(fp); GetMem( lCharRA, FileSz+1 ); BlockRead(fp, lCharRA^, FileSz, linpos); if lInPos <> FileSz then showmessage('Disk error: Unable to read full input file.'); linPos := 1; CloseFile(fp); FileMode := 2; //set to read/write repeat linstr := ''; while (linPos < FileSz) and (lCharRA[linPos] <> ord(kCR)) and (lCharRA[linPos] <> ord(UNIXeoln)) do begin lInStr := lInstr + chr(lCharRA[linPos]); inc(linPos); end; inc(lInPos); //read EOLN {lDynStr := lDynStr + lInStr; lDynStr := lDynStr+kCr;} lLen := length(lInStr); lPos := 1; lUpcaseStr := ''; if lLen < 1 then else if (lInStr[1] = '#') and (lHdrOK) then //# => comment //ignore comment else if (lInStr[1] <> '.') and (lHdrOK) then begin // SliceIndexData for lInc := 1 to 6 do //1=slicenum,2=echonum,3=dynScanNum,4=CardiacPhase,5=ImageType,6=SequenceType readParFloat; readparfloat; //7=index {lIndex := trunc(readParFloat); if lIndex < lIndexOld then begin Showmessage('Warning: Par file images were not stored in sequential order. Conversion is being aborted.'); goto 333; end; lIndexOld := lIndex; } readParFloat; //8=intercept lSlope := readParFloat; //9=lslope //Showmessage(inttostr(lIndex)+':'+floattostr(lSlope)); if (not lFirstSlope) and (lSlope <> lSlopeOld) then begin Showmessage('Warning: PAR file conversion is being aborted: scaling slope varies between slices.'); goto 333; end; lFirstSlope := false; lSlopeOld := lSlope; lDicomData.IntenScale := lSlope; end else begin // Signature not yet found or '.' starts all data except slice index While (lPos <= lLen) and (lInStr[lPos] <> ':') and ((not lHdrOK) or (lInStr[lPos] <> '#')) do begin if lInStr[lPos] in ['[',']','(',')','/','+','-',{' ',} '0'..'9','a'..'z','A'..'Z'] then lUpCaseStr := lUpCaseStr+upcase(linStr[lPos]); inc(lPos); end; inc(lPos); {read equal sign in := statement} if lUpCaseStr = ('DATADESCRIPTIONFILE') then begin //PAR file lHdrOK := true; lDicomData.little_endian := 1; lFileName := changefileext(lFilename,'.rec'); end; if (lUpCaseStr ='MAXNUMBEROFSLICES/LOCATIONS') then lDicomData.XYZdim[3] := round(readParFloat); if lUpCaseStr = 'FOV(APFHRL)[MM]' then begin lDicomData.XYZmm[2] := (readParFloat); //AP anterior->posterior lDicomData.XYZmm[3] := (readParFloat); //FH foot head lDicomData.XYZmm[1] := (readParFloat); //RL Right-Left end; if lUpCaseStr = 'SCANRESOLUTION(XY)' then begin lScanResX := round(readParFloat); lScanResY := round(readParFloat); end; if lUpCaseStr = 'RECONRESOLUTION(XY)' then begin lDicomData.XYZdim[1] := round(readParFloat); lDicomData.XYZdim[2] := round(readParFloat); end; if lUpCaseStr = 'MAXNUMBEROFDYNAMICS' then begin lDicomData.XYZdim[4] := round(readParFloat); end; if lUpCaseStr ='IMAGEPIXELSIZE[8OR16BITS]' then lDicomData.Allocbits_per_pixel := round(readPARFloat); if not lHdrOK then goto 333; lHdrOK := true; end; until (linPos >= FileSz) {or (lHdrEnd){EOF(fp)}; if (lScanResX > 0) then //Convert FOV to slice thickness lDicomData.XYZmm[1] := lDicomData.XYZmm[1]/lScanResX; if (lScanResY > 0) then //Convert FOV to slice thickness lDicomData.XYZmm[2] := lDicomData.XYZmm[2]/lScanResY; if (lDicomdata.XYZdim[1] > 0) then //Convert FOV to slice thickness lDicomData.XYZmm[3] := lDicomData.XYZmm[3]/lDicomdata.XYZdim[3]; lDicomData.Storedbits_per_pixel := lDicomData.Allocbits_per_pixel; lImageFormatOK := true; lDynStr := 'PAR/REC Format' +kCR+ 'XYZ dim: ' +inttostr(lDicomData.XYZdim[1])+'/'+inttostr(lDicomData.XYZdim[2])+'/'+inttostr(lDicomData.XYZdim[3]) +kCR+'Volumes: ' +inttostr(lDicomData.XYZdim[4]) +kCR+'XYZ mm: '+floattostrf(lDicomData.XYZmm[1],ffFixed,8,2)+'/' +floattostrf(lDicomData.XYZmm[2],ffFixed,8,2)+'/'+floattostrf(lDicomData.XYZmm[3],ffFixed,8,2); 333: FreeMem( lCharRA); end; procedure read_ge_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;lFileName: string); label 539; var lI: word; lExamHdr,lImgHdr,lDATFormatOffset,lHdrOffset,lCompress,linitialoffset,n,filesz: LongInt; tx : array [0..26] of Char; FP: file; lGEodd,lGEFlag,{lSpecial,}lMR: boolean; function GEflag: boolean; begin if (tx[0] = 'I') AND (tx[1]= 'M') AND (tx[2] = 'G')AND (tx[3]= 'F') then result := true else result := false; end; function swap16i(lPos: longint): word; var w : Word; begin seek(fp,lPos-2); BlockRead(fp, W, 2); result := swap(W); end; function swap32i(lPos: longint): Longint; type swaptype = packed record case byte of 0:(Word1,Word2 : word); //word is 16 bit 1:(Long:LongInt); end; swaptypep = ^swaptype; var s : LongInt; inguy:swaptypep; outguy:swaptype; begin seek(fp,lPos); BlockRead(fp, s, 4, n); inguy := @s; //assign address of s to inguy outguy.Word1 := swap(inguy^.Word2); outguy.Word2 := swap(inguy^.Word1); swap32i:=outguy.Long; end; function fswap4r (lPos: longint): single; type swaptype = packed record case byte of 0:(Word1,Word2 : word); //word is 16 bit 1:(float:single); end; swaptypep = ^swaptype; var s:single; inguy:swaptypep; outguy:swaptype; begin seek(fp,lPos); BlockRead(fp, s, 4, n); inguy := @s; //assign address of s to inguy outguy.Word1 := swap(inguy^.Word2); outguy.Word2 := swap(inguy^.Word1); fswap4r:=outguy.float; end; begin lImageFormatOK := true; lHdrOK := false; lHdrOffset := 0; if not fileexists(lFileName) then begin lImageFormatOK := false; exit; end; FileMode := 0; //set to readonly AssignFile(fp, lFileName); Reset(fp, 1); FIleSz := FileSize(fp); lDATFormatOffset := 0; Clear_Dicom_Data(lDicomData); if filesz < (3240) then begin showmessage('This file is too small to be a Genesis DAT format image.'); goto 539; end; lDynStr:= ''; //lGEFlag := false; lInitialOffset := 3228;//3240; seek(fp, lInitialOffset); BlockRead(fp, tx, 4*SizeOf(Char), n); lGEflag := GEFlag; if not lGEFlag then begin lInitialOffset := 3240; seek(fp, lInitialOffset); BlockRead(fp, tx, 4*SizeOf(Char), n); lGEflag := GEFlag; end; lGEodd := lGEFlag; if not lGEFlag then begin lInitialOffset := 0; seek(fp, lInitialOffset); BlockRead(fp, tx, 4*SizeOf(Char), n); if not GEflag then begin {DAT format} lDynStr := lDynStr+'GE Genesis Signa DAT tape format'+kCR; seek(fp,114+97); BlockRead(fp, tx, 25*SizeOf(Char), n); lDynStr := lDynStr + 'Patient Name: '; for lI := 0 to 24 do lDynStr := lDynStr + tx[lI]; lDynStr := lDynStr + kCR; seek(fp,114+84); BlockRead(fp, tx, 13*SizeOf(Char), n); lDynStr := lDynStr + 'Patient ID: '; for lI := 0 to 12 do lDynStr := lDynStr + tx[lI]; lDynStr := lDynStr + kCR; seek(fp, 114+305); BlockRead(fp, tx, 3*SizeOf(Char), n); if (tx[0]='M') and (tx[1] = 'R') then lMR := true else if (tx[0] = 'C') and(tx[1] = 'T') then lMR := false else begin Showmessage('Is this a Genesis DAT image? The modality is '+tx[0]+tx[1]+tx[3] +'. Expected ''MR'' or ''CT''.'); goto 539; end; if lMR then lInitialOffset := 3180 else lInitialOffset := 3178; seek(fp, lInitialOffset); BlockRead(fp, tx, 4*SizeOf(Char), n); if (tx[0] <> 'I') OR (tx[1] <> 'M') OR (tx[2] <> 'G') OR (tx[3] <> 'F') then begin showmessage('This image does not have the required label ''IMGF''. This is not a Genesis DAT image.'); goto 539; end else lDicomData.ImageNum := swap16i(2158+12); lDicomData.XYZmm[3] := fswap4r (2158+26);// slice thickness mm lDicomData.XYZmm[1] := fswap4r (2158+50);// pixel size- X lDicomData.XYZmm[2] := fswap4r (2158+54);//pixel size - Y lDATFormatOffset := 4; end; {DAT format} end; lDicomData.ImageStart := lDATFormatOffset+linitialoffset + swap32i(linitialoffset+4);//byte displacement to image data lDicomData.XYZdim[1] := swap32i(linitialoffset+8); //width lDicomData.XYZdim[2] := swap32i(linitialoffset+12);//height lDicomData.Allocbits_per_pixel := swap32i(linitialoffset+16);//bits lDicomData.Storedbits_per_pixel:= lDicomData.Allocbits_per_pixel; lCompress := swap32i(linitialoffset+20); //compression lExamHdr := swap32i(linitialoffset+136); lImgHdr := swap32i(linitialoffset+152); if (lImgHdr = 0) and (lDicomData.ImageStart = 8432) then begin lDicomData.ImageNum := swap16i(2310+12); //showmessage(inttostr(lDicomData.ImageNum)); lDicomData.XYZmm[3] := fswap4r (2310+26);// slice thickness mm lDicomData.XYZmm[1] := fswap4r (2310+50);// pixel size- X lDicomData.XYZmm[2] := fswap4r (2310+54);//pixel size - Y end else if {(lSpecial = false) and} (lDATFormatOffset = 0) then begin lDynStr := lDynStr+'GE Genesis Signa format'+kCR; if (not lGEodd) and (lExamHdr <> 0) then begin lHdrOffset := swap32i(linitialoffset+132);//x132- int ptr to exam heade seek(fp,lHdrOffset+97); BlockRead(fp, tx, 25*SizeOf(Char), n); lDynStr := lDynStr + 'Patient Name: '; for lI := 0 to 24 do lDynStr := lDynStr + tx[lI]; lDynStr := lDynStr + kCR; seek(fp,lHdrOffset+84); BlockRead(fp, tx, 13*SizeOf(Char), n); lDynStr := lDynStr + 'Patient ID: '; for lI := 0 to 12 do lDynStr := lDynStr + tx[lI]; lDynStr := lDynStr + kCR; lHdrOffset := swap32i(linitialoffset+148);//x148- int ptr to image heade end; if lGEodd then lHdrOffset := 2158+28; if ((lHdrOffset +16) < FileSz) and (lImgHdr <> 0) then begin //showmessage(inttostr(lHdrOffset)); lDicomData.ImageNum := swap16i(lHdrOffset+12); lDicomData.XYZmm[3] := fswap4r ({}lHdrOffset{linitialoffset+lHdrOffset}+26);// slice thickness mm lDicomData.XYZmm[1] := fswap4r ({}lHdrOffset{linitialoffset+lHdrOffset}+50);// pixel size- X lDicomData.XYZmm[2] := fswap4r ({}lHdrOffset{linitialoffset+lHdrOffset}+54);//pixel size - Y end; end; if (lCompress = 3) or (lCompress = 4) then begin lDicomData.GenesisCpt := true; lDynStr := lDynStr+'Compressed data'+kCR; end else lDicomData.GenesisCpt := false; if (lCompress = 2) or (lCompress = 4) then begin lDicomData.GenesisPackHdr := swap32i(linitialoffset+64); lDynStr := lDynStr+'Packed data'+kCR; end else lDicomData.GenesisPackHdr := 0; lDynStr := lDynStr+'Image Number: '+inttostr(lDicomData.ImageNum)+kCR +'XYZ dim: ' +inttostr(lDicomData.XYZdim[1])+'/' +inttostr(lDicomData.XYZdim[2])+'/'+inttostr(lDicomData.XYZdim[3]) +kCR+'XYZ mm: '+floattostrf(lDicomData.XYZmm[1],ffFixed,8,2)+'/' +floattostrf(lDicomData.XYZmm[2],ffFixed,8,2)+'/'+floattostrf(lDicomData.XYZmm[3],ffFixed,8,2); lHdrOK := true; 539: CloseFile(fp); FileMode := 2; //set to read/write end;//read_ge procedure write_dicom (lFileName: string; var pDICOMdata: DICOMdata;var lSz: integer; lDICOM3: boolean); var fp: file; lHiBit,lGrpError,lStart,lEnd,lInc,lPos: integer; lP: bytep; // WriteGroupElement(lDICOM3,-1,lPos,$0002,$0010,'U','I','1.2.840.10008.1.2')//implicit xfer syntax procedure WriteGroupElement(lExplicit: boolean; lInt2,lInt4: integer; var lPos: integer;lGrp,lEle: integer;lChar1,lChar2: char;lInStr: string); var lStr: string; lPad: boolean; n,lStrLen : Integer; lT0,lT1: byte; begin lStr := lInStr; lPad := false; lT0 := ord(lChar1); lT1 := ord(lChar2); //if (lGrp = $18) and (lEle = $50) then // lStr := lStr+'0'; if (lInt2 >= 0) then lStrLen := 2 else if (lInt4 >= 0) then lStrLen := 4 else begin lStrLen := length(lStr); if odd(lStrLen) then begin inc(lStrLen); lPad := true; //lStr := lStr + ' '; end; end; lP[lPos+1] := lGrp and $00FF; lP[lPos+2] := (lGrp and $FF00) shr 8; lP[lPos+3] := lEle and $00FF; lP[lPos+4] := (lEle and $FF00) shr 8; lInc := 4; //how many bytes have we added; if (lExplicit) and ( ((lT0=kO) and (lT1=kB)) or ((lT0=kO) and (lT1=kW)) or ((lT0=kS) and (lT1=kQ)) ) then begin lP[lPos+5] := lT0; lP[lPos+6] := lT1; lP[lPos+7] := 0; lP[lPos+8] := 0; lInc := lInc + 4; if lgrp <> $7FE0 then begin lP[lPos+9] := lStrLen and $000000FF; lP[lPos+10] := lStrLen and $0000FF00; lP[lPos+11] := lStrLen and $00FF0000; lP[lPos+12] := lStrLen and $FF000000; lInc := lInc + 4; end; end else if (lExplicit) and ( ((lT0=kA) and (lT1=kE)) or ((lT0=kA) and (lT1=kS)) or ((lT0=kA) and (lT1=kT)) or ((lT0=kC) and (lT1=kS)) or ((lT0=kD) and (lT1=kA)) or ((lT0=kD) and (lT1=kS)) or ((lT0=kD) and (lT1=kT)) or ((lT0=kF) and (lT1=kL)) or ((lT0=kF) and (lT1=kD)) or ((lT0=kI) and (lT1=kS)) or ((lT0=kL) and (lT1=kO))or ((lT0=kL) and (lT1=kT)) or ((lT0=kP) and (lT1=kN)) or ((lT0=kS) and (lT1=kH)) or ((lT0=kS) and (lT1=kL)) or ((lT0=kS) and (lT1=kS)) or ((lT0=kS) and (lT1=kT)) or ((lT0=kT) and (lT1=kM)) or ((lT0=kU) and (lT1=kI)) or ((lT0=kU) and (lT1=kL)) or ((lT0=kU) and (lT1=kS)) or ((lT0=kA) and (lT1=kE)) or ((lT0=kA) and (lT1=kS)) ) then begin lP[lPos+5] := lT0; lP[lPos+6] := lT1; lP[lPos+7] := lStrLen and $000000FF; lP[lPos+8] := lStrLen and $00000FF00; lInc := lInc + 4; //if (lGrp = $18) and (lEle = $50) then // if lPad then showmessage('bPad'+lStr); end else if (not ( ((lT0=kO) and (lT1=kB)) or ((lT0=kO) and (lT1=kW)) or ((lT0=kS) and (lT1=kQ)) )) then begin {Not explicit} lP[lPos+5] := lStrLen and $000000FF; lP[lPos+6] := lStrLen and $0000FF00; lP[lPos+7] := lStrLen and $00FF0000; lP[lPos+8] := lStrLen and $FF000000; lInc := lInc + 4; end; if lstrlen = 0 then exit; lPos := lPos + lInc; if lInt2 >= 0 then begin inc(lPos); lP[lPos] := lInt2 and $00FF; inc(lPos); lP[lPos] := (lInt2 and $FF00) shr 8; exit; end; if lInt4 >= 0 then begin inc(lPos); lP[lPos] := lInt4 and $000000FF; inc(lPos); lP[lPos] := (lInt4 and $0000FF00) shr 8; inc(lPos); lP[lPos] := (lInt4 and $00FF0000) shr 16; inc(lPos); lP[lPos] := (lInt4 and $FF000000) shr 24; exit; end; if lPad then begin //if (lGrp = $18) and (lEle = $50) then //if lPad then showmessage('A Pad'+lStr); for n := 1 to (lstrlen-1) do begin lPos := lPos + 1; lP[lPos] := ord(lStr[n]); end; lPos := lPos + 1; lP[lPos] := 0; end else begin for n := 1 to lstrlen do begin lPos := lPos + 1; lP[lPos] := ord(lStr[n]); end; end; end; begin lSz := 0; getmem(lP,1024); if lDiCOM3 then begin for lInc := 1 to 127 do lP[lInc] := 0; lP[lInc+1] := ord('D'); lP[lInc+2] := ord('I'); lP[lInc+3] := ord('C'); lP[lInc+4] := ord('M'); lPos := 128 + 4; lGrpError := 12; end else begin lPos := 0; lGrpError := 12; end; if lDICOM3 then begin lStart := lPos; WriteGroupElement(lDICOM3,-1,2,lPos,$0002,$0000,'U','L','');//length //xx WriteGroupElement(lDICOM3,256,-1,lPos,$0002,$0001,'O','B','');//meta info WriteGroupElement(lDICOM3,256,-1,lPos,$0002,$0001,'O','B',' ');//256 WriteGroupElement(lDICOM3,-1,-1,lPos,$0002,$0002,'U','I','1.2.840.10008.5.1.4.1.1.4');//implicit xfer syntax WriteGroupElement(lDICOM3,-1,-1,lPos,$0002,$0003,'U','I','999.999.2.19960619.163000.1.103');//implicit xfer syntax if not lDICOM3 then WriteGroupElement(lDICOM3,-1,-1,lPos,$0002,$0010,'U','I','1.2.840.10008.1.2')//implicit xfer syntax else if pDicomData.little_endian = 1 then WriteGroupElement(lDICOM3,-1,-1,lPos,$0002,$0010,'U','I','1.2.840.10008.1.2.1')//little xfer syntax else WriteGroupElement(lDICOM3,-1,-1,lPos,$0002,$0010,'U','I','1.2.840.10008.1.2.2');//furezx should be 2//big xfer syntax WriteGroupElement(lDICOM3,-1,-1,lPos,$0002,$0012,'U','I','999.999');//implicit xfer syntax lEnd := lPos; lPos := lStart; WriteGroupElement(lDICOM3,-1,lEnd-lStart-lGrpError,lPos,$0002,$0000,'U','L','');//length lPos := lEnd; end; lStart := lPos; WriteGroupElement(lDICOM3,-1,18,lPos,$0008,$0000,'U','L','');//length //DICOM part 3: 0008,0008 required for MR WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0008,'C','S','ORIGINAL\PRIMARY');// if not lDICOM3 then WriteGroupElement(lDICOM3,-1,2,lPos,$0008,$0010,'L','O','ACR-NEMA 2.0');//length WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0016,'U','I','1.2.840.10008.5.1.4.1.1.4');// WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0018,'U','I','999.999.2.19960619.163000.1.103'); //a WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0020,'D','A','1995.06.26');//implicit xfer syntax //a WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0023,'D','A','1995.06.26');//implicit xfer syntax //a WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0030,'T','M','11:20:00');//implicit xfer syntax WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0060,'C','S','OT');//modality WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0070,'L','O','MRIcro');//modality //a WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0080,'L','O','Community Hospital');//modality //a WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0081,'S','T','Anytown');//modality WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$0090,'P','N','Anonymized');//name WriteGroupElement(lDICOM3,-1,-1,lPos,$0008,$1030,'L','O','MRI');//modality lEnd := lPos; lPos := lStart; WriteGroupElement(lDICOM3,-1,lEnd-lStart-lGrpError,lPos,$0008,$0000,'U','L','');//length lPos := lEnd; lStart := lPos; WriteGroupElement(lDICOM3,-1,18,lPos,$0010,$0000,'U','L','');//length WriteGroupElement(lDICOM3,-1,-1,lPos,$0010,$0010,'P','N','Anonymized');//name lEnd := lPos; lPos := lStart; WriteGroupElement(lDICOM3,-1,lEnd-lStart-lGrpError,lPos,$0010,$0000,'U','L','');//length lPos := lEnd; lStart := lPos; WriteGroupElement(lDICOM3,-1,18,lPos,$0018,$0000,'U','L','');//length //z DICOM part 3: 0018,0020 required for MR //z DICOM part 3: 0018,0021 required for MR //z DICOM part 3: 0018,0022 required for MR //z DICOM part 3: 0018,0023 required for MR WriteGroupElement(lDICOM3,-1,-1,lPos,$0018,$0050,'D','S',floattostrf(pDicomData.XYZmm[3],ffFixed,8,2));//slice thickness //z DICOM part 3: 0018,0080 required for MR //z WriteGroupElement(lDICOM3,-1,-1,lPos,$0018,$0080,'D','S',floattostrf(1333.33,ffFixed,8,2));// //z DICOM part 3: 0018,0081 required for MR //z WriteGroupElement(lDICOM3,-1,-1,lPos,$0018,$0081,'D','S',floattostrf(11.98,ffFixed,8,2));// //z DICOM part 3: 0018,0091 required for MR WriteGroupElement(lDICOM3,-1,-1,lPos,$0018,$1020,'L','O',inttostr(pDicomData.XYZori[1])+'\'+inttostr(pDicomData.XYZori[2])+'\'+inttostr(pDicomData.XYZori[3]));//software version //a WriteGroupElement(lDICOM3,-1,-1,lPos,$0018,$1149,'I','S','350');//Study UID //b 0018,1314 found in demo MRs: //a WriteGroupElement(lDICOM3,-1,-1,lPos,$0018,$1314,'D','S','50');// lEnd := lPos; lPos := lStart; WriteGroupElement(lDICOM3,-1,lEnd-lStart-lGrpError,lPos,$0018,$0000,'U','L','');//length lPos := lEnd; lStart := lPos; WriteGroupElement(lDICOM3,-1,18,lPos,$0020,$0000,'U','L','');//length WriteGroupElement(lDICOM3,-1,-1,lPos,$0020,$000D,'U','I','999.999.2.19960619.163000');//Study UID WriteGroupElement(lDICOM3,-1,-1,lPos,$0020,$000E,'U','I','999.999.2.19960619.163000.1');//Study UID WriteGroupElement(lDICOM3,-1,-1,lPos,$0020,$0011,'I','S','1');//Study UID WriteGroupElement(lDICOM3,-1,-1,lPos,$0020,$0013,'I','S','103');//Study UID // WriteGroupElement(lDICOM3,-1,-1,lPos,$0020,$1041,'D','S',floattostrf(1-pDicomData.XYZdim[3],ffFixed,8,2));//$1041: info := 'Slice Location'; lEnd := lPos; lPos := lStart; WriteGroupElement(lDICOM3,-1,lEnd-lStart-lGrpError,lPos,$0020,$0000,'U','L','');//length lPos := lEnd; lStart := lPos; WriteGroupElement(lDICOM3,-1,28,lPos,$0028,$0000,'U','L','');//length //0028,0002: set value to 1 [plane]: greyscale, required by DICOM part 3 for MR WriteGroupElement(lDICOM3,1,-1,lPos,$0028,$0002,'U','S',''); //MONOCHROME1: low values = white, MONOCHROME2: low values = dark, 0028,0004 required for MR WriteGroupElement(lDICOM3,-1,-1,lPos,$0028,$0004,'C','S','MONOCHROME2'); WriteGroupElement(lDICOM3,-1,-1,lPos,$0028,$0008,'I','S',inttostr(pDicomData.XYZdim[3]));//num frames //Part 3 of DICOM standard: 0028,0009 is REQUIRED for Multiframe images: 1063/18 for time, 1065/18 for time vector and 13/20 for image number [space] WriteGroupElement(lDICOM3,-1,($0013 shl 16)+($20 ),lPos,$0028,$0009,'A','T','');//frame ptr WriteGroupElement(lDICOM3,pDicomData.XYZdim[2],-1,lPos,$0028,$0010,'U','S',' ');//inttostr(lDicomData.XYZdim[2]));//row WriteGroupElement(lDICOM3,pDicomData.XYZdim[1],-1,lPos,$0028,$0011,'U','S',' ');//inttostr(lDicomData.XYZdim[1]));//col //0030 order: row spacing[y], column spacing[x]: see DICOM part 3 WriteGroupElement(lDICOM3,-1,-1,lPos,$0028,$0030,'D','S',floattostrf(pDicomData.XYZmm[2],ffFixed,8,2)+'\'+floattostrf(pDicomData.XYZmm[1],ffFixed,8,2));//pixel spacing //DICOM part 3: 0028,0100 required for MR WriteGroupElement(lDICOM3,pDicomData.Allocbits_per_pixel,-1,lPos,$0028,$0100,'U','S',' ');//inttostr(lDicomData.Allocbits_per_pixel));//bitds alloc WriteGroupElement(lDICOM3,pDicomData.Storedbits_per_pixel,-1,lPos,$0028,$0101,'U','S',' ');//inttostr(lDicomData.Storedbits_per_pixel));//bits stored if pDicomData.little_endian <> 1 then lHiBit := 0 else lHiBit := pDicomData.Storedbits_per_pixel -1; WriteGroupElement(lDICOM3,lHiBit,-1,lPos,$0028,$0102,'U','S',' ');//inttostr(lDicomData.Storedbits_per_pixel -1));//high bit WriteGroupElement(lDICOM3,0,-1,lPos,$0028,$0103,'U','S',' ');//pixel representation//inttostr(lDicomData.Storedbits_per_pixel -1));//high bit WriteGroupElement(lDICOM3,-1,-1,lPos,$0028,$1052,'D','S',floattostrf(0,ffFixed,8,2));//rescale intercept WriteGroupElement(lDICOM3,-1,-1,lPos,$0028,$1053,'D','S',floattostrf(pDicomData.IntenScale,ffGeneral,7,2));//slice thickness lEnd := lPos; lPos := lStart; WriteGroupElement(lDICOM3,-1,lEnd-lStart-lGrpError,lPos,$0028,$0000,'U','L','');//length lPos := lEnd; WriteGroupElement(lDICOM3,-1,pDicomData.ImageSz+12,lPos,($7FE0),$0000,'U','L','');//data size if pDicomdata.Storedbits_per_pixel = 16 then WriteGroupElement(lDICOM3,-1,pDicomData.ImageSz,lPos,($7FE0),$0010,'O','W','')//data size else WriteGroupElement(lDICOM3,-1,pDicomData.ImageSz,lPos,($7FE0),$0010,'O','B','');//data size if lFileName <> '' then begin AssignFile(fp, lFileName); Rewrite(fp, 1); blockwrite(fp,lP^,lPos); close(fp); end; freemem(lP); lSz := lPos; end; //start siemens procedure read_siemens_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;lFileName: string); label 567; var lI: word; lYear,lMonth,lDay,n,filesz,lFullSz,lMatrixSz,lIHour,lIMin,lISec: LongInt; tx : array [0..26] of Char; lMagField,lTE,lTR: double; lInstitution,lName, lID,lMinStr,lSecStr: String; FP: file; function swap32i(lPos: longint): Longint; type swaptype = packed record case byte of 0:(Word1,Word2 : word); //word is 16 bit 1:(Long:LongInt); end; swaptypep = ^swaptype; var s : LongInt; inguy:swaptypep; outguy:swaptype; begin seek(fp,lPos); BlockRead(fp, s, 4, n); inguy := @s; //assign address of s to inguy outguy.Word1 := swap(inguy^.Word2); outguy.Word2 := swap(inguy^.Word1); swap32i:=outguy.Long; end; function fswap8r (lPos: longint): double; type swaptype = packed record case byte of 0:(Word1,Word2,Word3,Word4 : word); //word is 16 bit 1:(float:double); end; swaptypep = ^swaptype; var s:double; inguy:swaptypep; outguy:swaptype; begin seek(fp,lPos); BlockRead(fp, s, 8, n); inguy := @s; //assign address of s to inguy outguy.Word1 := swap(inguy^.Word4); outguy.Word2 := swap(inguy^.Word3); outguy.Word3 := swap(inguy^.Word2); outguy.Word4 := swap(inguy^.Word1); fswap8r:=outguy.float; end; begin lImageFormatOK := true; lHdrOK := false; if not fileexists(lFileName) then begin lImageFormatOK := false; exit; end; FileMode := 0; //set to readonly AssignFile(fp, lFileName); Reset(fp, 1); FIleSz := FileSize(fp); Clear_Dicom_Data(lDicomData); if filesz < (6144) then begin showmessage('This file is to small to be a Siemens Magnetom Vision image.'); goto 567; end; seek(fp, 96); BlockRead(fp, tx, 7*SizeOf(Char), n); if (tx[0] <> 'S') OR (tx[1] <> 'I') OR (tx[2] <> 'E') OR (tx[3] <> 'M') then begin {manufacturer is not SIEMENS} showmessage('Is this a Siemens Magnetom Vision image [Manufacturer tag should be ''SIEMENS''].'); goto 567; end; {manufacturer not siemens} seek(fp, 105); BlockRead(fp, Tx, 25*SizeOf(Char), n); lINstitution := ''; for lI := 0 to 24 do begin if tx[lI] in ['/','\','a'..'z','A'..'Z',' ','+','-','.',',','0'..'9'] then lINstitution := lINstitution + tx[lI]; end; seek(fp, 768); BlockRead(fp, Tx, 25*SizeOf(Char), n); lName := ''; for lI := 0 to 24 do begin if tx[lI] in ['/','\','a'..'z','A'..'Z',' ','+','-','.',',','0'..'9'] then lName := lName + tx[lI]; end; seek(fp, 795); BlockRead(fp, Tx, 12*SizeOf(Char), n); lID := ''; for lI := 0 to 11 do begin if tx[lI] in ['/','\','a'..'z','A'..'Z',' ','+','-','.',',','0'..'9'] then lID := lID + tx[lI]; end; lDicomData.ImageStart := 6144; lYear := swap32i(0); lMonth := swap32i(4); lDay := swap32i(8); lIHour := swap32i(68); lIMin := swap32i(72); lISec := swap32i(76); lDicomData.XYZmm[3] := fswap8r (1544); lMagField := fswap8r (2560); lTR := fswap8r (1560); lTE := fswap8r (1568); lMatrixSz := swap32i(2864); lFullSz := (2*lMatrixSz*lMatrixSz);//16bitdata if ((FileSz - 6144) mod lFullSz) = 0 then begin case ((FileSz-6144) div lFullSz) of 4: lFullSz := 2*lMatrixSz; 9: lFullSz := 3*lMatrixSz; 16: lFullSz := 4*lMatrixSz; 25: lFullSz := 5*lMatrixSz; 36: lFullSz := 6*lMatrixSz; 49: lFullSz := 7*lMatrixSz; 64: lFullSz := 8*lMatrixSz; else lFullSz := lMatrixSz; end; end else lFullSz := lMatrixSz; {3744/3752 are XY FOV in mm!} lDicomData.XYZdim[1] := lFullSz;//lMatrixSz; //width lDicomData.XYZdim[2] := lFullSz;//lMatrixSz;//height {5000/5008 are size in mm, but wrong for mosaics} if lMatrixSz <> 0 then begin lDicomData.XYZmm[2] := fswap8r (3744)/lMatrixSz; lDicomData.XYZmm[1] := fswap8r (3752)/lMatrixSz; end; { lDicomData.XYZmm[2] := fswap8r (5000); lDicomData.XYZmm[1] := fswap8r (5008);} lDicomData.Allocbits_per_pixel := 16;//bits lDicomData.Storedbits_per_pixel:= lDicomData.Allocbits_per_pixel; lDicomData.GenesisCpt := false; lDicomData.GenesisPackHdr := 0; lMinStr := inttostr(lIMin); if length(lMinStr) = 1 then lMinStr := '0'+lMinStr; lSecStr := inttostr(lISec); if length(lSecStr) = 1 then lSecStr := '0'+lSecStr; lDynStr := 'Siemens Magnetom Vision Format'+kCR+'Name: '+lName+kCR+'ID: '+lID+kCR+'Institution: '+lInstitution+kCR+ 'Study DD/MM/YYYY: '+inttostr(lDay)+'/'+inttostr(lMonth)+'/'+inttostr(lYear)+kCR+ 'Image Hour/Min/Sec: '+inttostr(lIHour)+':'+lMinStr+':'+lSecStr+kCR+ 'Magnetic Field Strength: '+ floattostrf(lMagField,ffFixed,8,2)+kCR+ 'Time Repitition/Echo [TR/TE]: '+ floattostrf(lTR,ffFixed,8,2)+'/'+ floattostrf(lTE,ffFixed,8,2)+kCR+ 'XYZ dim:' +inttostr(lDicomData.XYZdim[1])+'/' +inttostr(lDicomData.XYZdim[2])+'/'+inttostr(lDicomData.XYZdim[3]) +kCR+'XYZ mm:'+floattostrf(lDicomData.XYZmm[1],ffFixed,8,2)+'/' +floattostrf(lDicomData.XYZmm[2],ffFixed,8,2)+'/'+floattostrf(lDicomData.XYZmm[3],ffFixed,8,2); lHdrOK := true; 567: CloseFile(fp); FileMode := 2; //set to read/write end; //end siemens //start picker procedure read_picker_data(lVerboseRead: boolean; var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;lFileName: string); label 423; const kPickerHeader =8192; kRecStart = 280; //is this a constant? var lDataStart,lVal,lDBPos,lPos,lRecSz, lNumRecs,lRec,FileSz,n: Longint; lThkM,lThkN,lSiz: double; tx : array [0..6] of Char; FP: file; lDiskCacheRA: pChar; function ReadRec(lRecNum: integer): boolean; var lNameStr,lValStr: string; lOffset,lLen,lFPOs,lFEnd: integer; function ValStrToFloat: double; var lConvStr: string; lI: integer; begin Result := 0.0; lLen := Length(lValStr); if lLen < 1 then exit; lConvStr := ''; for lI := 1 to lLen do if lValStr[lI] in ['0'..'9'] then lConvStr := lConvStr+ lValStr[lI]; if Length(lConvStr) < 1 then exit; Result := strtofloat(lConvStr); end; begin Result := false; lFPos := ((lRecNum-1) * lRecSz)+ kRecStart; lFEnd := lFpos + 6; lNameStr := ''; for lFPos := lFPos to lFEnd do if ord(lDiskCacheRA[lFPos]) <> 0 then lNameStr := lNameStr +lDiskCacheRA[lFPos]; if (lVerboseRead) or (lNameStr = 'RCNFSIZ') or (lNameStr='SCNTHKM') or (lNameStr='SCNTHKN') then begin lFPos := ((lRecNum-1) * lRecSz)+ kRecStart+8; lFEnd := lFPos+1; lOffset := 0; for lFPos := lFPos to lFend do lOffset := ((lOffset)shl 8)+(ord(lDiskCacheRA[lFPos])); lFPos := ((lRecNum-1) * lRecSz)+ kRecStart+10; lFEnd := lFPos+1; lLen := 0; for lFPos := lFPos to lFend do lLen := ((lLen)shl 8)+(ord(lDiskCacheRA[lFPos])); lOffset := lDataStart+lOffset+1; lFEnd := lOffset+lLen-1; if (lLen < 1) or (lFEnd > kPickerHeader) then exit; lValStr := ''; for lFPos := (lOffset) to lFEnd do begin lValStr := lValStr+lDiskCacheRA[lFPos]; end; if lVerboseRead then lDynStr := lDynStr+kCR+lNameStr+': '+ lValStr; if (lNameStr = 'RCNFSIZ') then lSiz := ValStrToFloat; if (lNameStr='SCNTHKM') then lThkM := ValStrToFloat; if (lNameStr='SCNTHKN') then lThkN := ValStrToFloat; end; //verboseread, or vital value result := true; end; function FindStr(l1,l2,l3,l4,l5: Char; lReadNum: boolean; var lNum: integer): boolean; var //lMarker: integer; lNumStr: String; begin Result := false; repeat if (lDiskCacheRA[lPos-4]=l1) and (lDiskCacheRA[lPos-3]=l2) and (lDiskCacheRA[lPos-2]=l3) and (lDiskCacheRA[lPos-1]=l4) and (lDiskCacheRA[lPos]=l5) then Result := true; inc (lPos); until (Result) or (lPos >= kPickerHeader); if not Result then exit; if not lReadNum then exit; Result := false; lNumStr := ''; repeat if (lDiskCacheRA[lPos] in ['0'..'9']) then lNumStr := lNumStr + lDiskCacheRA[lPos] else if lNumStr <> '' then Result := true; inc(lPos); until (Result) or (lPos = kPickerHeader); lNum := strtoint(lNumStr); end; begin lSiz := 0.0; lThkM := 0.0; lThkN := 0.0; lImageFormatOK := true; lHdrOK := false; if not fileexists(lFileName) then begin lImageFormatOK := false; exit; end; FileMode := 0; //set to readonly AssignFile(fp, lFileName); Reset(fp, 1); FIleSz := FileSize(fp); Clear_Dicom_Data(lDicomData); if filesz < (kPickerHeader) then begin showmessage('This file is to small to be a Picker image.'); CloseFile(fp); FileMode := 2; //set to read/write exit; end; seek(fp, 0); BlockRead(fp, tx, 4*SizeOf(Char), n); if (tx[0] <> '*') OR (tx[1] <> '*') OR (tx[2] <> '*') OR (tx[3] <> ' ') then begin {manufacturer is not SIEMENS} showmessage('Is this a Picker image? Expected ''*** '' at the start of the file.'); CloseFile(fp); FileMode := 2; //set to read/write exit; end; {not picker} if filesz = (kPickerHeader + (1024*1024*2)) then begin lDICOMdata.XYZdim[1] := 1024; lDICOMdata.XYZdim[2] := 1024; lDICOMdata.XYZdim[3] := 1; lDICOMdata.ImageStart := 8192; end else if filesz = (kPickerHeader + (512*512*2)) then begin lDICOMdata.XYZdim[1] := 512; lDICOMdata.XYZdim[2] := 512; lDICOMdata.XYZdim[3] := 1; lDICOMdata.ImageStart := 8192; end else if filesz = (8192 + (256*256*2)) then begin lDICOMdata.XYZdim[1] := 256; lDICOMdata.XYZdim[2] := 256; lDICOMdata.XYZdim[3] := 1; lDICOMdata.ImageStart := 8192; end else begin showmessage('This file is the incorrect size to be a Picker image.'); CloseFile(fp); FileMode := 2; //set to read/write exit; end; getmem(lDiskCacheRA,kPickerHeader*sizeof(char)); seek(fp, 0); BlockRead(fp, lDiskCacheRA^, kPickerHeader, n); lRecSz := 0; lNumRecs := 0; lPos := 5; if not FindStr('d','b','r','e','c',false, lVal) then goto 423; lDBPos := lPos; if not FindStr('r','e','c','s','z',true, lRecSz) then goto 423; lPos := lDBPos; if not FindStr('n','r','e','c','s',true, lnumRecs) then goto 423; lPos := kRecStart; // IS THIS A CONSTANT??? lDataStart :=kRecStart + (lRecSz*lnumRecs)-1; //file starts at 0, so -1 if (lNumRecs = 0) or (lDataStart> kPickerHeader) then goto 423; lRec := 0; lDynStr := 'Picker Format'; repeat inc(lRec); until (not (ReadRec(lRec))) or (lRec >= lnumRecs); if lSiz <> 0 then begin lDICOMdata.XYZmm[1] := lSiz/lDICOMdata.XYZdim[1]; lDICOMdata.XYZmm[2] := lSiz/lDICOMdata.XYZdim[2]; if lVerboseRead then lDynStr := lDynStr+kCR+'Voxel Size: '+floattostrf(lDicomData.XYZmm[1],ffFixed,8,2) +'x'+ floattostrf(lDicomData.XYZmm[2],ffFixed,8,2); end; if (lThkM <> 0) and (lThkN <> 0) then begin lDICOMdata.XYZmm[3] := lThkN/lThkM; if lVerboseRead then lDynStr := lDynStr+kCR+'Slice Thickness: '+floattostrf(lDicomData.XYZmm[3],ffFixed,8,2); end; 423: freemem(lDiskCacheRA); lHdrOK := true; CloseFile(fp); FileMode := 2; //set to read/write end; //end picker (*procedure read_picker_data(var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;lFileName: string); var FileSz,n: Longint; tx : array [0..6] of Char; FP: file; begin lImageFormatOK := true; lHdrOK := false; if not fileexists(lFileName) then begin lImageFormatOK := false; exit; end; FileMode := 0; //set to readonly AssignFile(fp, lFileName); Reset(fp, 1); FIleSz := FileSize(fp); Clear_Dicom_Data(lDicomData); if filesz < (8192) then begin showmessage('This file is to small to be a Picker image.'); CloseFile(fp); FileMode := 2; //set to read/write exit; end; seek(fp, 0); BlockRead(fp, tx, 4*SizeOf(Char), n); if (tx[0] <> '*') OR (tx[1] <> '*') OR (tx[2] <> '*') OR (tx[3] <> ' ') then begin {manufacturer is not SIEMENS} showmessage('Is this a Picker image? Expected ''*** '' at the start of the file.'); CloseFile(fp); FileMode := 2; //set to read/write exit; end; {not picker} if filesz = (8192 + (1024*1024*2)) then begin lDICOMdata.XYZdim[1] := 1024; lDICOMdata.XYZdim[2] := 1024; lDICOMdata.XYZdim[3] := 1; lDICOMdata.ImageStart := 8192; end else if filesz = (8192 + (512*512*2)) then begin lDICOMdata.XYZdim[1] := 512; lDICOMdata.XYZdim[2] := 512; lDICOMdata.XYZdim[3] := 1; lDICOMdata.ImageStart := 8192; end else if filesz = (8192 + (256*256*2)) then begin lDICOMdata.XYZdim[1] := 256; lDICOMdata.XYZdim[2] := 256; lDICOMdata.XYZdim[3] := 1; lDICOMdata.ImageStart := 8192; end else begin showmessage('This file is the incorrect size to be a Picker image.'); CloseFile(fp); FileMode := 2; //set to read/write end; if not gSizeMMWarningShown then begin gSizeMMWarningShown := true; showmessage('Warning: this software does not read the size[mm] fields for Picker images.'); end; lDynStr := 'Picker Format'+kCR+ 'XYZ dim:' +inttostr(lDicomData.XYZdim[1])+'/' +inttostr(lDicomData.XYZdim[2])+'/'+inttostr(lDicomData.XYZdim[3]) +kCR+'XYZ mm:'+{floattostrf(lDicomData.XYZmm[1],ffFixed,8,2)}'?'+'/?/?' +kCR+'VOXEL SIZE[mm] UNKNOWN'; lHdrOK := true; CloseFile(fp); FileMode := 2; //set to read/write end;*) procedure read_dicom_data(lReadJPEGtables,lVerboseRead,lAutoDECAT7,lReadECAToffsetTables,lAutoDetectInterfile,lAutoDetectGenesis,lReadColorTables: boolean; var lDICOMdata: DICOMdata; var lHdrOK, lImageFormatOK: boolean; var lDynStr: string;var lFileName: string); label 666,777; const kMaxTextBuf = 50000; //maximum for screen output kDiskCache = 16384; //size of disk buffer type dicom_types = (unknown, i8, i16, i32, ui8, ui16, ui32, _string ); var lWord: word; lWordRA: Wordp; lDiskCacheRA: pChar{ByteP}; FP: file; lT0,lT1,lT2,lT3:byte; lTextOverFlow,lGenesis,lFirstPass,lrOK,lBig,lBigSet,lGrp,explicitVR,first_one : Boolean; {lTestError,}lByteSwap,lGELX,time_to_quit,lFirstFragment : Boolean; group, element, dummy, e_len, remaining, tmp : uint32; lgrpstr,tmpstr,lStr,info : string; t : dicom_types; lfloat1,lfloat2: double; lJPEGentries,lErr,liPos,lCacheStart,lCachePos,lDiskCacheSz,n, i,j,value, Width, max16,min16,slicesz,filesz,where,lStart,lEnd : LongInt; tx : array [0..21] of Char; buff: pCHar; lColorRA: bytep; procedure ByteSwap (var lInOut: integer); var lWord: word; begin lWord := lInOut; lWord := swap(lWord); lInOut := lWord; end; procedure dReadCache (lFileStart: integer); begin lCacheStart := lFileStart{lCacheStart + lDiskCacheSz};//eliminate old start if lCacheStart < 0 then lCacheStart := 0; if lDiskCacheSz > 0 then freemem(lDiskCacheRA); if (FileSz-(lCacheStart)) < kDiskCache then lDiskCacheSz := FileSz - (lCacheStart) else lDiskCacheSz := kDiskCache; lCachePos := 0; if (lDiskCacheSz < 1) then exit{goto 666}; if (lDiskCacheSz+lCacheStart) > FileSz then exit; //showmessage(inttostr(FileSz)+' / '+INTTOSTR(lDiskCacheSz)+ ' / '+inttostr(lCacheStart)); Seek(fp, lCacheStart); GetMem(lDiskCacheRA, lDiskCacheSz {bytes}); BlockRead(fp, lDiskCacheRA^, lDiskCacheSz, n); end; function dFilePos (var lInFP: file): integer; begin Result := lCacheStart + lCachePos; end; procedure dSeek (var lInFP: file; lPos: integer); begin if (lPos >= lCacheStart) and (lPos < (lDiskCacheSz+lCacheStart)) then begin lCachePos := lPos-lCacheStart; exit; end; dReadCache(lPos); end; procedure dBlockRead (var lInfp: file; lInbuff: pChar; e_len: integer; var n: integer); var lN: integer; begin N := 0; if e_len < 0 then exit; for lN := 0 to (e_len-1) do begin if lCachePos >= lDiskCacheSz then begin dReadCache(lCacheStart+lDiskCacheSz); if lDiskCacheSz < 1 then exit; lCachePos := 0; end; N := lN; lInBuff[N] := lDiskCacheRA[lCachePos]; inc(lCachePos); end; end; procedure readfloats (var fp: file; remaining: integer; var lOutStr: string; var lf1, lf2: double; var lReadOK: boolean); var lDigit : boolean; li,lLen,n: integer; lfStr: string; begin lf1 := 1; lf2 := 2; if e_len = 0 then begin lReadOK := true; exit; end; if (dFilePos(fp) > (filesz-remaining)) or (remaining < 1) then begin lOutStr := ''; lReadOK := false; exit; end else lReadOK := true; lOutStr := ''; GetMem( buff, e_len); dBlockRead(fp, buff{^}, e_len, n); for li := 0 to e_len-1 do if Char(buff[li]) in ['/','\','e','E','+','-','.','0'..'9'] then lOutStr := lOutStr +(Char(buff[li])) else lOutStr := lOutStr + ' '; FreeMem( buff); lfStr := ''; lLen := length(lOutStr); li := 1; lDigit := false; repeat if (lOutStr[li] in ['+','-','e','E','.','0'..'9']) then lfStr := lfStr + lOutStr[li]; if lOutStr[li] in ['0'..'9'] then lDigit := true; inc(li); until (li > lLen) or (lDigit); if not lDigit then exit; if li <= li then begin repeat if not (lOutStr[li] in ['+','-','e','E','.','0'..'9']) then lDigit := false else begin if lOutStr[li] = 'E' then lfStr := lfStr+'e' else lfStr := lfStr + lOutStr[li]; end; inc(li); until (li > lLen) or (not lDigit); end; //QStr(lfStr); try lf1 := strtofloat(lfStr); except on EConvertError do begin showmessage('Unable to convert the string '+lfStr+' to a real number'); lf1 := 1; exit; end; end; {except} lfStr := ''; if li > llen then exit; repeat if (lOutStr[li] in ['+','E','e','.','-','0'..'9']) then begin if lOutStr[li] = 'E' then lfStr := lfStr+'e' else lfStr := lfStr + lOutStr[li]; end; if (lOutStr[li] in ['0'..'9']) then lDigit := true; inc(li); until (li > lLen); if not lDigit then exit; //QStr(lfStr); try lf2 := strtofloat(lfStr); except on EConvertError do begin showmessage('Unable to convert the string '+lfStr+' to a real number'); exit; end; end; end; function read16( var fp : File; var lReadOK: boolean ): uint16; var t1, t2 : uint8; n : Integer; begin if dFilePos(fp) > (filesz-2) then begin read16 := 0; lReadOK := false; exit; end else lReadOK := true; GetMem( buff, 2); dBlockRead(fp, buff{^}, 2, n); T1 := ord(buff[0]); T2 := ord(buff[1]); freemem(buff); if lDICOMdata.little_endian <> 0 then Result := (t1 + t2*256) AND $FFFF else Result := (t1*256 + t2) AND $FFFF; end; function ReadStr(var fp: file; remaining: integer; var lReadOK: boolean) : string; var lInc, lN,Val,n: integer; t1, t2 : uint8; lStr : String; begin if dFilePos(fp) > (filesz-remaining) then begin lReadOK := false; exit; end else lReadOK := true; Result := ''; lN := remaining div 2; if lN < 1 then exit; lStr := ''; for lInc := 1 to lN do begin GetMem( buff, 2); dBlockRead(fp, buff{^}, 2, n); T1 := ord(buff[0]); T2 := ord(buff[1]); freemem(buff); if lDICOMdata.little_endian <> 0 then Val := (t1 + t2*256) AND $FFFF else Val := (t1*256 + t2) AND $FFFF; if lInc < lN then lStr := lStr + inttostr(Val)+ ', ' else lStr := lStr + inttostr(Val); end; Result := lStr; if odd(remaining) then begin getmem(buff,1); dBlockRead(fp, buff{t1}, SizeOf(uint8), n); freemem(buff); end; end; function ReadStrHex(var fp: file; remaining: integer; var lReadOK: boolean) : string; var lInc, lN,Val,n: integer; t1, t2 : uint8; lStr : String; begin if dFilePos(fp) > (filesz-remaining) then begin lReadOK := false; exit; end else lReadOK := true; Result := ''; lN := remaining div 2; if lN < 1 then exit; lStr := ''; for lInc := 1 to lN do begin GetMem( buff, 2); dBlockRead(fp, buff, 2, n); T1 := ord(buff[0]); T2 := ord(buff[1]); freemem(buff); if lDICOMdata.little_endian <> 0 then Val := (t1 + t2*256) AND $FFFF else Val := (t1*256 + t2) AND $FFFF; if lInc < lN then lStr := lStr + 'x'+inttohex(Val,4)+ ', ' else lStr := lStr + 'x'+inttohex(Val,4); end; Result := lStr; if odd(remaining) then begin getmem(buff,1); dBlockRead(fp, {t1}buff, SizeOf(uint8), n); freemem(buff); end; end; function SomaTomFloat: double; var lSomaStr: String; begin //dSeek(fp,5992); //Slice Thickness from 5790 "SL 3.0" //dSeek(fp,5841); //Field of View from 5838 "FoV 281" //dSeek(fp,lPos); lSomaStr := ''; tx[0] := 'x'; while (length(lSomaStr) < 64) and (tx[0] <> chr(0)) and (tx[0] <> '/') do begin dBlockRead(fp, tx, 1, n); if tx[0] in ['+','-','.','0'..'9','e','E'] then lSomaStr := lSomaStr + tx[0]; end; //showmessage(lSomaStr+':'+inttostr(length(lSOmaStr))); //showmessage(inttostr(length(lSOmaStr))); if length(lSOmaStr) > 0 then result := StrToFloat(lSomaStr) else result := 0; end; function read32 ( var fp : File; var lReadOK: boolean ): uint32; var t1, t2, t3, t4 : byte; n : Integer; begin if dFilePos(fp) > (filesz-4) then begin Read32 := 0; lReadOK := false; exit; end else lReadOK := true; GetMem( buff, 4); dBlockRead(fp, buff{^}, 4, n); T1 := ord(buff[0]); T2 := ord(buff[1]); T3 := ord(buff[2]); T4 := ord(buff[3]); freemem(buff); if lDICOMdata.little_endian <> 0 then Result := t1 + (t2 shl 8) + (t3 shl 16) + (t4 shl 24) else Result := t4 + (t3 shl 8) + (t2 shl 16) + (t1 shl 24) //if lDICOMdata.little_endian <> 0 //then Result := (t1 + t2*256 + t3*256*256 + t4*256*256*256) AND $FFFFFFFF //else Result := (t1*256*256*256 + t2*256*256 + t3*256 + t4) AND $FFFFFFFF; end; function read64 ( var fp : File; var lReadOK: boolean ): double; type swaptype = packed record case byte of 0:(Word1,Word2,Word3,Word4 : word); //word is 16 bit 1:(float:double); end; swaptypep = ^swaptype; var s:double; inguy:swaptypep; outguy:swaptype; begin if dFilePos(fp) > (filesz-8) then begin Read64 := 0; lReadOK := false; exit; end else lReadOK := true; //GetMem( buff, 8); dBlockRead(fp, @s, 8, n); inguy := @s; //assign address of s to inguy if lDICOMdata.little_endian <> 1 then begin outguy.Word1 := swap(inguy^.Word4); outguy.Word2 := swap(inguy^.Word3); outguy.Word3 := swap(inguy^.Word2); outguy.Word4 := swap(inguy^.Word1); end; read64:=outguy.float; end; begin //lTestError := false; lCacheStart := 0; lDiskCacheSz := 0; lFirstFragment := true; lTextOverFlow := false; lImageFormatOK := true; lHdrOK := false; if not fileexists(lFileName) then begin lImageFormatOK := false; exit; end; lGELX := false; lByteSwap := false; Clear_Dicom_Data(lDicomData); FileMode := 0; //set to readonly AssignFile(fp, lFileName); Reset(fp, 1); FIleSz := FileSize(fp); if fileSz < 1 then begin lImageFormatOK := false; exit; end; lDICOMdata.Little_Endian := 1; lDynStr:= ''; lJPEGEntries := 0; first_one := true; info := ''; lGrp:= false; lBigSet := false; if (lAutoDetectGenesis) and (FileSz > (5820{114+35+4})) then begin dseek(fp, 0); dBlockRead(fp, tx, 4*SizeOf(Char), n); //showmessage(tx[0]+tx[1]+tx[2]+tx[3]); lGenesis := false; if ((tx[0] <> 'I') OR (tx[1] <> 'M') OR (tx[2] <> 'G') OR (tx[3] <> 'F')) then begin {DAT format} {if (FileSz > 114+305+4) then begin dseek(fp, 114+305); dBlockRead(fp, tx, 3*SizeOf(Char), n); if ((tx[0]='M') and (tx[1] = 'R')) or ((tx[0] = 'C') and(tx[1] = 'T')) then lGenesis := true; end;} end else lGenesis := true; if (not lGenesis) and (FileSz > 3252) then begin dseek(fp, 3240); dBlockRead(fp, tx, 4*SizeOf(Char), n); if ((tx[0] = 'I') AND (tx[1] = 'M')AND (tx[2] = 'G') AND (tx[3] = 'F')) then lGenesis := true; if (not lGenesis) then begin dseek(fp, 3178); dBlockRead(fp, tx, 4*SizeOf(Char), n); if ((tx[0] = 'I') AND (tx[1] = 'M')AND (tx[2] = 'G') AND (tx[3] = 'F')) then lGenesis := true; end; if (not lGenesis) then begin dseek(fp, 3180); dBlockRead(fp, tx, 4*SizeOf(Char), n); if ((tx[0] = 'I') AND (tx[1] = 'M')AND (tx[2] = 'G') AND (tx[3] = 'F')) then lGenesis := true; end; end; if (not lGenesis) and (FileSz > 3252) then begin dseek(fp, 3228); dBlockRead(fp, tx, 4*SizeOf(Char), n); if (tx[0] = 'I') AND (tx[1]= 'M') AND (tx[2] = 'G')AND (tx[3]= 'F') then lGenesis := true; end; if lGenesis then begin CloseFile(fp); if lDiskCacheSz > 0 then freemem(lDiskCacheRA); FileMode := 2; //set to read/write read_ge_data(lDICOMdata, lHdrOK, lImageFormatOK,lDynStr,lFileName); exit; end; end; //AutodetectGenesis if (lAutoDetectInterfile) and (FileSz > 30) then begin dseek(fp, 0); dBlockRead(fp, tx, 20*SizeOf(Char), n); liPos := 1; lStr :=''; While (liPos <= 20) and (lStr <> 'INTERFILE') do begin if tx[liPos] in ['i','n','t','e','r', 'f','i','l','e','I','N','T','E','R', 'F','I','L','E'] then lStr := lStr+upcase(tx[liPos]); inc(liPos); end; if lStr = 'INTERFILE' then begin CloseFile(fp); if lDiskCacheSz > 0 then freemem(lDiskCacheRA); FileMode := 2; //set to read/write read_interfile_data(lDICOMdata, lHdrOK, lImageFormatOK, lDynStr, lFileName); if lHdrOk then exit; exit; end; //'INTERFILE' in first 20 char //begin parfile liPos := 1; lStr :=''; While (liPos <= 20) and (lStr <> 'DATADESC') do begin if tx[liPos] in ['A'..'Z','a'..'z'] then lStr := lStr+upcase(tx[liPos]); inc(liPos); end; if lStr = 'DATADESC' then begin CloseFile(fp); if lDiskCacheSz > 0 then freemem(lDiskCacheRA); FileMode := 2; //set to read/write read_par_data(lDICOMdata, lHdrOK, lImageFormatOK, lDynStr, lFileName); if lHdrOk then exit; exit; end; //'DATADESC' in first 20 char ->parfile //end parfile end;//detectint // try DICOM part 10 i.e. a 128 byte file preamble followed by "DICM" if filesz <= 300 then goto 666; {begin siemens somatom: DO THIS BEFORE MAGNETOM: BOTH HAVE 'SIEMENS' SIGNATURE, SO CHECK FOR 'SOMATOM'} if filesz = 530432 then begin dseek(fp, 281); dBlockRead(fp, tx, 8*SizeOf(Char), n); if (tx[0] = 'S') and (tx[1] = 'O') and (tx[2] = 'M') and (tx[3] = 'A') and (tx[4] = 'T') and (tx[5] = 'O') and (tx[6] = 'M') then begin //Showmessage('somatom'); lDicomData.ImageStart := 6144; lDicomData.Allocbits_per_pixel := 16; lDicomData.Storedbits_per_pixel := 16; lDicomData.little_endian := 0; lDicomData.XYZdim[1] := 512; lDicomData.XYZdim[2] := 512; lDicomData.XYZdim[3] := 1; dSeek(fp,5999); //Study/Image from 5292 "STU/IMA 1070/16" lDicomData.AcquNum := trunc(SomaTomFloat);//Slice Thickness from 5790 "SL 3.0" lDicomData.ImageNum := trunc(SomaTomFloat);//Slice Thickness from 5790 "SL 3.0" dSeek(fp,5792); //Slice Thickness from 5790 "SL 3.0" lDicomData.XYZmm[3] := SomaTomFloat;//Slice Thickness from 5790 "SL 3.0" dSeek(fp,5841); //Field of View from 5838 "FoV 281" lDicomData.XYZmm[1] := SomaTomFloat; //Field of View from 5838 "FoV 281" lDicomData.XYZmm[2] := lDicomData.XYZmm[1]/lDicomData.XYZdim[2];//do mm[2] first before FOV is overwritten lDicomData.XYZmm[1] := lDicomData.XYZmm[1]/lDicomData.XYZdim[1]; if lVerboseRead then lDynStr := 'Siemens Somatom Format'+kCR+ 'Image Series/Number: '+inttostr(lDicomData.AcquNum)+'/'+inttostr(lDicomData.ImageNum)+kCR+ 'XYZ dim:' +inttostr(lDicomData.XYZdim[1])+'/' +inttostr(lDicomData.XYZdim[2])+'/'+inttostr(lDicomData.XYZdim[3]) +kCR+'XYZ mm:'+floattostrf(lDicomData.XYZmm[1],ffFixed,8,2)+'/' +floattostrf(lDicomData.XYZmm[2],ffFixed,8,2)+'/'+floattostrf(lDicomData.XYZmm[3],ffFixed,8,2); CloseFile(fp); if lDiskCacheSz > 0 then freemem(lDiskCacheRA); FileMode := 2; //set to read/write lImageFormatOK := true; lHdrOK := true; exit; end; //signature found end; //correctsize for somatom {end siemens somatom} {siemens magnetom} dseek(fp,96); dBlockRead(fp, tx, 7*SizeOf(Char), n); if (tx[0] = 'S') and (tx[1] = 'I') and (tx[2] = 'E') and (tx[3] = 'M') and (tx[4] = 'E') and (tx[5] = 'N') and (tx[6] = 'S') then begin CloseFile(fp); if lDiskCacheSz > 0 then freemem(lDiskCacheRA); FileMode := 2; //set to read/write read_siemens_data(lDICOMdata, lHdrOK, lImageFormatOK, lDynStr, lFileName); exit; end; {end siemens magnetom vision} {siemens somatom plus} dseek(fp, 0); dBlockRead(fp, tx, 8*SizeOf(Char), n); if (tx[0] = 'S') and (tx[1] = 'I') and (tx[2] = 'E') and (tx[3] = 'M') and (tx[4] = 'E') and (tx[5] = 'N') and (tx[6] = 'S') then begin lDicomData.ImageStart := 8192; lDicomData.Allocbits_per_pixel := 16; lDicomData.Storedbits_per_pixel := 16; lDicomData.little_endian := 0; dseek(fp, 1800); //slice thickness lDicomData.XYZmm[3] := read64(fp,lrOK); dseek(fp, 4100); lDicomData.AcquNum := read32(fp,lrOK); dseek(fp, 4108); lDicomData.ImageNum := read32(fp,lrOK); dseek(fp, 4992); //X FOV lDicomData.XYZmm[1] := read64(fp,lrOK); dseek(fp, 5000); //Y FOV lDicomData.XYZmm[2] := read64(fp,lrOK); dseek(fp, 5340); lDicomData.XYZdim[1] := read32(fp,lrOK); dseek(fp, 5344); lDicomData.XYZdim[2] := read32(fp,lrOK); lDicomData.XYZdim[3] := 1; if lDicomData.XYZdim[1] > 0 then lDicomData.XYZmm[1] := lDicomData.XYZmm[1]/lDicomData.XYZdim[1]; if lDicomData.XYZdim[2] > 0 then lDicomData.XYZmm[2] := lDicomData.XYZmm[2]/lDicomData.XYZdim[2]; if lVerboseRead then lDynStr := 'Siemens Somatom Plus Format'+kCR+ 'Image Series/Number: '+inttostr(lDicomData.AcquNum)+'/'+inttostr(lDicomData.ImageNum)+kCR+ 'XYZ dim:' +inttostr(lDicomData.XYZdim[1])+'/' +inttostr(lDicomData.XYZdim[2])+'/'+inttostr(lDicomData.XYZdim[3]) +kCR+'XYZ mm:'+floattostrf(lDicomData.XYZmm[1],ffFixed,8,2)+'/' +floattostrf(lDicomData.XYZmm[2],ffFixed,8,2)+'/'+floattostrf(lDicomData.XYZmm[3],ffFixed,8,2); CloseFile(fp); if lDiskCacheSz > 0 then freemem(lDiskCacheRA); FileMode := 2; //set to read/write lImageFormatOK := true; lHdrOK := true; exit; end; {end siemens somatom plus } {picker} dseek(fp,0); dBlockRead(fp, tx, 8*SizeOf(Char), n); if (lAutoDECAT7) and (tx[0]='M') and (tx[1]='A') and (tx[2]='T') and (tx[3]='R') and (tx[4]='I') and (tx[5]='X') then begin CloseFile(fp); if lDiskCacheSz > 0 then freemem(lDiskCacheRA); FileMode := 2; //set to read/write read_ecat_data(lDICOMdata, lVerboseRead,lReadECAToffsetTables,lHdrOK, lImageFormatOK, lDynStr, lFileName); exit; end; if (tx[0] = '*') AND (tx[1] = '*') AND (tx[2] = '*') AND (tx[3] = ' ') then begin {manufacturer is not SIEMENS} CloseFile(fp); if lDiskCacheSz > 0 then freemem(lDiskCacheRA); FileMode := 2; //set to read/write read_picker_data(lVerboseRead,lDICOMdata, lHdrOK, lImageFormatOK, lDynStr, lFileName); exit; end; {not picker} {end picker} lBig := false; dseek(fp, {0}128); //where := FilePos(fp); dBlockRead(fp, tx, 4*SizeOf(Char), n); if (tx[0] <> 'D') OR (tx[1] <> 'I') OR (tx[2] <> 'C') OR (tx[3] <> 'M') then begin //if filesz > 132 then begin dseek(fp, 0{128}); //skip the preamble - next 4 bytes should be 'DICM' //where := FilePos(fp); dBlockRead(fp, tx, 4*SizeOf(Char), n); //end; if (tx[0] <> 'D') OR (tx[1] <> 'I') OR (tx[2] <> 'C') OR (tx[3] <> 'M') then begin //showmessage('DICM not at 0 or 128'); dseek(fp, 0); group := read16(fp,lrOK); if not lrOK then goto 666; if group > $0008 then begin group := swap(group); lBig := true; end; if NOT (group in [$0000, $0001, $0002,$0003, $0004, $0008]) then // one more group added begin goto 666; end; dseek(fp, 0); end; end; //else showmessage('DICM at 128{0}');; // Read DICOM Tags time_to_quit := FALSE; explicitVR := false; tmpstr := ''; tmp := 0; //lDicomData.RunLengthEncoding := true; //abba17 //lDicomData.JPEGlossyCpt := true;//abba17 while NOT time_to_quit do begin t := unknown; where := dFilePos(fp); lFirstPass := true; 777: group := read16(fp,lrOK); if not lrOK then goto 666; if (lFirstPass) and (group = 2048) then begin if lDicomData.little_endian = 1 then lDicomData.Little_endian := 0 else lDicomData.little_endian := 1; dseek(fp,where); lFirstPass := false; goto 777; end; element := read16(fp,lrOK); if not lrOK then goto 666; e_len:= read32(fp,lrOK); if not lrOK then goto 666; lGrpStr := ''; lt0 := e_len and 255; lt1 := (e_len shr 8) and 255; lt2 := (e_len shr 16) and 255; lt3 := (e_len shr 24) and 255; if (explicitVR) and (lT0=13) and (lT1=0) and (lT2=0) and (lT3=0) then e_len := 10; //hack for some GE Dicom images if explicitVR or first_one then begin if ((lT0=kO) and (lT1=kB)) or ((lT0=kU) and (lT1=kN)){<-UN added} or ((lT0=kO) and (lT1=kW)) or ((lT0=kS) and (lT1=kQ)) then begin lGrpStr := chr(lT0)+chr(lT1); e_len:= read32(fp,lrOK); if not lrOK then goto 666; if first_one then explicitVR := true; end else if ((lT3=kO) and (lT2=kB)) or ((lT3=kU) and (lT2=kN)){<-UN added} or((lT3=kO) and (lT2=kW)) or ((lT3=kS) and (lT2=kQ)) then begin e_len:= read32(fp,lrOK); if not lrOK then goto 666; if first_one then explicitVR := true; end else if ( ((lT0=kA) and (lT1=kE)) or ((lT0=kA) and (lT1=kS)) or ((lT0=kA) and (lT1=kT)) or ((lT0=kC) and (lT1=kS)) or ((lT0=kD) and (lT1=kA)) or ((lT0=kD) and (lT1=kS)) or ((lT0=kD) and (lT1=kT)) or ((lT0=kF) and (lT1=kL)) or ((lT0=kF) and (lT1=kD)) or ((lT0=kI) and (lT1=kS)) or ((lT0=kL) and (lT1=kO))or ((lT0=kL) and (lT1=kT)) or ((lT0=kP) and (lT1=kN)) or ((lT0=kS) and (lT1=kH)) or ((lT0=kS) and (lT1=kL)) or ((lT0=kS) and (lT1=kS)) or ((lT0=kS) and (lT1=kT)) or ((lT0=kT) and (lT1=kM)) or ((lT0=kU) and (lT1=kI)) or ((lT0=kU) and (lT1=kL)) or ((lT0=kU) and (lT1=kS)) or ((lT0=kA) and (lT1=kE)) or ((lT0=kA) and (lT1=kS)) ) then begin lGrpStr := chr(lT0) + chr(lT1); if lDicomData.little_endian = 1 then e_len := (e_len and $ffff0000) shr 16 else e_len := swap((e_len and $ffff0000) shr 16); if first_one then begin explicitVR := true; end; end else if ( ((lT3=kA) and (lT2=kT)) or ((lT3=kC) and (lT2=kS)) or ((lT3=kD) and (lT2=kA)) or ((lT3=kD) and (lT2=kS)) or ((lT3=kD) and (lT2=kT)) or ((lT3=kF) and (lT2=kL)) or ((lT3=kF) and (lT2=kD)) or ((lT3=kI) and (lT2=kS)) or ((lT3=kL) and (lT2=kO))or ((lT3=kL) and (lT2=kT)) or ((lT3=kP) and (lT2=kN)) or ((lT3=kS) and (lT2=kH)) or ((lT3=kS) and (lT2=kL)) or ((lT3=kS) and (lT2=kS)) or ((lT3=kS) and (lT2=kT)) or ((lT3=kT) and (lT2=kM)) or ((lT3=kU) and (lT2=kI)) or ((lT3=kU) and (lT2=kL)) or ((lT3=kU) and (lT2=kS))) then begin if lDicomData.little_endian = 1 then e_len := (256 * lT0) + lT1 else e_len := (lT0) + (256*lT1); if first_one then begin explicitVR := true; end; end; end; //not first_one or explicit if (first_one) and (lDicomdata.little_endian =0) and (e_len = $04000000) then begin ShowMessage('Switching to little endian'); lDicomData.little_endian := 1; dseek(fp, where); first_one := false; goto 777; end else if (first_one) and (lDicomData.little_endian =1) and (e_len = $04000000) then begin ShowMessage('Switching to little endian'); lDicomData.little_endian := 0; dseek(fp, where); first_one := false; goto 777; end; if e_len = ($FFFFFFFF) then begin e_len := 0; end; if lGELX then begin e_len := e_len and $FFFF; end; first_one := false; remaining := e_len; info := '?'; tmpstr := ''; (*if lTestError then begin showmessage(inttostr(group)+'pwr'+inttostr(element)); end;*) case group of $0001 : // group for normal reading elscint DICOM case element of $0010 : info := 'Name'; $1001 : info := 'Elscint info'; end; $0002 : case element of $00 : info := 'File Meta Elements Group Len'; $01 : info := 'File Meta Info Version'; $02 : info := 'Media Storage SOP Class UID'; $03 : info := 'Media Storage SOP Inst UID'; $10 : begin info := 'Transfer Syntax UID'; TmpStr := ''; if dFilePos(fp) > (filesz-e_len) then goto 666; GetMem( buff, e_len); dBlockRead(fp, buff{^}, e_len, n); for i := 0 to e_len-1 do if Char(buff[i]) in ['+','-',' ', '0'..'9','a'..'z','A'..'Z'] then TmpStr := TmpStr +(Char(buff[i])) else TmpStr := TmpStr +('.'); FreeMem( buff); lStr := ''; if TmpStr = '1.2.840.113619.5.2' then begin lGELX := true; LBigSet := true; lBig := true; end; if length(TmpStr) >= 19 then begin if TmpStr[19] = '1' then begin lBigSet:= true; explicitVR := true; //duran lBig := false; end else if TmpStr[19] = '2' then begin lBigSet:= true; explicitVR := true; //duran lBig := true; end else if TmpStr[19] = '4' then begin if length(TmpStr) >= 21 then begin //ShowMessage('Unable to extract JPEG: '+TmpStr[21]+TmpStr[22]) //lDicomData.JPEGCpt := true; if not lReadJPEGtables then begin lImageFormatOK := false; //showmessage('Unable to extract JPEG compressed DICOM files. Use MRIcro to convert this file.'); end else begin i := strtoint(TmpStr[21]+TmpStr[22]); //showmessage(inttostr(i)); //if (TmpStr[22] <> '0') or ((TmpStr[21] <> '7') or (TmpStr[21] <> '0')) if (i <> 57) and (i <> 70) then lDicomData.JPEGLossyCpt := true else lDicomData.JPEGLosslessCpt := true; end; end else begin Showmessage('Unknown Transfer Syntax: JPEG?'); lImageFormatOK := false; end; end else if TmpStr[19] = '5' then begin lDicomData.RunLengthEncoding := true; //ShowMessage('Note: Unable to extract lossless run length encoding: '+TmpStr[17]); //lImageFormatOK := false; end else begin ShowMessage('Unable to extract unknown data type: '+TmpStr[17]); lImageFormatOK := false; end; end; {length} remaining := 0; e_len := 0; {use tempstr} end; $12 : begin info := 'Implementation Class UID'; end; $13 : info := 'Implementation Version Name'; $16 : info := 'Source App Entity Title'; $100: info := 'Private Info Creator UID'; $102: info := 'Private Info'; end; $0008 : case element of $00 : begin info := 'Identifying Group Length'; end; $01 : info := 'Length to End'; $05 : info := 'Specific Character Set'; $08 : begin info := 'Image Type'; t := _string; end; $10 : info := 'Recognition Code'; $12 : info := 'Instance Creation Date'; $13 : info := 'Instance Creation Time'; $14 : info := 'Instance Creator UID'; $16 : info := 'SOP Class UID'; $18 : info := 'SOP Instance UID'; $20 : begin info := 'Study Date'; lDicomData.StudyDatePos := dFilePos(fp); end; $21 : info := 'Series Date'; $22 : info := 'Acquisition Date'; $23 : info := 'Image Date'; $30 : info := 'Study Time'; $31 : info := 'Series Time'; $32 : info := 'Acquisition Time'; $33 : info := 'Image Time'; $40 : info := 'Data Set Type'; $41 : info := 'Data Set Subtype'; $50 : info := 'Accession Number'; $60 : begin info := 'Modality'; t := _string; end; $64 : begin info := 'Conversion Type'; t := _string; end; $70 : info := 'Manufacturer'; $80 : info := 'Institution Name'; $81 : info := 'City Name'; $90 : info := 'Referring Physician''s Name'; $1010: info := 'Station Name'; $1030: begin info := 'Study Description'; t := _string; end; $103e: info := 'Series Description'; $1040: info := 'Institutional Dept. Name'; $1050: info := 'Performing Physician''s Name'; $1060: info := 'Name Phys(s) Read Study'; $1070: begin info := 'Operator''s Name'; t := _string; end; $1080: info := 'Admitting Diagnosis Description'; $1090: begin info := 'Manufacturer''s Model Name';t := _string; end; $1140: info := 'Referenced Image Sequence'; $2120: info := 'Stage Name'; $2122: begin info := 'Stage Number';t := _string; end; $2124: begin info := 'Number of Stages';t := _string; end; $2128: begin info := 'View Number';t := _string; end; $212A: begin info := 'Number of Views in stage';t := _string; end; $2204: info := 'Transducer Orientation'; end; (* $0009: case element of $1215: begin info := 'Image Number'; t := _string; lStr := ''; if dFilePos(fp) > (filesz-e_len) then goto 666; GetMem( buff, e_len); dBlockRead(fp, buff{^}, e_len, n); tmp := e_len -1; if tmp > 7 then tmp := tmp - 8 else tmp := 0; for i := tmp to e_len-1 do if Char(buff[i]) in ['+','-','0'..'9'] then lStr := lStr +(Char(buff[i])); FreeMem( buff); Val(lStr,i,lErr); if lErr = 0 then lDicomData.ImageNum := i;//strtoint(lStr); remaining := 0; tmp := lDicomData.ImageNum; //showmessage(lStr+' : '+inttostr(i)+' = '+inttostr(lErr)); end; //element 1215 end;//group 0009 *) $0010 : case element of $00 : info := 'Patient Group Length'; $10 : begin info := 'Patient''s Name'; t := _string; lDicomData.NamePos := dFilePos(fp); end; $20 : info := 'Patient ID'; $30 : info := 'Patient Date of Birth'; $40 : begin info := 'Patient Sex'; t := _string; end; $1005: info := 'Patient''s Birth Name'; $1010: info := 'Patient Age'; $1030: info := 'Patient Weight'; $21b0: info := 'Additional Patient History'; end; $0018 : case element of $00 : info := 'Acquisition Group Length'; $10 : begin info := 'Contrast/Bolus Agent'; t := _string; end; $15: info := 'Body Part Examined'; $20 : begin info := 'Scanning Sequence';t := _string; end; $21 : begin info := 'Sequence Variant';t := _string; end; $22 : info := 'Scan Options'; $23 : begin info := 'MR Acquisition Type'; t := _string; end; $24 : info := 'Sequence Name'; $25 : begin info := 'Angio Flag';t := _string; end; $30 : info := 'Radionuclide'; $50 : begin info := 'Slice Thickness'; readfloats (fp, remaining, TmpStr, lfloat1, lfloat2, lROK); if not lrOK then goto 666; e_len := 0; remaining := 0; lDICOMdata.XYZmm[3] := lfloat1; end; $60: info := 'KVP'; $70: begin t := _string; info := 'Counts Accumulated'; end; $71: begin t := _string; info := 'Acquisition Condition'; end; $80 : info := 'Repetition Time'; $81 : info := 'Echo Time'; $82 : begin t := _string; info := 'Inversion Time'; end; $83 : begin t := _string; info := 'Number of Averages'; end; $84 : info := 'Imaging Frequency'; $85 : begin info := 'Imaged Nucleus'; t := _string; end; $86 : begin info := 'Echo Number';t := _string; end; $87 : info := 'Magnetic Field Strength'; $88 : info := 'Spacing Between Slices'; $89 : begin t := _string; info := 'Number of Phase Encoding Steps'; end; $90 : info := 'Data collection diameter'; $91 : begin info := 'Echo Train Length';t := _string; end; $93: info := 'Percent Sampling'; $94: info := 'Percent Phase Field View'; $95 : info := 'Pixel Bandwidth'; $1000: begin t := _string; info := 'Device Serial Number'; end; $1020: begin info := 'Software Version';t := _string; end; $1030: info := 'Protocol Name'; $1040: info := 'Contrast/Bolus Route'; $1050 : begin t := _string; info := 'Spatial Resolution'; end; $1060: info := 'Trigger Time'; $1062: info := 'Nominal Interval'; $1063: info := 'Frame Time'; $1081: info := 'Low R-R Value'; $1082: info := 'High R-R Value'; $1083: info := 'Intervals Acquired'; $1084: info := 'Intervals Rejected'; $1088: begin info := 'Heart Rate'; t := _string; end; $1090: begin info := 'Cardiac Number of Images'; t := _string; end; $1094: begin info := 'Trigger Window';t := _string; end; $1100: info := 'Reconstruction Diameter'; $1110: info := 'Distance Source to Detector'; $1111: info := 'Distance Source to Patient'; $1120: info := 'Gantry/Detector Tilt'; $1130: info := 'Table Height'; $1140: info := 'Rotation Direction'; $1149: begin t := _string; info := 'Field of View Dimension[s]'; end; $1150: info := 'Exposure Time'; $1151: info := 'X-ray Tube Current'; $1152 : info := 'Exposure'; $1155: info := 'Radiation Setting'; $1160: info := 'Filter Type'; $1170 : info := 'Generator Power'; $1190 : info := 'Focal Spot[s]'; $1200 : info := 'Date of Last Calibration'; $1201 : info := 'Time of Last Calibration'; $1210: info := 'Convolution Kernel'; $1250: begin t := _string; info := 'Receiving Coil'; end; $1251: begin t := _string; info := 'Transmitting Coil'; end; $1260 : begin t := _string; info := 'Plate Type'; end; $1261 : begin t := _string; info := 'Phosphor Type'; end; $1310: begin info := 'Acquisition Matrix'; TmpStr := ReadStr(fp, remaining,lrOK); if not lrOK then goto 666; e_len := 0; remaining := 0; end; $1312: begin t := _string; info := 'Phase Encoding Direction'; end; $1314: begin t := _string; info := 'Flip Angle'; end; $1315: begin t := _string;info := 'Variable Flip Angle Flag'; end; $1316: begin t := _string;info := 'SAR'; end; $1400: info := 'Acquisition Device Processing Description'; $1401: begin info := 'Acquisition Device Processing Code';t := _string; end; $1402: info := 'Cassette Orientation'; $1403: info := 'Cassette Size'; $1500: info := 'Positioner Motion'; $1510: info := 'Positioner Primary Angle'; $1511: info := 'Positioner Secondary Angle'; $5020: info := 'Processing Function'; $5100: begin t := _string; info := 'Patient Position'; end; $5101: begin info := 'View Position';t := _string; end; $6000: begin info := 'Sensitivity'; t := _string; end; end; $0020 : case element of $00 : info := 'Relationship Group Length'; $0d : info := 'Study Instance UID'; $0e : info := 'Series Instance UID'; $10 : info := 'Study ID'; $11 : begin info := 'Series Number'; t := _string; end; $12 : // begin info := 'Acquisition Number'; t := _string; end; begin info := 'Acquisition Number'; t := _string; lStr := ''; if dFilePos(fp) > (filesz-e_len) then goto 666; GetMem( buff, e_len); dBlockRead(fp, buff{^}, e_len, n); for i := 0 to e_len-1 do if Char(buff[i]) in ['+','-','0'..'9'] then lStr := lStr +(Char(buff[i])); FreeMem( buff); Val(lStr,i,lErr); if lErr = 0 then lDicomData.AcquNum := i;//strtoint(lStr); remaining := 0; tmp := lDicomData.AcquNum; //showmessage(inttostr(tmp)); end; $13 : begin info := 'Image Number'; t := _string; lStr := ''; if dFilePos(fp) > (filesz-e_len) then goto 666; GetMem( buff, e_len); dBlockRead(fp, buff{^}, e_len, n); for i := 0 to e_len-1 do if Char(buff[i]) in ['+','-','0'..'9'] then lStr := lStr +(Char(buff[i])); FreeMem( buff); Val(lStr,i,lErr); if lErr = 0 then lDicomData.ImageNum := i;//strtoint(lStr); remaining := 0; tmp := lDicomData.ImageNum; end; $20 : begin info := 'Patient Orientation'; t := _string; end; $30 : info := 'Image Position'; $32 : info := 'Image Position Patient'; $35 : info := 'Image Orientation'; $37 : info := 'Image Orientation (Patient)'; $50 : info := 'Location'; $52 : info := 'Frame of Reference UID'; $91 : info := 'Echo Train Length'; $70 : info := 'Image Geometry Type'; $60 : info := 'Laterality'; $1001: info := 'Acquisitions in Series'; $1002: info := 'Images in Acquisition'; $1020: info := 'Reference'; $1040: begin info := 'Position Reference'; t := _string; end; $1041: info := 'Slice Location'; $3401: info := 'Modifying Device ID'; $3402: info := 'Modified Image ID'; $3403: info := 'Modified Image Date'; $3404: info := 'Modifying Device Mfg.'; $3405: info := 'Modified Image Time'; $3406: info := 'Modified Image Desc.'; $4000: info := 'Image Comments'; $5000: info := 'Original Image ID'; $5002: info := 'Original Image... Nomenclature'; end; $0028 : case element of $00 : info := 'Image Presentation Group Length'; $02 : begin info := 'Samples Per Pixel'; tmp := read16(fp,lrOK); if not lrOK then goto 666; lDicomData.SamplesPerPixel :=tmp; remaining := 0; end; $04 : begin info := 'Photometric Interpretation';{help} TmpStr := ''; if dFilePos(fp) > (filesz-e_len) then goto 666; GetMem( buff, e_len); dBlockRead(fp, buff{^}, e_len, n); for i := 0 to e_len-1 do if Char(buff[i]) in [{'+','-',' ', }'0'..'9','a'..'z','A'..'Z'] then TmpStr := TmpStr +(Char(buff[i])); FreeMem( buff); if TmpStr = 'MONOCHROME1' then lDicomdata.monochrome := 1 else if TmpStr = 'MONOCHROME2' then lDicomdata.monochrome := 2 else if TmpStr[1] = 'Y' then lDICOMdata.monochrome := 4 else lDICOMdata.monochrome := 3; remaining := 0; e_len := 0; {use tempstr} end; $05 : info := 'Image Dimensions (ret)'; $06 : begin info := 'Planar Configuration'; tmp := read16(fp,lrOK); if not lrOK then goto 666; lDicomData.PlanarConfig :=tmp; remaining := 0; end; $08 : begin t := _string; lStr := ''; if dFilePos(fp) > (filesz-e_len) then goto 666; GetMem( buff, e_len); dBlockRead(fp, buff{^}, e_len, n); for i := 0 to e_len-1 do if Char(buff[i]) in ['+','-','0'..'9'] then lStr := lStr +(Char(buff[i])); FreeMem( buff); Val(lStr,i,lErr); if lErr = 0 then lDicomData.XYZdim[3] := i;//strtoint(lStr); tmp := lDicomData.XYZdim[3]; remaining := 0; if lDicomData.XYZdim[3] < 1 then lDicomData.XYZdim[3] := 1; info := 'Number of Frames'; end; $09: begin info := 'Frame Increment Pointer'; TmpStr := ReadStrHex(fp, remaining,lrOK); if not lrOK then goto 666; e_len := 0; remaining := 0; end; $10 : begin info := 'Rows'; lDicomData.XYZdim[2] := read16(fp,lrOK); if not lrOK then goto 666; tmp := lDicomData.XYZdim[2]; remaining := 0; end; $11 : begin info := 'Columns'; lDicomData.XYZdim[1] := read16(fp,lrOK); if not lrOK then goto 666; tmp := lDicomData.XYZdim[1]; remaining := 0; end; $30 : begin info := 'Pixel Spacing'; readfloats (fp, remaining, TmpStr, lfloat1, lfloat2, lROK); if not lrOK then goto 666; //row spacing [y], then column spacing [x]: see part 3 of DICOM e_len := 0; remaining := 0; lDICOMdata.XYZmm[2] := lfloat1; lDICOMdata.XYZmm[1] := lfloat2; end; $31: info := 'Zoom Factor'; $32: info := 'Zoom Center'; $34: begin info :='Pixel Aspect Ratio';t := _string; end; $40: info := 'Image Format [ret]'; $50 : info := 'Manipulated Image [ret]'; $51: info := 'Corrected Image'; $60: begin info := 'Compression Code [ret]';t := _string; end; $0100: begin info := 'Bits Allocated'; tmp := read16(fp,lrOK); if not lrOK then goto 666; if tmp = 8 then lDicomData.Allocbits_per_pixel := 8 else if tmp = 12 then lDicomData.Allocbits_per_pixel := 12 else if tmp = 16 then lDicomData.Allocbits_per_pixel := 16 else begin lWord := tmp; lWord := swap(lWord); if lWord in [8,12,16] then begin //showmessage(inttostr(lWord)); lDicomData.Allocbits_per_pixel := tmp; lByteSwap := true; end else begin //lDicomData.Allocbits_per_pixel := 8; //asdf if lImageFormatOK then Showmessage('This software can only read 8, 12 and 16 bit DICOM files. This file allocates '+inttostr(tmp)+' bits per voxel.'); lImageFormatOK := false;{} end; end; remaining := 0; end; $0101: begin info := 'Bits Stored'; tmp := read16(fp,lrOK); if not lrOK then goto 666; if tmp <= 8 then lDicomData.Storedbits_per_pixel := 8 else if tmp <= 16 then lDicomData.Storedbits_per_pixel := 16 else begin //lDicomData.Storedbits_per_pixel := 8; //asdf lWord := tmp; lWord := swap(lWord); if lWord in [8,12,16] then begin //showmessage(inttostr(lWord)); lDicomData.Storedbits_per_pixel := tmp; lByteSwap := true; end else begin if lImageFormatOK then Showmessage('This software can only read 8, 12 and 16 bit DICOM files. This file stores '+inttostr(tmp)+' bits per voxel.'); lDicomData.Storedbits_per_pixel := tmp; lImageFormatOK := false;{ } end; end; remaining := 0; end; $0102: begin info := 'High Bit'; tmp := read16(fp,lrOK); if not lrOK then goto 666; (* could be 11 for 12 bit cr images so just skip checking it assert(tmp == 7 || tmp == 15); *) remaining := 0; end; $0103: info := 'Pixel Representation'; $0104: info := 'Smallest Valid Pixel Value'; $0105: info := 'Largest Valid Pixel Value'; $0106: info := 'Smallest Image Pixel Value'; $0107: info := 'Largest Image Pixel Value'; $120: info := 'Pixel Padding Value'; $200: info := 'Image Location [ret]'; $1040: begin t := _string; info := 'Pixel Intensity Relationship'; end; $1050: begin //t := _string; info := 'Window Center'; readfloats (fp, remaining, TmpStr, lfloat1, lfloat2, lROK); if not lrOK then goto 666; e_len := 0; remaining := 0; lDICOMdata.WindowCenter := round(lfloat1); end;{float} $1051: begin info := 'Window Width'; //t := _string; readfloats (fp, remaining, TmpStr, lfloat1, lfloat2, lROK); if not lrOK then goto 666; e_len := 0; remaining := 0; lDICOMdata.WindowWidth := round(lfloat1); end; $1052: begin t := _string;info :='Rescale Intercept'; end; {float} $1053:begin t := _string; info := 'Rescale Slope'; readfloats (fp, remaining, TmpStr, lfloat1, lfloat2, lROK); if not lrOK then goto 666; e_len := 0; remaining := 0; lDICOMdata.intenScale := lfloat1; end; {float} $1054:begin t := _string; info := 'Rescale Type';end; $1100: info := 'Gray Lookup Table [ret]'; $1101: begin info := 'Red Palette Descriptor'; TmpStr := ReadStr(fp, remaining,lrOK); if not lrOK then goto 666; e_len := 0; remaining := 0; end; $1102: begin info := 'Green Palette Descriptor'; TmpStr := ReadStr(fp, remaining,lrOK); if not lrOK then goto 666; e_len := 0; remaining := 0; end; $1103: begin info := 'Blue Palette Descriptor'; TmpStr := ReadStr(fp, remaining,lrOK); if not lrOK then goto 666; e_len := 0; remaining := 0; end; $1199: info := 'Palette Color Lookup Table UID'; $1200: info := 'Gray Lookup Data [ret]'; $1201, $1202,$1203: begin case element of $1201: info := 'Red Table'; {future} $1202: info := 'Green Table'; {future} $1203: info := 'Blue Table'; {future} end; if dFilePos(fp) > (filesz-remaining) then goto 666; if not lReadColorTables then begin dSeek(fp, dFilePos(fp) + remaining); end else begin {load color} width := remaining div 2; if width > 0 then begin getmem(lWordRA,width*2); for i := (width) downto 1 do lWordRA[i] := read16(fp,lrOK); value := 159; //showmessage(inttostr(lWordRA[value])); //lWordRA[width] := 65000; //if (lDICOMdata.little_endian=1) then // showmessage('little.'); value := lWordRA[1]; max16 := value; min16 := value; for i := (width) downto 1 do begin value := lWordRA[i]; if value < min16 then min16 := value; if value > max16 then max16 := value; end; //width..1 if max16 - min16 = 0 then max16 := min16+1; {avoid divide by 0} GetMem( lColorRA, width );(**) for i := width downto 1 do lColorRA[i] := (lWordRA[i] shr 8) {and 255}; FreeMem( lWordRA ); case element of $1201: begin red_table_size := width; red_table :=lColorRA;; end; $1202: begin green_table_size := width; green_table :=lColorRA;; end; else {x$1203:} begin blue_table_size := width; blue_table :=lColorRA;; end; {else} end; {case} end; //width > 0; if odd(remaining) then dSeek(fp, dFilePos(fp) + 1{remaining}); end; {load color} tmpstr := 'Custom'; remaining := 0; e_len := 0; {show tempstr} end; end; $54: case element of $0: info := 'Nuclear Acquisition Group Length'; $11: info := 'Number of Energy Windows'; $21: info := 'Number of Detectors'; $51: info := 'Number of Rotations'; $80: begin info := 'Slice Vector'; TmpStr := ReadStr(fp, remaining,lrOK); if not lrOK then goto 666; e_len := 0; remaining := 0; end; $81: info := 'Number of Slices'; $202: info := 'Type of Detector Motion'; $400: info := 'Image ID'; end; $2010 : case element of $0: info := 'Film Box Group Length'; $100: info := 'Border Density'; end; $4000 : info := 'Text'; $FFFE : begin //lVerbose := false; case element of $E000 : begin if (lReadJPEGtables) and ((lDICOMdata.JPEGLossyCpt) or (lDICOMdata.JPEGLosslessCpt)) and (not lFirstFragment) and (e_len > 1024) and ( (e_len+dFilePos(fp)) <= FileSz) then begin //first fragment is the index table, so the previous line skips the first fragment if (gECATJPEG_table_entries = 0) then begin gECATJPEG_table_entries := lDICOMdata.XYZDim[3]; getmem (gECATJPEG_pos_table, gECATJPEG_table_entries*sizeof(longint)); getmem (gECATJPEG_size_table, gECATJPEG_table_entries*sizeof(longint)); end; if lJPEGentries < gECATJPEG_table_entries then begin inc(lJPEGentries); gECATJPEG_pos_table[lJPEGEntries] := dFilePos(fp); gECATJPEG_size_table[lJPEGEntries] := e_len; end; end; lFirstFragment := false; if (lDICOMdata.CompressOffset =0) and ( (e_len+dFilePos(fp)) <= FileSz) then begin lDICOMdata.CompressOffset := dFilePos(fp); lDICOMdata.CompressSz := e_len; end; //if e_len > lDICOMdata.CompressSz then lDICOMdata.CompressSz := e_len; if e_len > lDICOMdata.CompressSz then lDICOMdata.CompressSz := e_len; info := 'Image Fragment'; //lJPEGOffset := true; dSeek(fp, dFilePos(fp) + e_len); tmpstr := inttostr(e_len); remaining := 0; e_len := 0; {show tempstr} end; $E0DD : begin info := 'Sequence Delimiter'; // showmessage('Sequence Delimiter'+ inttostr(e_len)); if (lDicomData.RunLengthEncoding) or ( ((lDicomData.JPEGLossycpt) or (lDicomData.JPEGLosslesscpt)) and (gECATJPEG_table_entries >= lDICOMdata.XYZdim[3])) then time_to_quit := TRUE; dSeek(fp, dFilePos(fp) + e_len); tmpstr := inttostr(e_len); remaining := 0; e_len := 0; {show tempstr} end; end; end; $FFFC : begin dSeek(fp, dFilePos(fp) + e_len); tmpstr := inttostr(e_len); remaining := 0; e_len := 0; {show tempstr} end; $7FE0 : case element of $00 : begin info := 'Pixel Data Group Length'; if not lImageFormatOK then time_to_quit := TRUE; end; $10 : begin info := 'Pixel Data'; if (not lDicomData.RunLengthEncoding) and (not lDicomData.JPEGLossycpt) and (not lDicomData.JPEGLosslesscpt) then time_to_quit := TRUE; lDicomData.ImageSz := e_len; TmpStr := inttostr(e_len);e_len := 0; end; end; else begin if (group >= $6000) AND (group <= $601e) AND ((group AND 1) = 0) then info := 'Overlay'; if element = $0000 then info := 'Group Length'; if element = $4000 then info := 'Comments'; end; end; lStr := ''; if (Time_TO_Quit) and (not lImageFormatOK) then begin lHdrOK := true; {header was OK} goto 666; end; if (e_len + dfilepos(fp)) > FileSz then //patch for GE files that only fill top 16-bytes w Random data e_len := e_len and $FFFF; if (e_len > $FFFF) {and (dfilepos(fp) > FileSz)} then begin showmessage('Very large DICOM header: is this really a DICOM file? '+inttostr(dfilepos(fp))); goto 666; end;//zebra if (NOT time_to_quit) AND (e_len > 0) and (remaining > 0) then begin if (e_len + dfilepos(fp)) > FileSz then begin if (lDICOMdata.GenesisCpt) or (lDICOMdata.JPEGlosslessCpt) or (lDICOMdata.JPEGlossyCpt) then lHdrOK := true else showmessage('Error: Dicom header exceeds file size.'); goto 666; end; if e_len > 0 then begin GetMem( buff, e_len); dBlockRead(fp, buff{^}, e_len, n); if lVerboseRead then case t of unknown : case e_len of 1 : lStr := ( IntToStr(Integer(buff[0]))); 2 : Begin if lDicomData.little_endian <> 0 then i := Integer(buff[0]) + 256*Integer(buff[1]) else i := Integer(buff[0])*256 + Integer(buff[1]); lStr :=( IntToStr(i)); end; 4 : Begin if lDicomData.little_endian <> 0 then i := Integer(buff[0]) + 256*Integer(buff[1]) + 256*256*Integer(buff[2]) + 256*256*256*Integer(buff[3]) else i := Integer(buff[0])*256*256*256 + Integer(buff[1])*256*256 + Integer(buff[2])*256 + Integer(buff[3]); lStr := (IntToStr(i)); end; else begin if e_len > 0 then begin for i := 0 to e_len-1 do begin if Char(buff[i]) in ['+','-','/','\',' ', '0'..'9','a'..'z','A'..'Z'] then lStr := lStr+(Char(buff[i])) else lStr := lStr+('.'); end; {for i..e_len} end else lStr := '*NO DATA*'; end; end; i8, i16, i32, ui8, ui16, ui32, _string : for i := 0 to e_len-1 do if Char(buff[i]) in ['+','-','/','\',' ', '0'..'9','a'..'z','A'..'Z'] then lStr := lStr +(Char(buff[i])) else lStr := lStr +('.'); end; FreeMem(buff); end; {e_len > 0... get mem} end else if e_len > 0 then lStr := (IntToStr(tmp)) else {if e_len = 0 then} begin //TmpStr := '?'; lStr := TmpStr; end; {add this to show length size ->}// lStr := lStr +'/'+inttostr(e_len); if (lGrp{info = 'identifying group'{}) then if MessageDlg(lStr+'= '+info+' '+IntToHex(where,4)+': ('+IntToHex(group,4)+','+IntToHex(element,4)+')'+IntToStr(e_len)+'. Continue?', mtConfirmation, [mbYes, mbNo], 0) = mrNo then GOTO 666; // if info = 'UNKNOWN' then showmessage(IntToHex(group,4)+','+IntToHex(element,4)); if lverboseRead then begin if length(lDynStr) > kMaxTextBuf then begin if not lTextOverFlow then begin lDynStr := lDynStr + 'Only showing the first '+inttostr(kMaxTextBuf) +' characters of this LARGE header'; lTextOverFlow := true; end; //showmessage('Unable to display the entire header.'); //goto 666; end else lDynStr := lDynStr +IntToHex(group,4)+','+IntToHex(element,4)+','{+inttostr(where)+': '+lGrpStr}+Info+': '+lStr+kCR ; end; //not verbose read end; // end for lDicomData.ImageStart := dfilepos(fp); if lBigSet then begin if LBig then lDicomData.little_endian := 0 else lDicomData.little_endian := 1; end; lHdrOK := true; if lByteSwap then begin ByteSwap(lDicomdata.XYZdim[1]); ByteSwap(lDicomdata.XYZdim[2]); if lDicomdata.XYZdim[3] <> 1 then ByteSwap(lDicomdata.XYZdim[3]); ByteSwap(lDicomdata.SamplesPerPixel); ByteSwap(lDicomData.Allocbits_per_pixel); ByteSwap(lDicomData.Storedbits_per_pixel); end; 666: if lDiskCacheSz > 0 then freemem(lDiskCacheRA); if not lHdrOK then lImageFormatOK := false; CloseFile(fp); FileMode := 2; //set to read/write end; end.