Morena 6 - image acquision framework tutorial

Morena is a Slavic goddess of death and winter. She is old and ugly. She has no wings and she was never seen in front of big "6". And this is just the beginning.

Some commonly used terms

TWAIN (Technology Without An Interesting Name), an image capture API for Microsoft Windows and Apple Macintosh operating systems. Introduced in 1992, and is currently ratified at version 1.9 (as of January 2000). Standard is maintained by TWAIN Working Group, a not-profit organization which represents the imaging industry.

SANE (Scanner Access Now Easy), an image capture API for Unix and Unix-like operating systems, but also available for OS/2 and Microsoft Windows. Introduced in 1996, currently in version 1.0. SANE is open source project and is available under the GNU General Public License.

History

Morena 6 is a direct successor of product called JavaTwain. The original product, a side effect of our forms recognition software, was made public in a version 2.0 in late 1999. It was a single-class java wrapper for TWAIN API with no error handling and just a couple of get and set methods for the most common image acquisition device capabilities. But it already introduced the philosophy of this product line - simple java native interface, which hides a complexity and inconsistency of an image acquisition API.

In 2002 JavaTwain 5 was introduced. With Apple MacOS and MacOS X port and TwainManager/TwainSource design it was the base for our current products. First one is ObjectiveTwain, image acquisition framework for ObjectiveC and Cocoa based applications for MacOS X and JavaTwain generalization for TWAIN, SANE and in future hopefully ISIS or WIA image acquisition APIs called Morena.

The last release, Morena 6, features even deeper API independence, simpler installation, and last but not least this tutorial.

Technical requirements

Morena is just a Java wrapper for TWAIN or SANE image capture API. It implies, that you need at least three components for its use:

Morena runs on any machine and/or operating system with these three components available. In rare situations Morena can crash Java VM. It probably means, that you have TWAIN driver installed, which is not able to run within heap or stack constraints of VM. You can't do much about it, except to install newer version of the driver.

You can try Morena even if you have no actual hardware. There is a kind of "virtual scanner" for both TWAIN and SANE. TWAIN "sample source" is a part of TWAIN Developer Toolkit and you can download it here. SANE "pnm", or at least "test" driver is usually part of SANE installation.

Installation

Since version 6.3, there is no installation necessary. Just put morena.jar, morena_windows.jar (or morena_osx.jar) and morena_license.jar to your CLASSPATH. Native components are stored in morena_windows.jar (or morena_osx.jar) and are installed automatically to .morena folder in your home directory if needed.

Lesson 1 - "Hello world!"

In our first example we'll just acquire an image and throw it away. There are two basic steps we need to do:

The code doing these two steps can look like this:

import SK.gnome.morena.*;

public class HelloWorld
{ public static void main(String[] args) throws MorenaException
  { MorenaSource source=Morena.selectSource(null);
    System.err.println("Selected source is "+source);
    if (source!=null)
    { MorenaImage image=new MorenaImage(source);
      System.err.println("Size of acquired image is "
      	+image.getWidth()+" x "
      	+image.getHeight()+" x "
      	+image.getPixelSize());
    }
    Morena.close();
  }
}

The first step is implemented by the following line:

    MorenaSource source=Morena.selectSource(null);

Morena.selectSource() method displays a sequence of dialog boxes asking for selecting between TWAIN and SANE API, for entering SANE daemon host name and for selecting one of installed devices. The return value is MorenaSource instance representing particular device. Its most important feature is, that it implements ImageProducer interface, the java native API for images.

If you want to work with TWAIN or SANE API only, you can use TwainManager.selectSource() or SaneConnection.selectSource() instead of Morena.selectSource() to skip first dialog box.

The second step is implemented by the following line:

    MorenaImage image=new MorenaImage(source);

MorenaImage class serves as a memory buffer for acquired image. It is both ImageConsumer and ImageProducer and can be quite useful if you want to avoid repetitive scanning each time Morena source is asked to produce an image as a simple replacement for BufferedImage.

Compile the code, run it with both morena.jar, morena_windows.jar (or morena_osx.jar) and morena_license.jar in the classpath. After selecting an image source the code should produce an output like this:

