2012-11-30 118 views
1

我有彩色OpenCV::Mat類型的JPEG圖像和我從他們的視頻使用avcodec創建。我得到的視頻是顛倒的,黑色&白色,每幀的每一行都移動了,我得到了對角線。什麼可能是這種輸出的原因? 按照this鏈接觀看,我開始使用avcodec中的視頻。 我使用acpicture_fill函數來創建avFramecv::Mat幀!如何使用avcodec從OpenCV :: Mat類型的jpeg圖像創建視頻?

P.S.我注意到avFrame(由acpicture_fill填充)有linesize[0]=2430 我試着手動設置avFrame->linesizep0]=2432而不是2430,但它仍然沒有幫助。

======== CODE ==================================== =====================

AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_H264); 
AVStream *outStream = avformat_new_stream(outContainer, encoder); 
avcodec_get_context_defaults3(outStream->codec, encoder); 

outStream->codec->pix_fmt = AV_PIX_FMT_YUV420P; 
outStream->codec->width = 810; 
outStream->codec->height = 610; 
//... 

SwsContext *swsCtx = sws_getContext(outStream->codec->width, outStream->codec->height, PIX_FMT_RGB24, 
            outStream->codec->width, outStream->codec->height, outStream->codec->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); 

for (uint i=0; i < frameNums; i++) 
{ 
    // get frame at location I using OpenCV 
    cv::Mat cvFrame; 
    myReader.getFrame(cvFrame, i); 
    cv::Size frameSize = cvFrame.size();  
    //Each cv::Mat cvFrame has width=810, height=610, step=2432 


1. // create AVPicture from cv::Mat frame 
2. avpicture_fill((AVPicture*)avFrame, cvFrame.data, PIX_FMT_RGB24, outStream->codec->width, outStream->codec->height); 
3avFrame->width = frameSize.width; 
4. avFrame->height = frameSize.height; 

    // rescale to outStream format 
    sws_scale(swsCtx, avFrame->data, avFrame->linesize, 0, outStream->codec->height, avFrameRescaledFrame->data, avFrameRescaledFrame ->linesize); 
encoderRescaledFrame->pts=i; 
avFrameRescaledFrame->width = frameSize.width; 
    avFrameRescaledFrame->height = frameSize.height; 

av_init_packet(&avEncodedPacket); 
    avEncodedPacket.data = NULL; 
    avEncodedPacket.size = 0; 

    // encode rescaled frame 
    if(avcodec_encode_video2(outStream->codec, &avEncodedPacket, avFrameRescaledFrame, &got_frame) < 0) exit(1); 
    if(got_frame) 
    { 
     if (avEncodedPacket.pts != AV_NOPTS_VALUE) 
      avEncodedPacket.pts = av_rescale_q(avEncodedPacket.pts, outStream->codec->time_base, outStream->time_base); 
     if (avEncodedPacket.dts != AV_NOPTS_VALUE) 
      avEncodedPacket.dts = av_rescale_q(avEncodedPacket.dts, outStream->codec->time_base, outStream->time_base); 

     // outContainer is "mp4" 
     av_write_frame(outContainer, & avEncodedPacket); 

     av_free_packet(&encodedPacket); 
    } 
} 

修訂

作爲@Alex建議我更改的行1-4的代碼低於

int width = frameSize.width, height = frameSize.height; 
avpicture_alloc((AVPicture*)avFrame, AV_PIX_FMT_RGB24, outStream->codec->width, outStream->codec->height); 
for (int h = 0; h < height; h++) 
{ 
    memcpy(&(avFrame->data[0][h*avFrame->linesize[0]]), &(cvFrame.data[h*cvFrame.step]), width*3); 
} 

我現在得到的視頻(here)幾乎是pe rfect。它不是倒置的,不是黑色的&白色,但似乎有一個RGB組件丟失。每個棕色/紅色變成藍色(在原始圖像中應該是副詩)。 可能是什麼問題?可以重新換算(sws_scale)到AV_PIX_FMT_YUV420P格式的原因造成的?

回答

2

問題簡而言之:avpicture_fill()預計在行之間沒有填充,即步幅(step)等於width*sizeof(pixel),即810 * 3 = 2430. cv :: Mat步驟中的實際步幅與您說是2432這是不同的,所以只是直接傳遞數據將無法正常工作。沒有辦法告訴avpicture_fill()對輸入數據使用不同的步幅;它不是API的一部分(你可能會說,這應該是:)

有兩種可能的解決方案:

創建一個數組,其中輸入數據是連續的,行之間沒有空白。你必須將cv :: Mat中的每一行記錄到該數組中。然後傳遞給avpicture_fill()

int width, height; // get from mat 
uint8_t* buf = malloc(width * height * 3); // 3 bytes per pixel 
for (int i = 0; i < height; i++) 
{ 
    memcpy(&(buf[ i*width*3 ]), &(mat->data[ i*mat->step ]), width*3); 
} 
avpicture_fill(..., buf, ...) 

順便說一句,垂直翻轉視頻,你可以做到這一點的最後一行復制到第一等:

... 
    memcpy(&(buf[ i*width*3 ]), &(mat->data[ (height - i - 1)*mat->step ]), width*3); 
... 

或者填寫AVPicture自己:

AVPicture* pic = malloc(sizeof(AVPicture)); 
avpicture_alloc(pic, PIX_FMT_BGR24, width, height); 
for (int i = 0; i < height; i++) 
{ 
    memcpy(&(pic->data[0][ i*pic->linesize[0] ]), &(mat->data[ i*mat->step ]), width*3); 
} 

沒有必要分配pic-> data [0]或設置pic-> linesize [0],avpicture_alloc()應該這樣做。也沒有必要填寫數據[1]或數據[2],那些應該是空的。

編輯:刪除舊的代碼,顯示覆制R,G,B分離飛機。 PIX_FMT_BGR24不是平面格式。

我不熟悉不夠用OpenCV的C++ API弄清楚如何獲得的寬度和高度(它不是mat->寬度,很明顯),但我想你明白我的意思。

P.S.順便說一句,你的視頻不是實際上是黑色和白色。只是每個連續的行都偏移了兩個字節,所以顏色會旋轉:紅色變爲綠色,綠色變爲藍色,等等。結果是灰度級,但如果仔細觀察,則各行都會着色。

+0

據我所知,我需要爲'pic-> data [0]'和'pic-> data [1]'和'pic-> data [2]'分配緩衝區多少? 'pic-> linesize [0]'也是0,我應該在那裏放2430嗎? – theateist

+0

我其實不太確定當格式是BGR24時avpicture_fill()如何填充圖片。請參閱上面的編輯。 –

+0

你不需要分配pic-> data [0]或設置linesize,avpicture_alloc()應該這樣做。無論如何,這都是第二種方法。請先嚐試第一種方法(將數據複製到沒有填充的buf並調用avpicture_fill)。 –

0

你有沒有使用OpenCV's features爲您創造視頻考慮?由於您的數據已存儲在cv::Mat中,因此更容易。

如果你想保持你的方法,你可以簡單地rotate the cv::Mat

+0

我不能使用的OpenCV的創建視頻前。我必須爲此使用avcodec。我試圖使用旋轉,但它沒有修復它。我仍然可以看到黑白視頻和對角線。我想這是步伐 – theateist

0

關於原始帖子UPDATE中的顏色問題。 ,是由於,

OpenCV的墊子(BGR) - > FFmpeg的AVFrame是(RGB)?

如果是這樣,嘗試,

cvtColor(cvFrame , cvFrame , CV_BGR2RGB) ; 

線1