Parameters lookup functions

Nika can be extended to look up calibration and normalization parameters automatically from various external records. This eliminates the need to manually locate and enter these values for each image. The following examples can be copied and modified to suit specific instruments and data formats.

Lookup from external text file

This method is used when image files (such as TIFFs) do not store metadata internally. The main disadvantage is that metadata and image files can become separated, and text file formats often change over time, requiring the lookup code to be updated accordingly. Despite these limitations, this is a widely used approach and needs to be supported.

Two common metadata file arrangements exist:

  1. One large text file per folder — A single text file in the same folder as the images (or in its parent folder), containing one line of information per image, usually including the image filename in each line.

  2. One text file per image or group of images — Each image (or group of images) has an associated text file with a related name. The text file may contain header lines followed by data lines, one per image.

For the first arrangement, the most efficient approach is to load the text file into Igor waves in a separate folder and look up values from those waves. Write a dedicated Igor function to load the file so that wave names and locations remain consistent.

For the second arrangement, the following example function shows how to read a specific value from a per-image text file. The setup assumed here: a folder contains groups of image files named image_file_01.tif, image_file_02.tif, etc. (pattern: ArbitraryString_XY.tif). Each group has an associated text file named image_file.txt (ArbitraryString.txt). The text file contains 9 header lines followed by tab-separated data lines, one per image, with columns: date, time, filename, Monitor1, Monitor2, Thickness, Transmission.

Example data line in a text editor:

10/04/2017 15:54:04 170410_refs_01.tif  87.259 87.145  1  45.457

Same line as seen by Igor (tabs shown as \t):

10/04/2017 15:54:04\t170410_refs_01.tif\t87.259\t87.145\t1\t45.457;

The following function reads the sample thickness (column 5, zero-indexed as item 4) from this text file:

Function ReadThickness(FileName)
     string FileName
    //This function depends on a fixed naming structure and known text file content.
    //Modify to return different values from the table as needed.
    //
    //To use: place this code in a .ipf file in
    //Documents/Wavemetrics/Igor Pro 9 Igor Procedures/
    //ipf files in that folder load automatically when Igor starts.
    //
    //Then in Nika: check "use sample Thickness (t)" checkbox and
    //on tab "Par" check "Use fnct?" for Sa Thickness; in the field
    //type ReadThickness (no quotes or parentheses, just the function name)
    //
    //This function assumes the naming template:
    //  image file: imageName_XX.tif  ->  text file: imageName.txt
    //Text file content line example:
    //  date time\tFileName\tMon1\tMon2\tThickness\tTransmission[%]
    //  10/04/2017 15:54:04\t170410_refs_01.tif\t87.259\t87.145\t1\t45.457
    //Returns the 5th element (thickness, item 4 in zero-based indexing)
    //
        // Check that the path to 2D data exists (Nika's symbolic path)
    PathInfo Convert2Dto1DDataPath
    if(V_Flag<1)
      Abort "Path to 2D data does not exist"
    endif
        // Determine the text file name from the image file name
    string TextFileName
    variable NumOfSeparators
    NumOfSeparators = ItemsInList(FileName, "_")
          // Assume the last "_"-separated segment is the XX.tif part
    string EndStuff=StringFromList(NumOfSeparators-1, FileName, "_")
    TextFileName = ReplaceString(EndStuff, FileName, "")
    TextFileName = removeEnding(TextFileName,"_")
    TextFileName = TextFileName+".txt"
        // Open the file as read-only (must be closed when done)
    variable i, refNum, matched
    string aLine
    Open /P=Convert2Dto1DDataPath /R /T=".txt" refNum  as TextFileName
        // Skip the 9 header lines
    For(i=0;i<10;i+=1)
      FreadLine refNum, aLine
    endfor
        // Read subsequent lines until the one containing this image filename is found
    Do
      i+=1
      FreadLine refNum, aLine
      if(strlen(aline)<1)
        Abort "Data for the image name "+FileName+" was not found in the text file."
      endif
      if(GrepString(aLine, FileName ))
        matched=1
        endif
    while(!matched)
    close refNum              // Important: always close the file
        // aLine now contains the data line for this image
        // Replace tabs with semicolons for use with StringFromList
    aLine=ReplaceString("\t", aLine, ";")+";"
        // Extract thickness (item 4, zero-based); Nika expects thickness in [mm]
    variable result
    result = str2num(StringFromList(4, aline, ";"))
      // Note: this approach is practical for up to a few hundred lines.
      // For thousands of images, load the full text file into Igor waves first
      // and look up values from those waves instead.
    return result
end

Lookup from wave note metadata

When Nika loads an image with embedded metadata — such as HDF5/NeXus files (see Nexus) — it appends the metadata to the image as a wave note in Keyword=Value; format:

KeyWord1=Value1;KeyWord2=Value2;...

Knowing the keyword names for the values of interest makes lookup straightforward using Igor’s NumberByKey function.

Useful wave paths:

  • Current 2D image: root:Packages:Convert2Dto1D:CCDimageToConvert

  • Current 2D empty: root:Packages:Convert2Dto1D:EmptyData

  • Current 2D dark: root:Packages:Convert2Dto1D:DarkFieldData

The following function reads an ion chamber count value from the wave note for normalization purposes. A similar approach can extract photodiode and ion chamber readings from both sample and blank images to calculate per-sample transmission automatically.

Function FindI0(SampleName)
  string sampleName
  Wave/Z w2D = root:Packages:Convert2Dto1D:CCDimageToConvert
  if(!WaveExists(w2D))
      Abort "image file not found"
  endif
  string OldNOte=note(w2D)
  //OldNOte should have data like this: ...;I0_cts=56.5;I0_gain=1000000;...
  variable I0 = NumberByKey("I0_cts", OldNote  , "=" , ";")
  variable I0gain = NumberByKey("I0_gain", OldNote  , "=" , ";")
  I0 = I0 / I0gain
  //check for NaN result
  if(numtype(I0)!=0)
      //abort "Nan result found"     //uncomment to abort on failure
      Print "I0 or I0gain value not found, setting to 1"
      I0=1
  endif
  return I0
end