Morena - Image Acquisition Framework version 6.3.0.0.
Copyright (c) Gnome s.r.o. 1999-2005. All rights reserved.
Licensed to "Anonymous evaluator".

The license will expire on 1/2/2006!

This license is valid for evaluation purposes only.

Selected source is TWAIN_32 Sample Source
Size of acquired image is 850 x 1100 x 8

If it really does, you can continue with more sophisticated examples, otherwise go to chapter troubleshooting first to find out what's wrong.

Lesson 2 - setting some parameters

We used default settings for all scanning parameters in the previous example. It may be enough in a case of using TWAIN driver native user interface, but if you want to keep the interface hidden for some reason or you are using SANE driver, you may need to set specific resolution, color model or frame. There are hundreds of parameters you can tweak for real device, unfortunately not all are supported by all devices and there is a different approach for TWAIN and for SANE devices.

Let's change the code of previous example in the following way:

import SK.gnome.morena.*;

public class HelloWorld
{ public static void main(String[] args) throws MorenaException
  { MorenaSource source=Morena.selectSource(null);
    System.err.println("Selected source is "+source);
    if (source!=null)
    { source.setVisible(false);  // Lesson 2
      source.setColorMode();     // Lesson 2
      source.setResolution(300); // Lesson 2
      System.err.println("Image resolution is "
      	   +source.getResolution()); // Lesson 2
      MorenaImage image=new MorenaImage(source);
      System.err.println("Size of acquired image is "
           +image.getWidth()+" x "
           +image.getHeight()+" x "
           +image.getPixelSize());
    }
    Morena.close();
  }
}

In the ideal case the driver should acquire an image without any user intervention, in full color and in 300 DPI resolution, because of the following lines:

      source.setVisible(false);  // Lesson 2
      source.setColorMode();     // Lesson 2
      source.setResolution(300); // Lesson 2

But the results may vary depending on your implementation. If the result is different, it means that your TWAIN driver does not honour these settings. With default error handling settings are ignored, if you are trying to set unsupported capability or unsupported value.

In the special case of TWAIN sample source the driver will acquire image and you will get the output like this:

Selected source is TWAIN_32 Sample Source
Image resolution is 100.0
Size of acquired image is 850 x 1100 x 24

As you can see, request for full color mode is accepted (24 bits per pixel), but request for 300 DPI is ignored. In the next lesson we'll see how to handle such situation using a standard java exception mechanism.

Lesson 3 - unmask exceptions

In general, there are two kinds of common exceptions you may want ignore or catch. First one is the situation, when you are trying to set or get the capability, which is not supported at all by your driver and another one is the situation, when you are trying to set value out of the range of supported values for particular capability, particular device or context.

The behaviour of Morena in such situations is controlled by maskUnsupportedCapabilityException() and maskBadValueException() methods. In the default mode the exceptions are ignored. To throw MorenaException, you can use methods similar to those in the code example below:

import SK.gnome.morena.*;

public class HelloWorld
{ public static void main(String[] args) throws MorenaException
  { MorenaSource source=Morena.selectSource(null);
    System.err.println("Selected source is "+source);
    if (source!=null)
    { source.maskUnsupportedCapabilityException(false); // Lesson 3
      source.maskBadValueException(false);              // Lesson 3
      source.setVisible(false);
      source.setColorMode();
      source.setResolution(300);
      System.err.println("Image resolution is "
      	   +source.getResolution());
      MorenaImage image=new MorenaImage(source);
      System.err.println("Size of acquired image is "
           +image.getWidth()+" x "
           +image.getHeight()+" x "
           +image.getPixelSize());
    }
    Morena.close();
  }
}

Let's try to run the program again with TWAIN Sample source:

Selected source is TWAIN_32 Sample Source
Exception in thread "main" SK.gnome.twain.TwainException: Failed to
		set capability ICAP_XRESOLUTION (FAILURE, BADVALUE)
        at SK.gnome.twain.TwainSource.setXResolution(Native Method)
        at SK.gnome.twain.TwainSource.setResolution(TwainSource.java:120)
        at HelloWorld.main(HelloWorld.java:15)

