http://blog.csdn.net/zajin/article/details/17021339 
 

介绍

佳能EOS数码SDK是一个 可以用来远程控制其数码单反相机相当强大的SDK。不幸的是,想在互联网上找些很好的例子相当难,而且提供的文档也不是很齐全。因为我已经找到了很多能让其他人更容易掌握它的东西,我就想可以把我认为最重要的东西组织在一起,做成一个教程。 
本教程包括:

  • 初始化和终止的SDK
  • 打开和关闭摄像头会话
  • 获取连接的像机列表
  • set和get相机设置
  • 获取可用的设置列表
  • 正常拍照和使用闪光灯模式
  • 处理相机事件
  • 将拍摄的照片下载到电脑上
  • 启动和实时查看
  • 记录实时查看
  • 锁定/解锁相机的用户界面

另外:其实我不从属于佳能公司,也不受其自助。 
还有:我不对这个软件做任何方式担保。使用时请您自己注意风险! (你可以在GPL许可证中找到有关此主题的更多信息。) 

背景

你必须有佳能EDSDK副本才能让这个运行起来。 (我认为)我不会被允许包含那些官方的DLL的到项目中,所以你必须自己通过申请去获取,它们在这里:

一旦你得到了那些DLL,就把它们放到项目中的 EDSDK文件夹里面,并确保再调试/发行目录中也要有相同的文件夹。 (或你认为的任何地方,同时据此调整 EDSDK.cs文件中的DLLPath变量(在右上部)。

使用代码

我使用了三个简单的类,SDKHandler,Camera和CameraValues,还有来自 佳能SDK的EDSDK。

在SDKHandler中有几个变量:

  1. /// <summary>
  2. /// The used camera
  3. /// </summary>
  4. public Camera MainCamera { get; private set; }
  5. /// <summary>
  6. /// States if a session with the MainCamera is opened
  7. /// </summary>
  8. public bool CameraSessionOpen { get; private set; }
  9. /// <summary>
  10. /// States if the LiveView is on or not
  11. /// </summary>
  12. public bool IsLiveViewOn { get; private set; }
  13. /// <summary>
  14. /// States if LiveView is recorded or not
  15. /// </summary>
  16. public bool IsEvfFilming { get; private set; }
  17. /// <summary>
  18. /// Directory to where photos will be saved
  19. /// </summary>
  20. public string ImageSaveDirectory { get; set; }
  21. /// <summary>
  22. /// Handles errors that happen with the SDK
  23. /// </summary>
  24. public uint Error
  25. {
  26. get { return EDSDK.EDS_ERR_OK; }
  27. set { if (value != EDSDK.EDS_ERR_OK) throw new Exception("SDK Error: " + value); }
  28. }
  29. /// <summary>
  30. /// Frame buffer for LiveView recording
  31. /// </summary>
  32. private Queue<byte[]> FrameBuffer = new Queue<byte[]>(1000);

还有一些来自SDK的以及我自己添加的一些事件:

  1. #region SDK Events
  2. public event EDSDK.EdsCameraAddedHandler SDKCameraAddedEvent;
  3. public event EDSDK.EdsObjectEventHandler SDKObjectEvent;
  4. public event EDSDK.EdsProgressCallback SDKProgressCallbackEvent;
  5. public event EDSDK.EdsPropertyEventHandler SDKPropertyEvent;
  6. public event EDSDK.EdsStateEventHandler SDKStateEvent;
  7. #endregion
  8. #region Custom Events
  9. public delegate void CameraAddedHandler();
  10. public delegate void ProgressHandler(int Progress);
  11. public delegate void ImageUpdate(Image img);
  12. public delegate void FloatUpdate(float Value);
  13. /// <summary>
  14. /// Fires if a camera is added
  15. /// </summary>
  16. public event CameraAddedHandler CameraAdded;
  17. /// <summary>
  18. /// Fires if any process reports progress
  19. /// </summary>
  20. public event ProgressHandler ProgressChanged;
  21. /// <summary>
  22. /// Fires if the LiveView image is updated
  23. /// </summary>
  24. public event ImageUpdate LiveViewUpdated;
  25. /// <summary>
  26. /// Fires if a new framerate is calculated
  27. /// </summary>
  28. public event FloatUpdate FrameRateUpdated;
  29. #endregion

这个类的方法将在稍后讨论。

Camera类相当简单,工作起来就像一个相机指针和有关相机的一些信息的容器:

  1. public class Camera
  2. {
  3. internal IntPtr Ref;
  4. public EDSDK.EdsDeviceInfo Info { get; private set; }
  5. public uint Error
  6. {
  7. get { return EDSDK.EDS_ERR_OK; }
  8. set { if (value != EDSDK.EDS_ERR_OK) throw new Exception("SDK Error: " + value); }
  9. }
  10. public Camera(IntPtr Reference)
  11. {
  12. this.Ref = Reference;
  13. EDSDK.EdsDeviceInfo dinfo;
  14. Error = EDSDK.EdsGetDeviceInfo(Reference, out dinfo);
  15. this.Info = dinfo;
  16. }
  17. }
CameraValues是一个拥有所有单元ID值以及存储从Av、Tv到ISO字符串值的静态类。

初始化和终止SDK

初始化和终止是最容易做的事情。当您一启动程序,就创建了一个SDKHandler的新实例。

  1. /// <summary>
  2. /// Initialises the SDK and adds events
  3. /// </summary>
  4. public SDKHandler()
  5. {
  6. //this is the important part of initialisation
  7. Error = EDSDK.EdsInitializeSDK();
  8. //here we subscribe to the CameraAddedEvent and tell the SDK we did so
  9. CameraAddedEvent += new EDSDK.EdsCameraAddedHandler(SDKHandler_CameraAddedEvent);
  10. EDSDK.EdsSetCameraAddedHandler(CameraAddedEvent, IntPtr.Zero);
  11. //here we subscribe to the rest of the camera events
  12. SDKStateEvent += new EDSDK.EdsStateEventHandler(Camera_SDKStateEvent);
  13. SDKPropertyEvent += new EDSDK.EdsPropertyEventHandler(Camera_SDKPropertyEvent);
  14. SDKProgressCallbackEvent += new EDSDK.EdsProgressCallback(Camera_SDKProgressCallbackEvent);
  15. SDKObjectEvent += new EDSDK.EdsObjectEventHandler(Camera_SDKObjectEvent);
  16. }

而当你关闭程序时,就会调用:

  1. /// <summary>
  2. /// Closes open session and terminates the SDK
  3. /// </summary>
  4. public void Dispose()
  5. {
  6. if (CameraSessionOpen) Error = EDSDK.EdsCloseSession(MainCamera.Ref);
  7. Error = EDSDK.EdsTerminateSDK();
  8. }

获取连接的像机列表

要打开一个会话,你必须选择一个相机。如果要获得所有连接的像机的列表,那就调用这个:

  1. /// <summary>
  2. /// Get a list of all connected cameras
  3. /// </summary>
  4. /// <returns>The camera list</returns>
  5. public List<Camera> GetCameraList()
  6. {
  7. IntPtr camlist;
  8. //Get cameralist
  9. Error = EDSDK.EdsGetCameraList(out camlist);
  10. //Get each camera from camlist
  11. int c;
  12. Error = EDSDK.EdsGetChildCount(camlist, out c);
  13. List<Camera> OutCamList = new List<Camera>();
  14. for (int i = 0; i < c; i++)
  15. {
  16. IntPtr cptr;
  17. Error = EDSDK.EdsGetChildAtIndex(camlist, i, out cptr);
  18. OutCamList.Add(new Camera(cptr));
  19. {
  20. return OutCamList;
  21. }

打开和关闭相机会话

从以前收到的像机列表中选择一个,打开一个使用它的会话:

  1. /// <summary>
  2. /// Opens a session with given camera
  3. /// </summary>
  4. /// <param name="NewCamera">The camera which will be used</param>
  5. public void OpenSession(Camera NewCamera)
  6. {
  7. //make sure the previous camera session is closed
  8. if (CameraSessionOpen) Error = EDSDK.EdsCloseSession(MainCamera.Ref);
  9. if (NewCamera != null)
  10. {
  11. MainCamera = NewCamera;
  12. //open a session
  13. Error = EDSDK.EdsOpenSession(MainCamera.Ref);
  14. //subscribe to the camera events (this time, in-Camera)
  15. EDSDK.EdsSetCameraStateEventHandler(MainCamera.Ref,
  16. EDSDK.StateEvent_All, SDKStateEvent, IntPtr.Zero);
  17. EDSDK.EdsSetObjectEventHandler(MainCamera.Ref,
  18. EDSDK.ObjectEvent_All, SDKObjectEvent, IntPtr.Zero);
  19. EDSDK.EdsSetPropertyEventHandler(MainCamera.Ref,
  20. EDSDK.PropertyEvent_All, SDKPropertyEvent, IntPtr.Zero);
  21. CameraSessionOpen = true;
  22. }
  23. }

如果你完成了对相机的使用,就使用这个方法关闭会话:

  1. /// <summary>
  2. /// Closes the session with the current camera
  3. /// </summary>
  4. public void CloseSession()
  5. {
  6. if (CameraSessionOpen)
  7. {
  8. Error = EDSDK.EdsCloseSession(MainCamera.Ref);
  9. CameraSessionOpen = false;
  10. }
  11. }

Set 和 Get 相机设置

通过ID去设置和获取相机的设置是非常简单的,但是一些有难度的结构值(这里还没有介绍).下面这个例子你可以在这个方法中获取到Tv,Av和ISO的设置。

  1. /// <summary>
  2. /// Gets the current setting of given property ID
  3. /// </summary>
  4. /// <param name="PropID">The property ID</param>
  5. /// <returns>The current setting of the camera</returns>
  6. public uint GetSetting(uint PropID)
  7. {
  8. if (MainCamera.Ref != IntPtr.Zero)
  9. {
  10. unsafe
  11. {
  12. uint property = 0;
  13. EDSDK.EdsDataType dataType;
  14. int dataSize;
  15. IntPtr ptr = new IntPtr(&property);
  16. //get the size of this property
  17. Error = EDSDK.EdsGetPropertySize(MainCamera.Ref, PropID, 0, out dataType, out dataSize);
  18. //get the data for this property
  19. Error = EDSDK.EdsGetPropertyData(MainCamera.Ref, PropID, 0, dataSize, ptr);
  20. return property;
  21. }
  22. }
  23. else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
  24. }

Setting方法(这里的参数一般是ID,从Camera类中获取这个string值):

  1. /// <summary>
  2. /// Sets a value for the given property ID
  3. /// </summary>
  4. /// <param name="PropID">The property ID</param>
  5. /// <param name="Value">The value which will be set</param>
  6. public void SetSetting(uint PropID, uint Value)
  7. {
  8. if (MainCamera.Ref != IntPtr.Zero)
  9. {
  10. int propsize;
  11. EDSDK.EdsDataType proptype;
  12. //get the size of this property
  13. Error = EDSDK.EdsGetPropertySize(MainCamera.Ref, PropID, 0, out proptype, out propsize);
  14. //set the property
  15. Error = EDSDK.EdsSetPropertyData(MainCamera.Ref, PropID, 0, propsize, Value);
  16. }
  17. else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
  18. }

可获取的设置值清单:

特定的相机没有特定的支持设置。这就是为什么你需要去获取所有可支持的设置值清单。这些只支持"AEModeSelect", "ISO", "Av", "Tv", "MeteringMode" 和"ExposureCompensation"。传给特定的ID你可以获取到对应的返回值。在Camera类中可以找到和Av,Tv和ISO的对应值。查看PDF格式的SDK文档可以获取其他的值。

  1. /// <summary>
  2. /// Gets the list of possible values for the current camera to set.
  3. /// Only the PropertyIDs "AEModeSelect", "ISO", "Av", "Tv", "MeteringMode"
  4. /// and "ExposureCompensation" are allowed.
  5. /// </summary>
  6. /// <param name="PropID">The property ID</param>
  7. /// <returns>A list of available values for the given property ID</returns>
  8. public List<int> GetSettingsList(uint PropID)
  9. {
  10. if (MainCamera.Ref != IntPtr.Zero)
  11. {
  12. if (PropID == EDSDK.PropID_AEModeSelect || PropID == EDSDK.PropID_ISOSpeed ||
  13. PropID == EDSDK.PropID_Av
  14. || PropID == EDSDK.PropID_Tv || PropID == EDSDK.PropID_MeteringMode ||
  15. PropID == EDSDK.PropID_ExposureCompensation)
  16. {
  17. EDSDK.EdsPropertyDesc des;
  18. Error = EDSDK.EdsGetPropertyDesc(MainCamera.Ref, PropID, out des);
  19. return des.PropDesc.Take(des.NumElements).ToList();
  20. }
  21. else throw new ArgumentException("Method cannot be used with this Property ID");
  22. }
  23. else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
  24. }

在bulb mode(灯泡模式)下正常拍照

用当前设置拍照,调用TakePhoto方法。有三点需要特别注意:

1、新线程启动了所以主线程没有被挂起。 
2、之所有这里用while 循环,是因为由相机有时不会立即就绪,需要稍后再试 
3、如果你将pc作为外设,那么请到下一个章节学习如何获取图片。

  1. /// <summary>
  2. /// Takes a photo with the current camera settings
  3. /// </summary>
  4. public void TakePhoto()
  5. {
  6. new Thread(delegate()
  7. {
  8. int BusyCount = 0;
  9. uint err = EDSDK.EDS_ERR_OK;
  10. while (BusyCount < 20)
  11. {
  12. //try to take a photo
  13. err = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_TakePicture, 0);
  14. //if the camer is currently busy, wait and try again.
  15. //If successful or an error happened, break the loop
  16. if (err == EDSDK.EDS_ERR_DEVICE_BUSY) { BusyCount++; Thread.Sleep(50); }
  17. else { break; }
  18. }
  19. Error = err;
  20. }).Start();
  21. }

在bulb 模式下拍照,调用带有时间参数的takePhoto方法

  1. /// <summary>
  2. /// Takes a photo in bulb mode with the current camera settings
  3. /// </summary>
  4. /// <param name="BulbTime">The time in milliseconds for how long the shutter will be open</param>
  5. public void TakePhoto(uint BulbTime)
  6. {
  7. new Thread(delegate()
  8. {
  9. if (BulbTime < 1000)
  10. { throw new ArgumentException("Bulbtime has to be bigger than 1000ms"); }
  11. int BusyCount = 0;
  12. uint err = EDSDK.EDS_ERR_OK;
  13. while (BusyCount < 20)
  14. {
  15. //open the shutter
  16. err = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_BulbStart, 0);
  17. if (err == EDSDK.EDS_ERR_DEVICE_BUSY) { BusyCount++; Thread.Sleep(50); }
  18. else { break; }
  19. }
  20. Error = err;
  21. //Wait for the specified time
  22. Thread.Sleep((int)BulbTime);
  23. //close the shutter
  24. Error = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_BulbEnd, 0);
  25. }).Start();
  26. }

