Transferring D90 images with WIA






3.67/5 (7 votes)
Use Windows Image Aquisition automation to transfer Images, RAW images and Video from your D90

Introduction
This article describes a program that automatically downloads pictures from a Digital Still Camera that supports the PTP/MTP protocol. It is primarily designed for the Nikon D90 but should be useable for other brands with minor adaptations. Windows Image Acquisition (WIA) Automation is used.
Highlights:
- Plug and play download of the pictures from your camera
- No user interaction on the computer, so can run on headless systems
- Handles RAW files and normal JPEG files
- Handles D90 video clips
- Automatic rename to selectable formats
- Original picture date and time are preserved
Background
Digital cameras used to support the mass storage USB class. You could simply connect the camera to your computer and have it appear in Windows Explorer as a normal harddisk. Nowadays this is no longer obvious. The Nikon D90 dSLR only supports the Picture Transfer Protocol (boo, Nikon!). You can use the 'Camera and Scanner' wizard to get the pictures from your camera, but you will soon find out that RAW images are not transferred. The Nikon transfer program is a near 50MB monstrosity that cannot work automatically.
The computer I used is a mini-ITX system without a keyboard, mouse or monitor. I want a simple workflow: Plug in the USB cable, switch on the camera and it starts transferring. By giving little beeps, I know the transfer is completed successfully (or not, then I hear a burp). The program that is described here, does exactly that, using the WIA automation control.
Installation
To use WIA Automation, it is necessairy to install WIAAut.DLL first. See the references below for a free download address at Microsoft. Microsoft states that WIA automation only works on Windows XP SP1 and onward. Remember to register the DLL:
RegSvr32 WIAAut.dll
The WIA automation library is a COM component (an ActiveX control), that needs to be encapsulated before you can use it in C#. The documentation from Microsoft (see below) only documents the control for use in VB.
After registering, you can automatically encapsulate it by adding a reference to the DLL in Visual Studio:
Add a using clause:
using WIA;
The library is now available.
Using the Code
First we must set up the device handlers for the DeviceConnected
and DeviceDisconnected
events.
wiaDevManager = new WIA.DeviceManagerClass( );
wiaDevManager.RegisterEvent( WIA.EventID.wiaEventDeviceConnected, "*" );
wiaDevManager.RegisterEvent( WIA.EventID.wiaEventDeviceDisconnected, "*" );
wiaDevManager.OnEvent += new WIA._IDeviceManagerEvents_OnEventEventHandler(
devManager_OnEvent );
The eventhandler is shown below. The device ID is a horrible GUID, that apparently means 'digital still camera.' Other GUIDs are for scanners and webcams. As you see, the DeviceConnected
event starts a separate thread to get the images from the camera. This is done to keep the user interface responsive during a time-consuming transfer.
public void devManager_OnEvent( string eventID, string deviceID, string itemID )
{
if ( eventID == WIA.EventID.wiaEventDeviceConnected )
{
if ( deviceID.StartsWith( "{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}" ) )
{
// Wait for camera to become ready
System.Threading.Thread.Sleep( 1000 );
if ( ConnectToCamera( ) )
{
System.Threading.Thread workerThread = new System.Threading.Thread(
GetImagesFromCamera );
workerThread.Start( );
}
}
}
else if ( eventID == WIA.EventID.wiaEventDeviceDisconnected )
{
if ( deviceID.StartsWith( "{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}" ) )
{
Log( DateTime.Now.ToString( ) + " Digital Still Camera Disconnected\r\n" );
wiaDevice = null;
if ( autoClear.Checked )
{
textLogging.Clear( );
}
}
}
}
When a DeviceConnected
event arrives, we look to see if it is a camera and when it is, connect to it:
// See if it is a CameraDeviceType and connect to it
public bool ConnectToCamera( )
{
try
{
// Device is already connected
if ( wiaDevice != null )
return true;
foreach ( IDeviceInfo DevInfo in new DeviceManagerClass( ).DeviceInfos )
{
// Look for CameraDeviceType devices
if ( DevInfo.Type == WiaDeviceType.CameraDeviceType )
{
wiaDevice = DevInfo.Connect( );
Beep( 440, 150 );
Beep( 880, 150 );
Log( DateTime.Now.ToString( ) + " Digital Still Camera Connected\r\n" );
return true;
}
}
// Not a still camera
return false;
}
catch ( Exception exp)
{
Log( "Something went wrong when connecting the camera\r\n", exp.ToString() );
return false;
}
}
Now that the camera is connected, the images can be transferred. In the source code you will find a lot of housekeeping but stripped of all that, the main line is the following:
for ( int i = 1; i < wiaDevice.Items.Count + 1; i++ )
{
// Get the picture file from the camera
// the FormatID doesn't seem to matter,
// you can even transfer a JPEG file using wiaFormatTIFF
wiaImageFile = (WIA.ImageFile)wiaDevice.Items[ i ].Transfer( FormatID.wiaFormatJPEG );
string extension = wiaImageFile.FileExtension.ToLower().Trim();
wiaImageFile.SaveFile( newName );
if ( wiaImageFile != null )
{
Marshal.ReleaseComObject( wiaImageFile );
}
}
Notice that the device items are counted from 1, not zero. According to the encapsulated class in Visual Studio, the argument to Transfer
must be one of the FormatID
enumerates, but when you look at the COM documentation it looks like the argument is optional. In fact, it doesn't seem to make any difference if you specify a wrong type (e.g. wiaFormatTIFF
to transfer a JPEG image). The extension is used later to differentiate between file types.
RAW files and Video
For the D90, a RAW image can be recognised by an empty extension. In the demo program, it is possible to define a name for these files. Nikon RAW files have the .NEF extension. When both RAW and JPEG images are recorded in the camera, they both have the same name. The JPEG image has the .jpg extension, the RAW has none. The demo program keeps the names the same when formatting them, but adds the .nef extension to the RAW file.
The D90 is capable of recording Video clips. These clips can be transferred in the same way as normal photographs, their extension is .AVI, but they don't have properties.
For normal images, the properties contain -amongst others- the original capture date and time, albeit in a peculiar format (like 2008:10:28 for October 28th 2008). The demo program uses this property to save the files with their original date and time.
// Get the original file date and time
DateTime dateTime = new DateTime();
if ( extension == "avi" )
{
// D90 avi files have no properties
dateTime = DateTime.Now;
}
else
{
foreach ( IProperty prop in wiaImageFile.Properties )
{
//Camera datetime format is a little strange:
//DateTime : 2008:10:28 22:20:45
if ( prop.Name == "DateTime" )
{
// replace the colons in the date by dashes
StringBuilder sdate = new StringBuilder( prop.get_Value( ).ToString( ) );
sdate[ 4 ] = '-';
sdate[ 7 ] = '-';
dateTime = DateTime.Parse( sdate.ToString( ) );
break;
}
}
}
In the demo program, a lot of lines are devoted to the formatting of the filenames. Though interesting, this goes beyond the scope of this article. The formatted names all conform to the 8.3 convention, which may be handy if you want to copy them to FAT devices.
Cross-threaded User Feedback
This is also a little off-topic, but to inform the user, the GetImagesFromCamera
thread has to update a textbox when it transfers a new file. This cross-threaded user-feedback calls for another trick. It is not allowed for a thread to call methods in another (the GUI) thread, so we need a delegate in between:
// Set up the delegate for the logging function
public delegate void UpdateLogCallback( LogInfo log );
// The function that is called by the invoke method of the textcontrol
public void LogText( LogInfo log )
{
// show the message to the user
textLogging.AppendText( log.info );
// automatically scroll down to bottom of textbox
textLogging.SelectionStart = textLogging.Text.Length;
textLogging.ScrollToCaret( );
if ( logToFile.Checked )
{
// Append to the logfile
System.IO.StreamWriter Log = new System.IO.StreamWriter( logFileName.Text, true );
Log.Write( log.info );
// If available write exception information in the logfile
if ( log.extraInfo != "" )
{
Log.Write( log.extraInfo );
}
Log.Close( );
}
}
public void Log( string info )
{
LogInfo log = new LogInfo();
log.info = info;
log.extraInfo = "";
textLogging.Invoke( new UpdateLogCallback( this.LogText ), new object[] { log } );
}
Points of Interest
Apparently, Microsoft support is unaware of the fact that RAW images can be transferred using WIA. It works great though.
I am also curious if this program also works for other camera brands.
References
Windows Image Acquisition Automation Layer
WIA scripting and .NET
Download WIAAutSDK.zip from Microsoft:
Windows Image Acquisition Automation Library v2.0