Мне интересно, как мне преобразовать стандартный тип OpenCV C ++ cv :: Mat в QImage. Я искал вокруг, но безуспешно. Я нашел код, который преобразует IPlimage в QImage, но это не то, что мне нужно. Спасибо.
Как преобразовать OpenCV cv :: Mat в QImage
Ответы (10)
Ответ Михала Коттмана действителен и дает ожидаемый результат для некоторых изображений, но в некоторых случаях он не работает. Вот решение, которое я нашел для этой проблемы.
QImage imgIn= QImage((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
Разница в добавлении части img.step. qt не будет жаловаться без него, но некоторые изображения не будут отображаться без него. Надеюсь, это поможет.
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.
- person Nolan Amy; 07.09.2012
img.step
имеет значение. У меня были странные проблемы с этим преобразованием, в том числе искаженное изображение с неправильными цветами. Немного потестировав, я получил тот же код.
- person MeloMCR; 19.09.2012
Вот код для 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;
}
Чтобы преобразовать 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
или преобразованный в соответствующий тип.
- person Michal Kottman; 18.02.2011
OpenCV по умолчанию загружает изображения в Mat
в формате сине-зелено-красный (BGR), а QImage
ожидает RGB. Это означает, что если вы конвертируете Mat
в QImage
, синий и красный каналы поменяются местами. Чтобы исправить это, перед построением QImage
вам необходимо изменить формат BRG вашего Mat
на RGB с помощью метода cvtColor
с аргументом CV_BGR2RGB
, например:
Mat mat = imread("path/to/image.jpg");
cvtColor(mat, mat, CV_BGR2RGB);
QImage image(mat.data, mat.cols, mat.rows, QImage::Format_RGB888);
Или используйте rgbSwapped()
на QImage
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);
Это то, что у меня сработало. Я изменил приведенный выше код Михала Коттмана.
У меня такая же проблема, как и у вас, поэтому я разрабатываю четыре функции для облегчения боли:
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);
Эти функции могут обрабатывать изображения с 1, 3, 4 каналами, каждый пиксель должен занимать только один байт (CV_8U-> Format_Indexed8, CV_8UC3-> QImage :: Format_RGB888, CV_8UC4 -> QImage :: Format_ARGB32), другими типами пока не занимаюсь (QImage :: Format_RGB16, QImage :: Format_RGB666 и так далее). Коды находятся на github.
Ключевые концепции ** преобразования мата в Qimage **:
/**
* @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 to 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());
}
};
Если было бы хорошо, если бы кто-нибудь мог расширить эти функции и заставить их поддерживать больше типов, пожалуйста, сообщите мне, если есть какие-либо ошибки.
cv :: Mat имеет оператор преобразования в IplImage, поэтому, если у вас есть что-то, что преобразует IplImage в QImage, просто используйте его (или внесите - возможно, незначительные - корректировки, чтобы напрямую использовать cv :: Mat, макет памяти - это то же самое, "просто" заголовок отличается.)
В этом посте показано, как преобразовать QImage
в IplImage
OpenCV и наоборот.
После этого, если вам понадобится помощь для преобразования 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!
Это взлом, но он выполнит свою работу.
Используйте статическую функцию convert16uc1 для изображения глубины:
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 image = //...some image you want to display
// 1. Save the cv::Mat to some temporary file
cv::imwrite("../Images/tmp.jpg",image);
// 2. Load the image you just saved as a QImage
QImage img;
img.load("../Images/tmp.jpg");
Готово!
Если вы, скажем, хотите отобразить его в QLabel, продолжайте:
// Set QImage as content of MyImageQLabel
ui-> MyImageQLabel->setPixmap(QPixmap::fromImage(img, Qt::AutoColor));
Я лично использую это для простого редактора изображений.