将拍摄的图片上传到电脑中

想要把拍摄的照片直接传到电脑上,代替相机存储,请调用SetSetting方法进行设置:

  1. SetSetting(EDSDK.PropID_SaveTo, (uint)EDSDK.EdsSaveTo.Host);

每拍摄一张照片,EDSDK.ObjectEvent_DirItemRequestTransfer 类型的SDKObjectEvent都会被触发

  1. /// <summary>
  2. /// An Objectevent fired
  3. /// </summary>
  4. /// <param name="inEvent">The ObjectEvent id</param>
  5. /// <param name="inRef">Pointer to the object</param>
  6. /// <param name="inContext"></param>
  7. /// <returns>An EDSDK errorcode</returns>
  8. private uint Camera_SDKObjectEvent(uint inEvent, IntPtr inRef, IntPtr inContext)
  9. {
  10. if(inEvent == EDSDK.ObjectEvent_DirItemRequestTransfer)
  11. DownloadImage(inRef, @"Images\");
  12. return EDSDK.EDS_ERR_OK;
  13. }

DownloadImage方法如下:

  1. /// <summary>
  2. /// Downloads an image to given directory
  3. /// </summary>
  4. /// <param name="Info">Pointer to the object.
  5. /// Get it from the SDKObjectEvent.</param>
  6. /// <param name="directory"></param>
  7. public void DownloadImage(IntPtr ObjectPointer, string directory)
  8. {
  9. EDSDK.EdsDirectoryItemInfo dirInfo;
  10. IntPtr streamRef;
  11. //get information about the image
  12. Error = EDSDK.EdsGetDirectoryItemInfo(ObjectPointer, out dirInfo);
  13. string CurrentPhoto = Path.Combine(directory, dirInfo.szFileName);
  14. //create a filestream for the image
  15. Error = EDSDK.EdsCreateFileStream(CurrentPhoto,
  16. EDSDK.EdsFileCreateDisposition.CreateAlways, EDSDK.EdsAccess.ReadWrite, out streamRef);
  17. uint blockSize = 1024 * 1024;
  18. uint remainingBytes = dirInfo.Size;
  19. //download the image data in blocks
  20. do
  21. {
  22. if (remainingBytes < blockSize) { blockSize = (uint)(remainingBytes / 512) * 512; }
  23. remainingBytes -= blockSize;
  24. Error = EDSDK.EdsDownload(ObjectPointer, blockSize, streamRef);
  25. } while (remainingBytes > 512);
  26. //download the last bit of the image
  27. Error = EDSDK.EdsDownload(ObjectPointer, remainingBytes, streamRef);
  28. //tell the camera that the download is done
  29. Error = EDSDK.EdsDownloadComplete(ObjectPointer);
  30. //release image and stream
  31. Error = EDSDK.EdsRelease(ObjectPointer);
  32. Error = EDSDK.EdsRelease(streamRef);
  33. }

打开并查看视频

视频是最难处理的事情之一,尤其是要求高性能的情况下。 首先我们这样打开视频:

  1. /// <summary>
  2. /// Starts the LiveView
  3. /// </summary>
  4. public void StartLiveView()
  5. {
  6. //make sure it's not already on
  7. if (!IsLiveViewOn)
  8. {
  9. //set the LiveView output to be the PC
  10. SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_PC);
  11. IsLiveViewOn = true;
  12. }
  13. }

