{ NIH-Image Macro to import ACR-NEMA 2.0 and DICOM 3.0 images Tested on NIH-Image 1.55 and 1.56 beta PPC This macro provides a simple way to import ACR-NEMA and DICOM 3.0 medical image files in NIH-Image. These formats use variable-length headers. I tried it with a various ACR-NEMA and some DICOM 3.0 images, coming from Internet archives, from various manufacturers like Elscint, Picker, and third-party developers like Evergreen Technologies. I had problems importing KODAK PDS 2.5 storage files, using an inversed byte order and separate header file. It certainly should not work with many other semi-proritary format files. It doesn't work yet either with part 10 complient DICOM 3.0 files unfortunately. I am working on part 10 compatible images from GE. ==== How to use ===== 1) Load the Macro using "Special", "Load Macros" menus. 2) The images you want to import have to be named "001.###", "002.###"... and so on. This a limitation that should be fixed in a later version. "###" can be any suffix. ACR-NEMA images often use the ".acr" suffix, and DICOM files ".dcm". 3) Type'a' to invoke the macro. It first asks you for the suffix, with the default answer "acr.". Change it if needed. 4) It then asks you the number of the first image. Defaults to 001. Change if needed. The number of the last image defaults to 020. If you want import one image then re-enter the first image number. 5) The macro then asks you to locate the first image in order to get its path. Be sure to select THE first image ("001.acr", for instance), it wouldn't work otherwise. 6) The macro will ask you the limit resolution to scale up small images to double resolution. It defaults to 300. Set it to a higer value if you don't want to scale up your images. 7) The "Display ACR-NEMA data?" defaults to no, because I did not have the time to develop a neat display of these data on image windows. But if you need this data, then change it to "Yes". It displays all data it can read, modifying pixel data, which is not desirable. ====== Written by ====== Tunc Iyriboz, MD, radiologist, Argenteuil Hospital, FRANCE Comments and bug reports to : iyriboz@dialup.francenet.fr I would like to know about your experience with this macro. Please give details about source of images used when reporting problems. Version 0.9 beta, last modified October 19, 1994 } var xscale,yscale,i,x,y,z,w:real; {global variables} thestring,otherstring:string; {global variables} procedure gettagcontentstring(fileposition,elementlength); begin tagcontent:=''; z:=fileposition+8; for x:=0 to elementlength-1 do begin tagcontent:=concat(tagcontent, chr(LineBuffer[z+x])); end; end; procedure ScaleImage(pixelspacing); begin z:=length(pixelspacing); showmessage('Pixel spacing calibrated for: ' pixelspacing); if z>=3 then begin y:=Pos('\',pixelspacing); thestring:=pixelspacing; Delete(thestring,1,y); yscale:=StringToNum(thestring); thestring:=pixelspacing; Delete(thestring,y,y); xscale:=StringToNum(thestring); setscale(1/xscale,'mm'); end; end; procedure getdicoelementname(groupword1,groupword2,elementword1,elementword2); begin dicoelementname:='¥!(Unrecognized ACR-NEMA/DICOM 3.0 data element)'; if groupword2=0 then begin {00xx,xxxx} if groupword1=8 then begin {0008,xxxx} if elementword2=0 then begin {0008,00xx} if elementword1=0 then dicoelementname:='¥Identifying Information group'; if elementword1=16 then dicoelementname:='Recognition code'; if elementword1=32 then dicoelementname:='Study date'; if elementword1=33 then dicoelementname:='Series date'; if elementword1=34 then dicoelementname:='Acquisition date'; if elementword1=35 then dicoelementname:='Image date'; if elementword1=48 then dicoelementname:='Study time'; if elementword1=49 then dicoelementname:='Series time'; if elementword1=50 then dicoelementname:='Acquisition time'; if elementword1=51 then dicoelementname:='Image time'; if elementword1=96 then dicoelementname:='Modality'; if elementword1=112 then dicoelementname:='Manufacturer'; if elementword1=128 then dicoelementname:='Institution name'; end; if elementword2=16 then begin {0008,10xx} if elementword1=144 then dicoelementname:='Manufacurers model name'; end; end; if groupword1=16 then begin {0010,xxxx} if elementword2=0 then begin {0010,00xx} if elementword1=0 then dicoelementname:='¥Patient Information group'; if elementword1=16 then dicoelementname:='Patient name'; if elementword1=32 then dicoelementname:='Patient ID'; if elementword1=48 then dicoelementname:='Patient birthdate'; if elementword1=64 then dicoelementname:='Patient sex'; end; if elementword2=16 then begin {0010,10xx} if elementword1=16 then dicoelementname:='Patient age'; if elementword1=48 then dicoelementname:='Patient weight'; end; end; if groupword1=24 then begin {0018,xxxx} if elementword2=0 then begin {0018,00xx} if elementword1=0 then dicoelementname:='¥Acquisition Information group'; if elementword1=80 then dicoelementname:='Slice Thickness'; end; end; if groupword1=32 then begin {0020,xxxx} if elementword2=0 then begin {0020,00xx} if elementword1=0 then dicoelementname:='¥Relationship Information group'; if elementword1=19 then dicoelementname:='Image number'; if elementword1=80 then dicoelementname:='location (ret)'; end; if elementword2=16 then begin {0020,10xx} if elementword1=2 then dicoelementname:='Images in acquisition'; if elementword1=65 then dicoelementname:='Slice location'; end; end; if groupword1=40 then begin {0028,xxxx} if elementword2=0 then begin {0028,00xx} if elementword1=0 then dicoelementname:='¥Relationship Information group'; if elementword1=5 then dicoelementname:='¥Image Dimensions'; if elementword1=16 then dicoelementname:='¥Rows'; if elementword1=17 then dicoelementname:='¥Columns'; if elementword1=48 then dicoelementname:='Pixel spacing'; end; if elementword2=1 then begin {0028,01xx} if elementword1=0 then dicoelementname:='¥Bits allocated'; if elementword1=1 then dicoelementname:='¥Bits stored'; if elementword1=2 then dicoelementname:='¥High bit'; if elementword1=3 then dicoelementname:='¥Pixel representation'; end; end; end; if groupword2=127 then begin {7Fxx,xxxx} if groupword1=224 then begin {7FE0,xxxx} if elementword2=0 then begin {7FE0,00xx} if elementword1=0 then dicoelementname:='¥Pixel Data Information group'; if elementword1=16 then dicoelementname:='¥Pixel Data'; end; end; end; end; macro 'Import ACR-NEMA 2.0 Files [a]'; var width,height,rows,columns,offset,groupword2,groupword1,elementword1,elementword2:integer; highbit,elementlength,dicoelement,fileposition,i,good,Winminval,Winmaxval,scalelimit:integer; tagcontent,patname,bitstored,dicoelementname,imno,stackname,filesuffix,datadisplay,pixelspacing: string; filename,patientstack,FirstFileNumber,LastFileNumber,Gap:integer; begin pixelspacing:='\'; patientstack:=0; FileSuffix:=GetString('Please enter file suffix','.acr'); FirstFilenumber:=GetNumber('Open begining file number',1); LastFileNumber:=GetNumber('Until file number',20); ScaleLimit:=GetNumber('Scale to double size if rows <',300); Datadisplay:=GetString('Display acr-nema data?','no'); for filename:=FirstFileNumber to LastFileNumber do begin {loop to import several files from a directory} dicoelementname:=''; bitstored:='16-bits'; good:=0; groupword1:=0; elementword1:=0; groupword2:=0; elementword2:=0; fileposition:=0; width:=4096; height:=1; offset:=0; SetImport('8-bits autoscale'); SetCustom(width,height,offset); Import(filename:3,filesuffix); {loop to import several files from a directory} GetRow(0,0,4096); {read header info into LineBuffer} Dispose; SetNewSize(300,300); SetBackgroundColor(0); SetForegroundColor(255); if (datadisplay='yes') then begin MakeNewWindow ('ACR-NEMA data'); {ACR-NEMA data pic window : faster, bitmap} {NewTextWindow ('ACR-NEMA data');} {ACR-NEMA data text window: slower, text} SelectWindow('ACR-NEMA data'); SetFont('times'); {ACR-NEMA data pic window : font setting} SetFontSize(9); {ACR-NEMA data pic window : font size} Moveto(5,5); {ACR-NEMA data pic window : write position} end; {get ACR-NEMA tags from header : } while (dicoelementname<>'pixel data') and (fileposition<4096) do begin {get one whole tag and its length : } groupword1:=(LineBuffer[fileposition+0]); groupword2:=(LineBuffer[fileposition+1]); elementword1:=(LineBuffer[fileposition+2]); elementword2:=(LineBuffer[fileposition+3]); elementlength:=LineBuffer[fileposition+4]+(LineBuffer[fileposition+5])*256; {get unique data element name from macro dictionnary : } getdicoelementname(groupword1,groupword2,elementword1,elementword2); {interpret binary data : } if ord(dicoelementname)=165 then begin {interpret binary data : } Delete(dicoelementname,1,1); {delete binary marker¥ : } if (dicoelementname='rows') then begin rows:=Linebuffer[fileposition+8]+(LineBuffer[fileposition+9])*256; height:=rows; good:=1; end; if (dicoelementname='columns') then begin columns:=LineBuffer[fileposition+8]+(LineBuffer[fileposition+9])*256; width:=columns; good:=1; end; if (dicoelementname='bits stored') then begin z:=LineBuffer[fileposition+8]+(LineBuffer[fileposition+9])*256; if z=8 then bitstored:='8-bits'; if (z=16) then bitstored:='16-bits'; if (z=12) then bitstored:='12-bits'; end; if (dicoelementname='high bit') then begin highbit:=LineBuffer[fileposition+8]+(LineBuffer[fileposition+9])*256; end; end else begin {interpret ascii data : } gettagcontentstring(fileposition,elementlength); if datadisplay='yes' then writeln(dicoelementname,' : ',tagcontent); if dicoelementname='patient name' then patname:=tagcontent; if dicoelementname='image number' then imno:=tagcontent; if dicoelementname='pixel spacing' then pixelspacing:=tagcontent; end; offset:=fileposition+8; fileposition:=elementlength+8+fileposition; end; {prepare to open actual image : } {fileposition+8=header length : } SetCustom(width,height,offset); if ((bitstored='16-bits') or (bitstored='12-bits')) then begin {SetImportMinMax(0,600);} if Winmaxval=0 then begin Winminval:=Getnumber('Minimum Window Value',Winminval); Winmaxval:=Getnumber('Maximum Window Value',Winmaxval); end; SetImportMinMax(Winminval,Winmaxval); SetImport('16-bits Signed Swap Bytes Calibrate Fixed'); end else begin SetImportMinMax(0,255); Setimport('8-bits Fixed'); end; {bad : } {if not good then begin PutMessage('This file does not seem to be a valid ACR-NEMA file!!!'); dispose; exit; end;} {import the actual image : } Import(filename:3,filesuffix); {loop to import several files from a directory} {Import('');} if highbit=7 then invert; Setpicname(patname,'.',imno); if (rows interpolated zoom'); Setpicname('atbeni'); SetScaling ('Nearest New Window'); ScaleandRotate (2,2,0); SetOption; Smooth; SelectWindow('atbeni'); Dispose; SelectWindow('Untitled'); Setpicname(patname,'.',imno); height:=height*2; width:=width*2; end; SelectAll; Copy; Dispose; if patientstack=0 then begin stackname:=patname; SetNewSize(height,width); MakeNewStack(stackname); patientstack:=1; end else begin SelectWindow(stackname); addslice; end; Paste; if (datadisplay='yes') then begin {SelectWindow('ACR-NEMA data'); SelectAll; Copy; SelectWindow(stackname); MakeROI(1,213,300,300); Paste; DoOr;} SelectWindow('ACR-NEMA data'); Invert; SelectAll; Copy; Dispose; SelectWindow(stackname); MakeROI(0,212,300,300); Paste; DoAnd; end; end; {loop to import several files from a directory} ScaleImage(pixelspacing); end;