Source does understand resolution capability, but does not accept 300 DPI as capability value.

Lesson 4 - TWAIN specific features - specific capabilities, ADF support

Even if our philosophy is to hide the complexity and the differences between TWAIN and SANE as much as possible, it is not always possible - the intersection of TWAIN and SANE API is not big enough to make one unified interface.

In the case of TWAIN, the driver is supposed to have its own user interface for setting the available capabilities and in many cases it is not possible to acquire an image without user intervention. There is a fixed set of capabilities listed by TWAIN specification that you can set or read. Therefore TwainSource has hundreds of different getXxx(), setXxx(), getDefaultXxx() and getSupportedXxx() methods for each capability listed in specification. Some of them will work for your particular device and some of them not. It is recommended to test all capability settings used in your code on specific target device.

As an useful tool you can also use code generated along with TwainSource toTwainAutomatedTest.java in example folder.

The following example shows how to use the ADF (Automatic Document Feeder), the specific TWAIN capability:

import SK.gnome.morena.*;
import SK.gnome.twain.*;

public class ADFTwainExample
{ public static void main(String[] args) throws MorenaException
  { TwainSource source=TwainManager.selectSource(null, null);
    System.err.println("Selected source is "+source);
    if (source!=null)
    { source.setFeederEnabled(true);
      source.setAutoFeed(true);
      source.setTransferCount(5);
      int count=1;
      do
      { MorenaImage image=new MorenaImage(source);
        System.err.println("Size of acquired image "+(count++)+" is "
             +image.getWidth()+" x "
             +image.getHeight()+" x "
             +image.getPixelSize());
      }
      while (source.hasMoreImages());
    }
    TwainManager.close();
  }
}

First, note that TwainManager is used to obtain an instance of TwainSource to make sure, that we are working with TWAIN, not SANE source. The following lines:

      source.setFeederEnabled(true);
      source.setAutoFeed(true);
      source.setTransferCount(5);

instruct driver to use ADF as input, to eject one page and feed the next page after the frame of the first page is acquired automaticaly and to scan no more than 5 pages (you can set this parameter to -1 if you want to scan unlimited number of pages).

The core is the loop repeating image production until source.hasMoreImages() returns false:

      do
      { MorenaImage image=new MorenaImage(source);
      	...
      }
      while (source.hasMoreImages());

An Output of running this code with TWAIN Samples Source is shown bellow:

Selected source is TWAIN_32 Sample Source
Size of acquired image 1 is 850 x 1100 x 8
Size of acquired image 2 is 850 x 1100 x 8
Size of acquired image 3 is 850 x 1100 x 8
Size of acquired image 4 is 850 x 1100 x 8
Size of acquired image 5 is 850 x 1100 x 8

It is possible not to buffer the acquired image. See Sane's ADF example for How To.

To learn more about different TWAIN driver capabilities, compare Morena TWAIN API with TWAIN Specification.

Lesson 5 - SANE specific features - option descriptors, ADF support

SANE driver has no user interface and client software is supposed to build its own according to the list of supported capabilities. This list can be different for each driver and is available to programmer by calling getOptionDescriptors() method. Obtained result is an array of SaneOptionDescriptor instances, one for each driver capability (as you can see from the class hierarchy, MorenaOptionDescriptor is already defined and TwainOptionDescriptor can be expected in one of the future releases of Morena).

The following code demonstrates what you can learn from information returned by SANE driver by getOptionDescriptors() method:

import SK.gnome.morena.*;
import SK.gnome.sane.*;

public class ListSaneOptionsExample
{ public static void main(String[] args) throws Exception
  { SaneSource source=SaneConnection.selectSource(null);
    System.err.println("Selected source is "+source);
    if (source!=null)
    { SaneOptionDescriptor descriptors[]=source.getOptionDescriptors();
      for (int i=0; i<descriptors.length; i++)
      { System.err.println();
        System.err.println("Descriptor "+i
            +" - \""+descriptors[i].title+"\"");
        System.out.println("name=\""+descriptors[i].name
            +"\", type="+descriptors[i].type
            +", cap="+ descriptors[i].cap
            +", size="+descriptors[i].size
            +", unit="+descriptors[i].unit);
      }
    }
  }
}

