【问题标题】:Canon EDSDK 13.11.10 not saving to host PC佳能 EDSDK 13.11.10 未保存到主机 PC
【发布时间】:2020-11-02 13:32:24
【问题描述】:

我正在使用佳能 EDSDK 实现远程相机控制应用程序。我的相机是佳能 PowerShot SX 70 HS。到目前为止,一切似乎都正常工作,除了将拍摄的照片保存到主机 PC 的功能。

我对所需工作流程的理解如下:

  1. 初始化相机
  2. 重写对象事件处理程序并传递给EdsObjectEventHandler以修改“save-image-behavior”
  3. 拍照
  4. 关闭流并销毁所有对象。

我已经调整了自己的初始版本,并尝试了这些线程中提供的解决方案:

canon EDSDK saving image in my PC

Canon SDK - Downloading image to host PC

下面是代码...

...对于第一个线程:

#include <iostream>

#include "EDSDK.h"
#include "EDSDKTypes.h"
#include "EDSDKErrors.h"

EdsError getFirstCamera(EdsCameraRef* camera);
EdsError downloadImage(EdsDirectoryItemRef directoryItem);
EdsError EDSCALLBACK handleStateEvent(EdsStateEvent event, EdsUInt32 parameter, EdsVoid* context);
EdsError EDSCALLBACK handleObjectEvent(EdsObjectEvent event, EdsBaseRef object, EdsVoid* context);
EdsError EDSCALLBACK handlePropertyEvent(EdsPropertyEvent event, EdsPropertyID property, EdsUInt32 inParam, EdsVoid* context);


int main(int argc, char** argv)
{
    EdsError err;
    EdsCameraRef camera = NULL;
    bool isSDKLoaded = false;
    EdsCapacity capacity = { 0x7FFFFFFF, 0x1000, 1 };
    EdsInt32 saveTarget = kEdsSaveTo_Host;

    // Initialize SDK
    err = EdsInitializeSDK();
    if (err == EDS_ERR_OK)
    {
        isSDKLoaded = true;
    }

    // Get first camera
    if (err == EDS_ERR_OK)
    {
        err = getFirstCamera(&camera);
    }


    // Open session with camera
    err = EdsOpenSession(camera);

    // Set event handler
    if (err == EDS_ERR_OK)
        err = EdsSetObjectEventHandler(camera, kEdsObjectEvent_All, handleObjectEvent, NULL);
    
    if (err == EDS_ERR_OK)
        err = EdsSetPropertyEventHandler(camera, kEdsPropertyEvent_All, handlePropertyEvent, NULL);
    
    if (err == EDS_ERR_OK)
        err = EdsSetCameraStateEventHandler(camera, kEdsStateEvent_All, handleStateEvent, NULL);

    err = EdsSetPropertyData(camera, kEdsPropID_SaveTo, 0, 4, &saveTarget);

    err = EdsSetCapacity(camera, capacity);

    ///// Take picture
    err = EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
    ////

    // Close session with camera
    if (err == EDS_ERR_OK)
    {
        err = EdsCloseSession(camera);
    }

    // Release camera
    if (camera != NULL)
    {
        EdsRelease(camera);
    }

    // Terminate SDK
    if (isSDKLoaded)
    {
        EdsTerminateSDK();
    }
}


EdsError EDSCALLBACK handleObjectEvent(EdsObjectEvent event, EdsBaseRef object, EdsVoid* context)
{
    std::cout << "I'm in the callback-function!!!\n"; // Never shows!
    EdsError err = EDS_ERR_OK;

    switch (event)
    {
    case kEdsObjectEvent_DirItemRequestTransfer:
        err = downloadImage(object);
        break;
    default:
        break;
    }

    // Object must be released
    if (object) {
        err = EdsRelease(object);
    }
    return err;
}

