I am trying to convert images to a vector of bytes and back again but each image is horrible distorted. I was hoping someone could tell me why.
I have this as my conversion methods.
typedef unsigned char byte;
std::vector<byte> matToBytes(cv::Mat image)
{
int size = image.total() * image.elemSize();
std::vector<byte> img_bytes(size);
img_bytes.assign(image.datastart, image.dataend);
return img_bytes;
}
cv::Mat bytesToMat(vector<byte> bytes,int width,int height)
{
cv::Mat image(height,width,CV_8UC3,bytes.data());
return image;
}
It works but not well, I hope someone can spot why. I am pretty lost!
I was playing around with my code and I got this to work.
cv::Mat bytesToMat(vector<byte> bytes,int width,int height)
{
cv::Mat image = cv::Mat(height,width,CV_8UC3,bytes.data()).clone(); // make a copy
return image;
}
I suppose the .clone() does something importat
Related
I have an application that takes in pangolin::Image format rgbd images. I would like to send in a cv::Mat. How can I convert a cv::Mat to a pangolin::Image?
(pangolin: https://github.com/stevenlovegrove/Pangolin)
Image header:
https://github.com/stevenlovegrove/Pangolin/blob/master/include/pangolin/image/image.h
currently the format is:
pangolin::ManagedImage<unsigned short> firstData(640, 480);
pangolin::Image<unsigned short> firstRaw(firstData.w, firstData.h, firstData.pitch, (unsigned short*)firstData.ptr);
where firstRaw is then sent through the application.
If I now have:
cv::Mat frame = cv::imread(filepath,0);
What is the conversion from frame to firstRaw?
I start like this:
int loadDepthFromMat(cv::Mat filepath, pangolin::Image<unsigned short> & depth)
{
int width = filepath.cols;
int height = filepath.rows;
pangolin::ManagedImage<unsigned short> depthRaw(width, height);
pangolin::Image<unsigned short> depthRaw16((unsigned short*)depthRaw.ptr, depthRaw.w, depthRaw.h, depthRaw.w * sizeof(unsigned short));
//copy data
}
Thank you.
So, assuming you have converted your cv::Mat to unsigned short format with the correct pitch (or channels, in OpenCV), you just use memcpy.
(I've renamed your cv::Mat from filepath to mat (why is it called filepath?)):
memcpy((void*)depthRaw16.begin(), (void*)mat.data, mat.total() * mat.elemSize());
Again, be sure your pangolin image has identical dimensions and be sure the cv::Mat is converted to unsigned short.
I'm working with a Ximea Camera, programming in c++ and using Ubuntu 14.04. I have a XI_IMG image and with the next conversion I'm creating an OpenCV image, copying data from xiAPI buffer to OpenCV buffer.
stat = xiGetImage(xiH, 5000, &image);
HandleResult(stat,"xiGetImage");
XI_IMG* imagen = ℑ
IplImage * Ima = NULL;
char fname_jpg[MAX_PATH] = "";
Ima = cvCreateImage(cvSize(imagen->width, imagen->height), IPL_DEPTH_8U, 1);
memcpy(Ima->imageData, imagen->bp, imagen->width * imagen->height);
imwrite("image1", Ima);
After doing that I should be able to save or show the image, but the next error is shown:
program.cpp:76:24:error:invalid initialization of reference of type 'cv::InputArray {aka const cv::_InputArray&}' from expression of type 'IplImage* {aka IplImage*}'
Is there any other way to obtain or save the image? What else can I do to save a jpg image?
You are mixing old (and obsolete) C syntax like IplImage*, cv<SomeFunction>(), etc... with current C++ syntax.
To make it work be consistent and use only one style.
Using IplImage
int main()
{
IplImage* img = NULL;
img = cvCreateImage(...);
// Save
cvSaveImage("myimage.png", img);
// Show
cvShowImage("Image", img);
cvWaitKey();
return 0;
}
Or using new syntax (much better):
int main()
{
Mat img(...);
// Save
imwrite("myimage.png", img);
// Show
imshow("Image", img);
waitKey();
return 0;
}
Note that you don't need to memcpy the data after you initialize your Mat, but you can call one of these constructors:
C++: Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
C++: Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
C++: Mat::Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)
Last trick, you can wrap your IplImage in a Mat and then use imwrite:
Mat mat(Ima);
imwrite("name.ext", mat);
What is the fastest method to convert IplImage IPL_DEPTH_32S to QImage Format_RGB32?
I need to catch pictures from cam and show it on form with frequency 30 frames in second. I tried to use QImage constructor:
QImage qImage((uchar *) image->imageData, image->width, image->height, QImage::Format_RGB32);
but image was corrupted after this. So, how can I do this fast (I think putting pixel by pixel into QImage is not a good decision)?
Before I start, OpenCV uses the BGR format by default. Not RGB! So before creating the QImage you need to convert your IplImage to RGB with:
cvtColor(image, image, CV_BGR2RGB);
Then you can:
QImage qImage((uchar*) image->imageData, image->width, image->height, QImage::Format_RGB32);
Note that the constructor above doesn't copy the data as you might be thinking, as the docs states:
The buffer must remain valid throughout the life of the QImage
So if you are having performance issues are not because of the conversion procedure. It is most probably caused by the drawing method you are using (which wasn't shared in the question). To summarize a lot of blah blah blah, you should render the QImage to an OpenGL texture and let the video card do all the drawing for you.
Your question is a bit misleading because your primary objective is not to find the fastest conversion method, but one that actually works since yours don't.
Another important thing to keep in mind, when you said:
image was corrupted after this
you must know that this is completely vague, and it doesn't help us at all because there is a number of causes for this effect, and without the source code is impossible to tell with certainty what are you doing wrong. Sharing the original and the corrupted image might gives some clues to where the problem is.
That's it for now. Good luck.
IPL_DEPTH_32S is greyscale with a 32bit pixel - normally used for depth data rather than an actual 'image', if you are packign a colour image into check what the pixel ordering is.
QImage::Format_ARGB32_Premultiplied is the fastest QImage format because its what the graphics card uses - note that the data order is actually BGRA and the A channel must be at least as large as the pixel, ie 255 if you don't want to use alpha.
There is a RGB2BGRA and BGR2RGBA in cv::cvtColor()
Time ago I found some examples in the internet of how to do that. I improved the examples a bit for how to copy the images in a more easy way. Qt works normally only with char pixels (other image formats are normally called HDR images). I was wondering how you get a video buffer of 32 bit rgb in opencv... I have never seen that !
If you have a color image in opencv you could use this function to allocate memory:
QImage* allocateqtimagefromcv(IplImage* cvimg)
{
//if (cvimg->nChannels==1)
// {
// return new QImage(cvimg->width,cvimg->height,QImage::Format_Indexed8);
// }
if (cvimg)
{
return new QImage(cvimg->width,cvimg->height,QImage::Format_ARGB32);
}
}
to just copy the IplImage to the qt image you could go the easy way and use this function:
void IplImage2QImage(IplImage *iplImg,QImage* qimg)
{
char *data = iplImg->imageData;
int channels = iplImg->nChannels;
int h = iplImg->height;
int w = iplImg->width;
for (int y = 0; y < h; y++, data += iplImg->widthStep)
{
for (int x = 0; x < w; x++)
{
char r, g, b, a = 0;
if (channels == 1)
{
r = data[x * channels];
g = data[x * channels];
b = data[x * channels];
}
else if (channels == 3)
{
r = data[x * channels + 2];
g = data[x * channels + 1];
b = data[x * channels];
}
if (channels == 4)
{
a = data[x * channels + 3];
qimg->setPixel(x, y, qRgba(r, g, b, a));
}
}
}
}
the following function could be a quicker solution:
static QImage IplImage2QImage(const IplImage *iplImage)
{
int height = iplImage->height;
int width = iplImage->width;
if (iplImage->depth == IPL_DEPTH_8U && iplImage->nChannels == 3)
{
const uchar *qImageBuffer =(const uchar*)iplImage->imageData;
QImage img(qImageBuffer, width, height, QImage::Format_RGB888);
return img.rgbSwapped();
}else{
//qWarning() << "Image cannot be converted.";
return QImage();
}
}
hope my function helped. Maybe someone know better ways of doing that :)
So I'm trying to use the webp API to encode images. Right now I'm going to be using openCV to open and manipulate the images, then I want to save them off as webp. Here's the source I'm using:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cv.h>
#include <highgui.h>
#include <webp/encode.h>
int main(int argc, char *argv[])
{
IplImage* img = 0;
int height,width,step,channels;
uchar *data;
int i,j,k;
if (argc<2) {
printf("Usage:main <image-file-name>\n\7");
exit(0);
}
// load an image
img=cvLoadImage(argv[1]);
if(!img){
printf("could not load image file: %s\n",argv[1]);
exit(0);
}
// get the image data
height = img->height;
width = img->width;
step = img->widthStep;
channels = img->nChannels;
data = (uchar *)img->imageData;
printf("processing a %dx%d image with %d channels \n", width, height, channels);
// create a window
cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
cvMoveWindow("mainWin",100,100);
// invert the image
for (i=0;i<height;i++) {
for (j=0;j<width;j++) {
for (k=0;k<channels;k++) {
data[i*step+j*channels+k] = 255-data[i*step+j*channels+k];
}
}
}
// show the image
cvShowImage("mainWin", img);
// wait for a key
cvWaitKey(0);
// release the image
cvReleaseImage(&img);
float qualityFactor = .9;
uint8_t** output;
FILE *opFile;
size_t datasize;
printf("encoding image\n");
datasize = WebPEncodeRGB((uint8_t*)data,width,height,step,qualityFactor,output);
printf("writing file out\n");
opFile=fopen("output.webp","w");
fwrite(output,1,(int)datasize,opFile);
}
When I execute this, I get this:
nato#ubuntu:~/webp/webp_test$ ./helloWorld ~/Pictures/mars_sunrise.jpg
processing a 2486x1914 image with 3 channels
encoding image
Segmentation fault
It displays the image just fine, but segfaults on the encoding. My initial guess was that it's because I'm releasing the img before I try to write out the data, but it doesn't seem to matter whether I release it before or after I try the encoding. Is there something else I'm missing that might cause this problem? Do I have to make a copy of the image data or something?
The WebP api docs are... sparse. Here's what the README says about WebPEncodeRGB:
The main encoding functions are available in the header src/webp/encode.h
The ready-to-use ones are:
size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height,
int stride, float quality_factor, uint8_t** output);
The docs specifically do not say what the 'stride' is, but I'm assuming that it's the same as the 'step' from opencv. Is that reasonable?
Thanks in advance!
First, don't release the image if you use it later. Second, your output argument is pointing to non-initialized address. This is how to use initialized memory for the output address:
uint8_t* output;
datasize = WebPEncodeRGB((uint8_t*)data, width, height, step, qualityFactor, &output);
You release the image with cvReleaseImage before you try to use the pointer to the image data for the encoding. Probably that release function frees the image buffer and your data pointer now doesn't point to valid memory anymore.
This might be the reason for your segfault.
so it looks like the problem was here:
// load an image
img=cvLoadImage(argv[1]);
The function cvLoadImage takes an extra parameter
cvLoadImage(const char* filename, int iscolor=CV_LOAD_IMAGE_COLOR)
and when I changed to
img=cvLoadImage(argv[1],1);
the segfault went away.
I'm using OpenCV to extract a subimage of a scanned document and would like to use tesseract to perform OCR over this subimage.
I found out that I can use two methods for text recognition in tesseract, but so far I wasn't able to find a working solution.
A.) How can I convert a cv::Mat into a PIX*?
(PIX* is a datatype of leptonica)
Based on vasiles code below, this is essentially my current code:
cv::Mat image = cv::imread("c:/image.png");
cv::Mat subImage = image(cv::Rect(50, 200, 300, 100));
int depth;
if(subImage.depth() == CV_8U)
depth = 8;
//other cases not considered yet
PIX* pix = pixCreateHeader(subImage.size().width, subImage.size().height, depth);
pix->data = (l_uint32*) subImage.data;
tesseract::TessBaseAPI tess;
STRING text;
if(tess.ProcessPage(pix, 0, 0, &text))
{
std::cout << text.string();
}
While it doesn't crash or anything, the OCR result still is wrong. It should recognize one word of my sample image, but instead it returns some non-readable characters.
The method PIX_HEADER doesn't exist, so I used pixCreateHeader, but it doesn't take the number of channels as an argument. So how can I set the number of channels?
B.) How can I use cv::Mat for TesseractRect() ?
Tesseract offers another method for text recognition with this signature:
char * TessBaseAPI::TesseractRect (
const UINT8 * imagedata,
int bytes_per_pixel,
int bytes_per_line,
int left,
int top,
int width,
int height
)
Currently I am using the following code, but it also returns non-readable characters (although different ones than from the code above.
char* cr = tess.TesseractRect(
subImage.data,
subImage.channels(),
subImage.channels() * subImage.size().width,
0,
0,
subImage.size().width,
subImage.size().height);
tesseract::TessBaseAPI tess;
cv::Mat sub = image(cv::Rect(50, 200, 300, 100));
tess.SetImage((uchar*)sub.data, sub.size().width, sub.size().height, sub.channels(), sub.step1());
tess.Recognize(0);
const char* out = tess.GetUTF8Text();
For Anybody using the JavaCPP presets of OpenCV/Tesseract, here is what works
Mat img = imread("file.jpg");
Mat gray = new Mat();
cvtColor(img, gray, CV_BGR2GRAY);
// api is a Tesseract client which is initialised
api.SetImage(gray.data().asBuffer(),gray.size().width(),gray.size().height(),gray.channels(),gray.size1())
cv::Mat image = cv::imread(argv[1]);
cv::Mat gray;
cv::cvtColor(image, gray, CV_BGR2GRAY);
PIX *pixS = pixCreate(gray.size().width, gray.size().height, 8);
for(int i=0; i<gray.rows; i++)
for(int j=0; j<gray.cols; j++)
pixSetPixel(pixS, j,i, (l_uint32) gray.at<uchar>(i,j));
First, make a deep copy of your subImage, so that it will be stored in a coninuous memory block:
cv::Mat subImage = image(cv::Rect(50, 200, 300, 100)).clone();
Then, init a PIX headed (I don't know how) with the correct parameters.
// ???? Put your own constructor here.
PIX* pix = new PIX_HEADER(width, height, channels, depth);
OR, create it manually:
PIX pix;
pix.width = subImage.width;
...
Then set the pix data pointer to the subImage data pointer
pix.data = subImage.data;
Finally, make sure your subImage objects does not go out of scope before you finish your work with pix.