Created
January 14, 2016 20:29
-
-
Save royshil/fa98604b01787172b270 to your computer and use it in GitHub Desktop.
Example of converting a YUYV buffer to JPEG using libJPEG
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <fstream> | |
#include <vector> | |
#include <cstdint> | |
#include <memory> | |
#include <jpeglib.h> | |
using namespace std; | |
/** | |
* converts a YUYV raw buffer to a JPEG buffer. | |
* input is in YUYV (YUV 422). output is JPEG binary. | |
* from https://linuxtv.org/downloads/v4l-dvb-apis/V4L2-PIX-FMT-YUYV.html: | |
* Each four bytes is two pixels. | |
* Each four bytes is two Y's, a Cb and a Cr. | |
* Each Y goes to one of the pixels, and the Cb and Cr belong to both pixels. | |
* | |
* inspired by: http://stackoverflow.com/questions/17029136/weird-image-while-trying-to-compress-yuv-image-to-jpeg-using-libjpeg | |
*/ | |
void compressYUYVtoJPEG(const vector<uint8_t>& input, const int width, const int height, vector<uint8_t>& output) { | |
struct jpeg_compress_struct cinfo; | |
struct jpeg_error_mgr jerr; | |
JSAMPROW row_ptr[1]; | |
int row_stride; | |
uint8_t* outbuffer = NULL; | |
uint64_t outlen = 0; | |
cinfo.err = jpeg_std_error(&jerr); | |
jpeg_create_compress(&cinfo); | |
jpeg_mem_dest(&cinfo, &outbuffer, &outlen); | |
// jrow is a libjpeg row of samples array of 1 row pointer | |
cinfo.image_width = width & -1; | |
cinfo.image_height = height & -1; | |
cinfo.input_components = 3; | |
cinfo.in_color_space = JCS_YCbCr; //libJPEG expects YUV 3bytes, 24bit | |
jpeg_set_defaults(&cinfo); | |
jpeg_set_quality(&cinfo, 92, TRUE); | |
jpeg_start_compress(&cinfo, TRUE); | |
vector<uint8_t> tmprowbuf(width * 3); | |
JSAMPROW row_pointer[1]; | |
row_pointer[0] = &tmprowbuf[0]; | |
while (cinfo.next_scanline < cinfo.image_height) { | |
unsigned i, j; | |
unsigned offset = cinfo.next_scanline * cinfo.image_width * 2; //offset to the correct row | |
for (i = 0, j = 0; i < cinfo.image_width * 2; i += 4, j += 6) { //input strides by 4 bytes, output strides by 6 (2 pixels) | |
tmprowbuf[j + 0] = input[offset + i + 0]; // Y (unique to this pixel) | |
tmprowbuf[j + 1] = input[offset + i + 1]; // U (shared between pixels) | |
tmprowbuf[j + 2] = input[offset + i + 3]; // V (shared between pixels) | |
tmprowbuf[j + 3] = input[offset + i + 2]; // Y (unique to this pixel) | |
tmprowbuf[j + 4] = input[offset + i + 1]; // U (shared between pixels) | |
tmprowbuf[j + 5] = input[offset + i + 3]; // V (shared between pixels) | |
} | |
jpeg_write_scanlines(&cinfo, row_pointer, 1); | |
} | |
jpeg_finish_compress(&cinfo); | |
jpeg_destroy_compress(&cinfo); | |
cout << "libjpeg produced " << outlen << " bytes" << endl; | |
output = vector<uint8_t>(outbuffer, outbuffer + outlen); | |
} | |
int main() { | |
ifstream input("frame-3.raw", ios::binary); | |
// copies all data into buffer | |
vector <uint8_t> inbuf((istreambuf_iterator<char>(input)), (istreambuf_iterator<char>())); | |
cout << "read " << inbuf.size() << " bytes" << endl; | |
vector<uint8_t> output; | |
compressYUYVtoJPEG(inbuf, 640, 480, output); | |
ofstream ofs("output.jpg", ios_base::binary); | |
ofs.write((const char*) &output[0], output.size()); | |
ofs.close(); | |
return 1; | |
} |
What the heck is the (width & -1) ? What is it ?
@effolkronium -1 would be a binary mask for a signed integer e.g. 0xFFFFFFFF
and &
is a binray AND operator. so effectively it would mask the width
with the integer making sure it doesn't overflow or get clipped
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What the heck is the (width & -1) ? What is it ?