Click here to Skip to main content
Click here to Skip to main content

Transferring D90 images with WIA

By , 5 Nov 2008
Rate this:
Please Sign up or sign in to vote.

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Hans de Rijck
Other
Netherlands Netherlands
I am working as a senior software engineer in finance. Projects like this I do as a hobby.

Comments and Discussions

 
QuestionHow can i get the event of shutter? Pinmembersuiqngheangle17-Apr-12 15:51 
Generalhi PinmemberHossam Mustafa21-Jun-09 19:05 
GeneralRe: hi Pinmembermbender17-Nov-09 10:15 
I believe if you download the demo app you'll have your compiled version you are looking for.
 
Coding blog http://mikebender.itsaphotobooth.net/
 
Fan of:
C#, JavaScript, PHP, VB, C/C++, XHTML/CSS

GeneralNice app Pinmemberkissdznuts6-Nov-08 6:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 5 Nov 2008
Article Copyright 2008 by Hans de Rijck
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid