当前位置: 首页 > news >正文

【11】大恒相机SDK C++开发 ——原图像数据IFrameData内存中上下颠倒,怎么裁剪ROI 实时显示在pictureBox中

文章目录

  • 3 当内存中的 图像数据是垂直翻转的时候怎么截取ROI 并显示
    • 3.1 对ROI在原图中的位置做转换
    • 3.2 将ROI的最后一行当做开始位置,从底部向上复制数据
    • 3.3 完整代码
    • 3.4 图像数据在内存中上下颠倒的情况
    • 3.5 调用验证
  • 4 unsafe代码 解释及注意事项 看我另一篇文章
  • 5 ConvertToRGB24详细解释 、示例、注意事项 看我另一篇文章
  • 8 问题与反思
    • 8.1 被反复创建和使用,需手动释放吗?
    • 8.2 创建一个全局Bitmap bitma,多线程访问会冲突吗?

3 当内存中的 图像数据是垂直翻转的时候怎么截取ROI 并显示

当原图垂直翻转时,此时原图是上下颠倒的,那么直接截取RO显示出来的效果会有两大问题

  • 1 .ROI的位置变了;
  • 2 ROI的内容上下颠倒了;
  • 比如截取的是左上角的ROI,结果显示出来的左下角的内容,并且内容还是上下颠倒的 ;

为了解决这个问题;
方法1:查看相机是否支库函数是否持 翻转操作,将图像再翻转回来,然后按照上面的方法操作;

方法2:①对ROI在原图中的位置做转换;②将ROI的最后一行当做开始位置,从底部向上复制数据;

由于原图数据在内存中是上下颠倒的,所以将 srcStartY 计算为 imgHeight -ROI.Y - ROI.Height,以确保从原图的正确位置开始获取ROI数据。这样,即使原始图像上下颠倒,也能正确地获取ROI区域。

3.1 对ROI在原图中的位置做转换

//由于原图数据在内存中是上下颠倒的,所以将 srcStartY 计算为 imgHeight -ROI.Y - ROI.Height,以确保从原图的正确位置开始获取ROI数据。这样,即使原始图像上下颠倒,也能正确地获取ROI区域。
int srcStartY = imgHeight - ROI.Y - ROI.Height; // ROI 起始 Y 坐标在原图数据中的位置,将ROI的最后一行当做开始位置,从底部向上复制数据
int srcOffset = srcStartY * srcStride + srcStartX; // ROI 起始位置在原图数据中的偏移量

3.2 将ROI的最后一行当做开始位置,从底部向上复制数据

调整行顺序以解决上下颠倒的问题,由于原图数据在内存中是上下颠倒的,调整起始位置为最后一行,从底部向上复制数据;

以下三种代码均可实现:

 //调整行顺序以解决上下颠倒的问题unsafe{for (int y = 0; y < ROI.Height; y++){byte* srcPtr = (byte*)pBuffer + srcOffset + y * srcStride;byte* destPtr = (byte*)bmpData.Scan0 + (ROI.Height - y - 1) * destStride; // 调整起始位置为最后一行,从底部向上复制数据for (int x = 0; x < ROI.Width * channel; x++){destPtr[x] = srcPtr[x];}}}