完成之后, SDKPropertyEvent这个事件的 inPropertyID参数就被设置成了 EDSDK.PropID_Evf_OutputDevice:

  1. /// <summary>
  2. /// A property changed
  3. /// </summary>
  4. /// <param name="inEvent">The PropetyEvent ID</param>
  5. /// <param name="inPropertyID">The Property ID</param>
  6. /// <param name="inParameter">Event Parameter</param>
  7. /// <param name="inContext">...</param>
  8. /// <returns>An EDSDK errorcode</returns>
  9. private uint Camera_SDKPropertyEvent
  10. (uint inEvent, uint inPropertyID, uint inParameter, IntPtr inContext)
  11. {
  12. if (inPropertyID == EDSDK.PropID_Evf_OutputDevice)
  13. {
  14. if (IsEvfFilming == true) DownloadEvfFilm();
  15. else if (IsLiveViewOn == true) DownloadEvf();
  16. }
  17. return EDSDK.EDS_ERR_OK;
  18. }

DownloadEvf方法如下:

  1. /// <summary>
  2. /// Downloads the LiveView image
  3. /// </summary>
  4. private void DownloadEvf()
  5. {
  6. new Thread(delegate()
  7. {
  8. //To give the camera time to switch the mirror
  9. Thread.Sleep(1500);
  10. IntPtr jpgPointer;
  11. IntPtr stream = IntPtr.Zero;
  12. IntPtr EvfImageRef = IntPtr.Zero;
  13. UnmanagedMemoryStream ums;
  14. uint err;
  15. uint length;
  16. //create streams
  17. err = EDSDK.EdsCreateMemoryStream(0, out stream);
  18. err = EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);
  19. Stopwatch watch = new Stopwatch();    //stopwatch for FPS calculation
  20. float lastfr = 24; //last actual FPS
  21. //Run LiveView
  22. while (IsLiveViewOn)
  23. {
  24. watch.Restart();
  25. //download current LiveView image
  26. err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);
  27. unsafe
  28. {
  29. //get pointer and create stream
  30. Error = EDSDK.EdsGetPointer(stream, out jpgPointer);
  31. Error = EDSDK.EdsGetLength(stream, out length);
  32. ums = new UnmanagedMemoryStream
  33. ((byte*)jpgPointer.ToPointer(), length, length, FileAccess.Read);
  34. //fire the LiveViewUpdated event with
  35. //the LiveView image created from the stream
  36. if (LiveViewUpdated != null) LiveViewUpdated(Image.FromStream(ums));
  37. ums.Close();
  38. }
  39. //calculate the framerate and fire the FrameRateUpdated event
  40. lastfr = lastfr * 0.9f + (100f / watch.ElapsedMilliseconds);
  41. if (FrameRateUpdated != null) FrameRateUpdated(lastfr);
  42. }
  43. //Release and finish
  44. if (stream != IntPtr.Zero) { Error = EDSDK.EdsRelease(stream); }
  45. if (EvfImageRef != IntPtr.Zero) { Error = EDSDK.EdsRelease(EvfImageRef); }
  46. //stop the LiveView
  47. SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_TFT);
  48. }).Start();
  49. }

