• Halcon HImage 与 Qt QImage 的相互转换


    Halcon HImage 与 Qt QImage 的相互转换

    以前一直是用 OpenCV 开发机器视觉算法,最近由于某些机缘开始接触学习 Halcon。Halcon 确实是功能强大,用 Halcon 写算法比 OpenCV 方便了太多。但是 Halcon 与OpenCV一样,专注于视觉算法,如果要开发软件界面或者与其他程序交互,Halcon 就不是很擅长了。所以我还是决定用 C++ Qt 做我的程序,只在需要视觉算法的地方用Halcon 提供的算子。今天花了半天时间研究了如何在 Halcon 和 Qt 间传递图像数据。

    原理讲解

    Qt 中用 QImage 来存放图像数据,Halcon 中则是 HImage。对于灰度图像,这两个类的存储方式类似,都是一个大数组。但是对于彩色图像,存储方式就有很大不同了。

    QImage 中彩色图像的各个分量是交替存储的。比如 RGB32 格式,RGB 三个颜色分量打包成一个 32 bits 的dword,然后像素点按照 row major 的方式依次存储在内存中。QImage 支持的彩色图像的种类很多,比如 RGB888、RGB555、RGB565、RGB32 等。我的代码实现中只支持 RGB888 和 RGB32 这两种。其余的因为很少能用到,所以暂时先不考虑。

    HImage 中彩色图像的各个分量是单独存储的,简单的说就是 RGB 分别存到 3 个数组中。而且 HImage 不支持 Alpha 通道。由于 HImage 与 QImage 在内存中存储彩色图像时的巨大区别,因此这两个类之间很难共享同一个图像数据。所以在下面的代码中都对图像数据进行了拷贝操作。当然如果只是灰度图像的话,是可以共享数据的。在本文的最后我也会简单介绍一下如何共享数据。

    HImage -> QImage

    我的实现方式是先给 QImage 分配合适的数据空间。然后把 HImage 里面的数据拷贝到 QImage 中。如果是 灰度图像可以用 HImage::GetImagePointer1 获得图像数据的首地址。

    void* HImage::GetImagePointer1(HString* Type, Hlong* Width, Hlong* Height)

    如果是彩色图像则是用 HImage::GetImagePointer3 来获得图像数据各个颜色分量的首地址。

    void HImage::GetImagePointer3(void** PointerRed, void** PointerGreen, void** PointerBlue, HString* Type, Hlong* Width, Hlong* Height) 

    得到了HImage 中数据的地址之后拷贝就很简单了。获得 QImage 中图像数据的地址可以用下面两个函数:

    1     uchar *QImage::bits()
    2     uchar *QImage::scanLine(int i)

    下面直接贴出实现代码:

     1     /**
     2      * @brief HImage2QImage 将 Halcon 的 HImage 转换为 Qt 的 QImage
     3      * @param from HImage ,暂时只支持 8bits 灰度图像和 8bits 的 3 通道彩色图像
     4      * @param to QImage ,这里 from 和 to 不共享内存。如果 to 的内存大小合适,那么就不用重新分配内存。所以可以加快速度。
     5      * @return  true 表示转换成功,false 表示转换失败
     6      */
     7     bool HImage2QImage(HalconCpp::HImage &from, QImage &to)
     8     {
     9         Hlong width;
    10         Hlong height;
    11         from.GetImageSize(&width, &height);
    12     
    13         HTuple channels = from.CountChannels();
    14         HTuple type = from.GetImageType();
    15     
    16         if( strcmp(type[0].S(), "byte" )) // 如果不是 byte 类型,则失败
    17         {
    18             return false;
    19         }
    20     
    21         QImage::Format format;
    22         switch(channels[0].I())
    23         {
    24         case 1:
    25             format = QImage::Format_Grayscale8;
    26             break;
    27         case 3:
    28             format = QImage::Format_RGB32;
    29             break;
    30         default:
    31             return false;
    32         }
    33     
    34         if(to.width() != width || to.height() != height || to.format() != format)
    35         {
    36             to = QImage(static_cast<int>(width),
    37                         static_cast<int>(height),
    38                         format);
    39         }
    40         HString Type;
    41         if(channels[0].I() == 1)
    42         {
    43             unsigned char * pSrc = reinterpret_cast<unsigned char *>( from.GetImagePointer1(&Type, &width, &height) );
    44             memcpy( to.bits(), pSrc, static_cast<size_t>(width) * static_cast<size_t>(height) );
    45             return true;
    46         }
    47         else if(channels[0].I() == 3)
    48         {
    49             uchar *R, *G, *B;
    50             from.GetImagePointer3(reinterpret_cast<void **>(&R),
    51                                   reinterpret_cast<void **>(&G),
    52                                   reinterpret_cast<void **>(&B), &Type, &width, &height);
    53     
    54             for(int row = 0; row < height; row ++)
    55             {
    56                 QRgb* line = reinterpret_cast<QRgb*>(to.scanLine(row));
    57                 for(int col = 0; col < width; col ++)
    58                 {
    59                     line[col] = qRgb(*R++, *G++, *B++);
    60                 }
    61             }
    62             return true;
    63         }
    64     
    65         return false;
    66     }

    QImage ->HImage

    这里的知识点就是拿到 QImage 的数据后如何构造 HImage。对于彩色图像来说,可以用如下的函数:

    void HImage::GenImageInterleaved(void* PixelPointer, const char* ColorFormat, Hlong OriginalWidth, Hlong OriginalHeight, Hlong Alignment, const char* Type, Hlong ImageWidth, Hlong ImageHeight, Hlong StartRow, Hlong StartColumn, Hlong BitsPerChannel, Hlong BitShift)

    对于灰度图像,如下面的函数:

    void HImage::GenImage1(const char* Type, Hlong Width, Hlong Height, void* PixelPointer)

    对于彩色图像,用如下的函数:

    void HImage::GenImageInterleaved(void* PixelPointer, const char* ColorFormat, Hlong OriginalWidth, Hlong OriginalHeight, Hlong Alignment, const char* Type, Hlong ImageWidth, Hlong ImageHeight, Hlong StartRow, Hlong StartColumn, Hlong BitsPerChannel, Hlong BitShift)

    这里多说一句,如果我们的图像数据的各个颜色分量本来就是分离的,那么可以用下面的函数:

    void HImage::GenImage3(const char* Type, Hlong Width, Hlong Height, void* PixelPointerRed, void* PixelPointerGreen, void* PixelPointerBlue)

    有了这两个函数就够了,下面是我的实现代码:

     1     /**
     2      * @brief QImage2HImage 将 Qt QImage 转换为 Halcon 的 HImage
     3      * @param from 输入的 QImage
     4      * @param to 输出的 HImage ,from 和 to 不共享内存数据。 每次都会为 to 重新分配内存。
     5      * @return true 表示转换成功,false 表示转换失败。
     6      */
     7     bool QImage2HImage(QImage &from, HalconCpp::HImage &to)
     8     {
     9         if(from.isNull()) return false;
    10     
    11         int width = from.width(), height = from.height();
    12         QImage::Format format = from.format();
    13     
    14         if(format == QImage::Format_RGB32 ||
    15                 format == QImage::Format_ARGB32 ||
    16                 format == QImage::Format_ARGB32_Premultiplied)
    17         {
    18             to.GenImageInterleaved(from.bits(), "rgbx", width, height, 0,  "byte", width, height, 0, 0, 8, 0);
    19             return true;
    20         }
    21         else if(format == QImage::Format_RGB888)
    22         {
    23             to.GenImageInterleaved(from.bits(), "rgb", width, height, 0,  "byte", width, height, 0, 0, 8, 0);
    24             return true;
    25         }
    26         else if(format == QImage::Format_Grayscale8 || format == QImage::Format_Indexed8)
    27         {
    28             to.GenImage1("byte", width, height, from.bits());
    29             return true;
    30         }
    31         return false;
    32     }

    灰度图像如何共享内存

    这里需要我们知道两个函数,一个是 HImage 的:

    void HImage::GenImage1Extern(const char* Type, Hlong Width, Hlong Height, void* PixelPointer, void* ClearProc)

    利用这个函数生成的 HImage 不拷贝图像数据,在析构时通过 ClearProc 来删除图像数据,如果 ClearProc 传个 null 进去,那么就不负责删除了。

    QImage 中也有类似的构造函数:

    QImage::QImage(uchar *data, int width, int height, Format format, QImageCleanupFunction cleanupFunction = Q_NULLPTR, void *cleanupInfo = Q_NULLPTR)

    用这个构造函数构造QImage 时不复制图像数据。

    有这两个函数,就可以实现 QImage 与 HImage 灰度图像的数据共享了。具体的实现代码大家自己写吧。
    但是彩色图像似乎是没有办法共享的。

  • 相关阅读:
    Kubernetes实战(第二版)--第六章 管理Pod容器的生命周期
    Kubernetes实战(第二版)--第五章 在Pods中运行应用程序
    Kubernetes实战(第二版)--第四章 介绍kubernetes API对象
    第三章 部署第一个应用程序
    Kubernetes实战(第二版)----第2章 理解容器
    dfs 例子演示
    Leetcode 135. 分发糖果
    mysql materialized_MySql优化- 执行计划解读与优化(二)
    说说Golang的使用心得
    最详细之教你Jenkins+github自动化部署.Net Core程序到Docker
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14188972.html
Copyright © 2020-2023  润新知