A result obtained by running this code on SANE frontend tester driver looks like this:

Selected source is frontend-tester [test:0]

Descriptor 0 - "Number of options"
name="", type=1, cap=4, size=4, unit=0

Descriptor 1 - "Scan Mode"
name="", type=5, cap=0, size=0, unit=0

Descriptor 2 - "Scan mode"
name="mode", type=3, cap=5, size=6, unit=0

Descriptor 3 - "Bit depth"
name="depth", type=1, cap=5, size=4, unit=0

Descriptor 4 - "Hand-scanner simulation"
name="hand-scanner", type=0, cap=5, size=4, unit=0

Descriptor 5 - "Three-pass simulation"
name="three-pass", type=0, cap=37, size=4, unit=0

Descriptor 6 - "Set the order of frames"
name="three-pass-order", type=3, cap=37, size=4, unit=0

Descriptor 7 - "Scan resolution"
name="resolution", type=2, cap=5, size=4, unit=4

Descriptor 8 - "Special Options"
name="", type=5, cap=0, size=0, unit=0

Descriptor 9 - "Select the test picture"
name="test-picture", type=3, cap=5, size=14, unit=0

Descriptor 10 - "Invert endianness"
name="invert-endianess", type=0, cap=37, size=4, unit=0

... and about 40 of another capabilities ...

For the meaning of attributes of option descriptor look to SANE Standard chapter "4.2.9 Option Descriptor Type".

Sane backend reports the "out of paper" situation by returning SANE_STATUS_NO_DOCS status code. Some backends do it by default, some require to set a special sane option, so that they accept the fact that "application wants to use ADF". In that situation you need to examine the list of backend's options and find the appropriate adf option name. E.g. setOption("source", "ADF");

The following example shows how to use the ADF (Automatic Document Feeder):

    Image image;
    SaneConnection sc=null;

    try
    {
      sc = SaneConnection.connect("localhost");
      SaneSource source=sc.selectSource(null,null);
      // Following type of calling is often required. 
      // The option name is backend specific.
      //source.setOption("source", "ADF");  
      do
      {
        image=Toolkit.getDefaultToolkit().createImage(source);
        MediaTracker tracker=new MediaTracker(this);
        tracker.addImage(image, 0);
        try
        { tracker.waitForAll();
        }
        catch (InterruptedException e)
        { e.printStackTrace();
        }
        tracker.removeImage(image);
        System.out.println("Size of acquired image is " 
            + image.getWidth(this) + " x " 
            + image.getHeight(this) 
            + ", sc.getResultCode()=" + sc.getResultCode());
      } while (sc.getResultCode() != SaneConstants.SANE_STATUS_NO_DOCS);
    }
    catch (Exception e)
    { e.printStackTrace();
    }
    finally
    { if(null!=sc)
      try
      { sc.close();
      }
      catch (Exception e)
      { e.printStackTrace();
      }
    }

Lesson 6 - applications vs. applets

Since version 6.3, there is no substantial difference between application and applet, because you don't have to install Morena yourself. Nevertheless there is one issue you should not forget.

Morena installer needs to create folder names .morena in your home directory upon first invocation of some classes (specifically TwainManager) and write some files into it. The easiest way to do it, is to digitally sign your applet.

Remember to add all of morena.jar, morena_windows.jar, morena_osx.jar and morena_license.jar to the ARCHIVE attribute of APPLET tag to keep your page as multiplatform as possible.

Lesson 7 - real world application

As an example of real world application you can find MorenaStudio.java in the distribution package. It demonstrates some common tasks you need to accomplish with Morena -- acquiring image, displaying, saving and uploading it and also some techniques necessary to make your code reliable, like buffering of image, testing image status and avoiding thread conflicts.

Make sure you understand the following tricky issues before you start writing your first application.

Troubleshooting and FAQ

You can find the answers for the common questions, tasks and problems at our Morena's technical support web page.