Получить текущий кадр с веб-камеры — DirectShowLib

Я работаю в VS 2012, мой проект представляет собой приложение C # WPF с .NetFramework 4.0.

Моя цель – необходимо отображать веб-камеру в режиме реального времени под управлением пользователя и обрабатывать текущий кадр (в виде растрового изображения) каждые 2 секунды.

Чего я добился. Я могу инициализировать веб-камеру и транслировать видео в реальном времени с помощью DirectShowLib. Я использовал этот пример.

Что мне нужно. Я не могу получить кадр (в виде растрового изображения). Существуют ли какие-либо методы по умолчанию для получения текущего кадра в DirectShow? Потребуется ли какая-либо реализация в моем пользовательском элементе управления (WebCamControl2, как указано в примере). Есть ли пример для моей потребности?

Спасибо.

Обновление:

Вот код, который я использую:

using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using DirectShowLib;
using System.Runtime.InteropServices.ComTypes;

namespace WebCamControl2
{
    [Guid("43878F19-1E0E-42d2-B72B-88A94418A302"),
    ComVisible(true)]
    public partial class WebCamControl2 : UserControl
    {
        public enum PlayState : int
        {
            Stopped,
            Paused,
            Running,
            Init
        }       

        private PlayState CurrentState = PlayState.Stopped;
        private int WM_GRAPHNOTIFY = Convert.ToInt32("0X8000", 16) + 1;
        private IVideoWindow videoWindow = null;
        private IMediaControl mediaControl = null;
        private IMediaEventEx mediaEventEx = null;
        private IGraphBuilder graphBuilder = null;
        private ICaptureGraphBuilder2 captureGraphBuilder = null;

        public WebCamControl2()
        {
            InitializeComponent();           
        }

        private void WebCamControl_Load(object sender, System.EventArgs e)
        {
            this.Resize += new System.EventHandler(WebCamControl_Resize);            
            CaptureVideo();
        }

        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(WebCamControl2));

