Example 13: High speed scanning

Top  Previous  Next

Example:  Acquire images using device that supports high-speed scanning.

 

To get the most out of a Twain driver when it comes to speed, the following tips should be followed when setting the Twain device:

 

1) Device should support buffered transfers with compression (if available)

2) Device should support batch buffers (if available)

3) Device should support auto-scan (if available)

4) Images should be low resolution or black and white.

 

Example:  Acquiring using the buffered mode with compression and with maximum usage of the device's buffering capabilities.  Write each image to a raw file.

 

#include "cdtwain.h"  // CDTwain

#include <iostream>

#include <algorithm>  // for sort

#include <string>

 

using namespace DTWAIN;

using namespace std; // for <iostream>

 

void ExpandTheRange( DTwainSource& Source, DTwainLongArray& array, int nCap)

{

  if ( Source.IsCapContainerRange( nCap ) )

      DTwainLongRange( ).CopyArrayToRange( array ).Expand( array );

}

 

int main( )

{

  // Initialize DTWAIN - return if initialization does not work

  DTwainInterface TI;

  if ( !TI.IsValid( ) )

      return 0;

 

  // Declare a Source and assign the default TWAIN source to it

  DTwainSource Source = DTwainSource::Select( );

  if ( !Source.IsValid( ) )

     return 0;

 

  // Enable the feeder

  if (Source.IsFeederSupported( ) )

      Source.EnableFeeder(true);

 

  // Check for Autoscan.  Enable if it's available

  if ( Source.IsCapSupported( DTWAIN_CV_CAPAUTOSCAN ) )

     Source.SetSingleCapValue( DTWAIN_CV_CAPAUTOSCAN, 1L );

 

 // Check for the maximum batch buffers

 if ( Source.IsMaxBuffersSupported( ))

 {

     LONG MaxBatchBuffers;

     // Get the max batch buffers

     DTwainLongArray aMaxBuffers;

 

     if ( Source.EnumMaxBuffers( aMaxBuffers ) && (aMaxBuffers.size( ) > 0) )

     {

          // Expand the buffer if the capabilities are returned in a range

          ExpandTheRange( Source, aMaxBuffers, DTWAIN_CV_CAPMAXBATCHBUFFERS );

 

          // sort the values in ascending order

          sort( aMaxBuffers.begin( ), aMaxBuffers.end( ) );

 

          // Set the max batch buffers by giving the highest value

          MaxBatchBuffers = aMaxBuffers[aMaxBuffers.size( ) - 1];

          if ( Source.SetMaxBuffers( MaxBatchBuffers ) )

               cout << "Max batch buffers set to " << MaxBatchBuffers;

     }

  }

 

  // Create an acquire object, attach the source

  DTwainAcquirer Acq( Source );

 

  // Set the acquire type to buffered mode

  Acq.SetAcquireType( TWAIN_ACQUIREBUFFERED );

 

  // Set the maximum number of pages

  Acq.SetMaxPages( DTWAIN_ACQUIREALL );

 

  // Set the pixel type to black and white (1 bpp)

  Acq.SetColorType( COLORTYPE_BW );

 

  // Set the UI mode to false (turn off UI when acquiring images)

  Acq.SetUIMode( false );

 

  // Create a buffered transfer helper object, and attach Source to the object,

  DTwainBufferedTransfer BT(Source);

 

  // Get the compression types.  If TIFF is available, we'll choose it

  // Otherwise, we'll choose  BMP (JPEG compression is also possible if supported by the Source)

  std::string Ext = ".BMP";

  DTwainCompressionType CompType = COMPRESS_NONE;

  if ( BT.IsTiffCompressionSupported( ) )

  {

       CompType = BT.GetBestCompressionType( COMPRESS_TIFF, true );  // Get best compression for black/white pages (1 bit-per-pixel transfers)

       Ext = ".TIF";

  }

  else

  if ( BT.IsBMPCompressionSupported( ) )

       CompType = BT.GetBestCompressionType( COMPRESS_BMP, true ); // get best compression for black/white pages.

 

  // Set the compression type

  BT.SetCompressionType( CompType );

 

  // Now set the strip size.  Preferred strip size is the best for the fastest transfer

  BT.SetStripSize( BT.GetPreferredStripSize( ) );

 

  // Attach to a special buffered transfer listener that writes images to files and increments the

  // file name to the "next" file.

  std::string fName = "FILE000";

  fName += Ext;

  DTwainBufferedListener Listener( fName, &BT );

  Acq.AddListener( &Listener );

 

  // Start the acquisition

  Acq.Acquire( BT );  

} // If acquisition completed, raw data files FILE000, FILE001, FILE002, etc. should be available with maximum supported TIFFor BMP compression.

 

 

 

