Introduction
Both Benjamin and I have been working with digital cameras for some time now. Of the few manufacturers that support remote capture, Canon has the best feature set. Unfortunately, the Canon SDK does not contain examples in C#, it is C++ only. Elias Torres is well known for his CDSDK wrapper, which I've used with much success. However, newer cameras are not supported by the CDSDK. Most new Canon cameras use the PRSDK. Our C# library (based on the C++ SDK samples and the Elias Torres wrapper) encapsulates Canon's CDSDK and PRSDK.
THE SAMPLE PROGRAM WILL NOT WORK !!!!!
Having written a number of CP articles, I know that we'll be asked this no matter what, but here goes. This is a wrapper of an existing SDK. It won't work without the SDK, or at least the DLLs that come with the SDK. In fact, there are two SDKs involved, although I hope to add a PRSDK-only mode at some point, given that no cameras on sale today work with the CDSDK. We can't give you the DLLs - Canon requires that you register with them to obtain a license. Elias' forum is full of 'please send me the SDK' requests. Please don't ask. We'd love to help you, but we will not disrespect software copyrights.
Sample program
The sample program simply attempts to connect to a Canon camera, enumerate some settings and allows you to show a preview and take pictures. More than the Canon sample does, and more in line with the feature set I'd imagine most people would be looking for. If you work through the sample, you'll see how to query the camera for it's settings, how to take pictures, etc.
Enumerations
The PRSDK and the CDSDK each define their own enums for settings, but these settings often overlap. In order to make things strongly typed, where the PRSDK contains all the values in the CDSDK, I've used the PRSDK enum as the property to get available settings, and to set a value. I did this as the CDSDK will not change, but the PRSDK may add new values. Where there are values in the CDSDK and not the PRSDK, you can use the enums, but the actual properties just take int, so as to not offer properties that are not available.
PRSDK and shooting modes
One thing I've discovered, neither my A620 or my A640 will change shooting mode with the PRSDK, it accepts the setting and ignores it. I think this is a bug in the SDK, I've written to Canon about it and will update the wrapper if I get a fix. In the meantime, I added an IsPRSDK property on the camera, so if the camera connects via the PRSDK and you need an effect such as grayscale, you can post process the images.
I have got a reply from Canon - the sequence went something like this:
Me: I don't think setting the PhotoEffect works in the PRSDK
Canon: Try reading the manual
Me: Thanks, I did, and I've done what it says, but I can't set grayscale. Does it work for you?
Canon: You can set the camera to grayscale manually, then connect. We've tested, and that works here.
Me: Will the camera remember this setting when powered off?
Canon: No. Thank you for developing software for Canon cameras.
I assume that this means the SDK is definately broken in this regard. I also found that trying to set the PhotoMode meant that when I set ExposureCompensation afterwards, I'd get an AccessDenied error, so I use the IsPRSDK method to make sure I don't try to set this property on a newer camera.
Multi-threading
The background to this wrapper is an interesting story. I wrote a wrapper for the PRSDK only, mainly in C++/CLI (so, no pinvoke, pretty much the opposite of how this wrapper works). I coded it for my specific application, not as broadly as this one is written. I wrote a test app and it worked fine. I integrated the DLL into my app, and it kept throwing a WMI error. It was at this point that I met Benjamin, who had also written a wrapper based on Elias' work.
Benjamin's wrapper used pinvoke and was C# only. Building on his code, I made several improvements - added some properties, fixed a few bugs and made some of the existing properties strongly typed. I wrote the included sample app while improving on the wrapper - everything worked. To my surprise, when I added the wrapper to my program, I immediately got WMI errors! After wrestling with the problem, I found that the SDK doesn't like being run from a thread, but a thread inside the DLL appears to work fine. Due to this limitation, I added the ability to do time-intensive things like camera enumeration, camera connection and image acquisition in another thread within the library. Events provide hooks for tracking when these threads end. This leads to code like this:
public Form1()
{
InitializeComponent();
lblPath.Text = Application.StartupPath;
brightnessValues = new List<int />();
exposureModesList = new Dictionary<string, int>();
camera = new Camera();
camera.Connected += new Camera.ConnectionEventHandler(camera_Connected);
camera.ReceivedFrame += new Camera.ReceivedFrameEventHandler(
camera_ReceivedFrame);
camera.DevicesEnumerated += new Camera.DeviceNamesEnumeratedEvent(
camera_DevicesEnumerated);
camera.ImageAcquired += new Camera.ImageAcquiredEventHandler(
camera_ImageAcquired);
}
private void btnConnect_Click(object sender, EventArgs e)
{
camera.StartSDK();
camera.GetDevices();
}
private void camera_DevicesEnumerated(object sender, DeviceListEventArgs e)
{
cboDevices.DataSource = e.DeviceNames;
}
In this code, the GetDevices
call returns right away, and the list of devices connected is passed to the camera_DevicesEnumerated
delegate function. Custom event args are provided for connection, recieving of a frame (which is the preview) and acquiring an image.
Points of Interest
Getting this to work with both SDKs was interesting, mostly due to bugs in the newer SDK. I also tested under Vista, and found the PRSDK doesn't work under Vista (at least it didn't for me). The older SDK works with Vista. I have found that a lot of issues I had, were caused by calling APIs that are broken in the PRSDK, which would kill the SDK for other things. However, I couldn't get the PRSDK to connect in Vista, with either my commercial app, or the demo that comes with this sample (the demo was in general more reliable, as it does less, it never breaks the SDK ).
Community involvement
This wrapper has had input from two people (the article authors), and is to be considered a work in progress. I'd love to spend some time making more of the properties typesafe, for example. So, if you use it, and you find that you improve it, let us know so we can keep improving the wrapper for the benefit of all who use it.
History