next up previous contents index
Next: Image menu Up: File menu Previous: Camera Interface   Contents   Index


Creating, executing, and testing plugins

In the event that specialized features not included in tnimage are needed, it is possible to create a ``plug-in''. Tnimage can send data to an external program and retrieve it after processing.

Running plug-ins


\begin{picture}( 100,100 )(0,60)
\put(0, 50){ \epsfig{file = tnimage_fig5.ps, width=3in }}
\end{picture}
Plugin execution dialog

Creating plug-ins

The plug-in interface is still experimental and may change slightly in future versions.

  1. The files plugin.cc and plugin.h provide a template for creating new plugins. xmtnimage.h is also required to compile a plugin. Tnimage passes image and configuration data to the plugin through a stream pipe, then reads the modified data back afterwards. Stream pipes are used because the amount of shared memory in most versions of Unix is quite limited.
  2. A message of up to 1024 characters may also be sent back to the parent. No information should be sent to stdout. Error messages can write to stderr if necessary.
  3. The new plugin may be compiled with the command line:
    gcc -o plugin -O3 -Wall plugin.cc (use -O2 on Irix).

Plugin programming considerations

  1. New images can be created within a plugin by including these 2 lines:
          newimage(g.image_count,x,y,bitsperpixel,colortype,frames);
          g.image_count++;
    
    where x $ \times$ y is the size in pixels for the image, frames is the total number of frames (must be at least 1), bitsperpixel is one of {8,15,16,24,32,48}, g.image_count is the image number to create, and colortype is either GRAY, INDEXED, or COLOR. Image numbers must be sequential.

    The image bytes can then be addressed as

          z[image_no].image[frame][y][x]
    

    Pixels are packed in standard format according to image depth:

    Indexed color:
    8 bits/pixel: One byte = 1 pixel

    Grayscale: Rounded up to next highest multiple of 8 bits/pixel, maximum of 32 bits/pixel.

    Color:
    15 bits/pixel: 0rrrrrgg gggbbbbb
    16 bits/pixel: rrrrrggg gggbbbbb
    24 bits/pixel: rrrrrrrr gggggggg bbbbbbbb
    32 bits/pixel: 00000000 rrrrrrrr gggggggg bbbbbbbb
    48 bits/pixel: 00000000 rrrrrrrr 00000000 gggggggg 00000000 bbbbbbbb

    This means that each horizontal line of n pixels in the image can be n, 2$ \times$ n, 3$ \times$ n, 4$ \times$ n, or 6$ \times$ n bytes long, depending on the image depth. When creating a loop over the pixels, it is necessary to increment by the correct number of bytes in the x direction. For example, the following loop increases the brightness of frame 0 of a 16-bit grayscale image by 100:

        uchar *address;
        int x, y, value;
        int bpp = z[ci].bpp;         // bpp is bits per pixel
        int step = g.off[z[ci].bpp]; // step is bytes per pixel 
        
        for(y=0; y < z[ci].ysize; y++)
        for(x=0; x < step*z[ci].xsize; x+=step)
        {    address = z[ci].image[0][y][x];
    
             // Read pixel value in depth-independent manner
             value = 100 + pixelat(address, bpp);  
             
             // Make sure value doesn't exceed maximum permitted value 
             // for the given image depth.
             value = min(value, (int)g.maxvalue[bpp]);
    
             // Put pixel back into image
             putpixelbytes(address, value, 1, bpp, 1);
             
        }
    

    For color images, the red, green, and blue components must be extracted from the pixel and processed separately. To determine whether the image is color, check z[ci].colortype which can be one of GRAY, INDEXED, COLOR, or GRAPH.

    The function uint RGBvalue(int red, int green, int blue, int bpp) takes the red, green, and blue components and converts them into a composite value that can be stored into the array.

    The function void valuetoRGB(uint pix, int &rr, int &gg, int &bb, int bpp ) does the reverse, putting values for red, green, and blue in the parameters rr, gg, and bb that are appropriate for the given bits/pixel. The parameters rr, gg, and bb are passed by reference. If you don't like this, you can easily change it to pointers in your plugin.

    The functions pixelat(), RGBvalue(), valuetoRGB, and putpixelbytes() can be copied to your plugin from xmtnimage42.cc .

    There are a number of other parameters in the Image struct that may prove useful in plugins - see xmtnimage.h for a description. Some of these, such as xsize and ysize, must correspond to the dimensions of the image array and should not be changed arbitrarily.

    The parent program will take care of scaling and redrawing the image when the plugin finishes, provided that g.changed[ci] is set to 1.

    All plugins must be recompiled if you change to a new version of tnimage.

  2. For new 8-bit images, it is recommended to also set an appropriate colormap, by the following:
        for(k=0;k<256;k++) 
        {   z[ino].palette[k].red = k/4;
            z[ino].palette[k].green = k/4;
            z[ino].palette[k].blue = k/4;
        }
        memcpy(z[ino].opalette, z[ino].palette,768);
        memcpy(z[ino].spalette, z[ino].palette,768);
    
    (where ino is the image number). Each image has 3 copies of the colormap, to facilitate undo operations.

  3. The following variables should be set to ensure correct handling of the new image once control returns to tnimage:
           g.changed[ino] = 1;   
           z[ino].touched = 1;
    
    and the image should be given a default title:

    strcpy(z[ino].name, "New_title"); (or other title).

  4. The user can enter up to 20 arguments to each plugin. Four additional command-line arguments defining user-selected upper left and lower right coordinates are then passed to the plugin, in the following format:
    1. arg[0] plugin name
    2. arg[1] to arg[n] user-supplied command-line arguments entered in the ``Execute plugin'' dialog. These arguments are automatically updated in the file $HOME/.tnimage/plugins, whenever they are changed.
    3. Left coordinate of current image or currently-selected area.
    4. Top coordinate of current image or currently-selected area.
    5. Right coordinate of current image or currently-selected area.
    6. Bottom coordinate of current image or currently-selected area.

    This is followed by a null character and the remaining configuration information, followed by the image data.

    This arrangement allows any program that takes command-line arguments and writes to stdout to be easily repackaged as a plugin, by modifying the skeleton program plugin.cc. This could be used, for example, in plugins that can read new file formats or for controlling a scanner. The stream pipe mechanism used in tnimage is very fast, and does not require an intricate interface with shared memory or error-prone function calls to internal routines in tnimage.

    Another advantage is that an unlimited amount of data can be sent to the plugin or returned to tnimage. If additional parameters, such as user-selected filenames using the graphical file selector, are desired, the plugin can be incorporated into a tnimage macro.

  5. A message can be displayed upon completion by setting have_message to 1 and copying the message string into display_message, e.g.
          strcpy(display_message, "Your error message\n\
          up to 1024 characters here");
    

  6. Some parameters of existing images, including size and bits/pixel, must not be altered by a plugin.

  7. The plugin also must not write to stdout, but can open files, write to stderr, and make system calls as needed.

  8. It is also possible to add a custom button on the left information area for your new plugin. This is easier than selecting it from the menu each time (See Sec.4.3).

Warning: Incorrectly setting parameters in the Image struct can cause your plugin to crash, or crash tnimage.

Notes on plugins:

  1. Writing to stdout in a plugin can cause the plugin and/or tnimage to hang.
  2. Plugins must be recompiled and reinstalled whenever a new point version of tnimage is installed.
  3. Check the file plugin.cc for any last-minute changes to the interface.
  4. In ver. 3.1.0, the interface was changed to accommodate creating Fourier-transforms in a plugin. All plugins should change the write_data() function as indicated in plugin.cc .
  5. Users are encouraged to send new plugins to the tnimage site so they can be incorporated into the tnimage distribution and benefit other users.

Example - scanner plugin

A typical use for a plugin would be to control a new scanner. This example demonstrates the preferred method for setting up a scanner plugin for interactive scanning in preview and image scan mode.

Assume the plugin is named ``myscan'' and that the plugin was written to take command-line arguments as follows:

  1. 0 = preview mode, 1 = image mode
  2. Scan resolution in DPI
  3. Left x coordinate to begin scan
  4. Top y coordinate to begin scan
  5. Right x coordinate to end scan
  6. Lower y coordinate to end scan

The scanner plugin could be incorporated into tnimage by the following procedure:

  1. Edit $HOME/.tnimage/tnimage.ini and change nbuttons 16 to nbuttons 18. This will add 2 new buttons on the left side of the tnimage screen.
  2. After the last button entry in tnimage.ini, add the line button Preview executeplugin myplugin 0 75 0 0 640 825 . This button will scan a preview image and place the result on the main window, or, if tnimage is configured to use separate windows for each image, it will open a new window of 640x825 pixels and place the preview scan in it. (Ideally, when executed in preview mode, the plugin would send back the message, ``Preview completed, now select area to scan with mouse'' or some similar message. See Plugin programming considerations above.)

    It is also possible to instead use the line button Preview macro pluginmacro. This macro could contain the lines

    windows(1); ( = Sets separate-window mode on)
    executeplugin(plugin,0,75,0,0,640,825); ( = executes plugin)
    windows(0);(= Sets separate-window mode off)

  3. After the Preview button in tnimage.ini, add the line button Myscan executeplugin myplugin 1 300. This new button will execute the plugin in image mode. The scan start and end coordinates are left blank. Tnimage automatically sends the coordinates of the currently-selected area immediately after the specified command-line arguments. Thus, if the user has selected the region (100,100) to (200,200), when the ``Myscan'' button is clicked the plugin will automatically be executed with these arguments: myplugin,1,300,100,100,200,200;

  4. Add the line ``myplugin'' to the file $HOME/.tnimage/plugins, which is a simple list of plugins (along with any command-line arguments).

  5. Finally, put the plugin in the same directory as tnimage . If this is not possible, then change the occurrences of ``myplugin'' above to include the actual path.

