OpenCV和FFMpeg图片转换对比
最近一直在处理图片,从H264解码后得到的图片是YUV图片,而且很多都是NV12的,不是YUV420P(它们的差别是NV12格式为YYY...Y UV UV UV ... UV,而420P格式为 YYY...YY UUU..U VVV...V),一张1920x1080的图片大小为3.1M,为了节省空间,我需要存储为jpg,jpeg格式实际上就是压缩后的YUV,别的不说先上代码
/* OpenCV */ cv::Mat srcimg, dstimg; srcimg.create(height*3/2, width, CV_8UC1); FILE* fp = fopen(yuvf, "rb"); fread(srcimg.data, 1, height*width*3/2, fp); // 需要进行一次转化 cv::cvtColor(srcimg, dstimg, cv::COLOR_YUV2BGR_NV12); // 文件保存,目前已经测试jpg和png文件可行 cv::imwrite(fout, dstimg);
/* libavcodec */
1 AVCodec* codec; 2 AVCodecContext* codecCtx; 3 struct SwsContext* swsCtx; 4 FILE* fp; 5 int inSz, tmpSz; 6 uint8_t *inbuff, *tmpbuff; 7 size_t rd; 8 int got_output = 0; 9 int ret = 0; 10 AVFrame *frame = NULL; 11 AVPacket pkt; 12 if (width < 1 || height < 1 || !in || !out) 13 return -1; 14 15 // 准备转换环境 16 av_register_all(); // 必须调用,否则 avcodec_find_encoder 会失败 17 codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG); 18 if (NULL == codec) { 19 return -2; 20 } 21 codecCtx = avcodec_alloc_context3(codec); 22 if (NULL == codecCtx) { 23 ret = -3; 24 goto codecctx_errout; 25 } 26 codecCtx->bit_rate = 4000000; 27 codecCtx->width =width; 28 codecCtx->height = height; 29 codecCtx->time_base = (AVRational){ 1, 25 }; 30 codecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P; 31 if (avcodec_open2(codecCtx, codec, NULL) < 0) { 32 ret = -4; 33 goto codecctx_errout; 34 } 35 frame = av_frame_alloc(); 36 av_init_packet(&pkt); 37 pkt.data = NULL; 38 pkt.size = 0; 39 40 // 加载源文件 41 fp = fopen(in, "rb"); 42 if (NULL == fp) { 43 ret = -5; 44 goto out; 45 } 46 47 inSz = av_image_get_buffer_size(infmt, width, height, 1); 48 if (inSz <= 0) { 49 ret = -6; 50 goto out; 51 } 52 inbuff = (uint8_t*)malloc(inSz); 53 if (NULL == inbuff) { 54 ret = -7; 55 goto out; 56 } 57 rd = fread(inbuff, 1, inSz, fp); 58 if (rd != (size_t)inSz) { 59 fclose(fp); 60 ret = -8; 61 goto allout; 62 } 63 fclose(fp); 64 65 // 转换格式为YUV420P 66 if (infmt != AV_PIX_FMT_YUVJ420P && infmt != AV_PIX_FMT_YUV420P) { 67 swsCtx = sws_getContext(width, height, infmt, width, height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, 0, 0, 0); 68 if (NULL == swsCtx) { 69 ret = -9; 70 goto allout; 71 } 72 tmpSz = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1); 73 tmpbuff = (uint8_t*)malloc(tmpSz); 74 if (NULL == tmpbuff) { 75 ret = -9; 76 goto allout; 77 } 78 79 AVFrame FrIn, FrOut; 80 av_image_fill_linesizes(FrIn.linesize, infmt, width); 81 av_image_fill_pointers(FrIn.data, infmt, height, inbuff, FrIn.linesize); 82 av_image_fill_linesizes(FrOut.linesize, AV_PIX_FMT_YUV420P, width); 83 av_image_fill_pointers(FrOut.data, AV_PIX_FMT_YUV420P, height, tmpbuff, FrOut.linesize); 84 85 sws_scale(swsCtx, (const uint8_t* const*)FrIn.data, FrIn.linesize, 0, height, FrOut.data, FrOut.linesize); 86 // 使用新的buffer 87 free(inbuff); 88 inbuff = tmpbuff; 89 } 90 // 填充AVFrame 91 frame->format = codecCtx->pix_fmt; 92 frame->width = width; 93 frame->height = height; 94 95 // 转换 96 av_image_fill_linesizes(frame->linesize, codecCtx->pix_fmt, width); 97 av_image_fill_pointers(frame->data, codecCtx->pix_fmt, height, inbuff, frame->linesize); 98 if (avcodec_send_frame(codecCtx, frame) < 0) { 99 ret = -10; 100 goto allout; 101 } 102 if (0 == avcodec_receive_packet(codecCtx, &pkt)) { 103 fp = fopen(out, "wb"); 104 if (fp) { 105 fwrite(pkt.data, 1, pkt.size, fp); 106 fclose(fp); 107 } 108 else { 109 ret = 12; 110 } 111 av_packet_unref(&pkt); 112 } 113 114 // 环境回收 115 allout: 116 free(inbuff); 117 out: 118 sws_freeContext(swsCtx); 119 av_frame_free(&frame); 120 codecctx_errout: 121 avcodec_close(codecCtx); 122 av_free(codecCtx); 123 errout: 124 return ret;
我的电脑配置I7 6700HQ,内存16G,系统Ubuntu 18.04,转换1920x1080图片结果为:OpenCV耗时超过80ms(在84左右浮动),而使用libavcodec版本只需要12ms。
OpevCV是很多做算法的喜欢的工具,可是这个效率,真的是很差 ,它的强项应该就是——代码足够短,FFMpeg使用将近100行的代码它只用了6行就完成了。
现在库包装越来越“豪华”,执行效率却是越来越低,我们真的需要这么高的硬件配置吗?还是我们被这么多工具给忽悠了?
近段在折腾Rockchip的RK3399,在前面被表扬的FFMepg这里就要被打脸了,解码1920x1080的H264视频流,FFMpeg也加入了硬解码,我测试了一下,CPU消耗也是100%,而我直接使用Rockchip提供的MPP借口解码CPU占用率维持在35%左右,没有细看FFMpeg代码,扫了一下,也是一样使用MPP接口,但就不知道为什么差别会这么大了。