EdsError EDSCALLBACK handlePropertyEvent(EdsPropertyEvent event, EdsPropertyID property, EdsUInt32 inParam, EdsVoid* context)
{
    return EDS_ERR_OK;
}

EdsError EDSCALLBACK handleStateEvent(EdsStateEvent event, EdsUInt32 parameter, EdsVoid* context)
{
    return EDS_ERR_OK;
}


EdsError getFirstCamera(EdsCameraRef* camera)
{
    EdsError err;
    EdsCameraListRef cameraList = NULL;

    err = EdsGetCameraList(&cameraList);
    err = EdsGetChildAtIndex(cameraList, 0, camera);
    return err;
}

EdsError downloadImage(EdsDirectoryItemRef directoryItem)
{
    EdsError err = EDS_ERR_OK;
    EdsStreamRef stream = NULL;
    // Get directory item information
    EdsDirectoryItemInfo dirItemInfo;
    err = EdsGetDirectoryItemInfo(directoryItem, &dirItemInfo);

    // Create file stream for transfer destination
    if (err == EDS_ERR_OK)
    {
        err = EdsCreateFileStream(dirItemInfo.szFileName, kEdsFileCreateDisposition_CreateAlways, kEdsAccess_ReadWrite, &stream);
    }
    // Download image
    if (err == EDS_ERR_OK)
    {
        err = EdsDownload(directoryItem, dirItemInfo.size, stream);
    }
    // Issue notification that download is complete
    if (err == EDS_ERR_OK)
    {
        err = EdsDownloadComplete(directoryItem);
    }
    // Release stream
    if (stream != NULL)
    {
        EdsRelease(stream);
        stream = NULL;
    }
    return err;
}

对于第二个线程:

#include <iostream>

#include "EDSDK.h"
#include "EDSDKTypes.h"
#include "EDSDKErrors.h"

static EdsError EDSCALLBACK handleObjectEvent(EdsObjectEvent event, EdsBaseRef object, EdsVoid* context)
{
    std::cout << "I'm in the callback-function!!!\n";  // NEVER PRINTS!
    EdsError err = EDS_ERR_OK;
    if (event == kEdsObjectEvent_DirItemRequestTransfer)
    {
        EdsStreamRef stream = NULL;
        EdsDirectoryItemInfo dirItemInfo;
        err = EdsGetDirectoryItemInfo(object, &dirItemInfo);
        err = EdsCreateFileStream(dirItemInfo.szFileName, kEdsFileCreateDisposition_CreateAlways, kEdsAccess_ReadWrite, &stream);
        err = EdsDownload(object, dirItemInfo.size, stream);
        err = EdsDownloadComplete(object);
        EdsRelease(stream);
        stream = NULL;
    }
    if (object)
        EdsRelease(object);

    return err;
}

void TakePhoto(EdsCameraRef camera)
{
    EdsError err = EDS_ERR_OK;
    EdsCameraListRef cameraList = NULL;
    EdsUInt32 count = 0;

    err = EdsInitializeSDK();
    err = EdsGetCameraList(&cameraList);
    err = EdsGetChildCount(cameraList, &count);
    if (count > 0)
    {
        err = EdsGetChildAtIndex(cameraList, 0, &camera);
        cameraList = NULL;
        err = EdsSetObjectEventHandler(camera, kEdsObjectEvent_All, handleObjectEvent, NULL);
        err = EdsOpenSession(camera);
        err = EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
    }
}

void Close(EdsCameraRef camera)
{
    EdsError err = EdsCloseSession(camera);
    EdsRelease(camera);
    EdsTerminateSDK();
}

int main() {
    EdsCameraRef camera = NULL;
    TakePhoto(camera);
    Close(camera);
    return 0;
}

