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.
Related
I am new to C++ (aswell as Cuda and OpenCV), so I am sorry for any mistakes on my side.
I have an existing code that uses Cuda. Recently it worked with .png (that was decoded) as an input but now I use a camera to generate live images. These images are the new input for the code. Here it is:
using namespace cv;
INT height = 2160;
INT width = 3840;
Mat image(height, width, CV_8UC3);
size_t pitch;
uint8_t* image_gpu;
// capture image
VideoCapture camera(0);
camera.set(CAP_PROP_FRAME_WIDTH, width);
camera.set(CAP_PROP_FRAME_HEIGHT, height);
camera.read(image);
// here I checked if image is definitly still a CV_8UC3 Mat with the initial height and width; and it is
cudaMallocPitch(&image_gpu, &pitch, width * 4, height);
// here I use cv::Mat::data to get the pointer to the data of the image:
cudaMemcpy2D(image_gpu, pitch, image.data, width*4, width*4, height, cudaMemcpyHostToDevice);
The code compiles but I get an "Exception Thrown" at the last line (cudaMemcpy2D) with the following error code:
Exception thrown at 0x00007FFE838D6660 (nvcuda.dll) in realtime.exe: 0xC0000005: Access violation reading location 0x000001113AE10000.
Google did not give me an answer and I do not know ho to proceed from here on.
Thanks for any hints!
A rather generic way to copy an OpenCV Mat to the device memory allocated using cudaMallocPitch is to utilize the step member of the Mat object. Also, while allocating device memory, you must have a visual intuition in mind that how the device memory will be allocated and how the Mat object will be copied to it. Here is a simple example demonstrating the procedure for a video frame captured using VideoCapture.
#include<iostream>
#include<cuda_runtime.h>
#include<opencv2/opencv.hpp>
using std::cout;
using std::endl;
size_t getPixelBytes(int type)
{
switch(type)
{
case CV_8UC1:
case CV_8UC3:
return sizeof(uint8_t);
break;
case CV_16UC1:
case CV_16UC3:
return sizeof(uint16_t);
break;
case CV_32FC1:
case CV_32FC3:
return sizeof(float);
break;
case CV_64FC1:
case CV_64FC3:
return sizeof(double);
break;
default:
return 0;
}
}
int main()
{
cv::VideoCapture cap(0);
cv::Mat frame;
if(cap.grab())
{
cap.retrieve(frame);
}
else
{
cout<<"Cannot read video"<<endl;
return -1;
}
uint8_t* gpu_image;
size_t gpu_pitch;
//Get number of bytes occupied by a single pixel. Although VideoCapture mostly returns CV_8UC3 type frame thus pixelBytes is 1 , but just in case.
size_t pixelBytes = getPixelBytes(frame.type());
//Number of actual data bytes occupied by a row.
size_t frameRowBytes = frame.cols * frame.channels * pixelBytes;
//Allocate pitch linear memory on device
cudaMallocPitch(&gpu_image, &gpu_pitch, frameRowBytes , frame.rows);
//Copy memory from frame to device mempry
cudaMemcpy2D(gpu_image, gpu_pitch, frame.ptr(), frame.step, frameRowBytes, frame.rows, cudaMemcpyHostToDevice);
//Rest of the code ...
return 0;
}
Disclaimer:
Code is written in the browser. Not tested yet. Please add CUDA error checking as required
I'm writing a converter from OpenImageIO ImageBufs to OpenCV IplImages. Running the following code causes the output images to get corrupted (pictures below). I am working on a pull request for OpenImageIO, but if someone from the OpenCV community has some insights on the OpenCV side, that would be super helpful. OpenImageIO Pull Request
Code
IplImage *
ImageBufAlgo::to_IplImage (const ImageBuf &src)
{
#ifdef USE_OPENCV
const ImageSpec &spec = src.spec();
const ImageBuf *tmp = &src;
ImageBuf localcopy;
int channels = std::min(spec.nchannels, 4);
// If the image has 4+ channels, then reduce to 4 channels, and use BGRA
// order (OpenCV ordering).
if (channels >=3) {
// OpenCV images support up to 4 channels (BGRA)
int channelorder[channels];
// Set the channel order
for (int i = 0; i < channels; ++i) {
channelorder[i] = i;
}
channelorder[0] = 2; // B
channelorder[1] = 1; // G
channelorder[2] = 0; // R
if (!ImageBufAlgo::channels(localcopy, src, channels, &channelorder[0])) {
DASSERT (0 && "Could not swap channels.");
return NULL;
}
tmp = &localcopy;
}
// Create an IplImage to write to.
CvSize size = cvSize(spec.width, spec.height);
IplImage *dst;
// Get the pixel data from the ImageBuf and send it to the IplImage.
switch (spec.format.basetype) {
case TypeDesc::UINT8: {
dst = cvCreateImageHeader(size, IPL_DEPTH_8U, channels);
if (!dst) {
DASSERT (0 && "Could not create dst IplImage.");
return NULL;
}
if (tmp->storage() != ImageBuf::IMAGECACHE) {
// Type is right and the IB is not backed by an ImageCache, so
// directly read from the local data of the IB.
cvSetData(dst, (void *)tmp->localpixels(), dst->widthStep);
} else {
// Either we are backed by an ImageCache, or we somehow still
// need a data format conversion, so make a local copy with
// get_pixels.
std::vector<unsigned char> data;
data.resize (spec.width * spec.height * spec.depth * channels);
tmp->get_pixels (ROI::All(), TypeDesc::INT8, &data[0]);
cvSetData(dst, &data[0], dst->widthStep);
}
break;
default:
DASSERT (0 && "unknown TypeDesc");
return NULL;
}
return dst;
#else
return NULL;
#endif
}
Test code
#include <ostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core_c.h>
#include <opencv2/highgui/highgui_c.h>
#include <OpenImageIO/imagebuf.h>
#include <OpenImageIO/imagebufalgo.h>
namespace oiio = OpenImageIO;
int main(int argc, char const *argv[]) {
oiio::ImageBuf src("/home/scott/Projects/oiio-test/images/input.jpg");
IplImage *dst = oiio::ImageBufAlgo::to_IplImage(src);
cvSaveImage("/home/scott/Projects/oiio-test/images/output.jpg", dst);
return 0;
}
Input images
Image 1
Image 2
Output images
Image 1
Image 2
My current theories
There's a difference in how the data is laid out. As far as I understand, OpenCV expects that a 3 channel image is laid out BGRBGRBGR... I think that by default, OpenImageIO's layout is RGBRGBRGB..., but that does get swapped. I'm not adding any padding, so there's padding already in the data, or I am missing something.
I am passing the wrong step into the cvSetData (step calculation: row * type size * channels). I tried manually setting some values but I either get worse results, or OpenCV raises an exception and segfaults.
Is there something else that I could be doing wrong?
I am trying to write a very basic video capturing program using opencv, but despite all my efforts, nothing gets written. I am fairly sure that i am following all the tutorials one can find on the subject.
Here is the code i am using:
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <videoInput.h>
static const cv::Size _SIZE(640, 480);
static const int FPS = 20;
int webcam = 0;
std::auto_ptr<videoInput> VI(NULL);
std::auto_ptr<cv::VideoWriter> outputVideo(NULL);
cv::Mat frame(_SIZE.height, _SIZE.width, CV_8UC3);
// get device list create videoInput
videoInput::listDevices();
VI.reset(new videoInput())
// choose first device
VI->setupDevice(webcam, _SIZE.width, _SIZE.height);
// always check
if(!VI->isDeviceSetup(webcam))
return -1; //-->
// set device frame rate
VI->setIdealFramerate(webcam, FPS);
// create named window and image
cv::namedWindow("CAM");
do
if (VI->isFrameNew(webcam))
{
VI->getPixels(webcam, (unsigned char*)frame.data, false, true); // получение пикселей в BGR
if (outputVideo.get())
{
(*outputVideo).write( frame);
}
char c = cv::waitKey(5);
if (!IsWindowVisible((HWND)cvGetWindowHandle("CAM")) || c==27)
{
exit(0);
}
cv::imshow("CAM", frame);
} while(1);
I have tried various extension and various Fourcc values. Usually, on any extension except avi, writer is created but does nothing. on the contrary, avi files writers simply fail to be created.
I have read that probably codecs are missing, but what does that mean - what exactly do i need to put where for them to be found by opencv?
All in all, i am very confused. This is tutorial code, it should work.
I want to learn image processing in C++, but I don't want to use any 3rd party library for image manipulation. Use of library for displaying the image(s) is okay, but all manipulations are to be done manually.
Please point me to some good tutorials. I'm a beginner in this field, so I also need to know how to display an image.
Seems you lack basic knowledge of Digital Image Processing, I recommand to you this book.
Digital Image Processing (3rd Edition) Rafael C.Gonzalez / Richard E.Woods http://www.amazon.com/dp/013168728X
For basic operation using OpenCV(which I am familiar with), here is an example:
/*
function:image reverse
*/
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cv.h>
#include <highgui.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 image
img=cvLoadImage(argv[1],-1);
if(!img)
{
printf("Could not load image file: %s\n",argv[1]);
exit(0);
}
// acquire image info
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",height,width,channels);
// create display window
cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
cvMoveWindow("mainWin", 100, 100);
// reverse 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];
// display reversed image
cvShowImage("mainWin", img );
cvWaitKey(0);
cvReleaseImage(&img );
printf("height=%d width=%d step=%d channels=%d",height,width,step,channels);
return 0;
}
Try CImg (it's entirely self-contained) - http://cimg.sourceforge.net/
Using GTK+ 3.6 I would like to display an image from a buffer in memory, not a file on disk. I have a const char *data with the image data, and I'm trying to create a GTK image from it.
So far I have tried two approaches which I thought could work. Both use GdkPixbuf, and thus require the image data to be guchar* (unsigned char*).
With that requirement I have to cast the data:
guchar *gudata = reinterpret_cast<guchar*>(const_cast<char*>(data));
I then tried the following:
Writing the data into a GdkPixbufLoader with gdk_pixbuf_loader_write. Here I get an error "Unrecognized image file format" or if I create the loader with a specific type (jpg) i get an error saying that it's not a JPG file format (and it is, explained below).
EDIT: A bit of code:
guchar *gudata = reinterpret_cast<guchar*>(const_cast<char*>(data));
int stride = ((1056 * 32 + 31) & ~31)/8;
GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
GError *error = NULL;
if(!gdk_pixbuf_loader_write(loader, gudata, data_size, &error)
{
printf("Error:\n%s\n", error->message);
}
EDIT 03/01/2013:
Removed stride parameter from write function - misprint.
Cairo surface does not work as well. Shows black screen and noise.
Initializing the pixbuf with gdk_pixbuf_new_from_data and then the image just looks like tv noise, which would indicate that either the data is wrong (and it has been cast), or that the other parameters were wrong (image row stride, but it's not :) ).
After errors I just tried writing the data to a file foo.jpg using ofstream and yes, I get a properly working image file. The file command in terminal confirms that it is a JPEG image, and with a simple block of code I've created a GdkPixbuf from that foo.jpg to check out it's row stride value and it matches the value I pass to the aforementioned function.
Does the image data become corrupt with the cast, and if so how can I address that? I get the image data in const char*. I have looked at QtPixmap and it also loads unsigned char*.
Do I need to use a seperate library? (libjpeg?)
I have libgtk3-dev installed.
Thank you!
03/01/2012 UPDATE:
Here's a simple working app that loads a "test.jpg" file near it (file size must be < 100000 bytes).
#include <glib.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gtk/gtk.h>
void on_destroy (GtkWidget *widget G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
{
gtk_main_quit ();
}
int main (int argc, char *argv[])
{
FILE *f;
guint8 buffer[100000];
gsize length;
GdkPixbufLoader *loader;
GdkPixbuf *pixbuf;
GtkWidget *window;
GtkWidget *image;
gtk_init (&argc, &argv);
f = fopen ("test.jpg", "r");
length = fread (buffer, 1, sizeof(buffer), f);
fclose (f);
loader = gdk_pixbuf_loader_new ();
gdk_pixbuf_loader_write (loader, buffer, length, NULL);
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
image = gtk_image_new_from_pixbuf (pixbuf);
gtk_container_add (GTK_CONTAINER (window), image);
gtk_widget_show_all (GTK_WIDGET (window));
g_signal_connect (window, "destroy", G_CALLBACK(on_destroy), NULL);
gtk_main ();
return 0;
}
Original Answer:
The char * or unsigned char * here has little importance.
gdk_pixbuf_new_from_data will only read uncompressed RGB data (the only colorspace supported is GDK_COLORSPACE_RGB) with an alpha channel (RGBA) or without it (RGB). No wonder passing it JPEG fails.
Calling gdk_pixbuf_loader_write looks like a better option, but we'd need some code to see what you may doing wrong. Check however that you have the pixbuf loader for jpg installed by running in a shell the gdk-pixbuf-query-loaders command, and verifying that JPEG is there.