在 Windows 中从 USB 相机拍摄高分辨率照片 (C++)

Take high resolution photo from USB camera in Windows (C++)

我正在开发一个 C++ 应用程序,它应该使用 USB 摄像头来捕捉高分辨率照片。它应该与 Windows 10 中的相机应用程序具有相同的行为。我正在尝试使用 DirectShow 来完成它。现在我只能拍摄延迟的高分辨率照片或及时拍摄但分辨率较低的照片。此外,我对 MS 文档感到非常困惑,很多东西都被弃用了,而且没有地方提到用什么来代替它们。我将描述我绝望的步骤,等待有人能够告诉我一条路。

让我们从头开始...

对Window中的视频捕获一无所知我开始搜索合适的库。经过一些谷歌搜索后,我发现 Windows 中有四个主要的视频捕获库。

  1. Windows
  2. 的视频
  3. DirectShow
  4. Windows 媒体基金会
  5. OpenCV

让我们观察一下:

  1. Windows
    的视频 不幸的是,这个库被标记为已弃用,但它似乎仍然有效。我写了"unfortunately",因为我觉得这是唯一好用的。从摄像机中查看视频只需要几行代码。我唯一想念的是 "TakePhoto" 函数。您可以使用 VFW 将视频或单帧捕获到 avi 文件。还是我遗漏了什么?

  2. DirectShow
    这是一个更复杂的库。您需要数百行代码才能看到视频预览。但是您可以在 MS Docs 上获得此代码。好的,现在我有一个视频预览,我只需要拍照。人们会期望这应该只是一个函数调用。但是功能在哪里呢?我没找到。

您可以简单地使用 IVMRWindowlessControl 中的 GetCurrentImage,但这只需要低分辨率预览中的一帧。如果您为预览设置更高的分辨率,则视频不流畅。

我能实现的最佳方法来自一篇名为 "Capturing an Image From a Still Image Pin" 的文章 https://docs.microsoft.com/en-us/windows/desktop/directshow/capturing-an-image-from-a-still-image-pin。当我找到这个网站时,我以为我赢了,我的任务就快完成了。但事实并非如此。

本文给您的第一个建议是不要使用它:Platform SDK 文档中的 "The recommended way to get still images from the device is to use the Windows Image Acquisition (WIA) APIs. For more information, see "Windows Image Acquisition”。但是,您也可以使用 DirectShow捕捉图像。” 我试图探索 WIA。但这停止在 Vista 上工作。我继续研究文章。
一切似乎都很清楚,但您需要实现您的 class,它继承了 ISampleGrabberCB,此处标记为已弃用 https://docs.microsoft.com/en-us/windows/desktop/directshow/isamplegrabbercb。为什么????在哪里可以找到替代品?
我在这里 https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/2ab5c212-5824-419d-b5d9-7f5db82f57cd/qedith-missing-in-current-windows-sdk-v70?forum=windowsdirectshowdevelopment 找到了一个可接受的解决方案。您需要从 elder SDK 添加头文件。 (顺便说一句,这是一个近十年前的建议。)在我用这个头文件编译应用程序后,我能够读取高分辨率图片,但我需要等待几秒钟,这是不可接受的。我知道问题不在相机中,因为它在相机应用程序中有效。此外,图像是在函数 SampleCB 而不是 BufferCB 中获得的,并且格式有些奇怪。我可以保存为jpg但是压缩不够

  1. Windows 媒体基金会
    我认为 MS 不喜欢程序员,这就是它发布 WMF 的原因。我完全不懂。我找到了这个教程 https://www.dreamincode.net/forums/topic/347938-a-new-webcam-api-tutorial-in-c-for-windows/。它可以工作,但它只存储预览中的一帧,这不是我想要的。
    接下来,我探索了 MS Docs 上的一些 WMF 接口。 IMFCapturePhotoSink 接口应该做的东西。但是如何实现它。该文档是无用的。

  2. OpenCV
    在我的研究过程中,我也发现了这个图书馆。但是我又一次无法拍摄高分辨率照片。它只存储预览中的一帧。

有人能告诉我应该关注什么吗?我相信这不会那么困难。网络摄像头有成百上千的应用程序。其他程序员如何实现它们?我怎么了?我想找到一种简单的方法来实现一项简单的任务。非常感谢您的帮助。

你的问题与主题无关 - 问题必须与代码有关 - 但我多年前遇到过类似的问题并且我找到了解决方案:

DirectShow 被声明为 Windows 10 已弃用,它在支持 USB 网络摄像头方面存在问题。在 Windows 10 中有 USB 视频 Class,只有 Media Foundation 支持。

因此,我围绕媒体基础代码编写了一个简单的 C++ 包装器,它简化了原始图像的获取 Capturing Video from Web-camera on Windows 7 and 8 by using Media Foundation

此外,还有项目CaptureManager SDK——它是一个DLL COM组件,接口简单,功能强大,有很多C++、Python、C#、Java的演示程序。

感谢 Evgeny。

重述:

  1. 下载CaptureEngine video capture sample

  2. 编辑CaptureManager::TakePhoto方法。在 CreatePhotoMediaType(pMediaType, &pMediaType2);

  3. 之前添加代码以查找最高分辨率媒体类型

将照片流设置为最高分辨率的额外代码:

DWORD dwMediaTypeIndex = 0;
UINT32 maxSize = 0;
DWORD maxSizeIndex = 0;
while (1) {
    IMFMediaType* pMediaType = NULL;
    hr = pSource->GetAvailableDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, dwMediaTypeIndex, &pMediaType);
    if (hr == MF_E_NO_MORE_TYPES)
        break;

    UINT32 w, h;
    MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &w, &h);
    UINT32 size = w * h;
    if (size > maxSize) {
        maxSize = size;
        maxSizeIndex = dwMediaTypeIndex;
    }
    SafeRelease(&pMediaType);
    dwMediaTypeIndex++;
}

SafeRelease(&pMediaType);
pSource->GetAvailableDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, maxSizeIndex, &pMediaType);