虽然这样下载视频图像不是最简单的,但可以说是最快的。

调用StopLiveView方法就能停止实物取景,实质上它的目的是让DownloadEvf方法跳出while循环:

  1. /// <summary>
  2. /// Stops the LiveView
  3. /// </summary>
  4. public void StopLiveView()
  5. {
  6. IsLiveViewOn = false;
  7. }

记录播放窗口

记录视频的工作跟播放视频的方式很像。

开始方法如下:

  1. /// <summary>
  2. /// Starts LiveView and records it
  3. /// </summary>
  4. public void StartEvfFilming()
  5. {
  6. if (!IsLiveViewOn)
  7. {
  8. SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_PC);
  9. IsLiveViewOn = true;
  10. IsEvfFilming = true;
  11. }
  12. }

捕获SDKPropertyEvent事件:

  1. /// <summary>
  2. /// A property changed
  3. /// </summary>
  4. /// <param name="inEvent">The PropetyEvent ID</param>
  5. /// <param name="inPropertyID">The Property ID</param>
  6. /// <param name="inParameter">Event Parameter</param>
  7. /// <param name="inContext">...</param>
  8. /// <returns>An EDSDK errorcode</returns>
  9. private uint Camera_SDKPropertyEvent
  10. (uint inEvent, uint inPropertyID, uint inParameter, IntPtr inContext)
  11. {
  12. if (inPropertyID == EDSDK.PropID_Evf_OutputDevice)
  13. {
  14. if (IsEvfFilming == true) DownloadEvfFilm();
  15. else if (IsLiveViewOn == true) DownloadEvf();
  16. }
  17. return EDSDK.EDS_ERR_OK;
  18. }