////// 调整行顺序以解决上下颠倒的问题
unsafe
{for (int y = 0; y < ROI.Height; y++){byte* srcPtr = (byte*)pBuffer + srcOffset + (ROI.Height - y - 1) * srcStride; // 从底部向上复制数据byte* destPtr = (byte*)bmpData.Scan0 + y * bmpData.Stride;for (int x = 0; x < ROI.Width * channel; x++){destPtr[x] = srcPtr[x];}}
}
 // 调整行顺序以解决上下颠倒的问题unsafe{byte* srcPtr = (byte*)pBuffer + srcOffset;byte* destPtr = (byte*)bmpData.Scan0 + (ROI.Height - 1) * destStride; // 调整起始位置为最后一行,从底部向上复制数据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; // 逐行递减,从底部向顶部复制}}

3.3 完整代码

bool isShowSrcImg = true;bool camImg_isProcess = false; //是否图像处理,不执行图像处理的时候,默认开启预览模式。bool isPreviewRoiImg = false; //是否图像处理,不执行图像处理的时候,默认开启预览模式。Rectangle ROI = new Rectangle(1323, 212, 2200, 1500);Bitmap bitmapB6;int SrcImgHeight = 0, SrcImgWidth = 0, channel = 0;

代码中未出现的变量,都是公共变量(或者库函中的变量)

        private void getRoiBmpData2(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 * channel; // ROI 起始 X 坐标在原图数据中的位置(二维视角)//对ROI在原图中的位置做转换//由于原图数据在内存中是上下颠倒的,所以将 srcStartY 计算为 SrcImgHeight -ROI.Y - ROI.Height,以确保从原图的正确位置开始获取ROI数据。这样,即使原始图像上下颠倒,也能正确地获取ROI区域。int srcStartY = SrcImgHeight - ROI.Y - ROI.Height; // ROI 起始 Y 坐标在原图数据中的位置,将ROI的最后一行当做开始位置,从底部向上复制数据int srcOffset = srcStartY * srcStride + srcStartX; // ROI 起始位置在原图数据中的偏移量(原图数据在内存中是一行,一维数组)// 调整行顺序以解决上下颠倒的问题,由于原图数据在内存中是上下颠倒的,调整起始位置为最后一行,从底部向上复制数据//调整行顺序以解决上下颠倒的问题---方法1unsafe{for (int y = 0; y < ROI.Height; y++){byte* srcPtr = (byte*)pBuffer + srcOffset + y * srcStride;byte* destPtr = (byte*)bmpData.Scan0 + (ROI.Height - y - 1) * destStride; // 调整起始位置为最后一行,从底部向上复制数据for (int x = 0; x < ROI.Width * channel; x++){destPtr[x] = srcPtr[x];}}}//////// 调整行顺序以解决上下颠倒的问题---方法2//unsafe//{//    for (int y = 0; y < ROI.Height; y++)//    {//        byte* srcPtr = (byte*)pBuffer + srcOffset + (ROI.Height - y - 1) * srcStride; // 从底部向上复制数据//        byte* destPtr = (byte*)bmpData.Scan0 + y * bmpData.Stride;//        for (int x = 0; x < ROI.Width * channel; x++)//        {//            destPtr[x] = srcPtr[x];//        }//    }//}//// 调整行顺序以解决上下颠倒的问题---方法3//unsafe//{//    byte* srcPtr = (byte*)pBuffer + srcOffset;//    byte* destPtr = (byte*)bmpData.Scan0 + (ROI.Height - 1) * destStride; // 调整起始位置为最后一行,从底部向上复制数据//    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);}}

3.4 图像数据在内存中上下颠倒的情况

为了验证 原图像数据在内存中上下颠倒的情况,我们在在取 pBuffer 的时候 ,故意把ConvertToRGB24 原图数据进行了垂直翻转 pBuffer = objIFrameData.ConvertToRGB24(emValidBits, GX_BAYER_CONVERT_TYPE_LIST.GX_RAW2RGB_NEIGHBOUR, true);//最后一个参数是否垂直翻转图像,true则翻转

        //此函数 ConvertToRGB24 转换时 对原图数据进行了垂直翻转private void getImgInfo2(IFrameData objIFrameData, ref IntPtr pBuffer){try{if (null != objIFrameData){//获取图像宽高SrcImgHeight = (int)objIFrameData.GetHeight();SrcImgWidth = (int)objIFrameData.GetWidth();//获取图像bufferGX_VALID_BIT_LIST emValidBits = GX_VALID_BIT_LIST.GX_BIT_0_7;emValidBits = __GetBestValudBit(objIFrameData.GetPixelFormat());// 判断图像是否成功接收if (GX_FRAME_STATUS_LIST.GX_FRAME_STATUS_SUCCESS == objIFrameData.GetStatus()){// 在关键部分的代码前加锁lock (this){if (colorFlag){// 彩色图像pBuffer = objIFrameData.ConvertToRGB24(emValidBits, GX_BAYER_CONVERT_TYPE_LIST.GX_RAW2RGB_NEIGHBOUR, true);//最后一个参数是否垂直翻转图像,true则翻转channel = 3;}else{// 黑白图像if (__IsPixelFormat8(objIFrameData.GetPixelFormat())){pBuffer = objIFrameData.GetBuffer();}else{pBuffer = objIFrameData.ConvertToRaw8(emValidBits);}channel = 1;}}}}}catch (Exception ex){MessageBox.Show(ex.Message);}}

3.5 调用验证

bool isShowSrcImg = true;bool camImg_isProcess = false; //是否图像处理,不执行图像处理的时候,默认开启预览模式。bool isPreviewRoiImg = false; //是否图像处理,不执行图像处理的时候,默认开启预览模式。Rectangle ROI = new Rectangle(1323, 212, 2200, 1500);Bitmap bitmapB6;int SrcImgHeight = 0, SrcImgWidth = 0, channel = 0;

代码中未出现的变量为 全局变量,或者库中的变量

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

4 unsafe代码 解释及注意事项 看我另一篇文章

5 ConvertToRGB24详细解释 、示例、注意事项 看我另一篇文章

8 问题与反思

C# 自定义函数getRoiBmpData,该函数功能“对相机获取到的 IFrameData 转换为pBuffer ,然后进行ROI裁剪操作,将ROI图像bitmap传给ShowProcessedImage函数进行处理”。getRoiBmpData函数被5个回调函数同时调用,因为有5个相机,每个相机调用一个回调函数。那么我为什么5个相机要用5个回调呢,因为这样每个回调函数只负责一个相机,这样看起来分工更明确。

现在有两个问题:
问题1.这行代码“ Bitmap bitmap = new Bitmap(ROI.Width, ROI.Height, format);”中bitmap 被反复创建和使用,在该函数中需要手动释放吗?

问题2.这行代码“ Bitmap bitmap = new Bitmap(ROI.Width, ROI.Height, format);”中bitmap 被反复创建和使用,那么我可以创建一个全局变量Bitmap bitmap ,这样就不用每次进入函数都会创建一次bitmap ,但是呢我担心5个回调函数共享这一个bitmap 会冲突吗?当然ShowProcessedImage函数和getRoiBmpData函数也只有一个,都是同时被5个回调函数调用的。

8.1 被反复创建和使用,需手动释放吗?

在这段代码中,虽然 bitmap 被反复创建和使用,但在每次使用后都被正确地释放了。这是因为在使用完毕后调用了 bitmap.UnlockBits(bmpData); 来释放 BitmapData 对象。所以不需要手动释放 bitmap,因为在 bitmap 超出范围时会被自动回收。

bitmap 对象是在函数内部创建的,它会在函数执行完毕后自动离开作用域,从而被垃圾回收机制回收。因为它的生命周期受到函数作用域的限制,一旦函数执行完毕就会被销毁。

8.2 创建一个全局Bitmap bitma,多线程访问会冲突吗?

问题2:创建一个全局变量 Bitmap bitmap 是一种有效的方法,可以避免在每次函数调用时都重新创建 bitmap 对象。然而,如果多个回调函数同时访问和修改同一个全局 bitmap 对象,就可能会发生竞态条件或数据竞争,导致程序行为不确定或产生错误。因此,你需要确保在对 bitmap 进行读写操作时进行适当的线程同步,以避免冲突。

为了解决这个问题,可以采取以下方法之一

  • 在访问全局 bitmap 对象时使用线程同步机制(如锁),以确保在任何时候只有一个回调函数可以访问或修改 bitmap 对象。这样可以避免并发访问导致的问题。一个简单的方法是在访问 bitmap 之前使用 lock 关键字来确保线程安全,就像你在代码中对关键部分加锁一样。这样可以确保每次只有一个线程能够访问 bitmap,从而避免并发冲突。

  • 将 bitmap 对象作为函数参数传递给每个回调函数,这样每个函数都有自己的 bitmap 对象实例,不会相互干扰。

选择哪种方法取决于你的具体需求和代码结构。如果需要在多个回调函数之间共享相同的 bitmap 对象,并且需要确保线程安全,则使用第一种方法;如果每个回调函数都需要独立的 bitmap 对象,则使用第二种方法。

http://www.dtcms.com/a/307725.html

相关文章:

  • 5G毫米波射频前端设计:从GaN功放到混合信号集成方案
  • 初始sklearn 数据集获取、分类、划分与特征工程
  • mysql笔记02:DML插入、更新、删除数据
  • 【读书笔记】Design Patterns (1994)✅
  • 贝锐蒲公英X4 Pro 5G新品路由器:异地组网+8网口+双频WiFi全都有
  • 大模型005
  • 反射之专题
  • C++:结构体(Structure)
  • Flux.1系列模型解析--Flux.1
  • OpenCV 中的「通道」(Channel)详解
  • C# 入门教程(四)委托详解
  • 国产芯+单北斗防爆终端:W5-D防爆智能手机,助力工业安全通信升级
  • Flutter Chen Generator - yaml配置使用
  • 一个清洁机器人的城市漂流记
  • C++面试5题--6day
  • 三维开放场景图助力机器人自主导航!Point2Graph:点云驱动的三维开放词汇场景图端到端机器人导航
  • Flutter 页面跳转及传参总结
  • Excel超级处理器,多个word表格模板中内容提取到Excel表格中
  • npm从入门到精通一篇全
  • 深度学习(鱼书)day07--误差反向传播(前四节)
  • [免费]基于Python的招聘职位信息推荐系统(猎聘网数据分析与可视化)(Django+requests库)【论文+源码+SQL脚本】
  • 无人机光伏巡检缺陷检出率↑32%:陌讯多模态融合算法实战解析
  • InfluxDB 与 Python 框架结合:Django 应用案例(三)
  • git使用秘诀(详解0到1)
  • C# 事件Event
  • python中高效构建提示词
  • 软件工程:软件复用
  • 当过滤条件不符合最左前缀时,如何有效利用索引? | OceanBase SQL 优化实践
  • Verilog实现RPC从机(配合AXI_Slave使用)
  • 消息队列学习-----消息消失与积压