2016-03-09 4 views
1

I'v собрал и протестировал это tutorial от here, который работает просто отлично. После того, как я попытался отредактировать учебник для чтения/преобразования кадров в grayscale. Я только что изменил pFrameRGB на pFrameGray, PIX_FMT_RGB24 до PIX_FMT_GRAY16 и сохранить только 200-й кадр. Он компилируется и запускается, но изображение не отображает ожидаемого. Что не так?Как извлечь изображение в оттенках серого из видео с помощью ffmpeg-library?

Изображение: image

Отредактированный код:

#include <libavcodec/avcodec.h> 
#include <libavformat/avformat.h> 
#include <libswscale/swscale.h> 

#include <stdio.h> 

// compatibility with newer API 
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) 
#define av_frame_alloc avcodec_alloc_frame 
#define av_frame_free avcodec_free_frame 
#endif 

void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) { 
    FILE *pFile; 
    char szFilename[32]; 
    int y; 

    // Open file 
    sprintf(szFilename, "frame%d.ppm", iFrame); 
    pFile=fopen(szFilename, "wb"); 
    if(pFile==NULL) 
    return; 

    // Write header 
    fprintf(pFile, "P6\n%d %d\n255\n", width, height); 

    // Write pixel data 
    for(y=0; y<height; y++) 
    fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile); 

    // Close file 
    fclose(pFile); 
} 

int main(int argc, char *argv[]) { 
    // Initalizing these to NULL prevents segfaults! 
    AVFormatContext *pFormatCtx = NULL; 
    int    i, videoStream; 
    AVCodecContext *pCodecCtxOrig = NULL; 
    AVCodecContext *pCodecCtx = NULL; 
    AVCodec   *pCodec = NULL; 
    AVFrame   *pFrame = NULL; 
    AVFrame   *pFrameGRAY = NULL; 
    AVPacket   packet; 
    int    frameFinished; 
    int    numBytes; 
    uint8_t   *buffer = NULL; 
    struct SwsContext *sws_ctx = NULL; 

    if(argc < 2) { 
    printf("Please provide a movie file\n"); 
    return -1; 
    } 
    // Register all formats and codecs 
    av_register_all(); 

    // Open video file 
    if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0) 
    return -1; // Couldn't open file 

    // Retrieve stream information 
    if(avformat_find_stream_info(pFormatCtx, NULL)<0) 
    return -1; // Couldn't find stream information 

    // Dump information about file onto standard error 
    av_dump_format(pFormatCtx, 0, argv[1], 0); 

    // Find the first video stream 
    videoStream=-1; 
    for(i=0; i<pFormatCtx->nb_streams; i++) 
    if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) { 
     videoStream=i; 
     break; 
    } 
    if(videoStream==-1) 
    return -1; // Didn't find a video stream 

    // Get a pointer to the codec context for the video stream 
    pCodecCtxOrig=pFormatCtx->streams[videoStream]->codec; 
    // Find the decoder for the video stream 
    pCodec=avcodec_find_decoder(pCodecCtxOrig->codec_id); 
    if(pCodec==NULL) { 
    fprintf(stderr, "Unsupported codec!\n"); 
    return -1; // Codec not found 
    } 
    // Copy context 
    pCodecCtx = avcodec_alloc_context3(pCodec); 
    if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) { 
    fprintf(stderr, "Couldn't copy codec context"); 
    return -1; // Error copying codec context 
    } 

    // Open codec 
    if(avcodec_open2(pCodecCtx, pCodec, NULL)<0) 
    return -1; // Could not open codec 

    // Allocate video frame 
    pFrame=av_frame_alloc(); 

    // Allocate an AVFrame structure 
    pFrameGRAY=av_frame_alloc(); 
    if(pFrameGRAY==NULL) 
    return -1; 

    // Determine required buffer size and allocate buffer 
    numBytes=avpicture_get_size(PIX_FMT_GRAY16, pCodecCtx->width, 
        pCodecCtx->height); 
    buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); 

    // Assign appropriate parts of buffer to image planes in pFrameGRAY 
    // Note that pFrameGRAY is an AVFrame, but AVFrame is a superset 
    // of AVPicture 
    avpicture_fill((AVPicture *)pFrameGRAY, buffer, PIX_FMT_GRAY16, 
     pCodecCtx->width, pCodecCtx->height); 

    // initialize SWS context for software scaling 
    sws_ctx = sws_getContext(pCodecCtx->width, 
       pCodecCtx->height, 
       pCodecCtx->pix_fmt, 
       pCodecCtx->width, 
       pCodecCtx->height, 
       PIX_FMT_GRAY16, 
       SWS_BILINEAR, 
       NULL, 
       NULL, 
       NULL 
       ); 

    // Read frames and save first five frames to disk 
    i=0; 
    while(av_read_frame(pFormatCtx, &packet)>=0) { 
    // Is this a packet from the video stream? 
    if(packet.stream_index==videoStream) { 
     // Decode video frame 
     avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); 

     // Did we get a video frame? 
     if(frameFinished) { 
    // Convert the image from its native format to GRAY 
    sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, 
      pFrame->linesize, 0, pCodecCtx->height, 
      pFrameGRAY->data, pFrameGRAY->linesize); 

    // Save the frame to disk 
    if(++i==200) 
     SaveFrame(pFrameGRAY, pCodecCtx->width, pCodecCtx->height, 
      i); 
     } 
    } 

    // Free the packet that was allocated by av_read_frame 
    av_free_packet(&packet); 
    } 

    // Free the GRAY image 
    av_free(buffer); 
    av_frame_free(&pFrameGRAY); 

    // Free the YUV frame 
    av_frame_free(&pFrame); 

    // Close the codecs 
    avcodec_close(pCodecCtx); 
    avcodec_close(pCodecCtxOrig); 

    // Close the video file 
    avformat_close_input(&pFormatCtx); 

    return 0; 
} 
+0

Что он показывает? –

+0

Обновлен мой вопрос и добавил изображение. Изображение искажено. Левая часть добавляется вправо, но размеры все еще правильные. Ни оттенки серого. – user1587451

ответ

2

Ваша главная проблема здесь:

fprintf(pFile, "P6\n%d %d\n255\n", width, height); 

Согласно некоторой информации о РРМЕ я нашел: http://paulbourke.net/dataformats/ppm/ вы написав заголовок PPM с идентификатором P6, что означает «цвет rgb». Если вы делаете изображение в оттенках серого, вы хотите вместо этого использовать P5.

Кроме того, вы используете формат пикселей PIX_FMT_GRAY16 из ffmpeg, но вы интерпретируете его как байты. вы, вероятно, должны были использовать PIX_FMT_GRAY8. Если вы действительно хотите получить 16-битный оттенок серого в своем выходе, вы должны изменить третье число в заголовке PPM с 255 на 65535.

+0

Спасибо, я отредактировал, как вы писали, но теперь внутренняя часть изображения увеличена до изображения. Другие части изображения обрезаны. – user1587451

+1

@ user1587451 просто измените '3 * width' на' width' в 'fwrite' в' SaveFrame'. Просто понять, зачем это необходимо. –