格式转换:相机帧void* pBuffer(含拜尔格式),QImage,cv::Mat,Halconcpp::HObject

【说明】

1、若传递的是指针,则内存共享,其一改变,另一个也被改变。为了避免输入被更改,做了些处理。如QImage2Mat中使用了两个变量mat, out。

2、与QImage相关的转换存在宽度方向4字节对齐情况,也做了些处理。如QImage2HObject中让宽度变为4的整数倍。

3、尽量避免new

【相机帧void* pBuffer赋给其他格式】

W、H是帧的宽、高,一般是4的倍数。

三通道:以相机采集格式BGR24为例

qImg=QImage((const uchar*)pBuffer, W, H, QImage::Format_BGR888);
mImg = cv::Mat(H,W, CV_8UC3, (void*)pBuffer);
//若采集格式为RGB24,还需转换下
//cv::cvtColor(mImg, out, cv::COLOR_RGB2BGR);
HalconCpp::GenImageInterleaved(&hImg,(Hlong)pBuffer, "bgr", W, H, 0, "byte", W, H, 0, 0, 8, 0);

单通道:以相机采集格式MONO8为例

qImg=QImage((const uchar*)pBuffer, W, H, QImage::Format_Grayscale8);
mImg = cv::Mat(H,W, CV_8UC1, (void*)pBuffer);
HalconCpp::GenImage1(&hImg, "byte", W, H, (Hlong)pBuffer);

bayer单通道:以bayerRG采集格式为例

先用上述单通道承接数据后,再进行拜尔插值,得到彩色图。也可以用相机的拜尔插值(一般相机SDK都会提供拜尔插值函数)

cv::cvtColor(mImg, bgrImg, cv::COLOR_BayerRG2BGR);  //OpenCV的彩色是bgr顺序
HalconCpp::CfaToRgb(hImg, &rgbImg, "bayer_rg", "bilinear");

当设置相机输出格式为BGR24等时,内部进行了拜尔插值,帧率会严重降低。若对帧率要求极高,可以让相机以拜尔格式输出,之后在需要用彩色图的地方再进行拜尔插值。

 有的相机设置为BGR24等非8位格式时,官方软件界面显示帧率未下降,其实是有误导的。真正拿到彩色图的频率肯定是降低了,只不过它展示的是采集帧率(拜尔格式采集,之后插值,再输出彩色图)。

【QImage与cv::Mat】

bool QImage2Mat(const QImage& in,cv::Mat& out)
{
    if (in.isNull())
    {
        return false;
    }
    cv::Mat mat;
    if (in.format()== QImage::Format_RGB32)        //BGRA顺序    //一般Qt读入本地彩色图后为此格式
    {
        mat = cv::Mat(in.height(), in.width(), CV_8UC4, (void*)in.constBits(), in.bytesPerLine());
        cv::cvtColor(mat, out, cv::COLOR_BGRA2BGR);             //转3通道,OpenCV一般用3通道的
        return true;
    }
    else if (in.format()== QImage::Format_RGB888)//RGB顺序
    {
        mat = cv::Mat(in.height(), in.width(), CV_8UC3, (void*)in.constBits(), in.bytesPerLine());
        cv::cvtColor(mat, out, cv::COLOR_RGB2BGR);
        return true;
    }
    else if (in.format() == QImage::Format_Grayscale8)
    {
        out = cv::Mat(in.height(), in.width(), CV_8UC1, (void*)in.constBits(), in.bytesPerLine());
        return true;
    }
    return false;
}

bool Mat2QImage(const cv::Mat& in,QImage& out)
{
    if (in.empty())
    {
        return false;
    }
    if (in.type() == CV_8UC3)
    {
        out = QImage((const uchar*)in.data, in.cols, in.rows, in.step, QImage::Format_BGR888);
        return true;
    }
    else if (in.type() == CV_8UC1 || in.type() == CV_8U)
    {
        out = QImage((const uchar*)in.data, in.cols, in.rows, in.step, QImage::Format_Grayscale8);
        return true;
    }
    return false;
}

【QImage与Halconcpp::HObject】

