格式转换:相机帧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; }