camerad: use the YUV buffer to create the jpeg thumbnail (#21936)

* yuv420_to_jpeg

continue

* add comments

* cleanup

* return ky::array
pull/22268/head
Dean Lee 2021-09-18 08:00:21 +08:00 committed by GitHub
parent e8e83a3dcf
commit e0b5b4573e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 38 deletions

View File

@ -1,6 +1,6 @@
Import('env', 'arch', 'cereal', 'messaging', 'common', 'gpucommon', 'visionipc', 'USE_WEBCAM')
libs = ['m', 'pthread', common, 'jpeg', 'OpenCL', cereal, messaging, 'zmq', 'capnp', 'kj', visionipc, gpucommon]
libs = ['m', 'pthread', common, 'jpeg', 'OpenCL', 'yuv', cereal, messaging, 'zmq', 'capnp', 'kj', visionipc, gpucommon]
if arch == "aarch64":
libs += ['gsl', 'CB', 'adreno_utils', 'EGL', 'GLESv3', 'cutils', 'ui']

View File

@ -216,66 +216,84 @@ kj::Array<uint8_t> get_frame_image(const CameraBuf *b) {
return kj::mv(frame_image);
}
static void publish_thumbnail(PubMaster *pm, const CameraBuf *b) {
uint8_t* thumbnail_buffer = NULL;
unsigned long thumbnail_len = 0;
unsigned char *row = (unsigned char *)malloc(b->rgb_width/4*3);
static kj::Array<capnp::byte> yuv420_to_jpeg(const CameraBuf *b, int thumbnail_width, int thumbnail_height) {
std::unique_ptr<uint8[]> buf(new uint8_t[(thumbnail_width * thumbnail_height * 3) / 2]);
uint8_t *y_plane = buf.get();
uint8_t *u_plane = y_plane + thumbnail_width * thumbnail_height;
uint8_t *v_plane = u_plane + (thumbnail_width * thumbnail_height) / 4;
{
int result = libyuv::I420Scale(
b->cur_yuv_buf->y, b->rgb_width, b->cur_yuv_buf->u, b->rgb_width / 2, b->cur_yuv_buf->v, b->rgb_width / 2,
b->rgb_width, b->rgb_height,
y_plane, thumbnail_width, u_plane, thumbnail_width / 2, v_plane, thumbnail_width / 2,
thumbnail_width, thumbnail_height, libyuv::kFilterNone);
if (result != 0) {
LOGE("Generate YUV thumbnail failed.");
return {};
}
}
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
uint8_t *thumbnail_buffer = nullptr;
size_t thumbnail_len = 0;
jpeg_mem_dest(&cinfo, &thumbnail_buffer, &thumbnail_len);
cinfo.image_width = b->rgb_width / 4;
cinfo.image_height = b->rgb_height / 4;
cinfo.image_width = thumbnail_width;
cinfo.image_height = thumbnail_height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
#ifndef __APPLE__
jpeg_set_quality(&cinfo, 50, true);
jpeg_start_compress(&cinfo, true);
#else
jpeg_set_quality(&cinfo, 50, static_cast<boolean>(true) );
jpeg_start_compress(&cinfo, static_cast<boolean>(true) );
#endif
jpeg_set_colorspace(&cinfo, JCS_YCbCr);
// configure sampling factors for yuv420.
cinfo.comp_info[0].h_samp_factor = 2; // Y
cinfo.comp_info[0].v_samp_factor = 2;
cinfo.comp_info[1].h_samp_factor = 1; // U
cinfo.comp_info[1].v_samp_factor = 1;
cinfo.comp_info[2].h_samp_factor = 1; // V
cinfo.comp_info[2].v_samp_factor = 1;
cinfo.raw_data_in = TRUE;
JSAMPROW row_pointer[1];
const uint8_t *bgr_ptr = (const uint8_t *)b->cur_rgb_buf->addr;
for (int ii = 0; ii < b->rgb_height/4; ii+=1) {
for (int j = 0; j < b->rgb_width*3; j+=12) {
for (int k = 0; k < 3; k++) {
uint16_t dat = 0;
int i = ii * 4;
dat += bgr_ptr[b->rgb_stride*i + j + k];
dat += bgr_ptr[b->rgb_stride*i + j+3 + k];
dat += bgr_ptr[b->rgb_stride*(i+1) + j + k];
dat += bgr_ptr[b->rgb_stride*(i+1) + j+3 + k];
dat += bgr_ptr[b->rgb_stride*(i+2) + j + k];
dat += bgr_ptr[b->rgb_stride*(i+2) + j+3 + k];
dat += bgr_ptr[b->rgb_stride*(i+3) + j + k];
dat += bgr_ptr[b->rgb_stride*(i+3) + j+3 + k];
row[(j/4) + (2-k)] = dat/8;
jpeg_set_quality(&cinfo, 50, TRUE);
jpeg_start_compress(&cinfo, TRUE);
JSAMPROW y[16], u[8], v[8];
JSAMPARRAY planes[3]{y, u, v};
for (int line = 0; line < cinfo.image_height; line += 16) {
for (int i = 0; i < 16; ++i) {
y[i] = y_plane + (line + i) * cinfo.image_width;
if (i % 2 == 0) {
int offset = (cinfo.image_width / 2) * ((i + line) / 2);
u[i / 2] = u_plane + offset;
v[i / 2] = v_plane + offset;
}
}
row_pointer[0] = row;
jpeg_write_scanlines(&cinfo, row_pointer, 1);
jpeg_write_raw_data(&cinfo, planes, 16);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
free(row);
kj::Array<capnp::byte> dat = kj::heapArray<capnp::byte>(thumbnail_buffer, thumbnail_len);
free(thumbnail_buffer);
return dat;
}
static void publish_thumbnail(PubMaster *pm, const CameraBuf *b) {
auto thumbnail = yuv420_to_jpeg(b, b->rgb_width / 4, b->rgb_height / 4);
if (thumbnail.size() == 0) return;
MessageBuilder msg;
auto thumbnaild = msg.initEvent().initThumbnail();
thumbnaild.setFrameId(b->cur_frame_data.frame_id);
thumbnaild.setTimestampEof(b->cur_frame_data.timestamp_eof);
thumbnaild.setThumbnail(kj::arrayPtr((const uint8_t*)thumbnail_buffer, thumbnail_len));
thumbnaild.setThumbnail(thumbnail);
pm->send("thumbnail", msg);
free(thumbnail_buffer);
}
float set_exposure_target(const CameraBuf *b, int x_start, int x_end, int x_skip, int y_start, int y_end, int y_skip) {