Thursday, July 29, 2010

Scanning images using WIA and TWAIN

Introduction

The development of windows imaging applications requires choosing an API to communicate with scanner s or cameras. The most used APIs are WIA and TWAIN.

The WIA (Windows Image Acquisition) platform enables imaging/graphics applications to interact with imaging hardware and standardizes the interaction between different applications and scanners. This allows those different applications to talk to and interact with those different scanners without requiring the application writers and scanner manufactures to customize their application or drivers for each application-device combination. (http://msdn.microsoft.com/en-us/library/ms630368(VS.85).aspx)

TWAIN is a standard software protocol and applications programming interface (API) that regulates communication between software applications and imaging devices such as scanners and digital cameras. (http://www.twain.org/)

Differences:

1. WIA uses a common dialog for all devices while TWAIN uses a dialog created by the device manufacturer. Practically speaking, this almost always means that the TWAIN dialog will provide more options and advanced control over the device.
2. TWAIN allows you to use custom capabilities that the device manufacturer has created even though they don't exist in the TWAIN specifications.
3. In general, when a device supports both Twain and WIA, TWAIN is better for scanners and WIA is better for acquiring images from cameras and video devices.
4. TWAIN has three transfer modes (Native, Memory, File) and WIA only has two (Memory, File).
5. Most TWAIN sources save the settings of the previous scan while WIA does not.
6. TWAIN supports options for each page when scanning in duplex mode but WIA uses the same settings for both sides.

Problem

Usually, older scanners (drivers) are build for TWAIN and does not supported by WIA platform, moreover newer devices which created under Microsoft standard does not supported by TWAIN API.


In order to build scanning application that will communicate with both types of devices I combined those APIs under a uniform interface which will provide the easy access to both type of devices.

ScannerAdapterBase
public abstract class ScannerAdapterBase
{
protected List<Image> m_ImagesList = null;
protected ScannerDeviceData m_ScannerDeviceData = null;
// Initializes the Adapter
public abstract void InitAdapter(nessScanning control, IMessageFilter messageFilter, IntPtr handle);
// Selects scanning device, and returns the indicator that the device selected
// Returns the indicator that the device selected
public abstract bool SelectDevice();
// Acquires images from scanning device and fills ImagesLis
public abstract void AcquireImages(bool showUI);
}
Factory Method
public static ScannerAdapterBase GetScannerAdapter(nessScanning control, IMessageFilter messageFilter, IntPtr handle)
{
lock (locker)
{
bool isTwainDeviceSelected = false;

if (m_ScannerAdapterBase != null)
{
return m_ScannerAdapterBase;
}

try
{
//Checks WIA Devices
m_ScannerAdapterBase = new WiaScannerAdapter();
m_ScannerAdapterBase.InitAdapter(control, messageFilter, handle);
isWiaDeviceSelected = m_ScannerAdapterBase.SelectDevice();
if (isWiaDeviceSelected)
{
return m_ScannerAdapterBase;
}

//Checks TWAIN Devices
m_ScannerAdapterBase = new TwainScannerAdapter();
m_ScannerAdapterBase.InitAdapter(control, messageFilter, handle);
isTwainDeviceSelected = m_ScannerAdapterBase.SelectDevice();
if (isTwainDeviceSelected)
{
return m_ScannerAdapterBase;
}
}
catch (ScannerException ex)
{
throw ex;
}

return null;
}
}
Wia Adapter AcquireImages method
public override void AcquireImages(bool showUI)
{
CommonDialogClass wiaCommonDialog = new CommonDialogClass();

//Select Device
if (m_DeviceID == null)
{
Device device = null;
try
{
device = wiaCommonDialog.ShowSelectDevice(WiaDeviceType.ScannerDeviceType, false, false);
if (device != null)
{
m_DeviceID = device.DeviceID;
FillDeviceData(device);
}
else
{
return;
}
}
catch (COMException ex)
{
if ((WiaScannerError)ex.ErrorCode == WiaScannerError.ScannerNotAvailable)
{
return;
}
else
{
WiaScannerException se = BuildScannerException(device, ex);
throw se;
}
}
}

//Create DeviceManager
DeviceManager manager = new DeviceManagerClass();
Device WiaDev = null;
foreach (DeviceInfo info in manager.DeviceInfos)
{
if (info.DeviceID == m_DeviceID)
{
WIA.Properties infoprop = null;
infoprop = info.Properties;
//connect to scanner
WiaDev = info.Connect();
break;
}
}

//Get Scanning Properties
WIA.Item scanningItem = null;
WIA.Items items = null;
if (showUI)
{
items = wiaCommonDialog.ShowSelectItems(WiaDev, WiaImageIntent.TextIntent, WiaImageBias.MinimizeSize, false, true, false);
}
else
{
items = WiaDev.Items;
}

if (items != null && items.Count > 0)
{
scanningItem = items[1] as WIA.Item;
}

WIA.ImageFile imgFile = null;
WIA.Item item = null;

//Prepare ImagesList
if (m_ImagesList == null)
{
m_ImagesList = new List<Image>;
}
//Start Scan
while (HasMorePages(WiaDev))
{
item = scanningItem;

try
{
//Scan Image
imgFile = (ImageFile)wiaCommonDialog.ShowTransfer(item, ImageFormat.Jpeg.Guid.ToString("B")/* wiaFormatJPEG*/, false);
byte[] buffer = (byte[])imgFile.FileData.get_BinaryData();
MemoryStream ms = new MemoryStream(buffer);
m_ImagesList.Add(Image.FromStream(ms));
imgFile = null;
}
catch (COMException ex)
{
if ((WiaScannerError)ex.ErrorCode == WiaScannerError.PaperEmpty)
{
break;
}
else
{
WiaScannerException se = BuildScannerException(WiaDev, ex);
throw se;
}
}
catch (Exception ex)
{
WiaScannerException se = BuildScannerException(WiaDev, ex);
throw se;
}
finally
{
item = null;
}
}
}
Wia Adapter HasMorePages method
private bool HasMorePages(Device WiaDev)
{
try
{
bool hasMorePages = false;
//determine if there are any more pages waiting
Property documentHandlingSelect = null;
Property documentHandlingStatus = null;
foreach (Property prop in WiaDev.Properties)
{
if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_SELECT)
documentHandlingSelect = prop;
if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_STATUS)
documentHandlingStatus = prop;
}
if (documentHandlingSelect != null) //may not exist on flatbed scanner but required for feeder
{
//check for document feeder
if ((Convert.ToUInt32(documentHandlingSelect.get_Value()) & WIA_DPS_DOCUMENT_HANDLING_SELECT.FEEDER) != 0)
{
hasMorePages = ((Convert.ToUInt32(documentHandlingStatus.get_Value()) & WIA_DPS_DOCUMENT_HANDLING_STATUS.FEED_READY) != 0);
}
}
return hasMorePages;
}
catch (COMException ex)
{
WiaScannerException se = BuildScannerException(WiaDev, ex);
throw se;
}
}
TWAIN Adapter AcquireImages method
public ArrayList TransferPictures()
{
ArrayList pics = new ArrayList();
if (srcds.Id == IntPtr.Zero)
{
return pics;
}
TwRC rc;
IntPtr hbitmap = IntPtr.Zero;
TwPendingXfers pxfr = new TwPendingXfers();

do
{
pxfr.Count = 0;
hbitmap = IntPtr.Zero;

TwImageInfo iinf = new TwImageInfo();
rc = DSiinf(appid, srcds, TwDG.Image, TwDAT.ImageInfo, TwMSG.Get, iinf);
if (rc != TwRC.Success)
{
CloseSrc();
return pics;
}
rc = DSixfer(appid, srcds, TwDG.Image, TwDAT.ImageNativeXfer, TwMSG.Get, ref hbitmap);
if (rc != TwRC.XferDone)
{
CloseSrc();
return pics;
}
rc = DSpxfer(appid, srcds, TwDG.Control, TwDAT.PendingXfers, TwMSG.EndXfer, pxfr);
if (rc != TwRC.Success)
{
CloseSrc();
return pics;
}
pics.Add(hbitmap);
}
while (pxfr.Count != 0);

rc = DSpxfer(appid, srcds, TwDG.Control, TwDAT.PendingXfers, TwMSG.Reset, pxfr);
return pics;
}