            this.Load += new System.EventHandler(WebCamControl_Load);
        }

        private void CaptureVideo()
        {
            int hr = 0;
            IBaseFilter sourceFilter = null;
            try
            {
                // create the necessary DirectShow interfaces
                GetInterfaces();

                hr = this.captureGraphBuilder.SetFiltergraph(this.graphBuilder);
                DsError.ThrowExceptionForHR(hr);

                sourceFilter = FindCaptureDevice();

                hr = this.graphBuilder.AddFilter(sourceFilter, "WebCamControl Video");
                DsError.ThrowExceptionForHR(hr);

                hr = this.captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, null, null);
                Debug.WriteLine(DsError.GetErrorText(hr));
                DsError.ThrowExceptionForHR(hr);

                Marshal.ReleaseComObject(sourceFilter);

                SetupVideoWindow();

                hr = this.mediaControl.Run();
                DsError.ThrowExceptionForHR(hr);

                this.CurrentState = PlayState.Running;
            }
            catch (Exception ex)
            {
                MessageBox.Show("An unrecoverable error has occurred.\r\n" + ex.ToString());
            }
        }

        private void GetInterfaces()
        {
            this.graphBuilder = (IGraphBuilder)(new FilterGraph());
            this.captureGraphBuilder = (ICaptureGraphBuilder2)(new CaptureGraphBuilder2());
            this.mediaControl = (IMediaControl)this.graphBuilder;
            this.videoWindow = (IVideoWindow)this.graphBuilder;
            this.mediaEventEx = (IMediaEventEx)this.graphBuilder;

            // send notification messages to the control window
            int hr = this.mediaEventEx.SetNotifyWindow(this.Handle, WM_GRAPHNOTIFY, IntPtr.Zero);

            DsError.ThrowExceptionForHR(hr);
        }

        private IBaseFilter FindCaptureDevice()
        {
            UCOMIEnumMoniker classEnum = null;
            UCOMIMoniker[] moniker = new UCOMIMoniker[1];
            object source = null;

            ICreateDevEnum devEnum = (ICreateDevEnum)(new CreateDevEnum());
            int hr = devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, out classEnum, CDef.None);
            DsError.ThrowExceptionForHR(hr);
            Marshal.ReleaseComObject(devEnum);

            if (classEnum == null)
            {
                throw new ApplicationException("No video capture device was detected.\\r\\n\\r\\n" + "This sample requires a video capture device, such as a USB WebCam,\\r\\nto be installed and working properly.  The sample will now close.");
            }

            int none = 0;

            if (classEnum.Next(moniker.Length, moniker, out none) == 0)
            {
                Guid iid = typeof(IBaseFilter).GUID;
                moniker[0].BindToObject(null, null, ref iid, out source);
            }
            else
            {
                throw new ApplicationException("Unable to access video capture device!");
            }

            Marshal.ReleaseComObject(moniker[0]);
            Marshal.ReleaseComObject(classEnum);

            return (IBaseFilter)source;
        }

        private void SetupVideoWindow()
        {
            int hr = 0;

            //set the video window to be a child of the main window
            //putowner : Sets the owning parent window for the video playback window. 
            hr = this.videoWindow.put_Owner(this.Handle);
            DsError.ThrowExceptionForHR(hr);


            hr = this.videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren);
            DsError.ThrowExceptionForHR(hr);

            //Use helper function to position video window in client rect of main application window
            WebCamControl_Resize(this, null);



            //Make the video window visible, now that it is properly positioned
            //put_visible : This method changes the visibility of the video window. 
            hr = this.videoWindow.put_Visible(OABool.True);
            DsError.ThrowExceptionForHR(hr);
        }

        //protected override void WndProc(ref Message m)
        //{
        //    if (m.Msg == WM_GRAPHNOTIFY)
        //    {
        //        HandleGraphEvent();
        //    }
        //    if (this.videoWindow != null)
        //    {
        //        this.videoWindow.NotifyOwnerMessage(m.HWnd, m.Msg, m.WParam.ToInt32(), m.LParam.ToInt32());
        //    }
        //    base.WndProc(ref m);
        //}         

        private void HandleGraphEvent()
        {
            int hr = 0;
            EventCode evCode = 0;
            int evParam1 = 0;
            int evParam2 = 0;

            while (this.mediaEventEx != null && this.mediaEventEx.GetEvent(out evCode, out evParam1, out evParam2, 0) == 0)
            {
                // Free event parameters to prevent memory leaks associated with
                // event parameter data.  While this application is not interested
                // in the received events, applications should always process them.
                hr = this.mediaEventEx.FreeEventParams(evCode, evParam1, evParam2);
                DsError.ThrowExceptionForHR(hr);

                // Insert event processing code here, if desired (see http://msdn2.microsoft.com/en-us/library/ms783649.aspx)
            }
        }       

        private void ReleaseInterfaces()
        {
            if (this.mediaControl != null)
                this.mediaControl.StopWhenReady();

            this.CurrentState = PlayState.Stopped;

            // stop notifications of events
            if (this.mediaEventEx != null)
                this.mediaEventEx.SetNotifyWindow(IntPtr.Zero, WM_GRAPHNOTIFY, IntPtr.Zero);

            //// Relinquish ownership (IMPORTANT!) of the video window.
            //// Failing to call put_Owner can lead to assert failures within
            //// the video renderer, as it still assumes that it has a valid
            //// parent window.
            if (this.videoWindow != null)
            {
                this.videoWindow.put_Visible(OABool.False);
                this.videoWindow.put_Owner(IntPtr.Zero);
            }

            // Release DirectShow interfaces
            Marshal.ReleaseComObject(this.mediaControl);
            this.mediaControl = null;

            Marshal.ReleaseComObject(this.mediaEventEx);
            this.mediaEventEx = null;

            Marshal.ReleaseComObject(this.videoWindow);
            this.videoWindow = null;

            Marshal.ReleaseComObject(this.graphBuilder);
            this.graphBuilder = null;

            Marshal.ReleaseComObject(this.captureGraphBuilder);
            this.captureGraphBuilder = null;
        }

        private void WebCamControl_Resize(object sender, System.EventArgs e)
        {
            //Resize the video preview window to match owner window size
            if (this.videoWindow != null)
                this.videoWindow.SetWindowPosition(0, 0, this.Width, this.ClientSize.Height);
        }
    }
}

person Gopichandar    schedule 05.03.2015    source источник
comment
почему вы не использовали EMGU и Opencv?   -  person Hazem Abdullah    schedule 05.03.2015
comment
Извините, @HazemAbdullah. . Я не знаю об этих библиотеках. Есть ли какое-нибудь демонстрационное приложение, использующее библиотеки. Пожалуйста, дайте мне знать, чтобы я мог видеть, поможет ли это мне в этом. Спасибо   -  person Gopichandar    schedule 05.03.2015