DownloadEvfFilmmethod和DownloadEvfmethod比较相似,但有以下不同:

  • 在开始while循环之前,已经下载了一个边框,并启动了 StartEvfVideoWriter方法。
  • 为了更好的性能,LiveViewUpdatedevent只在每次第四个边框中调用(让实时取景稍显缓慢,但视频很流畅)
  • 实时取景图像作为byte array放入队列中,供StartEvfVideoWriter方法处理
  1. /// <summary>
  2. /// Records the LiveView image
  3. /// </summary>
  4. private void DownloadEvfFilm()
  5. {
  6. new Thread(delegate()
  7. {
  8. //To give the camera time to switch the mirror
  9. Thread.Sleep(1500);
  10. IntPtr jpgPointer;
  11. IntPtr stream = IntPtr.Zero;
  12. IntPtr EvfImageRef = IntPtr.Zero;
  13. UnmanagedMemoryStream ums;
  14. uint err;
  15. uint length;
  16. err = EDSDK.EdsCreateMemoryStream(0, out stream);
  17. err = EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);
  18. //Download one frame to init the video size
  19. err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);
  20. unsafe
  21. {
  22. Error = EDSDK.EdsGetPointer(stream, out jpgPointer);
  23. Error = EDSDK.EdsGetLength(stream, out length);
  24. ums = new UnmanagedMemoryStream((byte*)jpgPointer.ToPointer(),
  25. length, length, FileAccess.Read);
  26. Bitmap bmp = new Bitmap(ums);
  27. StartEvfVideoWriter(bmp.Width, bmp.Height);
  28. bmp.Dispose();
  29. ums.Close();
  30. }
  31. Stopwatch watch = new Stopwatch();
  32. byte[] barr; //bitmap byte array
  33. const long ft = 41; //Frametime at 24FPS
  34. //(actually 41.66, but there is a bit of calculation overhead)
  35. float lastfr = 24; //last actual FPS
  36. int LVUpdateBreak1 = 0;
  37. //Run LiveView
  38. while (IsEvfFilming)
  39. {
  40. watch.Restart();
  41. err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);
  42. unsafe
  43. {
  44. Error = EDSDK.EdsGetPointer(stream, out jpgPointer);
  45. Error = EDSDK.EdsGetLength(stream, out length);
  46. ums = new UnmanagedMemoryStream((byte*)jpgPointer.ToPointer(),
  47. length, length, FileAccess.Read);
  48. barr = new byte[length];
  49. ums.Read(barr, 0, (int)length);
  50. //For better performance the LiveView is only updated with every 4th frame
  51. if (LVUpdateBreak1 == 0 && LiveViewUpdated != null)
  52. { LiveViewUpdated(Image.FromStream(ums)); LVUpdateBreak1 = 4; }
  53. LVUpdateBreak1--;
  54. FrameBuffer.Enqueue(barr);
  55. ums.Close();
  56. }
  57. //To get a steady framerate:
  58. while (true) if (watch.ElapsedMilliseconds >= ft) break;
  59. lastfr = lastfr * 0.9f + (100f / watch.ElapsedMilliseconds);
  60. if (FrameRateUpdated != null) FrameRateUpdated(lastfr);
  61. }
  62. //Release and finish
  63. if (stream != IntPtr.Zero) { Error = EDSDK.EdsRelease(stream); }
  64. if (EvfImageRef != IntPtr.Zero) { Error = EDSDK.EdsRelease(EvfImageRef); }
  65. SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_TFT);
  66. }).Start();
  67. }

