2

I am trying to use the ISampleGrabberCB::BufferCB to convert the current frame to bitmap using the following code:

int ISampleGrabberCB.BufferCB(double sampleTime, IntPtr buffer, int bufferLength)
    {
        try
        {

            Form1 form1 = new Form1("", "", "");
            if (pictureReady == null)
            {
                Debug.Assert(bufferLength == Math.Abs(pitch) * videoHeight, "Wrong Buffer Length");
            }

            Debug.Assert(imageBuffer != IntPtr.Zero, "Remove Buffer");

            Bitmap bitmapOfCurrentFrame = new Bitmap(width, height, capturePitch, PixelFormat.Format24bppRgb, buffer);
            MessageBox.Show("Works");
            form1.changepicturebox3(bitmapOfCurrentFrame);

            pictureReady.Set();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }

        return 0;
    }

However this does not seem to be working.

Additionally it seems to call this function when i press a button which runs the following code:

public IntPtr getFrame()
    {
        int hr;
        try
        {
            pictureReady.Reset();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
        imageBuffer = Marshal.AllocCoTaskMem(Math.Abs(pitch) * videoHeight);

        try
        {
            gotFrame = true;

            if (videoControl != null)
            {
                hr = videoControl.SetMode(stillPin, VideoControlFlags.Trigger);
                DsError.ThrowExceptionForHR(hr);
            }

            if (!pictureReady.WaitOne(9000, false))
            {
                throw new Exception("Timeout waiting to get picture");
            }

        }
        catch
        {
            Marshal.FreeCoTaskMem(imageBuffer);
            imageBuffer = IntPtr.Zero;
        }

        return imageBuffer;

    }

Once this code is ran I get a message box which shows 'Works' thus meaning my BufferCB must of been called however does not update my picture box with the current image.

Is the BufferCB not called after every new frame? If so why do I not recieve the 'Works' message box?

Finally is it possible to convert every new frame into a bitmap (this is used for later processing) using BufferCB and if so how?

Edited code:

int ISampleGrabberCB.BufferCB(double sampleTime, IntPtr buffer, int bufferLength)
    {           

            Debug.Assert(bufferLength == Math.Abs(pitch) * videoHeight, "Wrong Buffer Length"); 
            Debug.Assert(imageBuffer != IntPtr.Zero, "Remove Buffer");
            CopyMemory(imageBuffer, buffer, bufferLength);
            Decode(buffer);   


        return 0;
    }

public Image Decode(IntPtr imageData)
    {
        var bitmap = new Bitmap(width, height, pitch, PixelFormat.Format24bppRgb, imageBuffer);
        bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
        Form1 form1 = new Form1("", "", "");
        form1.changepicturebox3(bitmap);
        bitmap.Save("C:\\Users\\...\\Desktop\\A2 Project\\barcode.jpg");
        return bitmap;
    }

Button code:

public void getFrameFromWebcam()
{
   if (iPtr != IntPtr.Zero)
   {
       Marshal.FreeCoTaskMem(iPtr);
       iPtr = IntPtr.Zero;
   }

        //Get Image
        iPtr = sampleGrabberCallBack.getFrame();
        Bitmap bitmapOfFrame = new Bitmap(sampleGrabberCallBack.width, sampleGrabberCallBack.height, sampleGrabberCallBack.capturePitch, PixelFormat.Format24bppRgb, iPtr);
        bitmapOfFrame.RotateFlip(RotateFlipType.RotateNoneFlipY);
        barcodeReader(bitmapOfFrame);
}

public IntPtr getFrame()
    {
        int hr;

        try
        {
            pictureReady.Reset();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
        imageBuffer = Marshal.AllocCoTaskMem(Math.Abs(pitch) * videoHeight);

        try
        {
            gotFrame = true;

            if (videoControl != null)
            {
                hr = videoControl.SetMode(stillPin, VideoControlFlags.Trigger);
                DsError.ThrowExceptionForHR(hr);
            }

            if (!pictureReady.WaitOne(9000, false))
            {
                throw new Exception("Timeout waiting to get picture");
            }

        }
        catch
        {
            Marshal.FreeCoTaskMem(imageBuffer);
            imageBuffer = IntPtr.Zero;
        }

        return imageBuffer;

    }

I also still need to press the button to run the BufferCB

Thanks for reading.

legohead
  • 530
  • 2
  • 8
  • 23
  • First thing is that you skipped the `CopyMemory` step, that I proposed. Actually you're not using the passed `buffer` at all.. You're using `imageBuffer`. Second thing is, as I already mentioned, that you cannot access the ui thread from the sample grabber thread. – Matthias Oct 27 '13 at 21:26
  • So in the `Decode` i'm supposed to be using the buffer variable from the `BufferCB`? And yes sorry realised that after i posted the code, i now get an image in the saved file however the pictureBox3 is not updated? Also how am I supposed to repeatedly trigger the `BufferCB` method then (I know it happens after every frame but that does not result in what i receive when i press the button) – legohead Oct 27 '13 at 21:46
  • I already posted a link regarding cross-thread operations. When people try to help you, you should read everything they tell you. No offense, but you cannot expect that someone solves all of your problems in just one question. Please try to keep your upcoming questions shorter. – Matthias Oct 27 '13 at 21:57
  • I did read this webpage however did not understand it, from what i can gather you want me to use something like : `form1.Invoke((ISampleGrabberCB.BufferCB())delegate { MessageBox.Show(form1, "BufferCB"); });` But i do not see how this would work( have not used invoking before) – legohead Oct 27 '13 at 22:06
  • Okay, here is an article that you should read then: http://msdn.microsoft.com/en-us/library/ms171728(v=vs.85).aspx – Matthias Oct 27 '13 at 22:07
  • Using `form1.Invoke((Action)delegate { MessageBox.Show(form1, "BufferCB"); });` i get the error 'Invoke or BeginInvoke cannot be called on a control until the window handle has been created' :/ – legohead Oct 27 '13 at 22:19
  • That means that you're initiating your video control too early. It must happen *after* the `Shown` event of the form was triggered. – Matthias Oct 27 '13 at 22:21

1 Answers1

1

BufferCB is called for every new frame that has been captured by the camera. You don't see the message box, because the method is called from another thread (not the ui thread). See this question for details.

In my code I used AutoResetEvent for capturing a frame:

#region samplegrabber
/// <summary>
///   buffer callback, COULD BE FROM FOREIGN THREAD.
/// </summary>
int ISampleGrabberCB.BufferCB (double sampleTime,
                              IntPtr pBuffer,
                              int bufferLen)
{
  if (_sampleRequest)
  {
    _sampleRequest = false;

    if (bufferLen > _bufferSize)
      throw new Exception ("buffer is wrong size");

    Win32.CopyMemory (_buffer, pBuffer, _bufferSize);

    // Picture is ready.
    _resetEvent.Set ();
  }
  else
    _dropped++;
  return 0;
}

The image can then be decoded from the IntPtr with another function:

public Image Decode (IntPtr data)
{
  var bitmap = new Bitmap (_width, _height, _stride, PixelFormat.Format24bppRgb, data);

  bitmap.RotateFlip (RotateFlipType.RotateNoneFlipY);

  return bitmap;
}
Community
  • 1
  • 1
Matthias
  • 15,919
  • 5
  • 39
  • 84
  • In my code I use a `ManualResetEvent` which is the pictureReady.set() part of the first section of code. Do you think changing this to a `AutoResetEvent` would be required? Also where you use the image decode function where would this data byte array come from? – legohead Oct 27 '13 at 19:34
  • `_buffer` is the byte array, which should be passed to the `Decode` method. It's not important whether you use manual or auto reset events. What's important is in your `GetFrame` method to signal that you want a frame (`_sampleRequest`) and then wait for the data to arrive (`_resetEvent`). – Matthias Oct 27 '13 at 19:36
  • Oh yes I see this now, So where I use `gotFrame` instead of your `_sampleRequest` and `pictureReady` instead of your `_resetEvent` I am already telling the buffer I want a frame, however I am not currently using the `if(gotFrame)` in my `BufferCB`. Is this why i get the works message when I press the button to run the code? – legohead Oct 27 '13 at 19:41
  • Hard to tell for so many code ;) I'd say try to work on this another time with all in mind what I've said and then get back if you need further help. Btw: Instead of `MessageBox` you can use `Console.WriteLine` which should appear in visual studio. – Matthias Oct 27 '13 at 19:51
  • I currently have this working the way you suggested! thank you! do you know how I would automate this now? so it would rune the `BufferCB` without having to press the buttom? – legohead Oct 27 '13 at 19:51
  • what do you mean by automating? like 15fps? so that you can create a video from it? – Matthias Oct 27 '13 at 19:51
  • Basically I'm using it as barcode scanner (using a third party library to decode the bitmaps) so I need to convert every live frame into a bitmap so i can apply this library – legohead Oct 27 '13 at 19:54
  • Well, it depends. If your hardware is fast enough, you'd simply have the decoding in the `BufferCB` method without the `sampleRequest` and reset event... If not, I guess you have to use two reset events for synchronizing the two threads, so that the first thread signals that it can decode another image, and the other that it has captured the buffer. – Matthias Oct 27 '13 at 19:57
  • I've tried to do this before but it was as though the `BufferCB` was not running. The decoding is a simple line `var result = barcodeReader.Decode(bitmap);` So in the `BufferCB` would I use the example you gave above of the `Image Decode` function and use the example I just gave and this will automatically convert and decode every frame? – legohead Oct 27 '13 at 20:02
  • I am getting the error 'Cannont convert from System.IntPtr to byte[]' now :S how would I go about resolving this? – legohead Oct 27 '13 at 20:14
  • Sorry for that, you probably need another decoder. I will update my answer! – Matthias Oct 27 '13 at 20:43
  • So if i removed the `pictureReady.Set()` This should be converting every image into a bitmap? – legohead Oct 27 '13 at 21:11
  • I just tried saving the bitmap (as a check) and all it saved it a black box :/ i'll update the original post with my new code. – legohead Oct 27 '13 at 21:18