Code for TIFF export
jamie
Exporting a TIFF image with Igor Script Alone
This function exports a multiplane greyscale tiff from a 3D wave. Igor at one point didn't export multiplane TIFFs "out of the box'. Before that, it didn't import them, either, but I have lost my TIFF import procedure.
This function handles waves of different bit depths, signed or unsigned, and sets the tags appropriately so other apps will know how to open it, if they support it, that is. I have tested 8 and 16 bit signed and unsigned integer and 32 bit floating point import into ImageJ.
This should work with Igor Pro version 4, though I haven't recently recently tested it, not having version 4 lying around.
Function ExportGreyScaleTIFF (datawave, ExportPath)
wave DataWave //reference to a 2 or 3d wave with greyscale information
String ExportPath //contains the name of an Igor Path where the file will be saved
//make sure the wave exists
if (!(WaveExists (dataWave)))
doAlert 0, "Sorry, but the wave, " + nameofwave (datawave) + ", does not exist."
return 1
endif
//Check the path
PathInfo $ExportPath
if (V_Flag == 0)
if ((cmpStr (ExportPath, "")) == 0)
ExportPath = "ExportPath"
endif
NewPath /M="Where do you want to save the TIFF?" /O/Q ExportPath
if (V_Flag) // User cancelled the dialog to make new path
return 1
endif
endif
//check wave diminsions. 1D and 4D waves are no-go.
variable imdims = wavedims (datawave)
if (imdims == 1)
doalert 0, "Sorry, but you need an image or an image stack to write a tiff file."
return 1
else
if (imdims == 4)
doalert 0, "Sorry, but this procedure hasn't been extended to 4 dimensions yet"
return 1
endif
endif
// No Complex waves
if (WaveType(Datawave) & 0x01)
doalert 0, "Sorry, but this procedure doesn't do complex waves."
return 1
endif
//Make a name for the exported tiff from the wavename plus the tif extension
String FileNameStr = nameofwave (datawave) + ".TIF"
// Check the file name/location for existence
FileNameStr = CheckFileOnDisk (ExportPath, FileNameStr)
if ((cmpstr (FileNameStr, "")) == 0)
return 1
endif
//define some variables and find out some information about the image,
// also make a temp wave of right bit depth to hold a plane of the wave
variable ii // used for iterations
variable temp // a variable used to hold various values temporarily while writing to the file
variable imwidth = dimsize (datawave, 0) // the width of the image, in pixels
variable imlength=dimsize (datawave, 1) // the length of the image, in pixels
variable imdepth = max (1, dimsize (datawave, 2))// the image depth, i.e., number of planes
variable sampleBits // number of bits/per sample (8, 16, or 32)
variable sampleFormat // Sample Format 1 = unsigned integer data,
// 2 = two’s complement signed integer data ,3 = IEEE floating point data [IEEE]
if (!(datafolderExists ("root:packages:")))
newdatafolder root:packages
endif
if (WaveType (Datawave) & 0x04) // 64 bit floating point
SampleBits = 64
make/o/D/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
elseif (WaveType (DataWave) & 0x02) // 32 bit floating point
sampleBits = 32
make/o/s/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
elseif (WaveType (DataWave) & 0x20) // 32 bit integer
sampleBits = 32
if (WaveType(datawave) & 0x40) // unsigned
SampleFormat = 1
make/I/u/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
else
SampleFormat = 2 // signed
make/I/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
endif
elseif (WaveType(datawave) & 0x10) // 16 bit integer
sampleBits = 16
if (WaveType(datawave) & 0x40) // unsigned
SampleFormat = 1
make/W/u/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
else
SampleFormat = 2 // signed
make/W/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
endif
elseif (WaveType(datawave) & 0x08) // 8 bit integer
sampleBits = 8
if (WaveType(datawave) & 0x40) // unsigned
SampleFormat = 1
make/B/u/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
else
SampleFormat = 2 // signed
make/B/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
endif
else
doalert 0, "Sorry, but the data type of the wave, " + nameofwave (dataWave) + ", was not recognized."
return 1
endif
WAVE aplane = root:Packages:aTIFFplane
variable imBytes = imwidth * imlength * (sampleBits/8) // the number of bytes in an individual image plane
string byteOrderStr // MM for Macintosh, II for intel; we will write native byte order
// for the platform we are running on
if ((cmpstr (IgorInfo(2), "Macintosh")) == 0)
byteOrderStr = "MM"
else
byteOrderStr = "II"
endif
// Open a new file in the export path directory
variable daRefNum // reference number of the file we will open
Open/P=ExportPath/T= "TIFF" darefNum as FileNameStr
// first write the byte order string and the magic 42, in two bytes each
FBinWrite darefNum, byteOrderStr
temp = 42
FBinWrite /F=2/U darefNum, temp
//write offset to the first IFD unsigned 4 bytes, it will be after this 8 bit header, so 8
temp = 8
FBinWrite /F=3/U darefNum, temp
//Iterate through each plane in the image, making an image file directory and writing the plane
// Thus, IFDs and images alternate in the file. One could make a TIFF file with the IFDs all at the start, or
// any other way you like, this just seemed simplest to me.
For (ii = 0; ii < imdepth; ii += 1)
//write the IFD - start with 2 byte count of number of directories
temp = 10
FBinWrite /F=2/U darefNum, temp
temp = 256 //tag 256 = image width
FBinWrite /F=2 darefNum, temp
temp = 4 // Field type 4byte unsihned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
FBinWrite /F=3/U darefNum, imwidth
temp = 257 // tag 257 = image length
FBinWrite /F=2 darefNum, temp
temp = 4 // Field type 4byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
FBinWrite /F=3/U darefNum, imlength
temp = 258 // tag258 = bits/sample
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = sampleBits // number of bits per sample
FBinWrite /F=2/U darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 259 // tag259 = compression
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2 darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = 1 // No compression
FBinWrite /F=2 darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 262 // tag262 = photometric interpretation
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 //number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = 1 // black is 0, max value is white
FBinWrite /F=2/U darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 273 // tag 273 = strip offsets, we will only make 1 strip, so there is only one offset
FBinWrite /F=2 darefNum, temp
temp = 4 // Field type 4 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 //number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = 134 + ((126+ imBytes) * ii)// The image starts immediatley after this IFD.
// Initial header is 8 bytes, an IFD is 126 bytes
FBinWrite /F=3/U darefNum, temp
temp = 277 // tag 277 = samples/pixel
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = 1 // 1 sample/pixel, i.e., greyscale image
FBinWrite /F=2 darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 278 // tag278 = rows/strip we only make 1 strip/image, so this is the same as rows
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1
FBinWrite /F=3/U darefNum, temp
temp = imlength //1 strip/image, so this is the same as rows
FBinWrite /F=2/U darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 279 // tag279 = strip bytecounts (number of bytes in each strip, after compresion)
FBinWrite /F=2 darefNum, temp
temp = 4 // Field type 4 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
FBinWrite /F=3/U darefNum, imBytes // only 1 strip, so byte count is same as bytes in an image
temp = 339 // tag339 = Data Sample Format
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of samples = 1
FBinWrite /F=3/U darefNum, temp
FBinWrite /F=2 darefNum, sampleFormat
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
// Last thing in the IFD is 4 bytes for offset to start of next IFD (it will be right after the image),
//unless it's the last image plane, when 4 bytes of 0 suffice
if (ii < imdepth-1)
temp = 8 + ((126 +imBytes) * (ii + 1))
else
temp= 0
endif
FBinWrite/F=3 darefnum, temp
// finally, write the image plane
aplane = datawave [p] [q] [ii]
FBinWrite /f =0 darefNum,aplane
endfor
//Clean up
close darefnum
killwaves/z aplane
return 0
end
wave DataWave //reference to a 2 or 3d wave with greyscale information
String ExportPath //contains the name of an Igor Path where the file will be saved
//make sure the wave exists
if (!(WaveExists (dataWave)))
doAlert 0, "Sorry, but the wave, " + nameofwave (datawave) + ", does not exist."
return 1
endif
//Check the path
PathInfo $ExportPath
if (V_Flag == 0)
if ((cmpStr (ExportPath, "")) == 0)
ExportPath = "ExportPath"
endif
NewPath /M="Where do you want to save the TIFF?" /O/Q ExportPath
if (V_Flag) // User cancelled the dialog to make new path
return 1
endif
endif
//check wave diminsions. 1D and 4D waves are no-go.
variable imdims = wavedims (datawave)
if (imdims == 1)
doalert 0, "Sorry, but you need an image or an image stack to write a tiff file."
return 1
else
if (imdims == 4)
doalert 0, "Sorry, but this procedure hasn't been extended to 4 dimensions yet"
return 1
endif
endif
// No Complex waves
if (WaveType(Datawave) & 0x01)
doalert 0, "Sorry, but this procedure doesn't do complex waves."
return 1
endif
//Make a name for the exported tiff from the wavename plus the tif extension
String FileNameStr = nameofwave (datawave) + ".TIF"
// Check the file name/location for existence
FileNameStr = CheckFileOnDisk (ExportPath, FileNameStr)
if ((cmpstr (FileNameStr, "")) == 0)
return 1
endif
//define some variables and find out some information about the image,
// also make a temp wave of right bit depth to hold a plane of the wave
variable ii // used for iterations
variable temp // a variable used to hold various values temporarily while writing to the file
variable imwidth = dimsize (datawave, 0) // the width of the image, in pixels
variable imlength=dimsize (datawave, 1) // the length of the image, in pixels
variable imdepth = max (1, dimsize (datawave, 2))// the image depth, i.e., number of planes
variable sampleBits // number of bits/per sample (8, 16, or 32)
variable sampleFormat // Sample Format 1 = unsigned integer data,
// 2 = two’s complement signed integer data ,3 = IEEE floating point data [IEEE]
if (!(datafolderExists ("root:packages:")))
newdatafolder root:packages
endif
if (WaveType (Datawave) & 0x04) // 64 bit floating point
SampleBits = 64
make/o/D/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
elseif (WaveType (DataWave) & 0x02) // 32 bit floating point
sampleBits = 32
make/o/s/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
elseif (WaveType (DataWave) & 0x20) // 32 bit integer
sampleBits = 32
if (WaveType(datawave) & 0x40) // unsigned
SampleFormat = 1
make/I/u/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
else
SampleFormat = 2 // signed
make/I/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
endif
elseif (WaveType(datawave) & 0x10) // 16 bit integer
sampleBits = 16
if (WaveType(datawave) & 0x40) // unsigned
SampleFormat = 1
make/W/u/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
else
SampleFormat = 2 // signed
make/W/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
endif
elseif (WaveType(datawave) & 0x08) // 8 bit integer
sampleBits = 8
if (WaveType(datawave) & 0x40) // unsigned
SampleFormat = 1
make/B/u/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
else
SampleFormat = 2 // signed
make/B/o/n= ((imwidth), (imlength)) root:Packages:aTIFFplane
endif
else
doalert 0, "Sorry, but the data type of the wave, " + nameofwave (dataWave) + ", was not recognized."
return 1
endif
WAVE aplane = root:Packages:aTIFFplane
variable imBytes = imwidth * imlength * (sampleBits/8) // the number of bytes in an individual image plane
string byteOrderStr // MM for Macintosh, II for intel; we will write native byte order
// for the platform we are running on
if ((cmpstr (IgorInfo(2), "Macintosh")) == 0)
byteOrderStr = "MM"
else
byteOrderStr = "II"
endif
// Open a new file in the export path directory
variable daRefNum // reference number of the file we will open
Open/P=ExportPath/T= "TIFF" darefNum as FileNameStr
// first write the byte order string and the magic 42, in two bytes each
FBinWrite darefNum, byteOrderStr
temp = 42
FBinWrite /F=2/U darefNum, temp
//write offset to the first IFD unsigned 4 bytes, it will be after this 8 bit header, so 8
temp = 8
FBinWrite /F=3/U darefNum, temp
//Iterate through each plane in the image, making an image file directory and writing the plane
// Thus, IFDs and images alternate in the file. One could make a TIFF file with the IFDs all at the start, or
// any other way you like, this just seemed simplest to me.
For (ii = 0; ii < imdepth; ii += 1)
//write the IFD - start with 2 byte count of number of directories
temp = 10
FBinWrite /F=2/U darefNum, temp
temp = 256 //tag 256 = image width
FBinWrite /F=2 darefNum, temp
temp = 4 // Field type 4byte unsihned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
FBinWrite /F=3/U darefNum, imwidth
temp = 257 // tag 257 = image length
FBinWrite /F=2 darefNum, temp
temp = 4 // Field type 4byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
FBinWrite /F=3/U darefNum, imlength
temp = 258 // tag258 = bits/sample
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = sampleBits // number of bits per sample
FBinWrite /F=2/U darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 259 // tag259 = compression
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2 darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = 1 // No compression
FBinWrite /F=2 darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 262 // tag262 = photometric interpretation
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 //number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = 1 // black is 0, max value is white
FBinWrite /F=2/U darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 273 // tag 273 = strip offsets, we will only make 1 strip, so there is only one offset
FBinWrite /F=2 darefNum, temp
temp = 4 // Field type 4 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 //number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = 134 + ((126+ imBytes) * ii)// The image starts immediatley after this IFD.
// Initial header is 8 bytes, an IFD is 126 bytes
FBinWrite /F=3/U darefNum, temp
temp = 277 // tag 277 = samples/pixel
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
temp = 1 // 1 sample/pixel, i.e., greyscale image
FBinWrite /F=2 darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 278 // tag278 = rows/strip we only make 1 strip/image, so this is the same as rows
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1
FBinWrite /F=3/U darefNum, temp
temp = imlength //1 strip/image, so this is the same as rows
FBinWrite /F=2/U darefNum, temp
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
temp = 279 // tag279 = strip bytecounts (number of bytes in each strip, after compresion)
FBinWrite /F=2 darefNum, temp
temp = 4 // Field type 4 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of values = 1
FBinWrite /F=3/U darefNum, temp
FBinWrite /F=3/U darefNum, imBytes // only 1 strip, so byte count is same as bytes in an image
temp = 339 // tag339 = Data Sample Format
FBinWrite /F=2 darefNum, temp
temp = 3 // Field type 2 byte unsigned integer
FBinWrite /F=2/U darefNum, temp
temp = 1 // number of samples = 1
FBinWrite /F=3/U darefNum, temp
FBinWrite /F=2 darefNum, sampleFormat
temp = 0 // need to pad with 0
FBinWrite /F=2/U darefNum, temp
// Last thing in the IFD is 4 bytes for offset to start of next IFD (it will be right after the image),
//unless it's the last image plane, when 4 bytes of 0 suffice
if (ii < imdepth-1)
temp = 8 + ((126 +imBytes) * (ii + 1))
else
temp= 0
endif
FBinWrite/F=3 darefnum, temp
// finally, write the image plane
aplane = datawave [p] [q] [ii]
FBinWrite /f =0 darefNum,aplane
endfor
//Clean up
close darefnum
killwaves/z aplane
return 0
end
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
Seems that ImageSave now enables this
December 17, 2024 at 11:28 am - Permalink