Ответы (3)


Попробуйте использовать EMGU или Opencv

http://www.emgu.com/wiki/index.php/Main_Page

А это пример того, как захватывать кадры с вашего видео (камеры) http://www.emgu.com/wiki/index.php?title=Camera_Capture

1-Добавьте dll Emgu в ваше приложение 2- это пример

 private Capture _capture;
 Image<Gray, Byte> frame;

//0 is the default camera
 _capture = new Capture(0);

//here how you can get the frames from your video
 Image<Bgr, Byte> frame = _capture.QueryFrame();
person Hazem Abdullah    schedule 05.03.2015
comment
Я попытался запустить демо-приложение по предоставленной вами ссылке. Выдает ошибку. An unhandled exception of type 'System.TypeInitializationException' occurred in Emgu.CV.dll Additional information: The type initializer for 'Emgu.CV.CvInvoke' threw an exception. после нажатия кнопки «Начать захват». Я что-то упустил? - person Gopichandar; 05.03.2015
comment
Скопируйте opencvcore.dll рядом с исполняемым файлом - person Hazem Abdullah; 05.03.2015
comment
К сожалению, мне не удалось найти opencvcore.dll в примере проекта. - person Gopichandar; 05.03.2015
comment
Загрузите emgu, и вы увидите их в папке emgu stackoverflow.com/questions/17274372/ - person Hazem Abdullah; 05.03.2015

Вы найдете ряд вопросов, касающихся упомянутой проблемы, например.

Есть два решения. Вы либо получаете результат от компонента, который визуализирует видео (видеорендерер), либо добавляете Sample Grabber или аналогичный фильтр в конвейер и получаете его от обратного вызова. В обоих случаях вы получаете данные, которые можно использовать для инициализации растрового изображения .NET, но не совсем растровое изображение. То есть вы должны выполнить шаг преобразования (вдохновляя Q на это).

Вы, конечно, можете использовать библиотеки CV, которые, однако, являются огромным излишеством для такой простой задачи, и их захват видео может показаться негибким, если вам нужно добавить что-то еще.

person Roman R.    schedule 05.03.2015
comment
Спасибо @Роман. . Я обновил свой образец кода. Можете ли вы подсказать, как я могу добавить захват образца или фильтр, чтобы получить растровое изображение текущего кадра. - person Gopichandar; 06.03.2015
comment
Там, где вы делаете RenderStream, создайте SampleGrabber, добавьте фильтр, инициализируйте его, чтобы он работал только как 24 или 32-битный RGB. Подключите его вход к вашему источнику, а затем RenderStream его выход. Затем вы можете предоставить обратный вызов для получения кадров через вызов SampleCB. Выполните поиск по упомянутым ключевым словам, и у вас будет множество фрагментов кода для использования. - person Roman R.; 06.03.2015
comment
Дай мне попробовать. Спасибо за информацию. - person Gopichandar; 06.03.2015

Я преодолел эту проблему, сделав снимок экрана вместо того, чтобы брать кадр из рендеринга видео. Вот код, который мне помог

//Create a new bitmap.
var bmpScreenshot = new System.Drawing.Bitmap(Screen.PrimaryScreen.Bounds.Width, 
                                              Screen.PrimaryScreen.Bounds.Height,
                                              System.Drawing.Imaging.PixelFormat.Format32bppArgb);

// Create a graphics object from the bitmap.
var gfxScreenshot = System.Drawing.Graphics.FromImage(bmpScreenshot);

// Take the screenshot from the upper left corner to the right bottom corner.
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
                                                Screen.PrimaryScreen.Bounds.Y,
                                                0,
                                                0,
                                                Screen.PrimaryScreen.Bounds.Size,
                                                System.Drawing.CopyPixelOperation.SourceCopy);

Я знаю, что это неправильный путь. Тем не менее, это дает мне решение для моей потребности. Я пробовал с SampleGrabber и EMGU, но не могу получить ожидаемое решение. Я чувствую, что мое решение достаточно просто для моих нужд и может кому-то помочь.

Спасибо.

person Gopichandar    schedule 30.03.2015