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

Forum

Support

Gallery

Igor Pro 8

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More