1. 程式人生 > >OpenCV和FFMpeg圖片轉換對比

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介面,但就不知道為什麼差別會這麼大了。