Note that the files themselves will not have the image file header, since the device does not generate image headers.  The only thing in FILE000, FILE001, etc. is the raw image data.  If your application desires to attach the image header to the front of the data before it is written to the file the following steps should be done:

 

1) Derive a listener from DTwainBufferedListener, and override the OnTransferReady function.  Retrieve the image information by calling DTwainSource::GetImageInfo.  This information is needed for you to build the header.  If the image information is not available (i.e. the resolution value, image width/length, etc. are -1), then the device does not have the information ready when OnTransferReady is called.  In these cases, OnTransferDone should be overridden, and call DTwainSource::GetImageInfo should be done to retrieve the image information after the transfer has completed.

 

2) In the OnTransferReady or OnTransferDone method, the buffer is available using the DTwainBufferedListener::GetImageDataBuffer.  Your application will create the header, and write it to the head of this buffer.  Since the buffer is stored in a std::string variable, it is trivial to attach the data at the beginning:

   

3) Make sure that the overridden OnTransferReady calls the DTwainBufferedListener::OnTransferReady or DTwainBufferedListener::OnTransferDone first.

 

4) In the main application, use your class derived from DTwainBufferedListener when adding the listener to the DTwainAcquirer class.

 

In summary your OnTransferReady( ) function would look similar to the following (the code to build the buffer is the application's responsibility).  Assume that MyBufferedListener is a class derived publicly from DTwainBufferedListener:

 

class MyBufferedListener : public DTwainBufferedListener

{

    public:

         virtual LRESULT OnTransferReady( DTwainSource& Source, LONG UserParam );

    //..

};

 

 

LRESULT MyBufferedListener::OnTransferReady(DTwainSource&Source, LONG UserParam)

{

    // Call the parent function first

    DTwainBufferedListener::OnTransferReady( Source, UserParam );

 

    //...

    std::string& TheBuffer = GetImageDataBuffer( );

    std::string TheHeader;

 

    DTwainImageInfo ImageInfo;

    Source.GetImageInfo( ImageInfo );

    //...

    // App builds the header based on the image info retrieved

    //...

    // Attach the built header to the front of the buffer.

    TheBuffer = TheHeader + TheBuffer;

    //...

    return 1;

}

 

If you are overriding OnTransferDone instead of OnTransferReady, the image data is already present, so you must take care not to remove or erase this data.  The code above (TheBuffer = TheHeader + TheBuffer) is safe to do in either OnTransferReady or OnTransferDone, since the buffer is preserved.  The std::string class is capable of storing binary data by using the std::string::append( ) function, so you can create the header data within the std::string itself.

 

By default DTwainBufferedListener::OnTransferDone takes the data and writes to a file.  If you want to do something else with the data (maybe feed it to another imaging component that takes raw data and applies a header to it), then your derived class from DTwainBufferedListener should not call the default OnTransferDone.

 

The example below shows the difference if you need to override OnTransferDone:

 

class MyBufferedListener : public DTwainBufferedListener

{

    public:

         virtual LRESULT OnTransferDone( DTwainSource& Source, LONG UserParam );

    //..

};

 

 

LRESULT MyBufferedListener::OnTransferDone(DTwainSource&Source, LONG UserParam)

{

    //...

    std::string& TheBuffer = GetImageDataBuffer( );

    std::string TheHeader;

 

    DTwainImageInfo ImageInfo;

    Source.GetImageInfo( ImageInfo );

    //...

    // App builds the header based on the image info retrieved

    //...

    // Attach the built header to the front of the buffer.

    TheBuffer = TheHeader + TheBuffer;

    //...

    // Call the parent function last to finish up

    DTwainBufferedListener::OnTransferDone( Source, UserParam );

 

    return 1;

}

 

Note that the difference is that the parent function OnTransferDone, is called last, so that the DTwainBufferedListener has the chance to write the data to a file.