2

タイプのjpeg画像に色OpenCV::Matを付け、それらからを使用してビデオを作成しますavcodec。取得したビデオは上下逆で、白黒で、各フレームの各行がシフトされ、対角線が表示されます。そのような出力の理由は何でしょうか?このリンクをたどって、avcodecを使用して取得したビデオをご覧ください。フレームからacpicture_fill作成する関数を使用しています!avFramecv::Mat

PS各cv::MatcvFrameの幅=810、高さ= 610、ステップ= 2432 avFrame(acpicture_fillで埋められている)が2430ではなくlinesize[0]=2430 手動で設定しようとしたが、それでも役に立たなかった。avFrame->linesizep0]=2432

========コード========================================= ================

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);
}

私が今得ているビデオ(ここ)はほぼ完璧です。逆さまではなく、白黒ではありませんが、RGBコンポーネントの1つが欠落しているようです。すべての茶色/赤の色が青になりました(元の画像ではその逆である必要があります)。何が問題なのですか?フォーマットするためのrescaling(sws_scale)はこれを引き起こしますか?AV_PIX_FMT_YUV420P

4

3 に答える 3

2

簡単にavpicture_fill()言うと、行間にパディングがないこと、つまりストライド(ステップ)が810 * 3 =2430に等しいことを想定していますwidth*sizeof(pixel)。cv::Matステップのデータの実際のストライドは2432であり、これは異なります。 、したがって、データを直接渡すだけでは機能しません。avpicture_fill()入力データに別のストライドを使用するように指示する方法はありません。それはAPIの一部ではありません(あなたはそれがそうあるべきだと言うかもしれません:)

2つの可能な解決策があります:

入力データが連続し、行間にパディングがない配列を作成します。cv::Matの各行をその配列にmemcopyする必要があります。次に、それをに渡し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()がそれを行う必要があります。また、data[1]またはdata[2]を入力する必要はありません。これらはnullである必要があります。

編集: R、G、Bを別々の平面にコピーすることを示していた古いコードを削除しました。PIX_FMT_BGR24は平面形式ではありません。

私はOpenCVC++ APIに精通していないため、幅と高さを取得する方法を理解できません(明らかに、mat-> widthではありません)が、私が何を意味するかはご存知だと思います。

PSところで、あなたのビデオは実際には白黒ではありません。連続する各行が2バイトオフセットされているだけなので、色が回転します。赤は緑になり、緑は青になります。結果はグレースケールっぽいですが、よく見ると個々の行が色付けされています。

于 2012-12-01T23:57:14.943 に答える
0

OpenCVの機能を使用してビデオを作成することを検討しましたか?データはすでにに保存されているため、はるかに簡単ですcv::Mat

アプローチを維持したい場合は、を回転させるcv::Matだけです。

于 2012-12-01T10:51:19.443 に答える
0

元の投稿のUPDATEの色の問題について。それは、

OpenCVマットは(BGR)-> FFmpeg AVFrameは(RGB)ですか?

もしそうなら、試してみてください、

cvtColor( cvFrame , cvFrame , CV_BGR2RGB ) ; 

1行目の前。

于 2013-09-09T04:00:41.667 に答える