Снятие изображения с закрепленного кадра
[Функция, связанная с этой страницей, DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngineи Аудио/Видеозахват в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать новый код MediaPlayer, IMFMediaEngine и аудио-видеозахват в Media Foundation вместо DirectShowпо возможности. Корпорация Майкрософт предлагает, что существующий код, использующий устаревшие API, будет перезаписан для использования новых API, если это возможно.]
Некоторые камеры могут создавать статическое изображение отдельно от последовательности захвата, и часто такое изображение имеет более высокое качество, чем изображения, создаваемые последовательностью захвата. У камеры может быть кнопка, которая выступает в качестве аппаратного триггера или может поддерживать активацию программного обеспечения. Камера, поддерживающая неподвижные изображения, будет иметь контакт для неподвижных изображений, который является категорией контактов PIN_CATEGORY_STILL.
Рекомендуемый способ получения статических изображений с устройства — это использование API Windows Image Acquisition (WIA). Дополнительные сведения см. в разделе "Приобретение образов Windows" в документации по пакету SDK для платформы. Однако вы также можете использовать DirectShow для записи изображения.
Чтобы задействовать режим целевого кадра, используйте метод IAMVideoControl::SetMode, когда граф работает.
IAMVideoControl *pAMVidControl = NULL;
hr = pControl->Run(); // Run the graph.
if (FAILED(hr))
{
// Handle error.
}
hr = pCap->QueryInterface(IID_IAMVideoControl, (void**)&pAMVidControl);
if (SUCCEEDED(hr))
{
// Find the still pin.
IPin *pPin = NULL;
// pBuild is an ICaptureGraphBuilder2 pointer.
hr = pBuild->FindPin(
pCap, // Filter.
PINDIR_OUTPUT, // Look for an output pin.
&PIN_CATEGORY_STILL, // Pin category.
NULL, // Media type (don't care).
FALSE, // Pin must be unconnected.
0, // Get the 0'th pin.
&pPin // Receives a pointer to thepin.
);
if (SUCCEEDED(hr))
{
hr = pAMVidControl->SetMode(pPin, VideoControlFlag_Trigger);
pPin->Release();
}
pAMVidControl->Release();
}
Запросите фильтр захвата для IAMVideoControl. Если интерфейс поддерживается, получите указатель на интерфейс IPin IPin, вызвав метод ICaptureGraphBuilder2::FindPin, как показано в предыдущем примере. Затем вызовите IAMVideoControl::SetMode с указателем IPin и флагом VideoControlFlag_Trigger.
Заметка
В зависимости от камеры может потребоваться настройка захватывающего пина (PIN_CATEGORY_CAPTURE) перед тем, как подключится статический пин.
Пример: Использование фильтра захвата образцов
Один из способов захвата изображения — с помощью фильтра Sample Grabber. В Sample Grabber для обработки изображения используется функция обратного вызова, определяемую приложением. Дополнительные сведения о фильтре Sample Grabber см. в разделе Использование Sample Grabber.
В следующем примере предполагается, что статичный вывод доставляет несжатое RGB-изображение. Сначала определите класс, который реализует интерфейс обратного вызова Sample Grabber, ISampleGrabberCB:
// Class to hold the callback function for the Sample Grabber filter.
class SampleGrabberCallback : public ISampleGrabberCB
{
// Implementation is described later.
}
// Global instance of the class.
SampleGrabberCallback g_StillCapCB;
Описание реализации класса дано кратко.
Затем подключите неподвижный контакт к захватчику образцов, а захватчик образцов подключите к фильтру Null Renderer. Null-рендерер просто удаляет образцы мультимедиа, которые он получает; Основная работа будет выполнена в обратном вызове. (Единственная причина для нулевого визуализатора — подключить выходной контакт Sample Grabber к чему-то.) Вызовите CoCreateInstance, чтобы создать фильтры Sample Grabber и Null Renderer, и вызовите IFilterGraph::AddFilter, чтобы добавить оба фильтра в граф.
// Add the Sample Grabber filter to the graph.
IBaseFilter *pSG_Filter;
hr = CoCreateInstance(
CLSID_SampleGrabber,
NULL,
CLSCTX_INPROC_SERVER,
IID_IBaseFilter,
(void**)&pSG_Filter
);
hr = pGraph->AddFilter(pSG_Filter, L"SampleGrab");
// Add the Null Renderer filter to the graph.
IBaseFilter *pNull;
hr = CoCreateInstance(
CLSID_NullRenderer,
NULL,
CLSCTX_INPROC_SERVER,
IID_IBaseFilter,
(void**)&pNull
);
hr = pGraph->AddFilter(pNull, L"NullRender");
Вы можете использовать метод ICaptureGraphBuilder2::RenderStream для подключения всех трех фильтров в одном вызове метода, соединяя статический контакт с Sample Grabber, и от Sample Grabber к Null Renderer.
hr = pBuild->RenderStream(
&PIN_CATEGORY_STILL, // Connect this pin ...
&MEDIATYPE_Video, // with this media type ...
pCap, // on this filter ...
pSG_Filter, // to the Sample Grabber ...
pNull); // ... and finally to the Null Renderer.
Теперь используйте интерфейс ISampleGrabber для настройки sample Grabber так, чтобы он буферизует примеры:
// Configure the Sample Grabber.
ISampleGrabber *pSG = NULL;
hr = pSG_Filter->QueryInterface(IID_ISampleGrabber, (void**)&pSG);
if (SUCCEEDED(hr))
{
hr = pSG->SetOneShot(FALSE);
hr = pSG->SetBufferSamples(TRUE);
...
Задайте интерфейс обратного вызова указателем на объект обратного вызова:
hr = pSG->SetCallback(&g_StillCapCB, 0); // 0 = Use the SampleCB callback method.
Получите тип носителя, который использовал неподвижный штифт для подключения к Sample Grabber.
// Store the media type for later use.
AM_MEDIA_TYPE g_StillMediaType;
hr = pSG->GetConnectedMediaType(&g_StillMediaType);
pSG->Release();
Этот тип мультимедиа будет содержать структуру BITMAPINFOHEADER, которая определяет формат неподвижного изображения. Освободить тип носителя перед выходом приложения:
// On exit, remember to release the media type.
FreeMediaType(g_StillMediaType);
Ниже приведен пример класса обратного вызова. Обратите внимание, что класс реализует интерфейс IUnknown, который он унаследовал через интерфейс ISampleGrabber, но при этом не поддерживает подсчет ссылок. Это безопасно, так как приложение создает объект в стеке, и объект остается в области в течение всего времени существования графа фильтра.
Вся работа происходит в методе BufferCB, который вызывается Sample Grabber каждый раз, когда он получает новый образец. В следующем примере метод записывает растровое изображение в файл:
class SampleGrabberCallback : public ISampleGrabberCB
{
public:
// Fake referance counting.
STDMETHODIMP_(ULONG) AddRef() { return 1; }
STDMETHODIMP_(ULONG) Release() { return 2; }
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
{
if (NULL == ppvObject) return E_POINTER;
if (riid == __uuidof(IUnknown))
{
*ppvObject = static_cast<IUnknown*>(this);
return S_OK;
}
if (riid == __uuidof(ISampleGrabberCB))
{
*ppvObject = static_cast<ISampleGrabberCB*>(this);
return S_OK;
}
return E_NOTIMPL;
}
STDMETHODIMP SampleCB(double Time, IMediaSample *pSample)
{
return E_NOTIMPL;
}
STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen)
{
if ((g_StillMediaType.majortype != MEDIATYPE_Video) ||
(g_StillMediaType.formattype != FORMAT_VideoInfo) ||
(g_StillMediaType.cbFormat < sizeof(VIDEOINFOHEADER)) ||
(g_StillMediaType.pbFormat == NULL))
{
return VFW_E_INVALIDMEDIATYPE;
}
HANDLE hf = CreateFile("C:\\Example.bmp", GENERIC_WRITE,
FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
if (hf == INVALID_HANDLE_VALUE)
{
return E_FAIL;
}
long cbBitmapInfoSize = g_StillMediaType.cbFormat - SIZE_PREHEADER;
VIDEOINFOHEADER *pVideoHeader =
(VIDEOINFOHEADER*)g_StillMediaType.pbFormat;
BITMAPFILEHEADER bfh;
ZeroMemory(&bfh, sizeof(bfh));
bfh.bfType = 'MB'; // Little-endian for "BM".
bfh.bfSize = sizeof( bfh ) + BufferLen + cbBitmapInfoSize;
bfh.bfOffBits = sizeof( BITMAPFILEHEADER ) + cbBitmapInfoSize;
// Write the file header.
DWORD dwWritten = 0;
WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
WriteFile(hf, HEADER(pVideoHeader), cbBitmapInfoSize, &dwWritten, NULL);
WriteFile( hf, pBuffer, BufferLen, &dwWritten, NULL );
CloseHandle( hf );
return S_OK;
}
};
Связанные разделы