【13】大恒相机SDK C#开发 —— Fom1中实时处理的8个图像 实时显示在Form2界面的 pictureBox中

文章目录

  • [1 问题描述](#1 问题描述)
  • [1 Form2调用Form1的公共Bitmap------显示](#1 Form2调用Form1的公共Bitmap——显示)
    • [1.1 Form1代码实现](#1.1 Form1代码实现)
    • [1.2 Form2代码实现](#1.2 Form2代码实现)
  • [2 使用事件触发](#2 使用事件触发)
    • [2.1 Form1 代码实现](#2.1 Form1 代码实现)
    • [2.2 Form2代码实现](#2.2 Form2代码实现)

1 问题描述

在主界面 Form1中,多个相机采集的图像 经过处理后,实时显示 在Form2界面的 pictureBox中;共有8个区域的图像要实时显示。

此功能以下称为预览模式 ,Form1在代码中命名为 TestApp,Form2在代码中其实是两Form分别命名为PreviewLPreviewR。(实际使用中8个图像,一个屏幕预览效果不好,分成了两个屏幕预览。左屏4个预览画面,右屏4个预览画面)

下面是显示左屏4个区域的预览实现

1 Form2调用Form1的公共Bitmap------显示

1.1 Form1代码实现

在Form1声明8个 public static Bitmap:

csharp 复制代码
  public static Bitmap bitmapB6, bitmapC6, bitmapB5, bitmapB4, bitmapC4, bitmapB3, bitmapB2, bitmapC2, bitmapF6, bitmapF2;

在回调函数中获取8个区域的图像,

下面是bitmapB6,bitmapC6获取的示例。

csharp 复制代码
 private void __OnFrameCallbackFun_1(object objUserParam, IFrameData objIFrameData)
 {
     try
     {
         if (isShowSrcImg) //当开始处理图像时原视频要暂停,否则buffer中的数据会变化,图像上下颠倒交替出现(因Show(objIFrameData)中显示实现对图像数据做了垂直翻转)
         {
             //************************************************************
             //显示相机获取的原图
             //************************************************************                 
             m_objGxBitmap1.Show(objIFrameData);
         }

         //获取图像宽、高、pBuffer、channel等信息
         getImgInfo(objIFrameData, ref pBuffer1);


         //************************************************************
         // 对图像进行裁剪并显示在 PictureBox 中
         //*************************************************************
         if (isPreviewRoiImg)// 开启预览模式。
         {
             getRoiBmpData(ROI_B6, pBuffer1, ref bitmapB6);
             getRoiBmpData(ROI_C6, pBuffer1, ref bitmapC6);
         }

         //统计帧率
         m_objCFps1.IncreaseFrameNum();
     }
     catch (Exception ex)
     {
         MessageBox.Show("回调函数2" + ex.Message);
     }
 }

启动Form2,显示预览画面

csharp 复制代码
   private void bt_Preview_Click(object sender, EventArgs e)
   {
       isPreviewRoiImg = true;

       //预览窗口实例化,并显示
       PreviewL previewL = new PreviewL();
       PreviewR previewR = new PreviewR();
       previewL.Show();            
       previewR.Show();
       

       Thread.Sleep(500);
       //按键使能
       bt_Preview.Enabled = false;
   }

其中的 getRoiBmpData函数

csharp 复制代码
        private void getRoiBmpData(Rectangle ROI, IntPtr pBuffer, ref Bitmap ROI_bitmap)
        {
            try
            {
                if (null != pBuffer)
                { 
                    //判断像素格式
                    PixelFormat format = new PixelFormat();
                    // format = channels == 3 ? PixelFormat.Format24bppRgb : PixelFormat.Format8bppIndexed;//若3通道彩色图像,否则黑白图像
                    switch (channel)
                    {
                        case 1:
                            format = PixelFormat.Format8bppIndexed;
                            break;
                        case 3:
                            format = PixelFormat.Format24bppRgb;
                            break;
                        case 4:
                            format = PixelFormat.Format32bppArgb;
                            break;
                    }

                    // 在关键部分的代码前加锁
                    lock (this)
                    {
                        //创建ROI 空图像
                        Bitmap bitmap = new Bitmap(ROI.Width, ROI.Height, format);
                        BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, ROI.Width, ROI.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
        

                        int srcStride = SrcImgWidth * channel;// 原图每行数据长度
                        int destStride = bmpData.Stride;// 新图每行数据长度
                        int srcStartX = (ROI.X - 1) * channel; // ROI 起始 X 坐标在原图数据中的位置(二维视角)
                        int srcStartY = ROI.Y - 1; // ROI 起始 Y 坐标在原图数据中的位置(二维视角)
                        int srcOffset = srcStartY * srcStride + srcStartX; // ROI 起始位置在原图数据中的偏移量(原图数据在内存中是一行,一维数组)

                        unsafe
                        {
                            byte* srcPtr = (byte*)pBuffer + srcOffset;
                            byte* destPtr = (byte*)bmpData.Scan0;

                            for (int y = 0; y < ROI.Height; y++)
                            {
                                for (int x = 0; x < ROI.Width * channel; x++)
                                {
                                    destPtr[x] = srcPtr[x];
                                }

                                srcPtr += srcStride;
                                destPtr += destStride;
                            }
                        }

                        bitmap.UnlockBits(bmpData);

                        // 在这里处理 bitmap 图像
                        //ShowProcessedImage(m_nOperateID, bitmap);
                        ROI_bitmap = bitmap;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

1.2 Form2代码实现

(实际使用中8个图像,一个屏幕预览效果不好,分成了两个屏幕预览。左屏4个预览画面,右屏4个预览画面)

下面是显示左屏4个区域的预览实现

csharp 复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using static TestAppMain.TestApp;

namespace TestAppMain
{
    public partial class PreviewL : Form
    {

        //private System.Timers.Timer timer;
        //private System.Threading.Timer timer;
        //在这个案例中,使用 System.Windows.Forms.Timer,因为它是与 Windows 窗体应用程序兼容的计时器。它可以在 UI 线程上执行操作,因此更适合在窗体中刷新图像等 UI 相关的操作。
        private System.Windows.Forms.Timer timer;


        public PreviewL()
        {
            InitializeComponent();
            fullScreen();

            // 创建一个定时器,每隔一定时间刷新图像
            timer = new System.Windows.Forms.Timer();
            timer.Interval = 100; // 设置刷新间隔为100毫秒(可以根据需要调整)
            timer.Tick += Timer_Tick;
            timer.Start();
        }


        private void fullScreen()
        {
            // 判断当前窗体是否已经是最大化状态
            if (this.WindowState == FormWindowState.Maximized)
            {
                // 如果已经最大化,则恢复到正常状态
                this.WindowState = FormWindowState.Normal;
            }
            else
            {
                // 如果不是最大化,则将窗体最大化
                this.WindowState = FormWindowState.Maximized;
            }
        }


        private void Timer_Tick(object sender, EventArgs e)
        {
            // 在定时器触发事件中刷新图像
            RefreshPreview();
        }

        private void RefreshPreview()
        {
            // 更新PictureBox中显示的图像
            pictureBox_B6.Image = bitmapB6;
            pictureBox_B5.Image = bitmapB5;
            pictureBox_C6.Image = bitmapC6;
            pictureBox_C4.Image = bitmapC4;
        }


        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);

            // 关闭窗口时停止定时器
            timer.Stop();
            timer.Dispose();
        }
    }
}

因为定义个了8个全局公共变量的图像,浪费资源,下面是事件函数实现;

2 使用事件触发

因为定义个了8个全局公共变量的图像,浪费资源,下面是事件函数实现;

2.1 Form1 代码实现

在Form1中声明一个枚举变量,把需要显示的8个区域 放在枚举中。定义事件函数和方法;

csharp 复制代码
using GxIAPINET;
using MaterialSkin.Controls;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace TestAppMain
{

    //枚举类型声明
    public enum enum_ImgRegion
    {
        //cam1
        B6, C6,
        //cam2
        B4, C4, B5,
        //cam3
        B2, C2, B3,
        //cam4
        F6, E6,
        //cam5
        F2, E2
    };

    public partial class TestApp : Form
    {
        //From1主界面,这里Form1命名为TestApp

        // 定义事件---使回调中的图像在另一个form中显示
        public static event EventHandler<ImageEventArgs> ImageProcessed;

        // 在图像处理回调函数中调用该方法,参数为相机编号和处理后的图像
        public static void ShowProcessedImage(enum_ImgRegion imgRegion, Bitmap bitmap)
        {
            // 触发图像处理完成事件
            ImageProcessed?.Invoke(null, new ImageEventArgs(imgRegion, bitmap));
        }
 
        public TestApp()
        {
            InitializeComponent();
        }

        //

        //其他函数

        //


        //回调使用
          private void __OnFrameCallbackFun_1(object objUserParam, IFrameData objIFrameData)
		  {
		      try
		      {
		          if (isShowSrcImg) //当开始处理图像时原视频要暂停,否则buffer中的数据会变化,图像上下颠倒交替出现(因Show(objIFrameData)中显示实现对图像数据做了垂直翻转)
		          {
		              //************************************************************
		              //显示相机获取的原图
		              //************************************************************                 
		              m_objGxBitmap1.Show(objIFrameData);
		          }
		
		          //获取图像宽、高、pBuffer、channel等信息
		          getImgInfo(objIFrameData, ref pBuffer1);
		
		
		          //************************************************************
		          // 对图像进行裁剪并显示在 PictureBox 中
		          //*************************************************************
		          if (isPreviewRoiImg)// 开启预览模式。
		          {
		              getRoiBmpData(ROI_B6, pBuffer1, enum_ImgRegion.B6);
		              getRoiBmpData(ROI_C6, pBuffer1, enum_ImgRegion.C6);
		
		          }
		
		          //统计帧率
		          m_objCFps1.IncreaseFrameNum();
		      }
		      catch (Exception ex)
		      {
		          MessageBox.Show("回调函数2" + ex.Message);
		      }
		  }

        
        //其他回调函数函数
	  //启动Form2,显示预览画面
	   private void bt_Preview_Click(object sender, EventArgs e)
	   {
	       isPreviewRoiImg = true;
	
	       //预览窗口实例化,并显示
	       PreviewL previewL = new PreviewL();
	       PreviewR previewR = new PreviewR();
	       previewL.Show();            
	       previewR.Show();
	       
	
	       Thread.Sleep(500);
	       //按键使能
	       bt_Preview.Enabled = false;
	   }

    }

    //定义事件类,注意要 TestApp同级别,也就是都在namespace TestAppMain命名空间下的同一个级别
    public class ImageEventArgs : EventArgs
    {
        public enum_ImgRegion ImgRegion { get; }
        public Bitmap ProcessedImage { get; }

        public ImageEventArgs(enum_ImgRegion imgRegion, Bitmap processedImage)
        {
            ImgRegion = imgRegion;
            ProcessedImage = processedImage;
        }
    }
}

代码中的 getRoiBmpData函数,该函数与方法1中的函数一样,只是入口参数做了更改。

csharp 复制代码
        //方法法①双循环逐字节复制ROI区域的图像数据;
        //方法②单循环, 逐行复制ROI区域的图像数据,提高效率,但很耗内存;
        private void getRoiBmpData(Rectangle ROI, IntPtr pBuffer, enum_ImgRegion imgRegion)
        {
            try
            {
                if (null != pBuffer)
                {
                    //判断像素格式
                    PixelFormat format = new PixelFormat();
                    // format = channels == 3 ? PixelFormat.Format24bppRgb : PixelFormat.Format8bppIndexed;//若3通道彩色图像,否则黑白图像
                    switch (channel)
                    {
                        case 1:
                            format = PixelFormat.Format8bppIndexed;
                            break;
                        case 3:
                            format = PixelFormat.Format24bppRgb;
                            break;
                        case 4:
                            format = PixelFormat.Format32bppArgb;
                            break;
                    }

                    // 在关键部分的代码前加锁
                    lock (this)
                    {
                        //创建ROI 空图像
                        Bitmap bitmap = new Bitmap(ROI.Width, ROI.Height, format);
                        BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, ROI.Width, ROI.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);


                        int srcStride = SrcImgWidth * channel;// 原图每行数据长度
                        int destStride = bmpData.Stride;// 新图每行数据长度
                        int srcStartX = (ROI.X - 1) * channel; // ROI 起始 X 坐标在原图数据中的位置(二维视角)
                        int srcStartY = ROI.Y - 1; // ROI 起始 Y 坐标在原图数据中的位置(二维视角)
                        int srcOffset = srcStartY * srcStride + srcStartX; // ROI 起始位置在原图数据中的偏移量(原图数据在内存中是一行,一维数组)

                        unsafe
                        {
                            //int bytes = Math.Abs(destStride) * m_bitmap.Height;     // 计算 ROI 区域总字节数                                   
                            byte* srcPtr = (byte*)pBuffer + srcOffset;    // 指向原始图像数据的指针(获取位图数据的起始地址,并将其转换为 byte* 类型的指针。bmpData.Scan0 返回的是位图数据的第一个像素的内存地址)
                            byte* destPtr = (byte*)bmpData.Scan0; // 指向 ROI Bitmap 的扫描行指针   

                            ////双循环,逐字节操作;
                            //for (int y = 0; y < ROI.Height; y++)
                            //{
                            //    for (int x = 0; x < ROI.Width * channel; x++)
                            //    {
                            //        destPtr[x] = srcPtr[x];
                            //    }

                            //    srcPtr += srcStride;
                            //    destPtr += destStride;
                            //}

                            //单循环, 逐行操作
                            for (int y = 0; y < ROI.Height; y++)  // 逐行复制ROI区域的图像数据
                            {
                                // 复制当前行的图像数据到ROI Bitmap中
                                //从源地址 srcPtr开始,每次复制destStride个字节(一整行的数据)到目标地址 bmpPtr 处,第四个参数偏移量
                                Buffer.MemoryCopy(srcPtr, destPtr, destStride, destStride);
                                destPtr += destStride;
                                srcPtr += srcStride;
                            }
                        }

                        //解锁对位图数据的访问,使其能够被其他代码访问。释放与位图数据关联的内存区域,以便系统能够对其进行管理。
                        bitmap.UnlockBits(bmpData);

                        // 在这里处理 bitmap 图像
                        ShowProcessedImage(imgRegion, bitmap);//传给函数,进行处理显示,                      
                        //ROI_bitmap = bitmap;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

2.2 Form2代码实现

csharp 复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using static TestAppMain.TestApp;

namespace TestAppMain
{
    public partial class PreviewL : Form
    {
        public PreviewL()
        {
            InitializeComponent();
            //InitializeLayout();
            fullScreen();

            // 订阅图片处理事件
            TestApp.ImageProcessed += ImageShowFrom_ImageProcessed;
        }


      
        private void fullScreen()
        {
            // 判断当前窗体是否已经是最大化状态
            if (this.WindowState == FormWindowState.Maximized)
            {
                // 如果已经最大化,则恢复到正常状态
                this.WindowState = FormWindowState.Normal;
            }
            else
            {
                // 如果不是最大化,则将窗体最大化
                this.WindowState = FormWindowState.Maximized;
            }
        }

        // 图片处理事件处理程序
        private void ImageShowFrom_ImageProcessed(object sender, ImageEventArgs e)
        {
            switch (e.ImgRegion)
            {
                case enum_ImgRegion.B6:
                    pictureBox_B6.Image = e.ProcessedImage;
                    break;
                case enum_ImgRegion.B5:
                    pictureBox_B5.Image = e.ProcessedImage;
                    break;
                case enum_ImgRegion.C6:
                    pictureBox_C6.Image = e.ProcessedImage;
                    break;
                case enum_ImgRegion.C4:
                    pictureBox_C4.Image = e.ProcessedImage;
                    break;
                default:
                    break;
            }
        }
    }
}
相关推荐
钢铁男儿2 小时前
C# 枚举器和迭代器(常见迭代器模式)
数据库·c#·迭代器模式
R-G-B4 小时前
【05】VM二次开发——模块参数配置--带渲染/不带渲染(WinForm界面调用 模块参数配置)
c#·vm二次开发·vm模块参数配置·vm在winform界面调用
R-G-B10 小时前
【12】大恒相机SDK C#开发 ——多相机开发,枚举所有相机,并按配置文件中的相机顺序 将所有相机加入设备列表,以便于对每个指定的相机操作
c#·大恒相机sdk·大恒多相机开发·大恒多相机枚举·大恒多相机指定顺序
向宇it16 小时前
【unity小技巧】封装一套 Unity 的植物生长系统
游戏·unity·c#·游戏引擎
R-G-B17 小时前
【07】大恒相机SDK C#开发 —— 相机IO触发采集与信号输出
大恒相机sdk·大恒相机开发·大恒相机io触发采集·大恒相机io信号输出·io触发采集·信号输出
NFA晨曦18 小时前
力扣刷题日常(7-8)
算法·leetcode·c#
踏上青云路18 小时前
C# 闭包
java·前端·c#
86Eric18 小时前
C# 入门教程(四)委托详解
c#·委托·多播委托
80岁小姑娘1 天前
.NET Core部署服务器
c#