bool QImage2HObject(const QImage &in, HalconCpp::HObject &out)
{
    if (in.isNull())
    {
        return false;
    }
    if (in.format() == QImage::Format_RGB32 || in.format() == QImage::Format_ARGB32 || in.format() == QImage::Format_ARGB32_Premultiplied)
    {
        HalconCpp::GenImageInterleaved(&out,(Hlong)in.constBits(), "bgrx", in.width(), in.height(), 0, "byte", in.width(), in.height(), 0, 0, 8, 0);
        return true;
    }
    else if (in.format() == QImage::Format_RGB888)    
    {
        if (in.width() %4!=0)//宽非4的倍数,图像会显示错位
        {
            HalconCpp::GenImageInterleaved(&out, (Hlong)in.constBits(), "rgb", in.width()+(4-in.width()%4), in.height(), 0, "byte", in.width(), in.height(), 0, 0, 8, 0);
        }
        else
        {
            HalconCpp::GenImageInterleaved(&out, (Hlong)in.constBits(), "rgb", in.width(), in.height(), 0, "byte", in.width(), in.height(), 0, 0, 8, 0);            
        }
        return true;
    }
    else if (in.format() == QImage::Format_Grayscale8 || in.format() == QImage::Format_Indexed8)
    {
        if (in.width() % 4 != 0)//宽非4的倍数,图像会显示错位
        {
            HalconCpp::HObject hImg;
            HalconCpp::GenImage1(&hImg, "byte", in.width() + (4 - in.width() % 4), in.height(), (Hlong)in.constBits());
            HalconCpp::CropPart(hImg, &out, 0, 0, in.width(), in.height());
        }
        else
        {
            HalconCpp::GenImage1(&out, "byte", in.width(), in.height(), (Hlong)in.constBits());
        }
        return true;
    }
    return false;
}

Halconcpp::HObject转QImage,非常慢,很多秒~~~。该方法附到最后,不建议用。可以尝试【彩色图更好的方法——Halconcpp::HObject转其他格式】部分提供的方法。

Halconcpp::HImage转QImage,很快。参考Halcon HImage 与 Qt QImage 的相互转换_himage转qimage-CSDN博客

bool HImage2QImage(const HalconCpp::HImage &in, QImage &out)
{
    Hlong width;
    Hlong height;
    in.GetImageSize(&width, &height);

    HalconCpp::HTuple channels = in.CountChannels();
    HalconCpp::HTuple type = in.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_RGB32;
        break;
    default:
        return false;
    }

    if (out.width() != width || out.height() != height || out.format() != format)
    {
        out = QImage(static_cast<int>(width),
            static_cast<int>(height),
            format);
    }
    HalconCpp::HString Type;
    if (channels[0].I() == 1)
    {
        unsigned char * pSrc = reinterpret_cast<unsigned char *>(in.GetImagePointer1(&Type, &width, &height));
        memcpy(out.bits(), pSrc, static_cast<size_t>(width) * static_cast<size_t>(height));
        return true;
    }
    else if (channels[0].I() == 3)
    {
        uchar *R, *G, *B;
        in.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*>(out.scanLine(row));
            for (int col = 0; col < width; col++)
            {
                line[col] = qRgb(*R++, *G++, *B++);
            }
        }
        return true;
    }
    return false;
}

 

【cv::Mat与Halconcpp::HObject】

