I recently started to take a look at OpenCV for doing some (programmatic) image processing for a small project I’ll maybe talk about later on.
My problem: in my program I receive JPEG-images in a buffer over a network connection and not by opening a file. Now my question was: how to create an OpenCV Mat(rix) from this buffer? Normally should not fill a whole post, but it took me too much time to develop to not document it now.
Strange enough, even on Stackoverflow I only found partial answers. I did a half-hearted web-search and found nothing really complete. Here are the facts I gathered (no guarantee for their correctness, but this is my current state of understanding)
- OpenCV does not directly support the importation of JPEGs from a buffer (but it does support the reading of a file).
- You need to use a libjpeg-variant to create an uncompressed image which then can be imported into the Matrix
- OpenCV needs images in BGR-colorspaces to be processed further on, by default images are in RGB-colorspace
When doing this kind of processing I want to limit copies and processing time. Here’s the code I came up with:
class ImageProcessing { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; public: ImageProcessing() { cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); } ~ImageProcessing() { jpeg_destroy_decompress(&cinfo); } void handleImage(uint8_t *buffer, size_t size) { jpeg_mem_src(&cinfo, buffer, size); switch (jpeg_read_header(&cinfo, TRUE)) { case JPEG_SUSPENDED: case JPEG_HEADER_TABLES_ONLY: return; case JPEG_HEADER_OK: break; } cinfo.out_color_space = JCS_EXT_BGR; jpeg_start_decompress(&cinfo); cv::Mat src = cv::Mat( cv::Size(cinfo.output_width, cinfo.output_height), CV_8UC3); while (cinfo.output_scanline < cinfo.output_height) { JSAMPLE *row = src.ptr(cinfo.output_scanline); jpeg_read_scanlines(&cinfo, &row, 1); } jpeg_finish_decompress(&cinfo); cv::imshow("test", src); cv::waitKey(0); } };
Summary: I decode the JPEG-buffer using libpjeg-turbo (pre-installed) directly into the buffer allocated by cv::Mat, using the BGR-colorspace as expected by OpenCV.
Line 30: is where I’m telling libjpeg to decode directly to BGR-colorspace
Line 39-40: does the decoding line-by-line directly into the cv::Mat-buffer correspond to the line which can be easily retrieved by the cv::Mat::ptr()-method.
Careful, this code is just a snippet showing how I did. It is neither complete nor self-standing.
This looks straight forward.
Do you know how OpenCV imread() function reads the JPG from file? I believe it should be easy to implement a corresponding function to process a network buffer in the same way OpenCV does it with files.
Hmm, before posting I indeed found
imdecode()
, which is used byimread()
. Imdecode is using the built-in decoders to decode buffers taken from anInputArray
which based on avector
. In my code I couldn’t create avector
from my buffer without copying. Hence my manual approach.Whether or not copying once is really a bottleneck in the long run is to be proven.
imdecode()
‘s code can be found here: https://github.com/Itseez/opencv/blob/master/modules/imgcodecs/src/loadsave.cpp#L553Pingback: Create an OpenCV 2 matrix from a JPEG-image in a buffer #2 | Filter Failure