zoukankan      html  css  js  c++  java
  • 佳能相机操作 EDSDK 教程 C# 版本

    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中有几个变量:

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. /// <summary>  
    2. /// The used camera  
    3. /// </summary>  
    4. public Camera MainCamera { get; private set; }  
    5.   
    6. /// <summary>  
    7. /// States if a session with the MainCamera is opened  
    8. /// </summary>  
    9. public bool CameraSessionOpen { get; private set; }  
    10.   
    11. /// <summary>  
    12. /// States if the LiveView is on or not  
    13. /// </summary>  
    14. public bool IsLiveViewOn { get; private set; }  
    15.   
    16. /// <summary>  
    17. /// States if LiveView is recorded or not  
    18. /// </summary>  
    19. public bool IsEvfFilming { get; private set; }  
    20.   
    21. /// <summary>  
    22. /// Directory to where photos will be saved  
    23. /// </summary>  
    24. public string ImageSaveDirectory { get; set; }  
    25.   
    26. /// <summary>  
    27. /// Handles errors that happen with the SDK  
    28. /// </summary>  
    29. public uint Error  
    30. {  
    31.     get { return EDSDK.EDS_ERR_OK; }  
    32.     set { if (value != EDSDK.EDS_ERR_OK) throw new Exception("SDK Error: " + value); }  
    33. }  
    34.   
    35. /// <summary>  
    36. /// Frame buffer for LiveView recording  
    37. /// </summary>  
    38. private Queue<byte[]> FrameBuffer = new Queue<byte[]>(1000);  
    还有一些来自SDK的以及我自己添加的一些事件:
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #region SDK Events  
    2.   
    3. public event EDSDK.EdsCameraAddedHandler SDKCameraAddedEvent;  
    4. public event EDSDK.EdsObjectEventHandler SDKObjectEvent;  
    5. public event EDSDK.EdsProgressCallback SDKProgressCallbackEvent;  
    6. public event EDSDK.EdsPropertyEventHandler SDKPropertyEvent;  
    7. public event EDSDK.EdsStateEventHandler SDKStateEvent;  
    8.  
    9. #endregion  
    10.  
    11. #region Custom Events  
    12.   
    13. public delegate void CameraAddedHandler();  
    14. public delegate void ProgressHandler(int Progress);  
    15. public delegate void ImageUpdate(Image img);  
    16. public delegate void FloatUpdate(float Value);  
    17.   
    18. /// <summary>  
    19. /// Fires if a camera is added  
    20. /// </summary>  
    21. public event CameraAddedHandler CameraAdded;  
    22.   
    23. /// <summary>  
    24. /// Fires if any process reports progress  
    25. /// </summary>  
    26. public event ProgressHandler ProgressChanged;  
    27.   
    28. /// <summary>  
    29. /// Fires if the LiveView image is updated  
    30. /// </summary>  
    31. public event ImageUpdate LiveViewUpdated;  
    32.   
    33. /// <summary>  
    34. /// Fires if a new framerate is calculated  
    35. /// </summary>  
    36. public event FloatUpdate FrameRateUpdated;  
    37.  
    38. #endregion    
    这个类的方法将在稍后讨论。

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

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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.   
    11.     public Camera(IntPtr Reference)  
    12.     {  
    13.         this.Ref = Reference;          
    14.         EDSDK.EdsDeviceInfo dinfo;  
    15.         Error = EDSDK.EdsGetDeviceInfo(Reference, out dinfo);  
    16.         this.Info = dinfo;  
    17.     }  
    18. }   
    CameraValues是一个拥有所有单元ID值以及存储从Av、Tv到ISO字符串值的静态类。

    初始化和终止SDK

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

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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.   
    9.     //here we subscribe to the CameraAddedEvent and tell the SDK we did so  
    10.     CameraAddedEvent += new EDSDK.EdsCameraAddedHandler(SDKHandler_CameraAddedEvent);  
    11.     EDSDK.EdsSetCameraAddedHandler(CameraAddedEvent, IntPtr.Zero);  
    12.       
    13.     //here we subscribe to the rest of the camera events  
    14.     SDKStateEvent += new EDSDK.EdsStateEventHandler(Camera_SDKStateEvent);  
    15.     SDKPropertyEvent += new EDSDK.EdsPropertyEventHandler(Camera_SDKPropertyEvent);  
    16.     SDKProgressCallbackEvent += new EDSDK.EdsProgressCallback(Camera_SDKProgressCallbackEvent);  
    17.     SDKObjectEvent += new EDSDK.EdsObjectEventHandler(Camera_SDKObjectEvent);  
    18. }  
    而当你关闭程序时,就会调用:
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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. }  

    获取连接的像机列表

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

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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. }  

    打开和关闭相机会话

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

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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. }  
    如果你完成了对相机的使用,就使用这个方法关闭会话:
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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的设置。

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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值):
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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文档可以获取其他的值。

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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作为外设,那么请到下一个章节学习如何获取图片。

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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方法
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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.   
    21.         Error = err;  
    22.         //Wait for the specified time  
    23.         Thread.Sleep((int)BulbTime);  
    24.         //close the shutter  
    25.         Error = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_BulbEnd, 0);  
    26.     }).Start();  
    27. }  

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

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

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. SetSetting(EDSDK.PropID_SaveTo, (uint)EDSDK.EdsSaveTo.Host);  
    每拍摄一张照片,EDSDK.ObjectEvent_DirItemRequestTransfer 类型的SDKObjectEvent都会被触发
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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方法如下:
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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. }  

    打开并查看视频

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

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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:
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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方法如下:
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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.   
    11.         IntPtr jpgPointer;  
    12.         IntPtr stream = IntPtr.Zero;  
    13.         IntPtr EvfImageRef = IntPtr.Zero;  
    14.         UnmanagedMemoryStream ums;  
    15.         uint err;  
    16.         uint length;  
    17.         //create streams  
    18.         err = EDSDK.EdsCreateMemoryStream(0, out stream);  
    19.         err = EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);  
    20.   
    21.         Stopwatch watch = new Stopwatch();    //stopwatch for FPS calculation  
    22.         float lastfr = 24; //last actual FPS  
    23.   
    24.         //Run LiveView  
    25.         while (IsLiveViewOn)  
    26.         {  
    27.             watch.Restart();  
    28.             //download current LiveView image  
    29.             err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);  
    30.   
    31.             unsafe  
    32.             {  
    33.                 //get pointer and create stream  
    34.                 Error = EDSDK.EdsGetPointer(stream, out jpgPointer);  
    35.                 Error = EDSDK.EdsGetLength(stream, out length);  
    36.                 ums = new UnmanagedMemoryStream  
    37.                 ((byte*)jpgPointer.ToPointer(), length, length, FileAccess.Read);  
    38.                 //fire the LiveViewUpdated event with   
    39.                 //the LiveView image created from the stream  
    40.                 if (LiveViewUpdated != null) LiveViewUpdated(Image.FromStream(ums));  
    41.                 ums.Close();  
    42.             }  
    43.             //calculate the framerate and fire the FrameRateUpdated event  
    44.             lastfr = lastfr * 0.9f + (100f / watch.ElapsedMilliseconds);  
    45.             if (FrameRateUpdated != null) FrameRateUpdated(lastfr);  
    46.         }  
    47.   
    48.         //Release and finish  
    49.         if (stream != IntPtr.Zero) { Error = EDSDK.EdsRelease(stream); }  
    50.         if (EvfImageRef != IntPtr.Zero) { Error = EDSDK.EdsRelease(EvfImageRef); }  
    51.         //stop the LiveView  
    52.         SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_TFT);  
    53.     }).Start();  
    54. }  

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

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

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. /// <summary>  
    2. /// Stops the LiveView  
    3. /// </summary>  
    4. public void StopLiveView()  
    5. {  
    6.     IsLiveViewOn = false;  
    7. }  

    记录播放窗口

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

    开始方法如下:

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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事件:
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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方法处理
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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.   
    11.         IntPtr jpgPointer;  
    12.         IntPtr stream = IntPtr.Zero;  
    13.         IntPtr EvfImageRef = IntPtr.Zero;  
    14.         UnmanagedMemoryStream ums;  
    15.         uint err;  
    16.         uint length;  
    17.   
    18.         err = EDSDK.EdsCreateMemoryStream(0, out stream);  
    19.         err = EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);  
    20.   
    21.         //Download one frame to init the video size  
    22.         err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);  
    23.         unsafe  
    24.         {  
    25.             Error = EDSDK.EdsGetPointer(stream, out jpgPointer);  
    26.             Error = EDSDK.EdsGetLength(stream, out length);  
    27.             ums = new UnmanagedMemoryStream((byte*)jpgPointer.ToPointer(),   
    28.                     length, length, FileAccess.Read);  
    29.             Bitmap bmp = new Bitmap(ums);  
    30.             StartEvfVideoWriter(bmp.Width, bmp.Height);  
    31.             bmp.Dispose();  
    32.             ums.Close();  
    33.         }  
    34.   
    35.         Stopwatch watch = new Stopwatch();  
    36.         byte[] barr; //bitmap byte array  
    37.         const long ft = 41; //Frametime at 24FPS   
    38.                     //(actually 41.66, but there is a bit of calculation overhead)  
    39.         float lastfr = 24; //last actual FPS  
    40.         int LVUpdateBreak1 = 0;  
    41.   
    42.         //Run LiveView  
    43.         while (IsEvfFilming)  
    44.         {  
    45.             watch.Restart();  
    46.             err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);  
    47.   
    48.             unsafe  
    49.             {  
    50.                 Error = EDSDK.EdsGetPointer(stream, out jpgPointer);  
    51.                 Error = EDSDK.EdsGetLength(stream, out length);  
    52.                 ums = new UnmanagedMemoryStream((byte*)jpgPointer.ToPointer(),   
    53.                         length, length, FileAccess.Read);  
    54.                 barr = new byte[length];  
    55.                 ums.Read(barr, 0, (int)length);  
    56.   
    57.                 //For better performance the LiveView is only updated with every 4th frame  
    58.                 if (LVUpdateBreak1 == 0 && LiveViewUpdated != null)   
    59.                     { LiveViewUpdated(Image.FromStream(ums)); LVUpdateBreak1 = 4; }  
    60.                 LVUpdateBreak1--;  
    61.                 FrameBuffer.Enqueue(barr);  
    62.                 ums.Close();  
    63.             }  
    64.   
    65.             //To get a steady framerate:  
    66.             while (true) if (watch.ElapsedMilliseconds >= ft) break;  
    67.             lastfr = lastfr * 0.9f + (100f / watch.ElapsedMilliseconds);  
    68.             if (FrameRateUpdated != null) FrameRateUpdated(lastfr);  
    69.         }  
    70.   
    71.         //Release and finish  
    72.         if (stream != IntPtr.Zero) { Error = EDSDK.EdsRelease(stream); }  
    73.         if (EvfImageRef != IntPtr.Zero) { Error = EDSDK.EdsRelease(EvfImageRef); }  
    74.         SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_TFT);  
    75.     }).Start();  
    76. }  
    由于写硬件驱动转换图片对象很慢,并且这也不需要实时处理,所以有了下面的StartEvfVideoWriter 方法 。这个方法将边框从队列中取出来保存,直到队列为空并且电影处理已关闭。我这里没有包含实际的视频保存功能,你可以用你偏好的类库去完成。
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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,它们没被包含在这个项目中)
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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. }  
    关闭电影功能跟关闭实时取景方法一样:
    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. /// <summary>  
    2. /// Stops LiveView and filming  
    3. /// </summary>  
    4. public void StopEvfFilming()  
    5. {  
    6.     IsLiveViewOn = false;  
    7.     IsEvfFilming = false;  
    8. }   

    关闭/打开相机的接口

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

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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,对方法有改进或者有一些新的想法,非常希望你能告诉我。

    源码下载:

  • 相关阅读:
    安装VMWare tools 及安装后/mnt中有hgfs但没共享文件的解决办法
    linux挂载命令
    RHEL7/CentOS7 Network Service开机无法启动的解决方法
    linux消息队列编程实例
    Linux进程间通信——使用消息队列
    消息队列函数
    ipcs查看消息队列命令
    linux批量删除
    HTTP 请求消息头部实例:
    drf 序列化组件
  • 原文地址:https://www.cnblogs.com/carl2380/p/4730571.html
Copyright © 2011-2022 走看看