bool HObject2Mat(const HalconCpp::HObject &in, cv::Mat &out)
{
    //判断是否为空
    HalconCpp::HObject ho_Null;
    HalconCpp::GenEmptyObj(&ho_Null);
    HalconCpp::HTuple hv_n;
    HalconCpp::TestEqualObj(in, ho_Null, &hv_n);
    if (hv_n[0].I() == 1)
    {
        return false;
    }
    //转换
    HalconCpp::HTuple width, height, channels;
    HalconCpp::GetImageSize(in, &width, &height);
    HalconCpp::CountChannels(in, &channels);
    HalconCpp::HObject hImg;
    HalconCpp::ConvertImageType(in, &hImg, "byte");
    int H = height[0].I();
    int W = width[0].I();

    if (channels[0].I() == 3)
    {
        HalconCpp::HTuple ptrR, ptrG, ptrB;
        GetImagePointer3(hImg, &ptrR, &ptrG, &ptrB, NULL, NULL, NULL);        
        std::vector<cv::Mat> vecM(3);
        vecM[2].create(H, W, CV_8UC1);
        vecM[1].create(H, W, CV_8UC1);
        vecM[0].create(H, W, CV_8UC1);
        uchar *pR = (uchar *)ptrR[0].L();
        uchar *pG = (uchar *)ptrG[0].L();
        uchar *pB = (uchar *)ptrB[0].L();
        memcpy(vecM[2].data, pR, W * H); //Mat以bgr格式输出
        memcpy(vecM[1].data, pG, W * H);
        memcpy(vecM[0].data, pB, W * H);
        out.create(H, W, CV_8UC3); //三通道
        cv::merge(vecM, out);
        return true;
    }
    else if (channels[0].I() == 1)
    {
        HalconCpp::HTuple ptr;
        GetImagePointer1(hImg, &ptr, NULL, NULL, NULL);
        W = (int)width;
        H = (int)height;
        uchar* p = (uchar*)(ptr[0].L());    //必须是L()
        out = cv::Mat(H, W, CV_8UC1, p);
        return true;
    }
    return false;
}

bool Mat2HObject(const cv::Mat &in, HalconCpp::HObject &out)
{
    //判断是否为空
    if (in.empty())
    {
        return false;
    }
    //转换
    int W = in.cols;
    int H = in.rows;
    if (in.type() == CV_8UC3)
    {
        std::vector<cv::Mat> vecM(3);
        cv::split(in, vecM);
        uchar* pR = vecM[2].data;
        uchar* pG = vecM[1].data;
        uchar* pB = vecM[0].data;
        for (int i = 0; i < H; i++)
        {
            memcpy(pR + W*i, vecM[2].data + vecM[2].step*i, W);
            memcpy(pG + W*i, vecM[1].data + vecM[1].step*i, W);
            memcpy(pB + W*i, vecM[0].data + vecM[0].step*i, W);
        }
        HalconCpp::GenImage3(&out, "byte", W, H, (Hlong)pR, (Hlong)pG, (Hlong)pB);
        return true;
    }
    else if (in.type() == CV_8UC1 || in.type() == CV_8U)
    {
        uchar* p = in.data;
        for (int i = 0; i < H; i++)
        {
            memcpy(p + W*i, in.data + in.step*i, W);
        }            
        HalconCpp::GenImage1(&out, "byte", W, H, (Hlong)p);
        return true;
    }
    return false;
}

【彩色图更好的方法——Halconcpp::HObject转其他格式】

如果是Halcon18版本及以上,对彩色图像可以使用交错图方式,将Halconcpp::HObject的通道变成交错格式。

cv::Mat、QImage都是交错格式。

如24位位图,halcon中读取到的图像是按R,G,B三个通道进行单独存储排列。交错格式是rgbrgb等交替顺序。

//转交错图。此函数与GenImageInterleaved()相反,它是从交错图转HObject
void InterleaveChannels(const HObject& MultichannelImage, HObject* InterleavedImage, const HTuple& PixelFormat, const HTuple& RowBytes, const HTuple& Alpha)
//获取交错图指针
void GetImagePointer1(const HObject& Image, HTuple* Pointer, HTuple* Type, HTuple* Width, HTuple* Height)
//之后给其他格式即可

具体参考 Hobject 数据转换成Byte,传入DLL 转换成Mat数据供opencv调用_lisonglingaaa的博客-CSDN博客

【测试用例】

bool QImage2Mat(const QImage& in, cv::Mat& out);
bool QImage2HObject(const QImage &from, HalconCpp::HObject &to);
bool Mat2QImage(const cv::Mat& in, QImage& out);
bool Mat2HObject(const cv::Mat& in, HalconCpp::HObject& out);
bool HObject2QImage(const HalconCpp::HObject &in, QImage &out);
bool HObject2Mat(const HalconCpp::HObject &in, cv::Mat &out);

