Given the following
Bitmap raw image data in char array
Image width and height
Path wzAppDataDirectory in std::wstring generated using the following code
// Get a good path.
wchar_t wzAppDataDirectory[MAX_PATH];
wcscpy_s( wzAppDataDirectory, MAX_PATH, Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data() );
wcscat_s( wzAppDataDirectory, MAX_PATH, (std::wstring(L"\\") + fileName).c_str() );
How can we save the image as JPG? (Include encoding as well as the char array is raw bitmap form)
Code example is very much appreciated.
You'll need to use a library to encode the JPEG. Some possibilities are the Independent JPEG Group's jpeglib, stb_image, or DevIL.
This is an example code which I obtained from my friend.
It uses OpenCV's Mat data structure. Note that, you need to ensure the unsigned char data array within cv::Mat is in continuous form. cv::cvtColor will do the trick (Or, cv::Mat.clone).
Take note, do not use OpenCV's imwrite. As at current time of writing, imwrite doesn't pass Windows Store Certification Test. It is using several APIs, which is prohibited in WinRT.
void SaveMatAsJPG(const cv::Mat& mat, const std::wstring fileName)
{
cv::Mat tempMat;
cv::cvtColor(mat, tempMat, CV_BGR2BGRA);
Platform::String^ pathName = ref new Platform::String(fileName.c_str());
task<StorageFile^>(ApplicationData::Current->LocalFolder->CreateFileAsync(pathName, CreationCollisionOption::ReplaceExisting)).
then([=](StorageFile^ file)
{
return file->OpenAsync(FileAccessMode::ReadWrite);
}).
then([=](IRandomAccessStream^ stream)
{
return BitmapEncoder::CreateAsync(BitmapEncoder::JpegEncoderId, stream);
}).
then([=](BitmapEncoder^ encoder)
{
const Platform::Array<unsigned char>^ pixels = ref new Platform::Array<unsigned char>(tempMat.data, tempMat.total() * tempMat.channels());
encoder->SetPixelData(BitmapPixelFormat::Bgra8, BitmapAlphaMode::Ignore, tempMat.cols , tempMat.rows, 96.0, 96.0, pixels);
encoder->FlushAsync();
});
}
Related
I'm very new at C++ and I'm trying to create a DLL which uses OpenCV library.
My DLL gets a raw image from other application and creates a MAT from the application's memory buffer. I send the buffer's address, which has a raw image, to the DLL and get raw image to OpenCV. This part works.
But after processing image with OpenCV, I can't write raw image to same memory address.
This is the code snippet that I've tried:
fn_export double createImage(char* address double width, double height) {
unsigned char* pBuffer = (unsigned char*)address;
memcpy(&pBuffer,&address, sizeof(pBuffer));
cv::Mat img = cv::Mat(height,width, CV_8UC4, pBuffer);
cv::imshow("Original", img);
memcpy(&address, &img.data[0], sizeof(address));
return 1;
}
char* address is memory address from my application. Other application's buffer doesn't change this way. Anybody has any advice about this situation?
Ok. I solved this issue ;
Mat img = Mat(height, width, CV_8UC4, address);
cv::imshow("Image from GM", img);
// same image copy to buffer back;
memcpy(&address[0], &img.data[0], width*height*4.);
Recently, i am having trouble with converting a Mat frame captured from my webcam by OpenCV to a normal JPEG unsigned char array. I've tried one or two way on Google but the result seems not the correct jpeg uchar array. Here is a piece of my code:
VideoCapture cap(0);
if(!cap.isOpened())
return -1;
Mat frame;
cap >> frame;
if( frame.empty())
return -1;
int size = frame.total() * frame.elemSize();
unsigned char* buffer = new unsigned char[size];
memcpy(buffer, frame.data, size * sizeof(unsigned char));
Then i used fwrite to write that buffer into a file.jpg (it looks silly but it does work if the buffer is correct),but the file cannot be openned or be determined as a jpeg image.
Can anyone help me figure this out?
Check out the OpenCV function imencode(). It will fill a buffer with data encoded as the correct image type (based on the file type argument) so that it can be written to a file and other programs will know what to do with it.
The problem with your current approach is that you are attempting to write raw image data as a JPEG, but JPEG is a compressed data format so programs won't know what to do with the data you've written. It would be equivalent of taking a binary file and just saving it as a JPEG, the file won't have the right headers to be decoded as an image and the data otherwise likely won't match up with the JPEG format anyways.
I'm working on an C++ image viewer for Linux which is created using GTK+3 (gtkmm) for the GUI and Magick++ for image handling. My goal is to support as many image file formats as possible, including animated GIFs.
What is the best approach to take a Magick++ Image and draw it in a GTK+3 widget, such that it would work for (just about) any image file format?
What is the best approach to take a Magick++ Image and draw it in a GTK+3 widget, such that it would work for (just about) any image file format?
As long as ImageMagick has the format delegate, you should be able to draw the GtkWidget image.
image = Gtk::manage(new Gtk::Image());
// Load image into ImageMagick
Magick::Image img("wizard:");
/// Calculate how much memory to allocate
size_t to_allocate = img.columns() * img.rows() * 3;
// Create a buffer
guint8 * buffer = new guint8[to_allocate];
// Write pixel data to buffer.
img.write(0, 0, img.columns(), img.rows(), "RGB", Magick::CharPixel, buffer);
// Build a Pixbuf from pixel data in memory.
Glib::RefPtr<Gdk::Pixbuf> pBuff = Gdk::Pixbuf::create_from_data(buffer, Gdk::COLORSPACE_RGB, false, 8, img.columns(), img.rows(), img.columns()*3 );
// Set GtkImage from Pixbuf
image->set(pBuff);
Original Answer mixing C/C++ methods.
Use the following Magick++ method signature to export the pixel data into memory.
Magick::Image.write(const ssize_t x_,
const ssize_t y_,
const size_t columns_,
const size_t rows_,
const std::string &map_, //<= Usually "RGB"
const StorageType type_, //<= Usually CharType
void *pixels_) //<= Be sure to allocate _all_ the memory required (size of storage * number of channels * columns * rows)
Create a GdkPixBuf from the pixels exported above with the following GTK method.
GdkPixbuf *
gdk_pixbuf_new_from_bytes (GBytes *data, //<= Same as pixels_.
GdkColorspace colorspace, //<= Match colorspace channels from map_.
gboolean has_alpha, //<= Usually no.
int bits_per_sample, //<= Match StorageType bits
int width, //<= Same as columns_.
int height, //<= Same as rows_.
int rowstride); //<= size of data-type * number of channel * width.
Finally, build a GtkImage from the PixBuf with the following method.
GtkWidget * gtk_image_new_from_pixbuf (GdkPixbuf *pixbuf);
I suppose #emcconville is right for the ImageMagick part, as I have no clue about it.
For the GTKmm part, though, you'll want to stick to the C++ API, so use Gdk::Pixbuf::create_from_data to read the image from ImageMagick.
Also, as you're creating an image viewer, you will want to change the image shown by a Gtk::Image. So at startup just use an empty Gtk::Image created with Gtk::Image::Image (or Glade and Gtk::Builder), and later change the image displayed in it with Gtk::Image::set, passing it your pixbuf.
I tried to convert a WriteableBitmap to a cv::Mat in a c++/cx Microsoft universial App. But when I try to progress with the created Mat, I get the following error:
This is my Code:
void App1::MainPage::processImage(SoftwareBitmap^ bitmap)
{
WriteableBitmap^ wb = ref new WriteableBitmap(bitmap->PixelWidth, bitmap->PixelHeight);
bitmap->CopyToBuffer(wb->PixelBuffer);
Mat img_image(wb->PixelHeight, wb->PixelWidth, CV_8UC3,(void*)wb->PixelBuffer);
//next step results in error
cvtColor(img_image, img_image, CV_BGR2BGRA);
...
}
So my final question:
How to convert the SoftwareBitmap or the WriteableBitmap to a cv::Mat?
The problem is that PixelBuffer is not a void *, it is an IBuffer^.
To get at the raw data, you can either use the IBufferByteAccess interface if you're comfortable with COM programming, or you can initialize a DataReader with an IBuffer if you'd prefer to stay in WinRT (although this technique will make a copy of the data).
I used the DataReader to solve the problem:
void App1::MainPage::processImage(SoftwareBitmap^ bitmap)
{
WriteableBitmap^ wb = ref new WriteableBitmap(bitmap->PixelWidth, bitmap->PixelHeight);
bitmap->CopyToBuffer(wb->PixelBuffer);
IBuffer^ buffer = wb->PixelBuffer;
auto reader = ::Windows::Storage::Streams::DataReader::FromBuffer(buffer);
BYTE *extracted = new BYTE[buffer->Length];
reader->ReadBytes(Platform::ArrayReference<BYTE>(extracted, buffer->Length));
Mat img_image(wb->PixelHeight, wb->PixelWidth, CV_8UC4, extracted);
cvtColor(img_image, img_image, CV_RGBA2BGRA);
...
}
Thx to Peter Torr for the hint.
I want to load an image in c++ opencv that comes from a postgresql database.
The image, jpg extension, is stored as a binary data (bytea type) in the base, that I can access thanks to libpqxx.
The problem is that I do not know how to convert the data into a cv::Mat instance. With a regular image I could use imread('myImage.jpg', ...), but in this case I cannot even load the database image in the data attribute of Mat because it is jpeg and not bmp.
Any idea ? Is there some opencv method I could use that could understand directly the binary data and convert it to the appropriate structure ? the imdecode() functions seems to be used for bitmap datas.
edit : Berak, using a vector the imdecode function returns null Matrice What happens "If the buffer is too short or contains invalid data, the empty matrix/image is returned." Here is the code :
pqxx::result r=bdd::requete("SELECT image FROM lrad.img WHERE id=3",1);//returns the bytea image in r[0]["image"]
const char* buffer=r[0]["image"].c_str();
vector<uchar>::size_type size = strlen((const char*)buffer);
vector<uchar> jpgbytes(buffer, buffer+size);
Mat img = imdecode(jpgbytes, CV_LOAD_IMAGE_COLOR);
//jpgbytes.size()=1416562 img.size()=[0 x 0]
What am I missing ?
still, use imdecode . it can handle png,jpg,bmp,ppm,webp,jp2,exr, but no gif.
vector<uchar> jpgbytes; // from your db
Mat img = imdecode(jpgbytes);
(you should do the same for bmp or any other supported formats, don't mess with Mat's raw data pointers!)
Ok I have the process to convert a bytea data to a cv::Mat, here is the code.
inline int dec(uchar x){ //convert uchar to int
if (x>='0'&&x<='9') return (x-'0');
else if (x>='a'&&x<='f') return (x-'a'+10);
else if (x>='A'&&x<='F') return (x-'A'+10);
return 0;
}
cv::Mat bytea2Mat(const pqxx::result::field& f){
const char* buffer=f.c_str();
vector<uchar>::size_type size = strlen((const char*)buffer);
vector<uchar> jpgbytes(size/2-1);
for (size_t i=0; i!=size/2-1;i++) {
jpgbytes[i]=(dec(buffer[2*(i+1)])<<4)+dec(buffer[2*(i+1)+1]);
}
cout <<size/2<<";"<<jpgbytes.size()<<endl;
return imdecode(jpgbytes, CV_LOAD_IMAGE_COLOR);
}
The bytea output is encrypted as a char* looking like "\x41204230", for an original input string of "a b0" in hexa form. (the \x may not be present according to the data input)
to get the original data you have to calculate the original input from the two char, ('4','1'= 0x41=65). The vector is half the size of the char*.