Related
To convert YV12 to BGRA, I do like below:
cv::Mat yuv(height * 3/2, width, CV_8UC1, src);
cv::Mat dst;
cv::cvtColor(yuv, dst, CV_YUV2BGRA_YV12);
That code will work when the "src" is a contiguous buffer.
In case I have a YV12 buffer with its Y, U, V components stored in 3 separate planes and they are not contiguous, how can I do the conversion using cvtColor()?
For using cv::cvtColor, you have to copy the separate components into continuous image buffer as noted by Christoph Rackwitz.
I don't think copying the components is trivial, and I think you deserve an answer.
The continuous YV12 image applies the following structure:
Y (cols x rows) - Top Image.
V (cols/2 x rows/2) - Below Y
U (cols/2 x rows/2) - Below V
We may allocate a continuous buffer, and copy the components to the allocated buffer.
Since OpenCV "hides" the buffer in the data member, I thought it would be simpler to allocate the buffer using new, and create a cv:Mat that "wraps" the buffer.
Example:
unsigned char *YV12 = new unsigned char[cols*rows*3/2]; //Allocate continuous memory buffer for storing YV12 in as single buffer.
cv::Mat inYV12 = cv::Mat(rows*3/2, cols, CV_8UC1, YV12); //cv::Mat wrapper of YV12 buffer.
Defining cv:Mat wrappers to Y, U and V, sharing the continuous buffer:
cv::Mat inY0 = cv::Mat(rows, cols, CV_8UC1, YV12); //Copy Y color channel.
cv::Mat inU0 = cv::Mat(rows/2, cols/2, CV_8UC1, YV12 + cols*rows); //U color channel (after Y).
cv::Mat inV0 = cv::Mat(rows/2, cols/2, CV_8UC1, YV12 + cols*rows + cols*rows/4); //V color channel (after U).
Copy the three separate images to the continuous image:
inY.copyTo(inY0);
inU.copyTo(inU0);
inV.copyTo(inV0);
Complete code sample:
#include "opencv2/opencv.hpp"
int main()
{
const int cols = 192;
const int rows = 108;
//Read Y, U and V into 3 separate buffers for testing.
////////////////////////////////////////////////////////////////////////////
unsigned char *Y = new unsigned char[cols*rows];
unsigned char *U = new unsigned char[cols*rows / 4];
unsigned char *V = new unsigned char[cols*rows / 4];
FILE *f;
f = fopen("test.yv12", "rb"); //Note: In Visual Studio, you may have to use fopen_s or define _CRT_SECURE_NO_WARNINGS
fread(Y, 1, cols*rows, f); //Read Y
fread(U, 1, cols*rows/4, f); //Read U
fread(V, 1, cols*rows/4, f); //Read V
fclose(f);
//Define cv::Mat "wrappers".
cv::Mat inY = cv::Mat(rows, cols, CV_8UC1, Y);
cv::Mat inU = cv::Mat(rows/2, cols/2, CV_8UC1, U);
cv::Mat inV = cv::Mat(rows/2, cols/2, CV_8UC1, V);
////////////////////////////////////////////////////////////////////////////
unsigned char *YV12 = new unsigned char[cols*rows*3/2]; //Allocate continuous memory buffer for storing YV12 in as single buffer.
cv::Mat inYV12 = cv::Mat(rows*3/2, cols, CV_8UC1, YV12); //cv::Mat wrapper of YV12 buffer.
//Define cv::Mat "wrappers" to the continuous memory buffer.
cv::Mat inY0 = cv::Mat(rows, cols, CV_8UC1, YV12); //Copy Y color channel.
cv::Mat inU0 = cv::Mat(rows/2, cols/2, CV_8UC1, YV12 + cols*rows); //U color channel (after Y).
cv::Mat inV0 = cv::Mat(rows/2, cols/2, CV_8UC1, YV12 + cols*rows + cols*rows/4); //V color channel (after U).
//Copy the three separate images to the continuous image.
inY.copyTo(inY0);
inU.copyTo(inU0);
inV.copyTo(inV0);
//Convert YV12 to BGRA:
cv::Mat dstBGRA;
//cv::cvtColor(inYV12, dstBGRA, cv::CV_YUV2BGRA_YV12);
cv::cvtColor(inYV12, dstBGRA, cv::COLOR_YUV2BGRA_YV12);
//Release memory
delete[] Y;
delete[] U;
delete[] V;
delete[] YV12;
cv::imwrite("dstBGRA.png", dstBGRA); //Save output for testing
//Show result (for testing):
cv::imshow("dstBGRA", dstBGRA);
cv::waitKey();
cv::destroyAllWindows();
return 0;
}
Input (converted to continuous grayscale image):
Output:
I have float x, float y, float z values of an image. I want to construct a 16 bit png depth image by copying the z values. The image I am getting as a result has some invalid points. Below is my code.
uint16_t* depthValues = new uint16_t[size];
auto sampleVector(DepthPoints);
for (unsigned int i = 0; i < sampleVector.size(); i++)
{
depthValues[i] = (sampleVector.at(i).z) * 65536;
}
Mat newDepthImage = cv::Mat(var.height, var.width, CV_16UC1,depthValues);
imwrite(Location, CImage);
Can someone tell me, if I can copy the float values into an unsigned char array to create the image?
Is that why my image has invalid points?
auto sampleVector(DepthPoints);
const int size = sampleVector.size();
float* depthValues = new float[size];
for (unsigned int i = 0; i < sampleVector.size(); i++)
{
depthValues[i] = (sampleVector.at(i).z);
}
Mat depthImageOne, depthImageTwo;
Mat depthImageNew = cv::Mat(var.height, var.width, CV_32FC1,depthValues);
normalize(newDepthImageNew, depthImageOne, 1, 0, NORM_MINMAX, CV_32FC1);
depthImageOne.convertTo(depthImageTwo, CV_16UC1, 65536.0,0.0);
imwrite("path", depthImageTwo);
Normalization might cause lose of depth information. I have used normalization for visualization of the images. To preserve the depth information, I used the below code.
Mat depthImageNew = cv::Mat(var.height, var.width, CV_32FC1,depthValues);
depthImageOne.convertTo(depthImageTwo, CV_16UC1, 1000.0,0.0);
I am recently working with OpenCV and C++ for a project and I found a weird thing:
when I try to access single pixal value in IplImage and assign other value, it could run properly but the result is that it can only operate part of the whole image.
relevant code:
IplImage* output_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage* current_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
while((current_frame = cvQueryFrame(video_gray)) != 0 )
{
for (int row=0;row<height;row++)
{
uchar* ptr_current_frame = (uchar *)(current_frame->imageData+current_frame->widthStep*row);
uchar* ptr_output_frame = (uchar *)(output_frame->imageData+output_frame->widthStep*row);
for (int cols=0;cols<width;cols++)
{
//other codes...
ptr_output_frame[cols]=ptr_current_frame[cols];
}
}
}
The result is the left part of the image was copied to the output_frame. And when I run the following code:
IplImage* output_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage* current_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
while((current_frame = cvQueryFrame(video_gray)) != 0 )
{
for (int row=0;row<height;row++)
{
uchar* ptr_current_frame = (uchar *)current_frame->imageData+current_frame->width*row;
uchar* ptr_output_frame = (uchar *)output_frame->imageData+output_frame->width*row;
for (int cols=0;cols<width;cols++)
{
//other codes...
ptr_output_frame[cols]=ptr_current_frame[cols];
}
}
}
I got the upside part of the image in the output_frame.
I cannot get the the whole image copy in output_frame either way. Could anybody help me with this? Thanks a lot!
[updates] 05/16/2015
I have found out that output_frame->widthStep is different from current->widthStep after current_frame is executed in the following code:
current_frame = cvQueryFrame(video_gray);
It makes sense why the first part of the code wouldnt work. But I still dont know why the second part of the code doesnt work.
I now have my whole codes updated here and hope you can help me make this right. I am really appreciate your help.
CvCapture* video_gray = cvCreateFileCapture("test_gray.avi");
const double fps = cvGetCaptureProperty(video_gray, CV_CAP_PROP_FPS);
const int width = (int)cvGetCaptureProperty(video_gray, CV_CAP_PROP_FRAME_WIDTH);
const int height = (int)cvGetCaptureProperty(video_gray, CV_CAP_PROP_FRAME_HEIGHT);
const CvSize size = cvSize(width, height);
IplImage* current_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage* output_frame=cvCreateImage(size, IPL_DEPTH_8U, 1);
int flag = 0;
cvNamedWindow("test",1);
cvNamedWindow("test2",1);
while((current_frame = cvQueryFrame(video_gray)) != 0 )
{
cout<<flag++<<endl;
if(flag<500) continue;
for (int row=0;row<height;row++)
{
uchar* ptr_current_frame = (uchar *)(current_frame->imageData+current_frame->widthStep*row);
uchar* ptr_output_frame = (uchar *)(output_frame->imageData+output_frame->widthStep*row);
for (int cols=0;cols<width;cols++)
{
ptr_output_frame[cols]= ptr_current_frame[cols];
}
}
cvShowImage("test",output_frame);
cvShowImage("test2",current_frame);
cvWaitKey(10);
}
You don't handle the number of channels...
please try
IplImage* output_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage* current_frame = cvCreateImage(size, IPL_DEPTH_8U, 1);
while((current_frame = cvQueryFrame(video_gray)) != 0 )
{
for (int row=0;row<height;row++)
{
uchar* ptr_current_frame = (uchar *)(current_frame->imageData+current_frame->widthStep*row);
uchar* ptr_output_frame = (uchar *)(output_frame->imageData+output_frame->widthStep*row);
for (int cols=0;cols<width;cols++)
{
//other codes...
// here insead take care of the channels too. Only write ne channel to the output
ptr_output_frame[cols]=ptr_current_frame[cols*current_frame->nChannels];
}
}
}
but you should really try to switch to C++ API: try something like this:
cv::VideoCapture video = cv::VideoCapture("test_gray.avi");
int width = ...
int height = ...
cv::Mat image_captured;
cv::Mat image_gray; // if wanted
cv::Mat image_output1 = cv::Mat(height, width, CV_8UC1);
cv::Mat image_output2 = cv::Mat(height, width, CV_8UC1);
while(video.read(image_captured))
{
// if you want to convert the image to
if(image_captured.channels() > 1)
{
cv::cvtColor(image_captured, image_gray, CV_BGR2GRAY);
}
else
{
image_gray = image_captured;
}
[...]
for(int j=0; j<height; ++j)
{
for(int i=0; i<width; ++i)
{
image_output1.at<uchar>(j,i) = image_gray.at<uchar>(j,i); // read from single channel image
image_output1.at<uchar>(j,i) = image_captured.at<cv::Vec3b>(j,i)[0]; // only read the first channel of a multi-channel image
}
}
}
cv::imshow("output1", image_output1);
cv::imshow("output2", image_output2);
cv::imshow("input", image_capured);
cv::waitKey(0);
}
much easier to use, and you can still make it more efficient by using row-pointers in each iteration etc
I am new Here...
I need help in the following code..
I am a beginner in coding.
I am trying to convert a color image in .bmp format to gray scale image using CUDA and openCV
Can anyone find the error or bug or the mistake which i have done in my code and help me.
I am also attaching the input i used and the output(screenshot - Image name output) which i got from the code(image in my code).. In the screenshot the image in the background is the original image. You can use whatever image you want.
#include "cuda_runtime.h"
#include <iostream>
#include <ctime>
#include <stdio.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
__global__ void convertImage(int width, int height, int nchannels, int step, uchar *d_data, int nchannels2, int step2, uchar *d_data2)
{
int i, j, r, g, b, byte, z = 0;
for(i=0; i<height; i++)
for(j=0; j<width; j++)
{
r = d_data[i*step + j*nchannels + 0];
g = d_data[i*step + j*nchannels + 1];
b = d_data[i*step + j*nchannels + 2];
byte = (r+g+b)/3;
d_data2[i*step2 + j*nchannels2 + 0] = byte;
d_data2[i*step2 + j*nchannels2 + 1] = byte;
d_data2[i*step2 + j*nchannels2 + 2] = byte;
}
}
int main()
{
IplImage *img = cvLoadImage("D://1.bmp", CV_LOAD_IMAGE_COLOR);
int width = img->width;
int height = img->height;
int nchannels = img->nChannels;
int step = img->widthStep;
cout<<"Image 1 : "<<width<<"\t"<<height<<"\t"<<nchannels<<"\t"<<step<<endl;
uchar *data = (uchar*)img->imageData;
uchar *d_data;
int size = sizeof(data);
cudaMalloc(&d_data, size);
cudaMemcpy(d_data, data, size, cudaMemcpyHostToDevice);
IplImage *img2 = cvCreateImage(cvSize(img->height, img->width), IPL_DEPTH_8U, 1);
int width2 = img2->width;
int height2 = img2->height;
int nchannels2 = img2->nChannels;
int step2 = img2->widthStep;
cout<<"Image 2 : "<<width2<<"\t"<<height2<<"\t"<<nchannels2<<"\t"<<step2<<endl;
uchar *data2 = (uchar*)img2->imageData;
uchar *d_data2;
int size2 = sizeof(data2);
cudaMalloc(&d_data2, size2);
long long i;
uchar *temp = data;
convertImage<<<1,1>>>(width, height, nchannels, step, d_data, nchannels2, step2, d_data2);
cudaMemcpy(data2, d_data2, sizeof(data2), cudaMemcpyHostToDevice);
cvNamedWindow("Imagecolor");
cvShowImage("Imagecolor", img);
cvNamedWindow("Gray");
cvShowImage("Gray", img2);
cvWaitKey();
return 0;
}
There a lot of problems with the code!
1: The size of device memory being allocated:
int size = sizeof(data);
sizeof(data) will return the size of pointer on the current platform. Which is most likely 4 or 8 bytes. So you are allocating a maximum of 8 bytes of device memory and copying whole image into it!!!
The actual number of bytes of the image should be calculated as:
int size = step * height;
and
int size2 = step2 * height2;
2: Direction flag and data size of second cudaMemcpy call:
As pointed out in another answer...
cudaMemcpy(data2, d_data2, sizeof(data2), cudaMemcpyHostToDevice);
should be
cudaMemcpy(data2, d_data2, size2, cudaMemcpyDeviceToHost);
3: Type of output image
In the kernel, 3 values are being written to the output in each iteration, while the output image has a single channel. Either write only one value to the output, or create the output image with 3 channels.
The cvSize function should be called as cvSize(width,height) instead of cvSize(height, width) when creating img2.
Also, the kernel is being launched with 1 thread only, and most likely, it would trigger an execution timeout error if the image size is large.
One thing that I can point out by looking at your code is that in the second call to cudaMemcpy (after convertImage) you should pass in 'cudaMemcpyDeviceToHost' as the flag and NOT 'cudaMemcpyHostToDevice'. You want to get the converted image back from the card.
I am not sure if this will be all that it will take to get your program working.
I am wondering how would I convert the OpenCV C++ standard cv::Mat type to QImage. I have been searching around, but have no luck. I have found some code that converts the IPlimage to QImage, but that is not what I want. Thanks.
Michal Kottman's answer is valid and give expected result for some images but it'll fail on some cases. Here is a solution i found to that problem.
QImage imgIn= QImage((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
Difference is adding img.step part. qt won't complain without it but some images won't show properly without it. Hope this will help.
To convert from cv::Mat to QImage, you could try to use the QImage(uchar * data, int width, int height, Format format) constructor as follows (mat is a cv::Mat) :
QImage img((uchar*)mat.data, mat.cols, mat.rows, QImage::Format_RGB32);
It is more efficient than manually converting the pixels to the QImage, but you have to keep the original cv::Mat image in memory. It can be easily converted to a QPixmap and displayed using a QLabel:
QPixmap pixmap = QPixmap::fromImage(img);
myLabel.setPixmap(pixmap);
Update
Because OpenCV uses BGR order by default, you should first use cvtColor(src, dst, CV_BGR2RGB) to get an image layout that Qt understands.
Update 2:
If the image you are trying to show has nonstandard stride (when it is non-continuous, submatrix), the image may appeard distorted. In this case, it is better to explicitly specify the stride using cv::Mat::step1():
QImage img((uchar*)mat.data, mat.cols, mat.rows, mat.step1(), QImage::Format_RGB32);
Here is code for 24bit RGB and grayscale floating point. Easily adjustable for other types. It is as efficient as it gets.
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 loads images into a Mat in Blue-Green-Red (BGR) format by default, while QImage expects RGB. This means that if you convert a Mat to QImage, the blue and red channels will be swapped. To fix this, before constructing the QImage, you need to change the BRG format of your Mat to RGB, via the cvtColor method using argument CV_BGR2RGB, like so:
Mat mat = imread("path/to/image.jpg");
cvtColor(mat, mat, CV_BGR2RGB);
QImage image(mat.data, mat.cols, mat.rows, QImage::Format_RGB888);
Alternatively, use rgbSwapped() on the 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);
This is what worked for me. I modified Michal Kottman's code above.
I have the same problem as you too, so I develop four functions to alleviate my pain, they are
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);
These functions can handle the images with 1, 3, 4 channels, every pixel must occupy one byte only(CV_8U->Format_Indexed8, CV_8UC3->QImage::Format_RGB888, CV_8UC4->QImage::Format_ARGB32), I do not deal with other types yet(QImage::Format_RGB16, QImage::Format_RGB666 and so on). The codes are located
at github.
The key concepts of **transform mat to Qimage ** are
/**
* #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);
}
};
The key concepts of transform cv::Mat to Qimage are
/**
* #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());
}
};
If would be good if some one can extend these functions and make them support more types, please inform me if there are any bugs.
cv::Mat has a conversion operator to IplImage, so if you have something that converts the IplImage to a QImage, just use that (or make the - probably minor - adjustments to take the cv::Mat directly, the memory layout is the same, it's "just" the header that is different.)
This post shows how to convert a QImage to OpenCV's IplImage and vise-versa.
After that, if you need help to convert between IplImage* to 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!
It's a hack, but will get the job done.
Use the static function convert16uc1 for the depth image:
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);
}
This did the trick for me. It's a little dodgy, has terrible performance (as pointed out in the comments), but works with all color formats I have thrown at it so far, and it is also very simple to do.
The procedure is as follows:
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");
Done!
If you, say, want to display it in a QLabel, then continue with:
// Set QImage as content of MyImageQLabel
ui-> MyImageQLabel->setPixmap(QPixmap::fromImage(img, Qt::AutoColor));
I personally use this for a simple image editor.