12 comments:

  1. I have the same code in my scanner app. Problem is that it only scans ONE document, even though feeder has multiple. Looking at the last DSpxfer call in the 'do' loop, the pxfr variable always returns zero. The "ScandAllPro" software that comes with the scanner empties the feeder, but my code does not. Any ideas?

    ReplyDelete
  2. I need to develop Vb.net Windows Application for scan Image..so Which one is best for scan either WIA or Twain...?Please direct me in right direction

    ReplyDelete
  3. I think WIA is better, because it doesn't require any installations on your clint PC, while for TWAIN you should install scanner drives. Disadvantage of WIA that not all scanners support it.
    To cover all the cases you can use a combination of both, in example that i attached (ScannerAdapterFactory class), first it tried to connect scanner via WIA and in case of failure it tries TWAN.

    ReplyDelete
  4. Very nice, and Can it scan duplex?

    ReplyDelete
  5. nice information
    This can explain the difference between TWAIN and WIA in photocopy machine specs I was looking for
    http://plazafotocopy.com/product/canon-ir-adv-c2220

    ReplyDelete
  6. Thanks for sharing
    This can explain the difference between TWAIN and WIA

    ReplyDelete
  7. Nice pattern, but twain doesn't work. as Will posted previously. Have any patches?

    ReplyDelete
  8. nice post..much detailed than http://www.dynamsoft.com/blog/document-imaging/document-scanning-twain-wia-isis-sane/
    I enjoyed the reading very much. Thanks.

    ReplyDelete
  9. thanks for the detailed article..keep up the good work!

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. I have installed a Teller Scanner TS240 device which works perfectly using the default scanLite application.
    However, when I run your source code provided, I get a "no device selected error."
    Kindly assist.

    ReplyDelete