多视频叠加-黑色素材叠加

作者: Don_ | 来源:发表于2016-04-11 19:46 被阅读314次

    相关

    视频叠加算法-白色素材叠加
    视频叠加算法-彩色素材叠加
    视频叠加算法-彩色加亮融合
    视频叠加算法-彩色均值融合

    引言

    如果想在之上叠加一个静止图片很简单,像ffmpeg的滤镜、opencv等都能实现。但是假如文字拥有动画,而且文字出现比较频繁,全部使用序列的png图像会很大。例如如下的素材:

    黑色素材

    虽然与白色素材叠加算法中所用素材相同,但目的不同,以下demo将素材以“黑色部分叠加,白色区域透明”的效果叠加到视频之上。

    算法实现

    原视频:

    input
    #include <stdio.h>
    #include <math.h>
    #ifdef __cplusplus
    extern "C" {
    #endif
    #include <libavformat/avformat.h>
    #include <libavcodec/avcodec.h>
    #include <libavutil/frame.h>
    #include <libavutil/opt.h>
    #include <libavutil/imgutils.h>
    #include <libavutil/pixfmt.h>
    #ifdef __cplusplus
    };
    #endif
    int frame_cover_black( AVFrame* dst_frame, AVFrame* src_frame, AVFrame* cover_frame);
    int init_frame(AVFrame* frame,int width,int height,uint8_t* dst_buff);
    int mergeyuv(char* file, char* light,char* lightout,int width,int height);
    int write_yuvframe(AVFrame *pFrame,FILE *out);
    int main (char** args, int argv)
    {
        char* file = "test.yuv";
        char* light = "light.yuv";
        char* lightout = "lightout.yuv";
        int width = 480;
        int height = 480;
        mergeyuv(file,light,lightout,width,height);
        return 0;
    }
    int mergeyuv(char* file, char* light,char* lightout,int width,int height)
    {
        AVFrame* readframe,*lightframe,*outframe;
        readframe = av_frame_alloc();
        lightframe = av_frame_alloc();
        outframe = av_frame_alloc();
        FILE* readfile = (FILE*)fopen(file,"rb");
        FILE* lightfile = (FILE*)fopen(light,"rb");
        FILE* outfile = (FILE*)fopen(lightout,"wb+");
        if(lightfile==NULL||readframe==NULL||outfile==NULL)
            return -1;
        int length = width*height*3/2;
        uint8_t* readbuff = (uint8_t*)malloc(length);
        uint8_t* lightbuff = (uint8_t*)malloc(length);
        uint8_t* outbuff = (uint8_t*)malloc(length);
        init_frame(readframe,width,height,readbuff);
        init_frame(lightframe,width,height,lightbuff);
        init_frame(outframe,width,height,outbuff);
        while(fread(readbuff,1,length,readfile))
        {
            if(fread(lightbuff,1,length,lightfile))
            {
                puts("mrege one frame");
                frame_cover_black(readframe,readframe,lightframe);
                write_yuvframe(readframe,outfile);
            }
            else
                break;
        }
        fclose(readfile);
        fclose(lightfile);
        fclose(outfile);
        free(readbuff);
        free(lightbuff);
        free(outbuff);
        av_frame_free(&readframe);
        av_frame_free(&lightframe);
        av_frame_free(&outframe);
        return 0;
    }
    int init_frame(AVFrame* frame,int width,int height,uint8_t* dst_buff)
    {
        if(!avpicture_fill((AVPicture *) frame, dst_buff, AV_PIX_FMT_YUV420P,width,height))
        {
            puts("init frame error");
           av_frame_free(&frame);
            return NULL;
        }
        frame->width=width;
        frame->height=height;
        frame->format = AV_PIX_FMT_YUV420P;
        return 0;
    }
    int frame_cover_black( AVFrame* dst_frame, AVFrame* src_frame, AVFrame* cover_frame)
    {
        if(dst_frame == NULL || src_frame == NULL || cover_frame == NULL)
        {
            puts("frame_cover_black input or output frame is NULL");
            return -1;
        }
        int w2 = cover_frame->width;
        int h2 = cover_frame->height;
        int i = 0, j = 0;
        int a, a2;
        float rat;
        for(i = 0; i < h2; i++)
        {
            for(j = 0; j < w2; j++)
            {
                a2 = cover_frame->data[0][i * cover_frame->linesize[0] + j];
                a2 = a2 >= 0 ? a2 : 256 + a2;
                if(a2 <= 32)
                {
                    dst_frame->data[1][(j >> 1) + (int)(i >> 1) * (dst_frame->linesize[1])] = cover_frame->data[1][(j >> 1) + (int)(i >> 1) * (cover_frame->linesize[1])];
                    dst_frame->data[2][(j >> 1) + (int)(i >> 1) * (dst_frame->linesize[2])] = cover_frame->data[2][(j >> 1) + (int)(i >> 1) * (cover_frame->linesize[2])];
                    dst_frame->data[0][i * dst_frame->linesize[0] + j] = 16;
                    continue;
                }
                a = src_frame->data[0][i * src_frame->linesize[0] + j];
                a  = a >= 0 ? a : 256 + a;
                rat = (double)a2 / 256;
                rat = rat > 1 ? 1 : rat;
                dst_frame->data[0][i * dst_frame->linesize[0] + j] = a * rat;
            }
        }
        return 0;
    }
    int write_yuvframe(AVFrame *pFrame,FILE *out)
    {
        int height = pFrame->height,width = pFrame->width;
        if(pFrame==NULL)
        {
            puts("error:write frame is null");
            return -1;
        }
        if(out == NULL)
        {
            puts("give write file is null");
            return -1;
        }
        int j = 0;
        for (j = 0; j < height; j++)
            fwrite(pFrame->data[0] + j * pFrame->linesize[0], 1, width, out);
        for (j = 0; j < height / 2; j++)
            fwrite(pFrame->data[1] + j * pFrame->linesize[1], 1, width / 2, out);
        for (j = 0; j < height / 2; j++)
            fwrite(pFrame->data[2] + j * pFrame->linesize[2], 1, width / 2,out);
        return 0;
    }
    

    这是效果:

    output

    注:

    选用16作拐点的话,会出现大量泛白区域,所以选用32作为拐点来分离出黑色区域。但是同样会忽视某些细节。当然,很可能这些细节是由于编码的“有损”而产生的。使用200 作为全透明峰值。
    y为素材视频对应点的Y值,d(包含uv)为输出帧的数据
    if y< 32
    d设置为黑色
    else if y< 200
    d按比例趋近黑色
    else
    忽视素材叠加,取原帧对应点数据
    具体解释参看视频叠加算法-白色素材叠加

    三 待改进

    1. 应该将素材视频生成的尺寸缩小,通过指定坐标的方法融合,从而提升算法效率。

    2 会忽略素材视频中的细节,最终视频中有锯齿。

    3 对于半透明处,也就是算法中 d按比例趋近黑色处,该计算方法会使得输出视频透明略显生硬,梯度并不明显,该处计算方法待改进。

    相关文章

      网友评论

        本文标题:多视频叠加-黑色素材叠加

        本文链接:https://www.haomeiwen.com/subject/xepilttx.html