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 图像数据不是连续的,那么就一行一行的拷贝数据。

cpp 复制代码
#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 数据是否连续。不连续就一行一行的处理。

cpp 复制代码
/**
 * @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;
}
相关推荐
黑色叉腰丶大魔王4 小时前
基于 MATLAB 的图像增强技术分享
图像处理·人工智能·计算机视觉
mahuifa11 小时前
混合开发环境---使用编程AI辅助开发Qt
人工智能·vscode·qt·qtcreator·编程ai
冷眼看人间恩怨11 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
paixiaoxin15 小时前
CV-OCR经典论文解读|An Empirical Study of Scaling Law for OCR/OCR 缩放定律的实证研究
人工智能·深度学习·机器学习·生成对抗网络·计算机视觉·ocr·.net
AI视觉网奇15 小时前
人脸生成3d模型 Era3D
人工智能·计算机视觉
云空16 小时前
《QT 5.14.1 搭建 opencv 环境全攻略》
开发语言·qt·opencv
编码小哥16 小时前
opencv中的色彩空间
opencv·计算机视觉
吃个糖糖16 小时前
34 Opencv 自定义角点检测
人工智能·opencv·计算机视觉
小老鼠不吃猫17 小时前
力学笃行(二)Qt 示例程序运行
开发语言·qt
葡萄爱18 小时前
OpenCV图像分割
人工智能·opencv·计算机视觉