QtWidgetsApplication2::QtWidgetsApplication2(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

    QImage qImg;
    cv::Mat mImg;
    HalconCpp::HObject hImg;
    
    //【测试】QImage转其他
    qImg.load("D:/test79.jpg"); //默认QImage::Format_RGB32
    //qImg = qImg.convertToFormat(QImage::Format_RGB888);
    //qImg=qImg.convertToFormat(QImage::Format_Grayscale8);
    QImage2Mat(qImg, mImg);
    //QImage2HObject(qImg, hImg);
    
    /*
    //【测试】cv::Mat转其他
    mImg = cv::imread("D:/test79.jpg", 1);
    //mImg = cv::imread("D:/test79.jpg", 0);        
    Mat2QImage(mImg, qImg);
    //Mat2HObject(mImg, hImg);    
    */
    /*
    //【测试】HalconCpp::HObject转其他
    HalconCpp::ReadImage(&hImg, "D:/test79.jpg");
    //HalconCpp::Rgb1ToGray(hImg, &hImg);    
    HObject2QImage(hImg, qImg);
    //HObject2Mat(hImg, mImg);    
    */

    //展示Mat图
    cv::imshow("", mImg);
    /*
    //展示Halcon图
    HalconCpp::HTuple WinHandle;
    HalconCpp::OpenWindow(0, 0, 200, 200, 0, "visible", "", &WinHandle);
    HalconCpp::DispObj(hImg, WinHandle);
    */
    //展示QImage
    ui.label->setPixmap(QPixmap::fromImage(qImg));
}

 

【参考】

QImage/cv::Mat/HObject的图像格式互相转换,4字节对齐_qimage转hobject-CSDN博客

 

不建议使用,非常慢。

bool HObject2QImage(const HalconCpp::HObject &in, QImage &out)
{
    //判断是否为空
    HalconCpp::HObject ho_Null;
    HalconCpp::GenEmptyObj(&ho_Null);
    HalconCpp::HTuple hv_n;
    HalconCpp::TestEqualObj(in, ho_Null, &hv_n);
    if (hv_n[0].I()==1)
    {
        return false;
    }
    //转换
    HalconCpp::HTuple width,height,channels;
    HalconCpp::GetImageSize(in,&width, &height);
    HalconCpp::CountChannels(in, &channels);
    HalconCpp::HObject hImg;
    HalconCpp::ConvertImageType(in, &hImg, "byte");

    if (channels[0].I()==3)
    {
        out = QImage(width, height, QImage::Format_RGB888);
        HalconCpp::HTuple ptrR, ptrG, ptrB;
        HalconCpp::GetImagePointer3(hImg, &ptrR, &ptrG, &ptrB, NULL, NULL, NULL);
        uchar *dataR, *dataG, *dataB;
        dataR = (uchar*)(ptrR[0].L());
        dataG = (uchar*)(ptrG[0].L());
        dataB = (uchar*)(ptrB[0].L());
        int bytesperline = (width * 8 * 3/8 + 3) / 4 * 4;//宽度4字节对齐
        int lineheadid, pixid;
        for (int i = 0; i < height; i++) 
        {
            lineheadid = bytesperline * i;
            for (int j = 0; j < width; j++) 
            {
                pixid = lineheadid + j * 3;
                out.bits()[pixid + 0] = dataR[width * i + j];
                out.bits()[pixid + 1] = dataG[width * i + j];
                out.bits()[pixid + 2] = dataB[width * i + j];
            }
        }
        return true;
    }
    else if (channels[0].I()==1)
    {        
        HalconCpp::HTuple pSrc;
        HalconCpp::GetImagePointer1(hImg, &pSrc, NULL, NULL, NULL);
        uchar *data = (uchar*)(pSrc[0].L());
        if (width % 4 == 0)
        {
            out=QImage(data,width, height, QImage::Format_Grayscale8);
        }
        else
        {
            out = QImage(width, height, QImage::Format_Grayscale8);
            int bytesperline = (width * 8 * 1 / 8 + 3) / 4 * 4;    //宽度4字节对齐
            int lineheadid, pixid;
            for (int i = 0; i < height; i++)
            {
                lineheadid = bytesperline * i;
                for (int j = 0; j < width; j++)
                {
                    pixid = lineheadid + j;
                    out.bits()[pixid] = data[width*i + j];
                }
            }
        }        
        return true;
    }
    return false;
}

 

posted @ 2023-11-11 22:23  夕西行  阅读(488)  评论(0编辑  收藏  举报