由于写硬件驱动转换图片对象很慢,并且这也不需要实时处理,所以有了下面的StartEvfVideoWriter 方法 。这个方法将边框从队列中取出来保存,直到队列为空并且电影处理已关闭。我这里没有包含实际的视频保存功能,你可以用你偏好的类库去完成。

  1. /// <summary>
  2. /// Writes video frames from the buffer to a file
  3. /// </summary>
  4. /// <param name="Width">Width of the video</param>
  5. /// <param name="Height">Height of the video</param>
  6. private void StartEvfVideoWriter(int Width, int Height)
  7. {
  8. new Thread(delegate()
  9. {
  10. byte[] byteArray;
  11. ImageConverter ic = new ImageConverter();
  12. Image img;
  13. while (IsEvfFilming)
  14. {
  15. while (FrameBuffer.Count > 0)
  16. {
  17. //get byte array from queue
  18. byteArray = FrameBuffer.Dequeue();
  19. //convert it to an image object
  20. img = (Image)ic.ConvertFrom(byteArray);
  21. //Save video frame here. e.g. with the VideoFileWriter from the AForge library.
  22. }
  23. //if saving is faster than the LiveView, wait a bit for new frames and start over
  24. if (IsEvfFilming) Thread.Sleep(10);
  25. }
  26. }).Start();
  27. }

