techyian / MMALSharp

C# wrapper to Broadcom's MMAL with an API to the Raspberry Pi camera.
MIT License
195 stars 33 forks source link

Preview on Picturebox. #153

Closed orionkubilay closed 4 years ago

orionkubilay commented 4 years ago

I want to try prewiev on a picturebox. How can I do this?

techyian commented 4 years ago

I haven't worked with WinForms in a long time, but the basic idea will be to get the image data in a MemoryStream and load it into a Bitmap instance which can then be used by a PictureBox. Does the below help at all? I haven't tested this code but it should hopefully put you in the right direction.

private async void Form1_Load(object sender, EventArgs e)
{
    MMALCameraConfig.StillResolution = new Resolution(640, 480);

    MMALCamera cam = MMALCamera.Instance;

    using (var imgCaptureHandler = new MemoryStreamCaptureHandler())
    using (var imgEncoder = new MMALImageEncoder())
    using (var nullSink = new MMALNullSinkComponent())
    {
        cam.ConfigureCameraSettings();

        var portConfig = new MMALPortConfig(MMALEncoding.JPEG, MMALEncoding.I420, quality: 90);

        // Create our component pipeline.         
        imgEncoder.ConfigureOutputPort(portConfig, imgCaptureHandler);

        cam.Camera.StillPort.ConnectTo(imgEncoder);
        cam.Camera.PreviewPort.ConnectTo(nullSink);

        // Camera warm up time
        await Task.Delay(2000);
        await cam.ProcessAsync(cam.Camera.StillPort);

        using (var bitmap = Bitmap.FromStream(imgCaptureHandler.CurrentStream))
        {
            var pictureBox = new PictureBox()
            {
                Width = 640,
                Height = 480,
                Image = (Image)bitmap
            };

            // Do something with your picturebox...
        }
    }
}
orionkubilay commented 4 years ago

Thanks for quick reply.

MMALCameraConfig.StillResolution = new Resolution(640, 480); line give me "StillResolution not found" error message. I ve changed that line to MMALCameraConfig.Resolution = new Resolution(640, 480); Ok this is working for still image. How can I do this for video? I think I have to use callbacks. How can I do that? I am newbye MMALSharp. Thanks in advace.

techyian commented 4 years ago

Ah sorry, yes, I assumed you were using the current version in NuGet (v0.6) - that property has been changed recently in the latest development branch.

Yes, so with processing video to a PictureBox I think you should be able to construct your own capture handler which accepts a delegate and do the processing in there. In the below example I'm using the rapid capture functionality documented here which will pass a JPEG to the delegate on each full frame processed. Again, not tested but hopefully will point you in the right direction:

public class PictureBoxInMemoryCaptureHandler : InMemoryCaptureHandler, IVideoCaptureHandler
{
    private Action<byte[]> _callback;

    public PictureBoxInMemoryCaptureHandler(Action<byte[]> callback)
    {
        _callback = callback;                
    }

    public override void Process(ImageContext context)
    {
        // The InMemoryCaptureHandler parent class has a property called "WorkingData". 
        // It is your responsibility to look after the clearing of this property.

        // The "eos" parameter indicates whether the MMAL buffer has an EOS parameter, if so, the data that's currently
        // stored in the "WorkingData" property plus the data found in the "data" parameter indicates you have a full image frame.

        Console.WriteLine("I'm in here");

        base.Process(context);

        if (context.Eos)
        {
            _callback(this.WorkingData.ToArray());

            this.WorkingData.Clear();
            Console.WriteLine("I have a full frame. Clearing working data.");
        }
    }

    public void Split()
    {
        throw new NotImplementedException();
    }
}

private void ProcessFrameCallback(byte[] callbackData)
{
    using (var ms = new MemoryStream(callbackData))
    using (var bitmap = Bitmap.FromStream(ms))
    {
        var pictureBox = new PictureBox()
        {
            Width = 640,
            Height = 480,
            Image = (Image)bitmap
        };

        // Do something with your picturebox...

    }
}

private async void Form1_Load(object sender, EventArgs e)
{
    MMALCameraConfig.Resolution = new Resolution(640, 480);

    MMALCamera cam = MMALCamera.Instance;

    using (var imgCaptureHandler = new PictureBoxInMemoryCaptureHandler(this.ProcessFrameCallback))
    using (var splitter = new MMALSplitterComponent())
    using (var imgEncoder = new MMALImageEncoder(continuousCapture: true))
    using (var nullSink = new MMALNullSinkComponent())
    {
        cam.ConfigureCameraSettings();

        var portConfig = new MMALPortConfig(MMALEncoding.JPEG, MMALEncoding.I420, quality: 90);

        // Create our component pipeline.         
        imgEncoder.ConfigureOutputPort(portConfig, imgCaptureHandler);

        cam.Camera.VideoPort.ConnectTo(splitter);
        splitter.Outputs[0].ConnectTo(imgEncoder);
        cam.Camera.PreviewPort.ConnectTo(nullSink);

        // Camera warm up time
        await Task.Delay(2000);

        var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));

        // Process images for 15 seconds.        
        await cam.ProcessAsync(cam.Camera.VideoPort, cts.Token);
    }
}
orionkubilay commented 4 years ago

Thanks for help. ProcessFrameCallback routine creats new picturebox for every frame. But want to render over a picturebox on Windows form. So I changed this routine as below.

private void ProcessFrameCallback(byte[] callbackData) { using (var ms = new MemoryStream(callbackData)) using (var bitmap = Bitmap.FromStream(ms)) { var g = Graphics.FromImage(pictureBox1.Image); g.DrawImage(bitmap, new PointF(0, 0)); pictureBox1.Refresh(); } }

This is enought for my startup. Thanks again.