The functionality of the scanner plugin will now be transparent to the user. If the user clicks the Preview button, the plugin is automatically executed, scans an image at 75 dpi, and the preview image appears in the main tnimage window. The user then uses the mouse to select the desired scan region and clicks the Myscan button. This executes the plugin again, which scans the desired area at 300 dpi.

Here is a concrete example of a minimal plugin that takes the current image (ci), and subtracts its pixel values from 255. If the image was an 8 bit grayscale image, this would create a photographic negative of the image.

 
    int f,i,j, bytesperline;
    //// Calculate number of bytes in a scan line based on bits/pixel
    bytesperline = z[ci].xsize * g->off[z[ci].bpp];
    for(f=0; f<z[ci].frames; f++)
    for(j=0; j<z[ci].ysize; j++)
    for(i=0; i<bytesperline; i++)
         z[ci].image[f][j][i] = 255 - z[ci].image[f][j][i];
    //// Set g->changed so parent redraws the image
    g->changed[ci] = 1;   
    //// Set `touched' so user is prompted to save image before quitting
    z[ci].touched = 1;
    //// Change the title of the image
    strcpy(z[ci].name, "Modified");

The actual mechanics of transferring the data to and from the parent are handled by the skeleton program, plugin.cc, in which this code would be placed.

Testing plug-ins

To debug a plug-in, create a macro containing the following line:

executeplugin( plugin_name , 0)

or equivalently,

testplugin( plugin_name )

The `0' causes the plugin to be executed in `debug mode', i.e., its stdout is not redirected, whereas `1' causes it to be executed normally.

Note: It is advisable not to move the mouse, open other menus, or execute other commands while the plugin is executing.

Supplied plugins

`Plugin'

This is an example plugin that simply creates a small test image.

`Readtif'

This plugin uses the Leffler libtiff library from ftp.sgi.com . It was not feasible to use the libtiff routines in tnimage, because the libtiff library does not support many of the TIFF subtypes commonly found in the laboratory, including 16 bit grayscale, 15 and 16 bit color, and cannot read Image Quant files. Some well-known image viewers incorporating libtiff will even crash when trying to read such files. On the other hand, some TIFF subtypes not supported by tnimage (such as Fax images) may be readable by this plugin. Also note that readtif expands all images to 32 bits/pixel. The user should down-convert the image to the desired pixel depth before saving it. Enterprising users who strongly prefer libtiff (you know who you are) can even uncomment the libtiff code in xmtnimage11.cc. The include file (tiffio.h) must be present on your system.

`Readhdf'

Readhdf reads images in NCSA's HDF format. The HDF format is extremely complex and can contain a variety of data types along with or instead of images. HDF files not containing images are not readable by readhdf. Users are encouraged to adapt the readhdf code to read other data types. Feel free to send any working plugins to the tnimage site (by email only - the incoming directory has been closed). If your plugin is useful, it may be of great benefit to some other user.

Compiling plugins

  1. Make sure config.h and tnimage.h are present. If you did not compile tnimage yourself, copy config.h.example to config.h and edit as needed for your system.
  2. Compile the plugin using the command:

    gcc -O2 -o plugin plugin.cc

  3. Copy the plugin to /usr/local/bin .
  4. Add the plugin command line to /usr/local/lib/tnimage/plugins .

Compiling and installing the HDF image format plugin

  1. Install the HDF software as instructed in the INSTALL file in the HDF distribution. The most recent version of the distribution can be obtained from the NCSA ftp archive site at:

    ftp://ftp.ncsa.uiuc.edu/HDF/HDF_Current

    A Fortran compiler is not necessary to compile this library provided that the defaults in the Makefile.in and Makefile files in the distribution are changed to FC = NONE.

  2. Copy the files complex.h, xmtnimage.h, xmtnimageb.h, plugin.h, and readhdf.cc to a directory containing the HDF libraries libdf.a, libjpeg.a, and libmfhdf.a.
  3. Edit the readhdf.cc plugin if desired and compile it with the command:

    gcc -o readhdf readhdf.cc libdf.a libmfhdf.a libjpeg.a libz.a -I../src

    changing the last parameter if necessary to reflect the location of the HDF include files (hdf.h et al.).

  4. Move the plugin readhdf to a convenient location and edit the file
    /usr/local/lib/tnimage/plugins
    (which is a simple list of available plugins, one entry per line) to indicate the location of the plugin.


next up previous contents index
Next: Image menu Up: File menu Previous: Camera Interface   Contents   Index
root 2006-11-13