下面是如何利用 AForgelibrary的一个例子 (请注意correct DLLs,它们没被包含在这个项目中)

  1. private void StartVideoWriter(int Width, int Height)
  2. {
  3. new Thread(delegate()
  4. {
  5. VideoFileWriter writer = new VideoFileWriter();
  6. writer.Open("LiveViewVideo.avi", Width, Height, 24, VideoCodec.MPEG4);
  7. byte[] byteArray;
  8. ImageConverter ic = new ImageConverter();
  9. Image img;
  10. while (IsEvfFilming)
  11. {
  12. while (FrameBuffer.Count > 0)
  13. {
  14. byteArray = FrameBuffer.Dequeue();
  15. img = (Image)ic.ConvertFrom(byteArray);
  16. writer.WriteVideoFrame(new Bitmap(img));
  17. }
  18. if (IsEvfFilming) Thread.Sleep(10);
  19. }
  20. writer.Close();
  21. }).Start();
  22. }

关闭电影功能跟关闭实时取景方法一样:

  1. /// <summary>
  2. /// Stops LiveView and filming
  3. /// </summary>
  4. public void StopEvfFilming()
  5. {
  6. IsLiveViewOn = false;
  7. IsEvfFilming = false;
  8. }

关闭/打开相机的接口

为了避免或允许用户在相机上改变设置,你可以这样关闭或者打开相机的接口:

  1. /// <summary>
  2. /// Locks or unlocks the cameras UI
  3. /// </summary>
  4. /// <param name="LockState">True for locked, false to unlock</param>
  5. public void UILock(bool LockState)
  6. {
  7. if (LockState == true) Error =
  8. EDSDK.EdsSendStatusCommand(MainCamera.Ref, EDSDK.CameraState_UILock, 0);
  9. else Error = EDSDK.EdsSendStatusCommand
  10. (MainCamera.Ref, EDSDK.CameraState_UIUnLock, 0);
  11. }

利用图形化界面

在图像化界面向导代码中,你可以看到如何将以上所有的代码运用到一个真实可用的软件中。你也可以设置 Av,Tv,ISO和白平衡,实时取景和拍照等模式.

插入相机,打开图形化界面就可以开始你的设置啦。

题外话

我用EOS 40D测试了以上代码:

如果你尝试了不同的方法,请告诉我,我会把它添加到这篇文章中。

如果你发现一些bug,对方法有改进或者有一些新的想法,非常希望你能告诉我。

源码下载:

