Делайте снимки высокого разрешения с 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);