Делайте снимки высокого разрешения с USB-камеры в Windows (C++)

Я занимаюсь разработкой приложения на C++, которое должно использовать USB-камеру для съемки фотографий с высоким разрешением. Оно должно иметь такое же поведение, как и приложение Camera в Windows 10. Я пытаюсь использовать DirectShow для этого. Теперь я могу делать только снимки с высоким разрешением, которые задерживаются, или снимать во времени, но с низким разрешением. Также я очень запутался в документации MS, многие вещи устарели и нигде не упоминается, что их заменяет. Я опишу мои безнадежные шаги, ожидающие, что найдется кто-то, кто мог бы показать мне путь.

Давайте начнем с начала...

Ничего не зная о захвате видео в Window, я начал с поиска подходящей библиотеки. После некоторого поиска в Google я обнаружил, что есть четыре основные библиотеки для захвата видео в Windows. 1/ Видео для Windows 2/ DirectShow 3/ Windows Media Foundation 4/ OpenCV

Давайте посмотрим: 1/ Видео для Windows Эта библиотека, к сожалению, помечена как устаревшая, но, похоже, все еще работает. Я написал "к сожалению", потому что я думаю, что это единственный, который прост в использовании. Для просмотра видео с камеры требуется всего несколько строк кода. Единственное, что мне здесь не хватает - это функция "TakePhoto". Вы можете использовать VFW для захвата видео или отдельных кадров в файл AVI. Или я что-то упустил?

2 / DirectShow Это гораздо более сложная библиотека. Вам нужны сотни строк кода, чтобы увидеть предварительный просмотр видео. Но вы можете получить этот код на MS Docs. Хорошо, теперь у меня есть предварительный просмотр видео, и мне нужно только сделать фотографию. Можно ожидать, что это должен быть только один вызов функции. Но где функция? Я не нашел это.

Вы можете просто использовать GetCurrentImage из IVMRWindowlessControl, но для этого требуется только один кадр из предварительного просмотра с низким разрешением. Если вы установите более высокое разрешение для предварительного просмотра, видео не будет плавным.

Лучший подход, которого я мог бы достичь, - это статья "Захват изображения из булавки для неподвижного изображения", доступная здесь https://docs.microsoft.com/en-us/windows/desktop/directshow/capturing-an-image-from-a-still-image-pin. Когда я нашел этот сайт, я думал, что выиграл, и моя задача была почти завершена. Но это не так.

Первый совет, который дает вам статья, - не используйте его: "Рекомендуемый способ получения неподвижных изображений с устройства - это использование API-интерфейсов Windows Image Acquisition (WIA). Для получения дополнительной информации см."Windows Image Acquisition"на платформе. Документация SDK. Однако вы также можете использовать DirectShow для захвата изображения. " Я пытался исследовать WIA. Но это перестало работать на Vista. Я продолжил изучать статью. Кажется, все ясно, но вам нужно реализовать свой класс, который наследует 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, но он недостаточно сжат.

3 / Windows Media Foundation Я думаю, что MS не любит программистов и поэтому выпустила WMF. Я ничего не понимаю. Я нашел этот учебник https://www.dreamincode.net/forums/topic/347938-a-new-webcam-api-tutorial-in-c-for-windows/. Это работает, но он сохраняет только один кадр из предварительного просмотра, и это не то, что я хочу. Затем я изучил некоторые интерфейсы WMF в MS Docs. Интерфейс IMFCapturePhotoSink должен делать вещи. Но как это реализовать. Документация бесполезна.

4 / OpenCV Во время моего исследования я также нашел эту библиотеку. Но опять же я не могу сделать снимок в высоком разрешении. Он сохраняет только один кадр из предварительного просмотра.

Может ли кто-нибудь сказать мне, на чем я должен сосредоточиться? Я считаю, что это не может быть так сложно. Существуют десятки и сотни приложений для веб-камер. Как могли другие программисты реализовать их? Что со мной не так? Я хотел бы найти простой способ реализовать простую задачу. Большое спасибо за любую помощь.

2 ответа

Решение

Ваш вопрос не относится к теме - вопрос должен быть связан с кодом - но я столкнулся с подобной проблемой много лет назад, и я нашел решение: DirectShow объявлен устаревшим для Windows 10, и у него есть проблемы с поддержкой USB веб-камера - в Windows 10 есть USB Video Class, который поддерживается только Media Foundation. Итак, я написал простую оболочку C++ для кода Media Foundation, которая упрощает получение необработанных изображений. Захват видео с веб-камеры в Windows 7 и 8 с помощью Media Foundation. Также есть проект CaptureManager SDK - это DLL-компонент COM с простые интерфейсы, огромный функционал и множество демонстрационных программ на C++, Python, C#, Java.

Спасибо Евгению.

Рекапитуляция:

1 / Загрузить образец видеозахвата CaptureEngine

2 / Редактировать метод CaptureManager::TakePhoto. Добавьте код, чтобы найти тип носителя с самым высоким разрешением непосредственно перед "CreatePhotoMediaType(pMediaType, &pMediaType2);" линия

Дополнительный код для настройки фотопотока на максимальное разрешение:

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);
Другие вопросы по тегам