legohead
legohead

Reputation: 540

How to convert ISampleGrabber::BufferCB's buffer to a bitmap

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.

Upvotes: 2

Views: 2412

Answers (1)

Matthias
Matthias

Reputation: 16209

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;
}

Upvotes: 1

Related Questions