Michal Kottman的回答对于某些图像是有效的并且可以给出期望的结果,但在某些情况下会失败。以下是我发现的解决方法。
QImage imgIn= QImage((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
区别在于添加img.step部分。如果没有这个部分,qt不会抱怨,但某些图片可能无法正确显示。希望这可以帮助。
要将cv::Mat
转换为QImage
,您可以尝试使用如下所示的QImage(uchar * data, int width, int height, Format format)
构造函数(其中mat
是一个cv::Mat
):
QImage img((uchar*)mat.data, mat.cols, mat.rows, QImage::Format_RGB32);
使用该方法比手动将像素转换为QImage
更有效率,但您需要在内存中保留原始的cv::Mat
图像。可以轻松将其转换为QPixmap
并使用QLabel
进行显示:
QPixmap pixmap = QPixmap::fromImage(img);
myLabel.setPixmap(pixmap);
更新
由于OpenCV默认使用BGR顺序,因此您应该先使用cvtColor(src, dst, CV_BGR2RGB)
来获取Qt可以理解的图像布局。
更新2:
如果您尝试显示的图像具有非标准的步幅(当它是非连续、子矩阵时),则该图像可能会出现扭曲。在这种情况下,最好使用cv::Mat::step1()
显式地指定步幅:
QImage img((uchar*)mat.data, mat.cols, mat.rows, mat.step1(), QImage::Format_RGB32);
imread
加载或转换为适当类型。 - Michal Kottman这里是24位RGB和灰度浮点数的代码。可轻松调整为其他类型。它尽可能高效。
QImage Mat2QImage(const cv::Mat3b &src) {
QImage dest(src.cols, src.rows, QImage::Format_ARGB32);
for (int y = 0; y < src.rows; ++y) {
const cv::Vec3b *srcrow = src[y];
QRgb *destrow = (QRgb*)dest.scanLine(y);
for (int x = 0; x < src.cols; ++x) {
destrow[x] = qRgba(srcrow[x][2], srcrow[x][1], srcrow[x][0], 255);
}
}
return dest;
}
QImage Mat2QImage(const cv::Mat_<double> &src)
{
double scale = 255.0;
QImage dest(src.cols, src.rows, QImage::Format_ARGB32);
for (int y = 0; y < src.rows; ++y) {
const double *srcrow = src[y];
QRgb *destrow = (QRgb*)dest.scanLine(y);
for (int x = 0; x < src.cols; ++x) {
unsigned int color = srcrow[x] * scale;
destrow[x] = qRgba(color, color, color, 255);
}
}
return dest;
}
默认情况下,OpenCV将图像加载到Mat
中的蓝绿红(BGR)格式中,而QImage
则期望RGB格式。这意味着如果您将一个Mat
转换为QImage
,则蓝色和红色通道将被交换。为了解决这个问题,在构造QImage
之前,您需要通过cvtColor
方法使用参数CV_BGR2RGB
来将Mat
的BRG格式更改为RGB格式,如下所示:
Mat mat = imread("path/to/image.jpg");
cvtColor(mat, mat, CV_BGR2RGB);
QImage image(mat.data, mat.cols, mat.rows, QImage::Format_RGB888);
或者在 QImage
上使用 rgbSwapped()
QImage image = QImage(mat.data, mat.cols, mat.rows, QImage::Format_RGB888).rgbSwapped());
Mat opencv_image = imread("fruits.jpg", CV_LOAD_IMAGE_COLOR);
Mat dest;
cvtColor(opencv_image, dest,CV_BGR2RGB);
QImage image((uchar*)dest.data, dest.cols, dest.rows,QImage::Format_RGB888);
以下是我成功的做法。我修改了 Michal Kottman 上面提供的代码。
我和你一样也有同样的问题,所以我开发了四个函数来缓解我的痛苦,它们分别是
QImage mat_to_qimage_cpy(cv::Mat const &mat, bool swap = true);
QImage mat_to_qimage_ref(cv::Mat &mat, bool swap = true);
cv::Mat qimage_to_mat_cpy(QImage const &img, bool swap = true);
cv::Mat qimage_to_mat_ref(QImage &img, bool swap = true);
/**
* @brief copy QImage into cv::Mat
*/
struct mat_to_qimage_cpy_policy
{
static QImage start(cv::Mat const &mat, QImage::Format format)
{
//The fourth parameters--mat.step is crucial, because
//opencv may do padding on every row, you need to tell
//the qimage how many bytes per row
//The last thing is if you want to copy the buffer of cv::Mat
//to the qimage, you need to call copy(), else the qimage
//will share the buffer of cv::Mat
return QImage(mat.data, mat.cols, mat.rows, mat.step, format).copy();
}
};
struct mat_to_qimage_ref_policy
{
static QImage start(cv::Mat &mat, QImage::Format format)
{
//every thing are same as copy policy, but this one share
//the buffer of cv::Mat but not copy
return QImage(mat.data, mat.cols, mat.rows, mat.step, format);
}
};
将 cv::Mat 转换为 Qimage 的关键概念包括:
/**
* @brief copy QImage into cv::Mat
*/
struct qimage_to_mat_cpy_policy
{
static cv::Mat start(QImage const &img, int format)
{
//same as convert mat to qimage, the fifth parameter bytesPerLine()
//indicate how many bytes per row
//If you want to copy the data you need to call clone(), else QImage
//cv::Mat will share the buffer
return cv::Mat(img.height(), img.width(), format,
const_cast<uchar*>(img.bits()), img.bytesPerLine()).clone();
}
};
/**
* @brief make Qimage and cv::Mat share the same buffer, the resource
* of the cv::Mat must not deleted before the QImage finish
* the jobs.
*/
struct qimage_to_mat_ref_policy
{
static cv::Mat start(QImage &img, int format)
{
//same as copy policy, but this one will share the buffer
return cv::Mat(img.height(), img.width(), format,
img.bits(), img.bytesPerLine());
}
};
QImage
转换为OpenCV的 IplImage
,反之亦然。 IplImage *
和 cv :: Mat
之间进行转换:// Assume data is stored by:
// IplImage* image;
cv::Mat mat(image, true); // Copies the data from image
cv::Mat mat(image, false); // Doesn't copy the data!
cv::Mat有一个转换运算符可以将其转换为IplImage,因此如果你有一些将IplImage转换为QImage的东西,只需使用它(或进行 - 可能是次要的 - 调整以直接使用cv::Mat,内存布局相同,只是头文件不同)。
QPixmap Viewer::convert16uc1(const cv::Mat& source)
{
quint16* pSource = (quint16*) source.data;
int pixelCounts = source.cols * source.rows;
QImage dest(source.cols, source.rows, QImage::Format_RGB32);
char* pDest = (char*) dest.bits();
for (int i = 0; i < pixelCounts; i++)
{
quint8 value = (quint8) ((*(pSource)) >> 8);
*(pDest++) = value; // B
*(pDest++) = value; // G
*(pDest++) = value; // R
*(pDest++) = 0; // Alpha
pSource++;
}
return QPixmap::fromImage(dest);
}
QPixmap Viewer::convert8uc3(const cv::Mat& source)
{
quint8* pSource = source.data;
int pixelCounts = source.cols * source.rows;
QImage dest(source.cols, source.rows, QImage::Format_RGB32);
char* pDest = (char*) dest.bits();
for (int i = 0; i < pixelCounts; i++)
{
*(pDest++) = *(pSource+2); // B
*(pDest++) = *(pSource+1); // G
*(pDest++) = *(pSource+0); // R
*(pDest++) = 0; // Alpha
pSource+=3;
}
return QPixmap::fromImage(dest);
}
QPixmap Viewer::convert16uc3(const cv::Mat& source)
{
quint16* pSource = (quint16*) source.data;
int pixelCounts = source.cols * source.rows;
QImage dest(source.cols, source.rows, QImage::Format_RGB32);
char* pDest = (char*) dest.bits();
for (int i = 0; i < pixelCounts; i++)
{
*(pDest++) = *(pSource+2); // B
*(pDest++) = *(pSource+1); // G
*(pDest++) = *(pSource+0); // R
*(pDest++) = 0; // Alpha
pSource+=3;
}
return QPixmap::fromImage(dest);
}
cv::Mat mat // defined elsewhere
QImage image(mat.data, mat.cols, mat.rows, QImage::Format_YOUR_FORMAT);
mat.deallocate(); // <-- this 'removes' the pointer from mat
cv::Mat::deallocate
函数,这会导致cv::Mat
在被删除时不会删除底层数据。QImage
保存为类成员或从函数返回它,您通常需要从Mat
复制数据,因为在堆栈结束后,它将删除后面的数组。QImage
的数据(否则QImage
将指向已释放的内存)。
image2Draw_qt = QImage(image2Draw_mat-> data.ptr, image2Draw_mat-> cols,image2Draw_mat-> rows,image2Draw_mat-> step,QImage :: Format_RGB888);
还有(大约在2400行)cvConvertImage(mat,image2Draw_mat,CV_CVTIMG_SWAP_RB);
用于从BGR转换为RGB。 - Nolan Amyimg.step
起到了至关重要的作用。我在进行转换时遇到了一些奇怪的问题,包括生成的图像显示扭曲和颜色错误。经过一些测试,我得到了相同的代码。 - MeloMCR