In Part 3 of his series on image acquisition in Java, Jeff Friesen turns his attention from TWAIN to the *nix-friendly SANE and shows how to use it with Java.
Explore the SANE alternative to TWAIN
TWAIN is the standard for image acquisition from scanners and digital cameras, but its GUI assumptions make it ill-suited for Linux and other *nix operating systems. In part three of his series looking at image acquisition in Java, Jeff Friesen looks at the SANE alternative, and how to use it with Java.
Java doesn't provide a standard API for acquiring images from
digital cameras, scanners, and other image-acquisition devices.
This omission has inspired this three-part Java Tech series
that explores the TWAIN and SANE image-acquisition specifications,
and how to make use of those specifications in a Java context. The
previous two articles in this series--the first
TWAIN and providing a simple TWAIN library and Java application
that demonstrates that library, the second
on the library and demo application --focused on the TWAIN
specification from a Microsoft Windows perspective, because TWAIN's
origin lies in the Windows world. In contrast, this article largely
moves away from TWAIN (and Windows), by focusing on the Unix-based
SANE image-acquisition specification.
This article begins with an introduction to SANE, where you
learn about SANE's environment, API, and network protocol. The
article next explores a Java API for acquiring images with SANE.
Moving forward, the article discusses the need for both SANE and
TWAIN. The article (and series) closes by looking at merging SANE
with TWAIN into a unified image-acquisition specification.
What Is SANE?
SANE, an acronym for "Scanner Access Now Easy," is an
image-capture API. The API provides standardized access to any
raster-image scanner, camera, or other kinds of image-acquisition
devices. Version 1.03 is the current version.
SANE was introduced a few years after TWAIN to support (and
standardize) image acquisition on Unix and Linux platforms, because
TWAIN could not (and still is not able to) do the job. Although
SANE originated for Unix and Linux, it has been ported to Mac OS X,
OS/2, and other operating systems.
SANE is maintained by volunteers who meet at SANE's official
website (see the Resources section for a link to
the website). From that website, you can download SANE source code
and documentation for your Unix or Linux platform.
Make sure to download "SANE Standard Version 1.03" (see
Resources for a link). That document
introduces SANE, describes SANE's environment, explores the SANE
API, and discusses SANE's network protocol. The three sections
below are based on material found in that document.
SANE provides a standard interface to raster-image devices.
Applications using that interface are known as SANE
front ends. Drivers that implement that interface and control
raster-image devices are known as SANE back ends. Figure 1
shows the relationship between SANE front ends and back ends.
Figure 1. Relating front ends to back ends
Application front ends communicate with driver back ends through
an intermediary known as
link (symlink, for short) to a driver back end that controls a
specific image-acquisition device. When an application communicates
libsane.so, it's actually communicating with
whatever driver is represented by
Changing symlinks is convenient for changing drivers without
needing to relink applications. However, that is not convenient
whenever you want to dynamically switch image-acquisition devices.
SANE overcomes this problem by providing the pseudo-drivers
net. Those pseudo-drivers talk to
other SANE drivers instead of physical devices:
talks to other SANE drivers on the same machine and
net talks to other SANE drivers across a network.
Figure 2 presents an example of a SANE driver hierarchy involving
net. This example is based on,
but isn't identical to, an example found in the SANE
Figure 2. A SANE driver hierarchy
Figure 2 reveals two machines: A and B. Machine A's
libsane.so is a symlink to the
pseudo-driver. That pseudo-driver uses dynamically linked libraries
to access both the
mustek scanner driver (which
controls the local mustek scanner) and the
provides network access to Machine B, by
connecting Machine A to the SANE daemon (
) that runs on
Machine B. The daemon communicates with
dynamically loads the
scanner driver. As a result,
an application that runs on Machine A can access Machine B's HP
Of course, Figure 2 is just one example. On Machine A,
libsane.so could be a symlink to the
pseudo-driver, or a real driver (such as the
scanner driver). System administrators have lots of flexibility in
how they set up the SANE driver hierarchy.
Perhaps the most important part of the SANE environment is the
format used to represent acquired images. SANE regards an image as
a rectangular area that is subdivided into rows and columns. SANE
refers to the intersection of a row and column as a quadratic
pixel. This pixel consists of one or more sample values, where a
sample value represents a color channel (red, green, blue) or a
shade of gray. Each sample also has a bit depth: one, 8, and 16 bits
per sample are valid bit depths.
SANE transmits an image as a sequence of frames. Each frame
covers the entire rectangular area but may only contain a subset of
the channels. For example, a frame may only contain the red sample
values, or the green sample values. Alternatively, a frame could
consist of the sample values of all three color channels. More
information on SANE's image data format and image transmission can
be found in the document mentioned earlier.
The SANE API was written in the C language. Just as TWAIN
supplies the twain.h header file for inclusion in
TWAIN-based source code, SANE supplies the sane.h
header file for inclusion in SANE-based source code.
The sane.h header file largely consists of type and
function declarations. The type declarations range from simple
types, such as
SANE_String, to complex types, such as
SANE_Option_Descriptor. A total of 14 functions
compose the API:
sane_init(): Initializes SANE and must be called
before any other function.
sane_get_devices(): Returns a list of available
sane_open(): Opens a named image-acquisition
device and returns a handle that must be passed to other API
functions used to communicate with the device.
sane_get_option_descriptor(): Returns an option
descriptor for a specific
option (a device parameter).
Options describe device controls (such as scan resolution) in a
user-interface-independent way and control nearly all aspects of
device operation. Option number 0 returns the number of available
sane_control_option(): Sets/gets the value of a
specified option, for the device indicated by a device handle. For
example, this function would be used to set a brightness control to
sane_start(): Initiates image acquisition from a
specific device, as indicated by a device handle.
sane_get_parameters(): Returns information on scan
parameters for the device indicated by a device handle. Scan
parameters include pixels per line and bytes per line.
sane_set_io_mode(): Enables either blocking or
non-blocking I/O for the device indicated by a device handle. This
function can be called only after making a call to
sane_get_select_fd(): Obtains a platform-specific
file descriptor from a device handle. This function can only be
called after a call has been made to
sane_read(): Reads image data from an open
image-acquisition device, as indicated by a specific device handle.
This function can only be called after a call has been made to
sane_cancel(): Cancels the current operation for
the device that is indicated by a device handle.
sane_close(): Closes the open image-acquisition
device indicated by a specific device handle.
sane_exit(): Terminates an application's
communication with SANE. The application must call
sane_init() before it can communicate once more with
sane_strstatus(): Translates a SANE status code
into a printable string. Examples of status codes include
An application calls
sane_init() to begin
interacting with SANE. The application next typically calls
sane_get_devices() to obtain a list of accessible
devices. A device will be picked from this list and its name passed
sane_open() to open the device. Once the device is
open, its controls can be set up or queried. This occurs in a loop,
where each iteration invokes
sane_get_option_descriptor() to obtain a control's
descriptor, followed by
sane_control_option() to query
or set up the control.
Device setup is followed by image acquisition. This task begins
with a call to
sane_start(), and continues with a loop
sane_get_parameters() and then
sane_read() are invoked. Image acquisition continues
sane_read() returns end-of-file, or the user
chooses to terminate image acquisition (assuming the application
allows image acquisition to be cancelled), whereby the application
sane_cancel(). Following image acquisition,
sane_close() is invoked and the open device is closed.
sane_exit() is then called to break the application's
connection with the SANE back end.
SANE Network Protocol
SANE was designed to facilitate network access to
image-acquisition devices. Most SANE implementations support a
net pseudo-driver and a corresponding network daemon
to access those devices through a network connection.
SANE provides a network protocol designed to enable the
efficient transmission of images (because of low encoding
overhead), provide efficient access to option descriptors on the
client side (because this is a common operation), and be simple and
easy to implement on any host architecture and in any programming
The protocol provides an encoding scheme for primitive data
types and type constructors. For example, a
value is encoded as four 8-bit bytes, ordered from most-significant
to least-significant. Also, arrays are encoded by a word denoting
the array's length followed by the array values.
The protocol is based on remote procedure calls (RPCs). All
activity is initiated by the client, and the server is restricted
to answering client requests. For example, the
SANE_NET_INIT RPC establishes a connection to a
particular SANE network daemon. The RPC passes a version code and a
username to the daemon, and receives a status value and another
version code in reply.
A SANE-Based API for Java
After exploring SANE, creating a Java API that accesses SANE
back ends seems to be the next logical step. Unfortunately, my lack
of access to either a Unix or a Linux SANE implementation forces me
to seek an alternative. That alternative is to explore an existing
Java-based SANE API. For this article, I have chosen JSane.
JSane is an open source project whose Java classes communicate
directly with a SANE daemon (and adhere to the SANE daemon's
network protocol). Consult the Resources
section for a link to JSane's website.
JSane's classes are organized in two packages:
uk.org.jsane.JSane_Base provides exception classes
that represent SANE statuses, and a foundation for
uk.org.jsane.JSane_Net, whose classes emulate the
To communicate with SANE-based image-acquisition devices, a
JSane-enabled Java program must first connect to the host on which
the SANE daemon runs. This can be accomplished by creating a
JSane_Net_Connection object. That object's constructor
requires the SANE daemon's host name and the port on which the
daemon runs (typically port 6566). If a connection cannot be
established, the constructor throws either of two exceptions:
java.net.UnknownHostException. The code fragment below
shows how to make the connection.
[prettify]JSane_Net_Connection con = new JSane_Net_Connection ("host", 6566);
Once a connection has been established, calls to the
getDevice() methods make it possible to enumerate all
image-acquisition devices recognized by the SANE daemon.
getDevice() returns an object whose class subclasses
A device is opened by calling
open() method, and closed by calling
close(). Prior to closing the device, a list of option
descriptors can be obtained by calling
To show you how to enumerate devices and their options via
JSane, I've written an enumeration program that is similar to a
sample program that comes with JSane. If you have access to a SANE
daemon, I urge you to compile and execute the code, and view the
list. The enumeration program's Java source code appears below:
// Enumerate devices and device options.
public class EnumDevOpt
public static void main (String  args)
// Attempt to connect to the SANE daemon
// running on host host and port number
con = new JSane_Net_Connection ("host",
// For each device accessible through
// the SANE daemon ...
for (int devNum = 0;
devNum < con.getNumberDevices ();
// Obtain the device's descriptor.
dev = con.getDevice (devNum);
// If a descriptor was returned
// (devNum is in range) ...
if (dev != null)
// Output device information.
System.out.println("Device = "
// Attempt to open the device.
// Obtain a count of options
// supported by the device.
nopt = dev.getNumberOptions ();
// For each option, return and
// output the option's
for (int i = 0; i < nopt; i++)
// Close the device.
System.out.println ("Unable " +
"to get " +
"device " +
// Drop the connection to the SANE
catch (IOException e)
catch (JSane_Exception e)
The sample program on which I based
contains an oddity that needs an explanation. The sample program
drops its connection to the SANE daemon by invoking
exit() method. But
according to JSane's Java documentation,
not exist. Instead, there is a
which I've substituted in EnumDevOpts.java.
So now we know how to enumerate devices and their options via
JSane. But how do we acquire images? Using JSane to acquire an
image is very simple: only two methods (
getImage()) need to be called, as the following code
[prettify]// Attempt to open a device.
// Manipulate options.
// Perform a scan.
JSane_Base_Frame frame = dev.getFrame ();
// Retrieve the image.
BufferedImage image = frame.getImage ();
// Close the device.
I recommend exploring the classes and interfaces in JSane's two
packages. That way, you'll get comfortable with this API and can
begin experimenting with JSane on your own.
The Need for Two Specifications
TWAIN has been around since 1992. It's supported on the Apple
Macintosh and on several versions of Microsoft Windows.
Furthermore, IBM's OS/2 supports TWAIN. But there is no support (at
least none that I can find) for Unix and Linux.
Why doesn't TWAIN support Unix and Linux? The TWAIN Working
Group's "Expanding TWAIN's Portability to Include Unix" white paper
(check out the Resources section for a link to
this white paper) provides an answer. According to that white
paper, which focuses more on Unix than Linux, the fact that TWAIN
drivers require an attachment to the application's message loop,
and the fact that TWAIN vendors must provide custom dialog boxes
that expose all of the features of their devices, cause problems for
Unlike Macintosh and Windows, the Unix operating system can run
with different GUIs. Furthermore, a GUI isn't essential to running
Unix. How do we reconcile TWAIN's emphasis on GUI support at the
driver level and non-standard or non-existent GUIs on Unix machines? This is
the crux of the problem.
SANE came into existence to overcome this problem. By separating
device controls from their representation in a GUI, SANE
applications can be created to run in a command-line or a GUI
context. Another benefit (previously shown in Figure 2) brought out
by SANE's separation of device controls from their GUI
representations: network transparent access to image-acquisition
devices. Unlike TWAIN, SANE supports remote access to
image-acquisition devices over the network. Imagine manipulating a
web camera located thousands of miles from your desktop. The
internet and SANE make this possible.
Merging SANE with TWAIN
Some image-acquisition devices only support TWAIN, and other
image-acquisition devices exclusively support SANE. Out of the box,
you cannot use TWAIN devices in a SANE context (and vice versa). To
overcome this problem, we need to first consider merging SANE with
TWAIN into a unified specification.
One approach to achieving SANE-TWAIN unification was put forward
by the TWAIN Working Group in "Expanding TWAIN's Portability to
Include Unix." According to that white paper, it's desirable to
achieve synergy between the SANE and TWAIN specifications, by using
SANE as the back end (letting TWAIN avoid reinventing solutions to
fundamental device communication problems) and by using TWAIN as
the front end of a unified specification.
To achieve synergy between SANE and TWAIN, the white paper
recommends changing the architecture of existing TWAIN drivers,
which results in hybrid TWAIN-SANE drivers. Figure 3 presents the
current architecture of a TWAIN driver (which I copied from the
Figure 3. Current TWAIN driver architecture
Modifications include moving Capability Negotiation
(Programmatic Controls) to the SANE side of a hybrid TWAIN-SANE
driver, and implementing a mechanism that passes all capabilities
operations from the TWAIN side to the SANE side of the hybrid
driver. Figure 4 presents the proposed hybrid driver architecture
(also copied from the white paper).
Figure 4. Hybrid TWAIN-SANE driver architecture
The white paper goes on to propose writing a generic TWAIN-on-SANE driver that implements these modifications. Although the TWAIN
Working Group has not released a generic TWAIN-on-SANE driver in
the five years since they wrote the white paper, I've discovered an
interesting Windows project that seems to accomplish their
objective: SaneTwain. This project bridges the SANE and TWAIN
worlds by providing a SANE-TWAIN data source and software to
connect to a SANE server. Check out the
Resources section for a link to the SaneTwain
So what has merging SANE with TWAIN got to do with Java? A
future Java version will probably support image-acquisition devices
in its libraries, perhaps as part of the Image I/O API. This future
support will most likely embrace both TWAIN and SANE drivers
(because of their ubiquity). Furthermore, it will be necessary to
develop a standard API, perhaps modelled after SANE or TWAIN. This
task might be handled by introducing separate subsystems for TWAIN
and SANE. As this seems rather complicated, it might be
advantageous to think about merging SANE with TWAIN and providing a
single subsystem for the result.
SANE is an API and image-acquisition specification that
primarily targets Unix and Linux. Unlike TWAIN, SANE is GUI-neutral, which makes SANE ideal for use in command-line-driven Unix
environments. Another advantage over TWAIN: SANE enables network
transparent access to image-acquisition devices located across
We've reached the end of this series. You should have a basic
understanding of TWAIN and SANE, along with an understanding of the
Java APIs presented in this series' articles. Hopefully, that
knowledge will help you when you find yourself needing to integrate
support for image acquisition into your Java programs.
Where do we go from here? I believe that Java needs a unified
framework that makes it possible for Java programs to transparently
obtain images from either SANE or TWAIN drivers. Would you be
interested in working with me on an open source project that just
might make this framework a reality? Let me know.
I have some homework for you to accomplish:
- If you have access to a SANE daemon, download JSane and try out
- The first part of this series presented a JTwainDemo
application. Convert that application's source code to equivalent
applet source code. Test your JTwainDemo applet using the
appletviewer tool found in the J2SE 5.0 SDK. Don't worry about
making this applet work within a real web browser.
Next time, Java Tech presents language lessons that focus on the
problems of inheritance, interfaces versus abstract classes, when
assertions should not be used, the usefulness of covariant return
types, and more.
Answers to Previous Homework
Java Tech article presented you with some challenging homework
on JTwain and JTwainDemo. Let's revisit that homework and
- Examine the capabilities list in Chapter 9 of the TWAIN
Specification and then choose a capability that you might like to
add to JTwain. Implement at least two native methods that get the
current value and set the current value of that capability. You
might also need to implement a third native method to get a list of
supported values for the capability. Modify JTwainDemo's source
code to demonstrate your capability of choice.
A simple capability that you might consider adding (for a
scanner device) is scan orientation (portrait or landscape). Use
ICAP_ORIENTATION in a pair of C++ functions that get
and set the current scan orientation.
width="1" height="1" border="0" alt=" " />