1,Camera nv12格式数据实现镜像旋转
该操作即实现图像的左右翻转
步骤:
1.1根据width和height来计算stride跨距,和内存空间大小。
1.2NV12格式转换成I420格式,即分离出u和v plane。
1.3将I420格式数据镜像旋转。
1.4镜像旋转后,在转换格式为NV12即可。
代码示例
void HdmiRxStreamBuffer::LibyuvMirror_NV12(int buffer_width, int buffer_height, char* src_y_frame, char* src_uv_frame){
//y stride
int plane1_stride = buffer_width;
//uv stride
int plane2_stride = buffer_width;
//y height
int plane1_height = buffer_height;
//uv height
int plane2_height = buffer_height / 2;
//y_size
int32_t plane1_size = plane1_stride * plane1_height;
//uv_size
int32_t plane2_size = plane2_stride * plane2_height;
//yuv_size(内存空间)
int32_t frame_size = plane1_size + plane2_size;
// 1.NV12转换为I420
// 开辟buffer_frame大小的内存空间用于存放转换好的i420数据
uint8_t* buffer_y = (unsigned char *)malloc(frame_size);
uint8_t* buffer_u = buffer_y + plane1_size;
uint8_t* buffer_v = buffer_u + plane1_size / 4;
libyuv::NV12ToI420(/*const uint8 *src_y*/ (uint8_t *)src_y_frame,
/*int src_stride_y*/ plane1_stride,
/*const uint8 *src_uv*/ (uint8_t *)src_uv_frame,
/*int src_stride_uv*/ plane2_stride,
/*uint8 *dst_y*/ buffer_y,
/*int dst_stride_y*/ plane1_stride,
/*uint8 *dst_u*/ buffer_u,
/*int dst_stride_u*/ plane1_stride >> 1,
/*uint8 *dst_v*/ buffer_v,
/*int dst_stride_v*/ plane1_stride >> 1,
/*int width*/ buffer_width,
/*int height*/ buffer_height);
uint8_t* mirror_y = (unsigned char *)malloc(frame_size);
uint8_t* mirror_u = mirror_y + plane1_size;
uint8_t* mirror_v = mirror_u + plane1_size / 4;
libyuv::I420Mirror(/*const uint8* src_y*/buffer_y,
/*int src_stride_y*/plane1_stride,
/*const uint8* src_u*/buffer_u,
/*int src_stride_u*/plane1_stride >> 1,
/*const uint8* src_v*/buffer_v,
/*int src_stride_v*/plane1_stride >> 1,
/*uint8* dst_y*/mirror_y,
/*int dst_stride_y*/plane1_stride,
/*uint8* dst_u*/mirror_u,
/*int dst_stride_u*/plane1_stride >> 1,
/*uint8* dst_v*/mirror_v,
/*int dst_stride_v*/plane1_stride >> 1,
/*int width*/buffer_width,
/*int height*/buffer_height);
free(buffer_y);
uint8_t *nv12_y = (uint8_t *)malloc(frame_size);
uint8_t* nv12_uv = nv12_y + plane1_stride * plane1_height;
libyuv::I420ToNV12(/*const uint8 *src_y*/ mirror_y,
/*int src_stride_y*/ plane1_stride,
/*const uint8 *src_u*/ mirror_u,
/*int src_stride_u*/ plane1_stride >> 1,
/*const uint8 *src_v*/ mirror_v,
/*int src_stride_v*/ plane1_stride >> 1,
/*uint8 *dst_y*/ nv12_y,
/*int dst_stride_y*/ plane1_stride,
/*uint8 *dst_uv*/ nv12_uv,
/*int dst_stride_uv*/ plane1_stride,
/*int width*/ buffer_width,
/*int height*/ buffer_height);
free(mirror_y);
memcpy(src_y_frame, nv12_y, plane1_size);
memcpy(src_uv_frame, nv12_uv, plane2_size);
free(nv12_y);
}
2,Camera NV12格式数据实现缩放
即实现不同分辨率的差值,实现放大或者缩小。ScalePlane中算法参数kFilterNone执行速度快,但是图像模糊,尤其是小文本字体。如果使用kFilterBox算法,执行速度相对会变慢10ms左右。但是图像效果很清晰。
步骤:
2.1根据width和height计算stride等。
2.2对y plane实现缩放。
2.3分离uvplane成独立的u和vplane
2.4分别对u和vplane实现缩放
2.5再把u和vplane实现merge操作即可
参考代码:
void HdmiRxStreamBuffer::LibyuvResize_NV12(const Buffer *src,
Buffer *dst) {
uint32_t src_halfheight = SUBSAMPLE(src->height, 1, 1);
uint32_t src_halfwidth = SUBSAMPLE(src->width, 1, 1);
dst->stride[0] = dst->width;
dst->stride[1] = dst->width;
dst->stride[2] = 0;
dst->color = src->color;
// resize y_plane
libyuv::ScalePlane(src->data[0], src->stride[0],
src->width, src->height,
dst->data[0], dst->stride[0],
dst->width, dst->height,
/*libyuv::FilterMode::kFilterNone*/libyuv::FilterMode::kFilterBox);
// Split VUplane
uint8_t* uv_data = new uint8_t[src->width * src->height / 2];
uint8_t* u_data = uv_data;
uint8_t* v_data = uv_data + src->width * src->height / 4;
//malloc memory to store temp u v
uint8_t* temp_uv_data = new uint8_t[dst->width * dst->height / 2];
uint8_t* temp_u_data = temp_uv_data;
uint8_t* temp_v_data = temp_uv_data + dst->width * dst->height / 4;
libyuv::SplitUVPlane(src->data[1], src->stride[1],
u_data, src->stride[1] >> 1,
v_data, src->stride[1] >> 1,
src_halfwidth, src_halfheight);
// resize u and v
libyuv::ScalePlane(u_data, src->stride[1] >> 1,
src->width >> 1, src_halfheight,
temp_u_data, dst->stride[1] >> 1,
dst->width >> 1, dst->height >> 1,
/*libyuv::FilterMode::kFilterNone*/libyuv::FilterMode::kFilterBox);
libyuv::ScalePlane(v_data, src->stride[1] >> 1,
src->width >> 1, src_halfheight,
temp_v_data, dst->stride[1] >> 1,
dst->width >> 1, dst->height >> 1,
/*libyuv::FilterMode::kFilterNone*/libyuv::FilterMode::kFilterBox);
libyuv::MergeUVPlane(temp_u_data, dst->stride[1] >> 1,
temp_v_data, dst->stride[1] >> 1,
dst->data[1], dst->stride[1],
dst->width >> 1, dst->height >> 1);
delete[] uv_data;
u_data = v_data = uv_data = nullptr;
delete[] temp_uv_data;
temp_u_data = temp_v_data = temp_uv_data = nullptr;
}
3,将yuv422(UYVY)格式数据转换成NV12
3.1主要是计算好width和height,及跨距,就可以实现转换。转换后图像效果也很清晰,颜色上相差不大。也可以自行实现,隔行对uvplane读取就行,但是图像效果差很多。
参考代码:
libyuv::UYVYToNV12(mCache,
1920 * 2,
mHdmi_y_data,
1920,
mHdmi_uv_data,
1920,
1920,
1080);
3.2自行实现yuv422格式数据转换成nv12格式,主要思路是对u和v隔行读取即可,代码参考:
for (i = 0; i < 1920 * 1080 * 2; i += 4) {
// copy y channel
mHdmi_y_data[ycount++] = *(mCache + i + 1);
mHdmi_y_data[ycount++] = *(mCache + i + 3);
if (0 == i % pixels_in_a_row) {
++lines;
}
if (lines % 2) { // extract the UV value of odd rows
// copy uv channel
mHdmi_uv_data[ucount++] = *(mCache + i);
mHdmi_uv_data[ucount++] = *(mCache + i + 2);
}
}
4,总结
yuv格式的转换,旋转,缩放等操作都可以通过libyuv库接口实现。libyuv库有很多封装接口效率高,图像变换的效果对比相差不大。
android系统中libyuv接口路径:external\libyuv\files\source
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/182003.html