简体   繁体   中英

Saving a bitmap from a variable gives “invalid parameter”

I need to save an image to disk that came from a web-cam between 5 and 10 seconds ago from when the "save" command comes in via serial port.

To get there, I have the webcam going into a pictureBox.Image (using opencv4), and then 2 Bitmap variables. Every 5 seconds a timer ticks, and Stored_bitmap_2 = Stored_bitmap_1, then Stored_bitmap_1 = (bitmap) pictureBox.Image.

When the right serial command comes in, I try to
Stored_image_2.Save("C:\Users\GreenWorld\Desktop\test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); and I get a run-time error of invalid parameter.

When I do the same thing in a stand-alone project with some buttons (inside the button-click event handler), it works every time.

When I do this inside the serialPort_DataReceived handler, I get all kinds of cross-thread errors. So, I moved the save attempt to its own subroutine, and that fixed that but now this.

I am by no means a professional programmer, I'm an engineer with a simple problem and I can usually write a little simplistic code to fix my immediate issue. Please go easy on me in the explanation:-)

Sample code:

using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace Weld_picture
{
    public partial class Form1 : Form
    {
        // Create class-level accessible variables
        int Welding_camera_ID = 1;
        VideoCapture capture;
        Mat frame;
        Bitmap image;
        private Thread camera;
        bool isCameraRunning = false;

        string Serial_command = "";

        Bitmap Stored_image_1;
        Bitmap Stored_image_2;

        bool Image_saved = false;
        string Station_ID = "GWM-PWS-01";
        string File_name = "";

        private void CaptureCamera() // from someone else's sample code
        {
            camera = new Thread(new ThreadStart(CaptureCameraCallback));
            camera.Start();
        }

        private void CaptureCameraCallback() // from someone else's sample code
        {
            frame = new Mat();
            capture = new VideoCapture(Welding_camera_ID);
            capture.Open(Welding_camera_ID);

            if (capture.IsOpened())
            {
                while (isCameraRunning)
                {
                    capture.Read(frame);
                    image = BitmapConverter.ToBitmap(frame);
                    if (pictureBox1.Image != null)
                    {
                        pictureBox1.Image.Dispose();
                    }
                    pictureBox1.Image = image;
                }
            }
        }

        public Form1()
        {
            InitializeComponent();

            SerialPort1.Open();
        }

        private void SerialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            string dummy;
            char CR = (char)0x0D;

            while (SerialPort1.BytesToRead > 0)
            {
                Serial_command += (char)SerialPort1.ReadByte();
            }

            while (Serial_command.IndexOf(CR) > 0)
            {
                dummy = Serial_command.Substring(0, Serial_command.IndexOf(CR));
                Serial_command = Serial_command.Substring(Serial_command.IndexOf(CR) + 1, (Serial_command.Length - (Serial_command.IndexOf(CR) + 1)));

                Serial_command.Trim();
                dummy.Trim();

                proc_Process_serial_data(dummy); 
            }
        }

        //*************************************************************************************************************************************
        /* the first timer is a 5-second interval. It's the "memory" function so that if/when the save-to-disk is triggered I can store the last-time-shutter-open image */
        //*************************************************************************************************************************************

        private void Timer_picture_interval_Tick(object sender, EventArgs e)
        {
            checkBox1.Checked = !checkBox1.Checked;

            Timer_picture_interval.Stop();
            Stored_image_2 = Stored_image_1;
            Stored_image_1 = (Bitmap) pictureBox1.Image;
            Timer_picture_interval.Start();
        }

        //*************************************************************************************************************************************
        // the second timer is a 30-second interval. It's the way to turn capture off if the PLC/camera box somehow goes off-line 
        //*************************************************************************************************************************************

        private void Timer_camera_powerdown_Tick(object sender, EventArgs e)
        {
            if (isCameraRunning)
                capture.Release();
            isCameraRunning = false;
            Timer_picture_interval.Stop();
        }

        //*************************************************************************************************************************************

        private void proc_Process_serial_data(string Serial_string)
        {
            if (Serial_string.IndexOf("Still here") > 0)
            {
                if (!isCameraRunning)
                    CaptureCamera();
                isCameraRunning = true;
            }

            if (Serial_string.IndexOf("Sun's up") > 0)
            {
                Timer_picture_interval.Start();
                Timer_camera_powerdown.Start();
            }

            if (Serial_string.IndexOf("It's dark") > 0)
            {
                if ((Stored_image_2 != null) && (!Image_saved)) // in case there's 2 subsequent requests to save the same thing (weld stutter)
                {
                    File_name = "C:\\Users\\GreenWorld\\Desktop\\" + Station_ID + " D" + DateTime.Now.ToString("yyyy_MM_dd THH_mm_ss") + ".jpg";
                    Stored_image_2.Image.Save("C:\\Users\\GreenWorld\\Desktop\\test.bmp" , System.Drawing.Imaging.ImageFormat.Bmp );

                    Image_saved = true;
                    Timer_picture_interval.Stop();
                }
                Timer_camera_powerdown.Start();
            }
        }
    }
}

You likely be able to fix the cros-threading error by simply calling your proc_Process_serial_data() method within an Invoke() call.

Change:

proc_Process_serial_data(dummy);

To:

this.Invoke((MethodInvoker)delegate ()
{
    proc_Process_serial_data(dummy);
});     

Also, these two lines aren't actually doing anything:

Serial_command.Trim();
dummy.Trim();

To Trim() the strings, you have capture the returned strings and re-assign them to the original variables:

Serial_command = Serial_command.Trim();
dummy = dummy.Trim();
       

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM