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

Halcon HImage 与 Qt QImage 的相互转换(修订版)

很久以前,我写过一遍文章来介绍 HImage 和 QImage 之间的转换方法。(https://blog.csdn.net/liyuanbhu/article/details/91356988)

这个代码其实是有些问题的。因为我们知道 QImage 中的图像数据不一定是连续的,尤其是图像的宽度是奇数时,每行数据后面基本都会多填充几个字节将每行的字节数凑成4的整倍数。

HImage 内部如何存储我不是很确定。但是GenImage1() 函数,GenImage3() 函数和 GenImageInterleaved() 函数里面输入图像数据都要求是连续的。因此,如果不判断QImage 的数据是否连续,直接将图像数据传过来有可能获得的图像是错误的。

本来我懒得写这篇博客。但是这么多年过去了。在网上搜相关的代码,竟然还是没有一个代码是正确考虑这个问题的。中文互联网上技术博客水平之低真是令人瞠目。

所以我还是写一写这个问题,避免大家重复的遇到这个 bug 吧。

首先是将 QImage 转换为 HImage 的代码。与原来的代码相比,就是在每种图像类型转换时增加了个判断。如果QImage 图像数据不是连续的,那么就一行一行的拷贝数据。

#include "himageqimage.h"
#include <QDebug>
using namespace HalconCpp;

/**
 * @brief QImage2HImage 将 Qt QImage 转换为 Halcon 的 HImage
 * @param from 输入的 QImage
 * @param to 输出的 HImage ,from 和 to 不共享内存数据。 每次都会为 to 重新分配内存。
 * @return true 表示转换成功,false 表示转换失败。
 */
bool QImage2HImage(QImage &from, HalconCpp::HImage &to)
{
    if(from.isNull()) return false;

    int width = from.width(), height = from.height();
    QImage::Format format = from.format();

    if(format == QImage::Format_RGB32 ||
            format == QImage::Format_ARGB32 ||
            format == QImage::Format_ARGB32_Premultiplied)
    {
        if(from.bytesPerLine() == 4 * width)
        {
            to.GenImageInterleaved(from.bits(), "bgrx", width, height, 0,  "byte", width, height, 0, 0, 8, 0);
        }
        else
        {
            to.GenImageInterleaved(from.bits(), "bgrx", width, height, 0,  "byte", width, height, 0, 0, 8, 0);
            uchar *R, *G, *B;
            HString Type;
            Hlong Width, Height;
            to.GetImagePointer3(reinterpret_cast<void **>(&R),
                                  reinterpret_cast<void **>(&G),
                                  reinterpret_cast<void **>(&B), &Type, &Width, &Height);
            for(int row = 0; row < height; row ++)
            {
                QRgb* line = reinterpret_cast<QRgb*>(from.scanLine(row));
                for(int col = 0; col < width; col ++)
                {
                    *R = qRed(line[col]);
                    *G = qGreen(line[col]);
                    *B = qBlue(line[col]);
                    ++R;
                    ++G;
                    ++B;
                }
            }
        }
        return true;
    }
    else if(format == QImage::Format_RGB888)
    {
        if(from.bytesPerLine() == 3 * width)
        {
            to.GenImageInterleaved(from.bits(), "rgb", width, height, 0,  "byte", width, height, 0, 0, 8, 0);
        }
        else
        {
            to.GenImageInterleaved(from.bits(), "rgb", width, height, 0,  "byte", width, height, 0, 0, 8, 0);
            uchar *R, *G, *B;
            HString Type;
            Hlong Width, Height;
            to.GetImagePointer3(reinterpret_cast<void **>(&R),
                                reinterpret_cast<void **>(&G),
                                reinterpret_cast<void **>(&B), &Type, &Width, &Height);
            for(int row = 0; row < height; row ++)
            {
                unsigned char* line = reinterpret_cast<unsigned char *>(from.scanLine(row));
                for(int col = 0; col < width; col ++)
                {
                    *R ++ = *line ++;
                    *G ++ = *line ++;
                    *B ++ = *line ++;
                }
            }
        }
        return true;
    }
    else if(format == QImage::Format_Grayscale8 || format == QImage::Format_Indexed8)
    {
        if(from.bytesPerLine() == width)
        {
            to.GenImage1("byte", width, height, from.bits());
        }
        else// 这时说明每行数据之间有填充字节。因此需要重新写数据
        {
            to.GenImageConst("byte", width, height);
            Hlong W, H; HString Type;
            unsigned char * pTo = reinterpret_cast<unsigned char *>( to.GetImagePointer1(&Type, &W, &H) );
            for(int row = 1; row < H; row ++)
            {
                const unsigned char * pSrc = from.constScanLine(row);
                unsigned char * pDist = pTo + row * W;
                memcpy( pDist, pSrc, static_cast<size_t>(W));
            }
        }
        return true;
    }
    return false;
}

下面是 HImage 转 QImage。基本原理也是相同的。拷贝数据之前判断一下QImage 数据是否连续。不连续就一行一行的处理。

/**
 * @brief HImage2QImage 将 Halcon 的 HImage 转换为 Qt 的 QImage
 * @param from HImage ,暂时只支持 8bits 灰度图像和 8bits 的 3 通道彩色图像
 * @param to QImage ,这里 from 和 to 不共享内存。如果 to 的内存大小合适,那么就不用重新分配内存。所以可以加快速度。
 * @return  true 表示转换成功,false 表示转换失败
 */
bool HImage2QImage(HalconCpp::HImage &from, QImage &to)
{
    Hlong width;
    Hlong height;
    from.GetImageSize(&width, &height);

    HTuple channels = from.CountChannels();
    HTuple type = from.GetImageType();

    if( strcmp(type[0].S(), "byte" )) // 如果不是 byte 类型,则失败
    {
        return false;
    }

    QImage::Format format;
    switch(channels[0].I())
    {
    case 1:
        format = QImage::Format_Grayscale8;
        break;
    case 3:
        format = QImage::Format_RGB888;
        break;
    default:
        return false;
    }

    if(to.width() != width || to.height() != height || to.format() != format)
    {
        to = QImage(static_cast<int>(width),
                    static_cast<int>(height),
                    format);
    }
    HString Type;
    if(channels[0].I() == 1)
    {
        unsigned char * pSrc = reinterpret_cast<unsigned char *>( from.GetImagePointer1(&Type, &width, &height) );
        if(to.bytesPerLine() == width)
        {
            memcpy( to.bits(), pSrc, static_cast<size_t>(width) * static_cast<size_t>(height) );
        }
        else
        {
            for(int row = 1; row < height; row ++)
            {
                unsigned char * pDistLine = to.scanLine(row);
                const unsigned char * pSrcLine = pSrc + row * width;
                memcpy( pDistLine, pSrcLine, static_cast<size_t>(width));
            }
        }

        return true;
    }
    else if(channels[0].I() == 3)
    {
        uchar *R, *G, *B;
        from.GetImagePointer3(reinterpret_cast<void **>(&R),
                              reinterpret_cast<void **>(&G),
                              reinterpret_cast<void **>(&B), &Type, &width, &height);

        for(int row = 0; row < height; row ++)
        {
            unsigned char * line = reinterpret_cast<unsigned char *>(to.scanLine(row));
            for(int col = 0; col < width; col ++)
            {
                *line++ = *R++;
                *line++ = *G++;
                *line++ = *B++;
            }
        }
        return true;
    }

    return false;
}


http://www.kler.cn/a/399041.html

相关文章:

  • LeetCode 445.两数相加 II
  • 【MySQL】优化方向+表连接
  • JS的学习与使用
  • 基于Springboot+Vue的中国蛇类识别系统 (含源码数据库)
  • 【工具插件类教学】在 Unity 中使用 iTextSharp 实现 PDF 文件生成与导出
  • 从零到一:利用 AI 开发 iOS App 《震感》的编程之旅
  • 7z 解压器手机版与解压专家:安卓解压工具对决
  • MIT6.5840 Lab 1: MapReduce(6.824)
  • RHCE-DNS域名解析服务器
  • 第8章 利用CSS制作导航菜单
  • 数字图像处理(c++ opencv):图像分割-基本边缘检测
  • PHP服务器如何开启WSS服务端Websocket
  • uni-app快速入门(七)--组件路由跳转和API路由跳转及参数传递
  • 【Qt 蓝牙服务器实现】
  • Cuda和Pytorch的兼容性
  • 大数据时代的隐私保护:数据治理的新视角
  • OMV7 树莓派 tf卡安装
  • 2024 RISC-V 中国峰会 演讲幻灯片和视频回放 均已公开
  • 21.UE5游戏存档,读档,函数库
  • Conda环境与Ubuntu环境移植详解
  • 【机器学习】机器学习中用到的高等数学知识-4.数值分析 (Numerical Analysis)
  • 怎么用Python+selenium自动化生成测试报告
  • Brave127编译指南 Windows篇:配置Git(四)
  • UE5运行时创建slate窗口
  • iOS UI自动化 Appium的元素定位方式及比较
  • matlab-fmincon函数做优化、optimoptions用法