但是,如前所述:在我的主机 PC 上找不到图像。如果我提供EdsCreateFileStream 的完整路径,这不会改变。将图像保存在二进制文件所在目录的默认行为(使用dirItemInfo.szFileName 作为EdsCreateFileStream 的第一个参数也不起作用。

可能是因为我尝试的最新解决方案是从 2014 年开始的,同时 API 发生了变化。我也在 GitHub 上查找了实现并查看了官方手册,但两者对我来说都非常不透明。

真正让我困惑的是,当我尝试 printf 风格的调试(在 handleObjectEvent 中添加输出时,打印从未出现过!这是因为函数的回调性质吗?我必须承认,我以前没有使用过回调。

下面提供的所有版本都构建并执行。我也可以在这里点击相机,但如前所述:我在磁盘上找不到图像!

回调是否已执行?为什么打印永远不会显示呢?我如何最终修改程序以将图像保存到磁盘?

【问题讨论】:

    标签: c++ camera remote-control edsdk canon-sdk


    【解决方案1】:

    我能想到它不起作用的两个原因:

    1. 这是一个控制台应用程序,因此它没有消息泵。这意味着事件不会“自动”触发。您必须反复致电EdsGetEvent,直到您收到所需的事件。在您的情况下,理想的点是在发送 TakePicture 命令之后。

    2. 发送 TakePicture 命令后,您会立即关闭所有内容,因此没有时间触发事件。 TakePicture 命令几乎立即返回,并且不会等到整个拍照过程完成(但它确实会等到对焦完成)。幸运的是,我上面描述的 EdsGetEvent 循环也解决了这个问题。

    所以本质上:

        err = EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
    
        // eventHasFired is a boolean variable you can
        // set to true after you have downloaded the image.
        // or use a more sophisticated synchronization approach if necessary
        while (!eventHasFired)
        {
            EdsGetEvent();
            // you could add a little pause here to reduce CPU usage
        }
    
        if (err == EDS_ERR_OK)
        {
            err = EdsCloseSession(camera);
        }
    

    编辑

    在讨论了 cmets 中的一些可能性并查看了 EDSDK 中的错误代码后,我们发现,第一次发送 TakePicture 时,相机无法自动对焦。在 while 循环中重复发送命令提供了一种解决方法:

    while (err != EDS_ERR_OK)
        err = EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
    

    【讨论】:

    • 您好 Johannes,感谢您的回答。你能稍微扩展一下二进制变量吗?为了改变while条件,我必须在EdsGetEvent()和暂停之后修改eventHasFired。如何检查事件是否实际触发?
    • @TimHilt 欢迎您并确定:您将在下载图像之后和致电return err 之前将其设置在您的handleObjectEvent 回调中。 EdsGetEvent 函数将在内部调用 handleObjectEvent。所以基本上eventHasFired 变量将设置在while 循环内。您会知道该事件已触发,因为您应该会看到打印输出,并且理想情况下,图像将被保存。
    • 按照建议,我使用上面的第二个代码 n-p 编辑了我的解决方案,以包含一个全局 event-变量。我将编辑后的代码粘贴在这里:pastebin.com/H0teptGq 我没有添加暂停以尽可能快地调用EdsGetEvent,但是我从未看到任何打印输出,并且该函数卡在了while循环中!似乎甚至从未触发回调!
    • @TimHilt 那么我现在唯一能想到的就是你可能不得不在main() 的开头使用COINIT_APARTMENTTHREADED 调用CoInitializeEx。我不是 C++ 开发人员,所以我不完全确定这是否可行,但我知道要使 SDK 正常工作,它运行的线程必须处于单线程单元模式。
    • 好吧,我的消息很有趣:我再次尝试使用其他代码-sn-p,现在触发了事件回调,但使用了错误的事件:0x201 (kEdsObjectEvent_VolumeInfoChanged) 而不是0x208 (kEdsObjectEvent_DirItemRequestTransfer)!如果我反复拨打EdsGetEvent(),我只会得到0x201,而且只有一次;似乎是唯一的事件。这是我更改的代码:pastebin.com/5rzZCAKu
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多