Morena 7 - image acquision framework tutorial

Commonly used terms

WIA Windows Image Acquisition (WIA) is the still image acquisition platform in the Windows family of operating systems starting with Windows Me and Windows XP and continuing to the most recent Windows 7.

Image Capture is an application program as well as the architecture that enables users to transfer images from camera or scanner on Mac OS X platform.

History

Morena 7 is not a direct successor but rather an evolution of the Morena 6 (TWAIN/SANE implementation) concept. It brings a new common Java API for WIA and ICA (Image Capture) interface that opens the door to the native image acquisition API for the most established commercial desktop operating systems such as MS Windows and Mac OS X.

Differences to Morena6 (TWAIN/SANE)

The most notable change that result from asynchronous nature of both native interfaces (WIA and ICA) is that provided Morena7 API is also asynchronous. This difference appears when obtaining available sources where only actually connected devices are listed. It is an oposite to Morena6 (TWAIN/SANE) where the list of currently installed drivers is returned (no mater if the device is connecter or not). Difference can be found also when acquiring an image since in Morena7 the image is obtained in callback method TransferDone(File file)

Technical requirements

Morena7 acts as Java wrapper for WIA and ICA API. Following components are necessary for its use:

Installation

There is no extra installation necessary. Following Java archives morena7.jar, morena_license.jar and morena_win.jar or morena_osx.jar has to be in the CLASSPATH. Native components stored in morena_win.jar or morena_osx.jar are copied automatically into TMP_DIR folder.


Lesson 1 - Quick start

In this example we'll make an overview how to acquire a single image using Morena7 library. There are six basic steps needed to do:

Following code initialize the WIA/ICA manager and retrieves the list of actually connected devices.

  Manager manager=Manager.getInstance();
  List devices=Manager.listDevices();

This code selects first device from the device list and runs the scanner resp. camera specific code.

  Device device=devices.get(0);
  if (device instanceof Scanner)  {
    // scanner specific code 
  }
  else if (device instanceof Camera) {
    // camera specific code 
  }

Following code snippet makes a setting of the parameters. Mode designates a colorMode that can be selected from the following options while a resolution is set in DPI units. Method setFrame(int x, int y, int width, int height) selects the frame of the acquired image (in pixels).

    Scanner scanner = (Scanner) device;
    scanner.setMode(Scanner.RGB_8);
    scanner.setResolution(75);
    scanner.setFrame(100, 100, 500, 500);

For a single image acquisition we may use SynchronousHelper to make a synchronization for us. It will wait until image is available for the application. Two signatures can be used according to what we plan to do with the image. . BufferedImage is returned by the first method signature or exception is thrown when image acquisition fails.

  BufferedImage bimage = SynchronousHelper.scanImage(device);
  // make the necessary image processing or conversion

Second method signature returns File containing the image or throws an exception when image acquisition fails. Exact format of the file is platform and device dependent. On Windows we receive BMP image by default while JPEG image is returned on Mac OS X.

  File imageFile = SynchronousHelper.scanFile(device);
  // make the image processing or save the file in the repository

When no other image acquisition is planned then it is time to close manager down and release bound system resources.

  manager.close();

Source code example: MorenaExample

Lesson 2 - Batch scanning

Batch scanning usually involves using scanner with an automatic document feeder (ADF). For scanning bunch of sheets we need just slightly modify the code above.

First we need to retrieve ADF functional unit number (index) because scanner may have more than one paper source input unit e.g. flatbed and ADF. If no ADF unit is found or recognized then the default unit (0) is used.

    int feederUnit=scanner.getFeederFunctionalUnit();
    if (feederUnit<0)
      feederUnit=0; // 0 designates a default unit

Then we can check if scanner provide the duplex scanning and enable this feature if possible.

    if (device.isDuplexSupported())
      device.setDuplexEnabled(true);

Finally we may use ScanSession helper to make a synchronization for us. It allows to process the images as they will come. Empty feeder exception is indication of a successful operation. Any other exception means that operation failed for some reason.

    ScanSession session=new ScanSession();
    try {
      session.startSession(device, feederUnit);
      File file=null; 
      while (null!=(file=session.getImageFile())) {
        
        // make the necessary image processing or save it in repository
        
      }
    } catch (Exception ex) { // check if error is related to empty ADF
      if (session.isEmptyFeeder())
        System.out.println("No more sheets in the document feeder");
      else
        ex.printStackTrace();
    }

Source code example: MorenaExample

Lesson 3 - scanning with UI

When we need to incorporate image scanning ability into GUI application the different approach is used. Some of the tasks like selection of device or setting of parameters may be done using built-in or customized UI components. There is also usually no need to wait for the scanned images since it would block an application UI.

First we can use a built-in dialog for device selection. Parameter provided is a UI component that will be an owner of the dialog.

  Device device = manager.selectDevice(MainPanel.this);

We may also use built-in dialog for setting device parameters.

  if (scanner.setupDevice(MainPanel.this)) {
    // start scanning
  }
  else {
    // scanning is canceled
  }

Finally we start transfer of the images. While this method finishes immediately images are handled within TransferDoneListener that is provided as an argument.

    scanner.startTransfer(listener);

Here is the example how such TransferDoneListener may look like. Two methods has to be implemented: transferDone(file) and transferFailed(code, error message)

  private class TransferDoneListenerImplementation implements TransferDoneListener {
    @Override
    public void transferDone(File file)
    { try 
      { if (file!=null)
        {  
          // do some image processing (e.g. show image on the desktop)
        }
        else
          System.out.println("nothing scanned");
      }
      catch (Exception e)
      { e.printStackTrace();
      }
    }
  
  
    @Override
    public void transferFailed(String error)
    { System.out.println("Scanning failed");
    }
  }

If monitoring of the scanning process is considered important then TransferListener interface with the additional method transferProgress(percent) needs to be implemented.

  private class TransferListenerImplementation implements TransferListener {

  ...

    public void transferProgress(int percent) {
      System.out.println(percent + "%");
    }
  }

Source code example: MorenaStudio

Lesson 4 - advanced options

Morena 7 library contains a class Configuration for advanced options setting.

Native UI

Sometimes it is useful to show a native UI for certain reason (e.g. parameter setting via API is not implemented correctly in driver).

Please note that this option is available on Windows only. When used on Windows XP (WIA1) it also affects general behavior of the application (e.g. device connection and disconnection detection does not work by default).

Due technical reasons it is necessary to specify this option before initialization of Manager.

    Configuration.setMode(Configuration.MODE_NATIVE_UI);
    manager = Manager.getInstance();
    ...

When we need to preserve device connection & disconnection detection together with using the native UI we need to enable polling.

    Configuration.setMode(Configuration.MODE_NATIVE_UI | Configuration.MODE_WIA1_POLL_ENABLED);
    manager = Manager.getInstance();
    ...

Logging

Morena 7 use Java logging API to monitor application. Log records are directed to logger named "morena".

Here is the code example of setting log level detail to FINEST.

    Configuration.setLogLevel(Level.FINEST);
    manager = Manager.getInstance();
    ...

Device type setting

There are some scanners that incorrectly identify itself as a camera. It applies to most of HP home and office scanners and MFP.

Here is the code example how to workaround this issue. Regular expression matching name property is provided as an argument.

    Configuration.addDeviceType(".*fficejet.*", true);  
    manager = Manager.getInstance();
    ...

Troubleshooting and FAQ

Answers to the most common questions, tasks and problems can be found at our Morena's technical support web page.