佳能相机操作 EDSDK 教程 C# 版本的更多相关文章

  1. agentzh 的 Nginx 教程(版本 2019.07.31)

    agentzh 的 Nginx 教程(版本 2019.07.31) agentzh 的 Nginx 教程(版本 2019.07.31) https://openresty.org/download/a ...

  2. Git安装教程最新版本(国内gitee国外github)

    Git安装教程最新版本(国内gitee国外github) 欢迎关注博主公众号「Java大师」, 专注于分享Java领域干货文章, 关注回复「资源」, 获取大师使用的typora主题: http://w ...

  3. 安装MYSQL详细教程 版本:mysql-installer-community-5.7.16.0 免安装版本和安装版本出现错误的解决

    一.版本的选择 之前安装的Mysql,现在才来总结,好像有点晚,后台换系统了,现在从新装上Mysql,感觉好多坑,我是来踩坑,大家看到坑就别跳了,这样可以省点安装时间,这个折腾了两天,安装了好多个版本 ...

  4. Git教程之版本回退(4)

    现在,我们已经学会了修改文件,然后把修改提交到Git版本库,现在再次修改readme.txt文件如下:

  5. git教程--git版本库的使用

    向版本控制器提交文件 我们已经成功地添加并提交了一个readme.txt文件,现在,是时候继续工作了,于是,我们继续修改readme.txt文件,改成如下内容: Git is a distribute ...

  6. git教程:版本退回

    转载:版本回退 现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改readme.txt文件如下: Git is a distributed version control ...

  7. Git 基础教程 之 版本回退

    不断对文件进行修改,然后不断提交修改到版本库里. 当你觉得文件修改到一定程度时,可以保存一个“快照”,这个“快照”在Git中称为“commit”. 一旦文件被改乱了.误删了,都可以从最近一个“comm ...

  8. [SpringCloud教程]2. 版本选型和项目搭建

    Spring Cloud Alibaba 版本选型 建议先选择Spring Cloud Alibaba的大版本,方便兼容 选择 Spring Cloud Alibaba 大版本 访问链接,找到标题&q ...

  9. VirtualBox安装Ubuntu教程

    1.VirtualBox虚拟机安装,及VirtualBox安装Ubuntu教程VirtualBox版本为VirtualBox-4.3.12-93733-Win.exe,Ubuntu版本为ubuntu- ...

随机推荐

  1. Windows Azure 系列-- Azure Redis Cache的配置和使用

    假设还没有配置Azure Power shell 能够參照这里进行配置:http://blog.csdn.net/lan_liang/article/details/46850221 打开Azure ...

  2. flume A simple example

    http://flume.apache.org/FlumeUserGuide.html A simple example

  3. GNU_MAKE--工程管理

    GNU MAKE--工程管理 makefile是为工程组织编译,为“自动化编译”,一旦写成,只需要一个make命令,整个工程完全自动编译,极大提高了软件开发效率.make是一个命令工具,是一个解释ma ...

  4. MongoDb数据结构详解

    首先,向数据库插入一条bjson数据 首先是定义文档,然后使用admin用户名密码登录,进入test数据库,向test数据库中插入此文档(“表名称和表中的记录”) 插入结果,查看mongoVUE如下图 ...

  5. 【spring boot】在spring boot下使用多线程

    使用场景: 方法处理到某一步,需要将信息交给另一个线程去处理!! =================================================================== ...

  6. TextFlow with JavaFX 2

    http://sahits.ch/blog/?p=2372 ———————————————————————————————————————————————————— TextFlow with Jav ...

  7. OC 基础语法

    :Obect c 与 c 语言的区别 () 后缀名不一样,C语言是.c 结尾 ,OC 是 .h结尾. () 输出信息不同 C语言是用print() 输出,OC 是用NSLog输出. () NSLog会 ...

  8. haml入门

    1.什么是Haml Haml是HTML abstraction markup language,遵循的原则是标记应该是美的.Haml能够加速和简化模版,长处是简洁.可读.高效. 2.erbm模板和ha ...

  9. Android开发:使用DialogFragment实现dialog自定义布局

    使用DialogFragment实现dialog的自定义布局最大的好处是可以更好控制dialog的生命周期. TestFragment的代码: public class TestFragment ex ...

  10. codevs2894、2837、1669、2503、3231

    6.25动态规划之背包回顾 2894 Txx考试  时间限制: 1 s  空间限制: 32000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description Txx是一个 ...