I want to read image from a database, image column is a MYSQL_TYPE_BLOB type and I read column using this code. Currently, Blob image converted as a char * array
//Get the total number of fields
int fieldCount = mysql_num_fields(result);
//Get field information of a row of data
MYSQL_FIELD *fields = mysql_fetch_fields(result);
while (m_row = mysql_fetch_row(result))
{
for (int i = 0;i < fieldCount; ++i)
{
if (fields[i].type == MYSQL_TYPE_BLOB)
{
unsigned long length = mysql_fetch_lengths(result)[i];
char* buffer = new char[length + 1];
memset(buffer, 0x00, sizeof(buffer));
memcpy(buffer, m_row[i], length);
}
}
}
In order to do some tests on image, I should know the image dimension without writing image on disk and reading it again?
Another way to read data from Mysql database is :
res = stmt->executeQuery("MY QUERY TO DATABASE");
while (res->next())
{
std::istream *blobData = res->getBlob("image");
std::istreambuf_iterator<char> isb = std::istreambuf_iterator<char>(*blobData);
std::string blobString = std::string(isb, std::istreambuf_iterator<char>());
tempFR.image = blobString;
blobData->seekg(0, ios::end);
tempFR.imageSize = blobData->tellg();
std::istream *blobIn;
char buffer[tempFR.imageSize];
memset(buffer, '\0', tempFR.imageSize);
blobIn = res->getBlob("image");
blobIn->read((char*)buffer, tempFR.imageSize);
}
Notice:
imageSize and length are the overall image size, for example 1000.
Update#1: How image will be reconstruct meanwhile writing it to disk?
In the first case it's possible to write the blob_image to disk via this codes:
stringstream pic_name;
pic_name << "car.jpeg";
ofstream outfile(pic_name.str(), ios::binary);
outfile.write(buffer, length);
and in the second ones:
std::ofstream outfile ("car.jpeg",std::ofstream::binary);
outfile.write (buffer, tempFR.imageSize);
outfile.close();
In both cases image writed to disk correctly. But I want to find image dimension without writing it to disk and reading it again?
By decoding buffered image:
length = mysql_fetch_lengths(result)[i];
buffer = new char[length + 1];
memset(buffer, 0x00, sizeof(buffer));
memcpy(buffer, m_row[i], length);
matImg = cv::imdecode(cv::Mat(1, length, CV_8UC1, buffer), cv::IMREAD_UNCHANGED);
At first, copy array to buffer, then convert it to a cv::Mat and finally decode it. It will be a cv::Mat image.
Related
Im try to make my real-time video streaming app.
Right now, im try to speed up my application.
And i have such question:
How to speed up "for" loop here:
boost::array<uchar, 30000> RECV_DATA; // array for receive all data from socket
size_t ImageSize = image_recver.read_some(
boost::asio::buffer(RECV_DATA), ignored_error); // complete image size
vector<uchar> Img (ImageSize); // the new array, will contains only image data
for (int i = 0; i < ImageSize; i++) {
Img[i] = RECV_DATA[i]; // Image array filling
}
You can use std::vector range constructor to copy RECV_DATA:
std::vector<uchar> Img(RECV_DATA.begin(), RECV_DATA.begin() + ImageSize);
Or, better, read directly into std::vector<uchar>:
std::vector<uchar> RECV_DATA(image_recver.available());
size_t imageSize = image_recver.read_some(boost::asio::buffer(RECV_DATA), ignored_error);
RECV_DATA.resize(imageSize);
I can use imwrite() to write the image(like "face.jpg") into disk,
then use fstream to read this jpg into a array.this array is what I want.
but,how to get this quickly? from memmory not disk.
I thought the image data in Mat.data,length is Mat.cols*Mat.rows.I was not sure it is or not right.so,I used fstream write it into disk,then opened it with image viewer,nothing.there must something wrong.
Mat frame;
VideoCapture cap(0);
if (!cap.isOpened())
{
return -1;
}
cap.set(CV_CAP_PROP_FRAME_WIDTH, 160);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 120);
cap >> frame;
if(frame.empty()){
return -2;
}
//I just want the pointer and length of image information,the following is just for testing
//whether that the same as I thought,if it's right ,frame.data and len is what I want,but it not work.
FILE *fp = fopen("face.jpg", "wb");
if (NULL==fp)
{
return -1;
}
int len = frame.cols*frame.rows; //or 3*frame.cols*frame.rows
fwrite(frame.data, len, sizeof(char), fp);
fclose(fp);
namedWindow("face", 1);
imshow("face", frame);
waitKey(1000);
I'm new in opencv,and I just want get the image data.thanks for help!
Have you check the dimensions before you write it to disk? It'll be helpful for the others to see your code here. In the case of Mat, unless your data is grayscale, the size will be more than cols * rows. You should verify if the format is RGB, RGBA, or YUV, etc. In the case of JPEG, it'll be most likely RGBX; so you should really check that your stream size is either 3 * cols * rows or 4 * cols * rows.
I did this just with imencode(),thanks for #ZdaR.
vector<uchar> buff;
vector<int>param = vector<int>(2);
param[0] = CV_IMWRITE_JPEG_QUALITY;
param[1] = 95;
imencode(".jpg", frame, buff, param);
int len = buff.size();
FILE *fout;
fout = fopen("555.jpg", "wb");
if(NULL==fout){
return -3;
}
fwrite(&buff[0], 1, len*sizeof(uchar), fout);
fclose(fout);
There is anyway to convert opencv mat object to base64.
I was using the below url for base64 encoding and decoding:
http://www.adp-gmbh.ch/cpp/common/base64.html
Below is the code snippet:
const unsigned char* inBuffer = reinterpret_cast(image.data);
There you go! (C++11)
Encode img -> jpg -> base64 :
std::vector<uchar> buf;
cv::imencode(".jpg", img, buf);
auto *enc_msg = reinterpret_cast<unsigned char*>(buf.data());
std::string encoded = base64_encode(enc_msg, buf.size());
Decode base64 -> jpg -> img :
string dec_jpg = base64_decode(encoded);
std::vector<uchar> data(dec_jpg.begin(), dec_jpg.end());
cv::Mat img = cv::imdecode(cv::Mat(data), 1);
Note that you can change JPEG compression quality by setting the IMWRITE_JPEG_QUALITY flag.
I'm encountering nearly the same problem, but I'm trying to encode a Mat into jpeg format and then convert it into base64.
The code on that page works fine!
So here is my code:
VideoCapture cam(0);
cam>>img;
vector<uchar> buf;
imencode(".jpg", img, buf);
uchar *enc_msg = new uchar[buf.size()];
for(int i=0; i < buf.size(); i++) enc_msg[i] = buf[i];
string encoded = base64_encode(enc_msg, buf.size());
if you just want to convert a Mat into base64, you need make sure the Mat size and channels. For a CV_8UC1, this will work:
string encoded = base64_encode(img.data, img.rows * img.cols);
I have created an example for this using Qt5 and OpenCV:
cv::Mat1b image;
this->cameraList[i]->getImage(image);
std::vector<uint8_t> buffer;
cv::imencode(".png", image, buffer);
QByteArray byteArray = QByteArray::fromRawData((const char*)buffer.data(), buffer.size());
QString base64Image(byteArray.toBase64());
base64ImageList.append(base64Image);
I was looking for a solution to the same problem. Using Jean-Christophe's answer above, this worked for me:
cv::Mat image = cv::imread("path/to/file");
std::vector<uchar> buffer;
buffer.resize(static_cast<size_t>(image.rows) * static_cast<size_t>(image.cols));
cv::imencode(".jpg", image, buffer);
std::string encoding = base64_encode(buffer.data(), buffer.size());
Also, c++ std does not have a base64_encode implementation so you can look at this answer which aggregated a bunch of implementations.
Without using opencv, we can convert the image or file into base 64.Read the file byte by byte, store it in a buffer and base64 encode it. Cheers!
FILE* f = fopen(imagePath, "rb");
fseek(f, 0, SEEK_END);
size_t length = ftell(f);
rewind(f);
BYTE* buffer = (BYTE*)malloc(length + 2);
while ((!feof(f))) {
BYTE c;
if (fread(&c, 1, 1, f) == 0) break; //read byte by byte of the PNG image file
buffer[i++] = (int)c;
}
fclose(f);
string base64String = base64_encode(&buffer[0], i + 1);
To be honest I'm suprised nobody has run into this thus far.
I'm loading a picture from OpenCV into cv::Mat, which I want to base64 encode before I send it over a socket.
For base64 I am using libb64 as it is native to Debian/Ubuntu, and easy to use and very fast. The encoding function takes as a parameter an std::ifstream, and outputs an std::ofstream.
#include <opencv2/opencv.hpp>
#include <b64/encode.h>
#include <fstream>
using namespace cv;
Mat image;
image = imread( "picture.jpg", CV_LOAD_IMAGE_COLOR );
if ( image.data )
{
std::ifstream instream( ???, std::ios_base::in | std::ios_base::binary);
std::ofstream outstream;
// Convert Matrix to ifstream
// ...
base64::encoder E;
E.encode( instream, outstream );
// Now put it in a string, and send it over a socket...
}
I don't really know how to populate the instream from the cv::Mat.
Googling around, I found that I can iterate a cv::Mat, by columns and rows, and get each (pixel I am assuming) RGB values:
for ( int j = 0; j < image.rows; j++ )
{
for ( int i = 0; i < image.cols; i++ )
{
unsigned char b = input [ image.step * j + i ] ;
unsigned char g = input [ image.step * j + i + 1 ];
unsigned char r = input [ image.step * j + i + 2 ];
}
}
Is this the right way of going on about it? Is there some more elegant way?
In order to be able to send an image via HTTP, you also need to encode its width, height and type. You need to serialize the Mat into a stream and encode that stream with libb64. On the other side you need to decode that stream and deserialize the image to retrieve it.
I implemented a small test program that does this serialization and deserialization using std::stringstream as a buffer. I chose it because it extends both std::istream and std::ostream which libb64 uses.
The serialize function serializes a cv::Mat into a std::stringstream. In it, I write the image width, height, type, size of the buffer and the buffer itself.
The deserialize function does the reverse. It reads the width, height, type, size of the buffer and the buffer. It's not as efficient as it could be because it needs to allocate a temporary buffer to read the data from the stringstream. Also, it needs to clone the image so that it does not rely on the temporary buffer and it will handle its own memory allocation. I'm sure that with some tinkering it can be made more efficient.
The main function loads an image, serializes it, encodes it using libb64, then decodes it, deserializes it and displays it in a window. This should simulate what you are trying to do .
// Serialize a cv::Mat to a stringstream
stringstream serialize(Mat input)
{
// We will need to also serialize the width, height, type and size of the matrix
int width = input.cols;
int height = input.rows;
int type = input.type();
size_t size = input.total() * input.elemSize();
// Initialize a stringstream and write the data
stringstream ss;
ss.write((char*)(&width), sizeof(int));
ss.write((char*)(&height), sizeof(int));
ss.write((char*)(&type), sizeof(int));
ss.write((char*)(&size), sizeof(size_t));
// Write the whole image data
ss.write((char*)input.data, size);
return ss;
}
// Deserialize a Mat from a stringstream
Mat deserialize(stringstream& input)
{
// The data we need to deserialize
int width = 0;
int height = 0;
int type = 0;
size_t size = 0;
// Read the width, height, type and size of the buffer
input.read((char*)(&width), sizeof(int));
input.read((char*)(&height), sizeof(int));
input.read((char*)(&type), sizeof(int));
input.read((char*)(&size), sizeof(size_t));
// Allocate a buffer for the pixels
char* data = new char[size];
// Read the pixels from the stringstream
input.read(data, size);
// Construct the image (clone it so that it won't need our buffer anymore)
Mat m = Mat(height, width, type, data).clone();
// Delete our buffer
delete[]data;
// Return the matrix
return m;
}
void main()
{
// Read a test image
Mat input = imread("D:\\test\\test.jpg");
// Serialize the input image to a stringstream
stringstream serializedStream = serialize(input);
// Base64 encode the stringstream
base64::encoder E;
stringstream encoded;
E.encode(serializedStream, encoded);
// Base64 decode the stringstream
base64::decoder D;
stringstream decoded;
D.decode(encoded, decoded);
// Deserialize the image from the decoded stringstream
Mat deserialized = deserialize(decoded);
// Show the retrieved image
imshow("Retrieved image", deserialized);
waitKey(0);
}
I have a curl callback that contains a some header info and then a jpeg image.
I want to copy out the jpeg image from this data and save it to a file.
I have never used malloc or memcpy before but I have did the following:
//data = the data that curl has returned
//datalength = the length of the data that curl has returned
//startpos = the starting position of the jpeg image in data
//the length of the jpeg image
//example data
//datalength=13209
//startpos = 62
//imagelangth=13127
bool SaveImage( void* data, size_t datalength, int startpos, int imageLength)
{
//1. Allocate a buffer to store the jpeg image
BYTE* image = (BYTE*)malloc(sizeof(BYTE)*imageLength);
if( image != nullptr)
{
//2. Copy out the image info to the buffer
BYTE* imageStartPos = (BYTE*)data + startpos;
memcpy( image, imageStartPos, imageLength);
//3. Save the image to file
FILE* pFile;
fopen_s(&pFile, "image.jpeg", "w");
if(pFile != NULL)
{
fwrite(image,sizeof(BYTE), imageLength, pFile);
fclose(pFile);
}
}
}
The result is that I get a jpeg image created that is about 13k in size but I cannot open it in ms paint as it says its corrupt. I assume that I have made a mistake in my pointer calculations above.
Any ideas anyone as to what I'm doing wrong?