Code for TIFF export

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

Seems that ImageSave   now enables this

ImageSave/T="Tiff"/c=1/U/DS=32/O   

Forum

Support

Gallery

Igor Pro 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More