ffmpeg图像变换函数sws_scale

一、图像转换swscale库

FFmpeg中,swscale库主要功能为对图像进行变换,主要做:
1.图像大小变换
例:可以将图像从19201080(1080P) 变大3840×2160(4K) 或缩小为1280×720(720p)
2.图像格式转化

例:可以将图像从YUV420P 转化为RGB格式
3.图像滤波处理
会对图像进行滤波处理,例如平滑处理。

主要函数为sws_getCachedContext、sws_freeContext、sws_scale

sws_getCachedContext: 目的是创建一个SwsContext结构体
SwsContext *context : 传入一个SwsContext结构体,判断传入是否context 是否和传入的参数是一致的,如果是一致的就直接返回。不一致的话,则会释放掉后再创建。传入NULL的话就直接创建了。
int srcW:传入图像的宽度
int srcH:传入图像的高度
srcFormat: 传入图像的格式
int dstW: 希望转换的图像宽度
int dstH: 希望转换的图像高度
dstFormat:希望转换的图像格式
flags:选择缩放算法(当输入输出图像大小不同时生效),选择SWS_FAST_BILINEAR速度会比较快
srcFilter:输入图像的滤波器信息, 若不需要传NULL
dstFilter:输出图像的滤波器信息, 若不需要传NULL
param:特定缩放算法需要的参数,默认为NULL

struct SwsContext *sws_getCachedContext(struct SwsContext *context, int srcW,
                                        int srcH, enum AVPixelFormat srcFormat,
                                        int dstW, int dstH,
                                        enum AVPixelFormat dstFormat, int flags,
                                        SwsFilter *srcFilter,
                                        SwsFilter *dstFilter,
                                        const double *param)
{
    
    
    static const double default_param[2] = {
    
     SWS_PARAM_DEFAULT,
                                             SWS_PARAM_DEFAULT };
    int64_t src_h_chr_pos = -513, dst_h_chr_pos = -513,
            src_v_chr_pos = -513, dst_v_chr_pos = -513;

    if (!param)
        param = default_param;

    /* 先判断context是否为空,不为空对比参数是否与传输进来的参数一致 */
    if (context &&
        (context->srcW      != srcW      ||
         context->srcH      != srcH      ||
         context->srcFormat != srcFormat ||
         context->dstW      != dstW      ||
         context->dstH      != dstH      ||
         context->dstFormat != dstFormat ||
         context->flags     != flags     ||
         context->param[0]  != param[0]  ||
         context->param[1]  != param[1])) {
    
    

        av_opt_get_int(context, "src_h_chr_pos", 0, &src_h_chr_pos);
        av_opt_get_int(context, "src_v_chr_pos", 0, &src_v_chr_pos);
        av_opt_get_int(context, "dst_h_chr_pos", 0, &dst_h_chr_pos);
        av_opt_get_int(context, "dst_v_chr_pos", 0, &dst_v_chr_pos);
        sws_freeContext(context);
        context = NULL;
    }
  
    /* 为空则执行下一步申请空间 */
    if (!context) {
    
    
        if (!(context = sws_alloc_context()))
            return NULL;
        context->srcW      = srcW;
        context->srcH      = srcH;
        context->srcFormat = srcFormat;
        context->dstW      = dstW;
        context->dstH      = dstH;
        context->dstFormat = dstFormat;
        context->flags     = flags;
        context->param[0]  = param[0];
        context->param[1]  = param[1];

        av_opt_set_int(context, "src_h_chr_pos", src_h_chr_pos, 0);
        av_opt_set_int(context, "src_v_chr_pos", src_v_chr_pos, 0);
        av_opt_set_int(context, "dst_h_chr_pos", dst_h_chr_pos, 0);
        av_opt_set_int(context, "dst_v_chr_pos", dst_v_chr_pos, 0);

        if (sws_init_context(context, srcFilter, dstFilter) < 0) {
    
    
            sws_freeContext(context);
            return NULL;
        }
    }
    return context;
}

SwsContext *c:传入一个上下文地址
srcSlice :输入数据每个通道的指针,例如rgb只有一个通道,YUV数据则有3个通道
srcStride:输入数据的每个通道一行的字节数,例如 rgb 为packed模式所以填 宽度 * 3, YUV420P为planar模式 所以填一个数组
{ 宽度, 宽度 /2, 宽度 / 2 }
srcSliceY:处理数据起始位置
srcSliceH: 处理多少行数据,可以多线程进行处理。例如线程1处理0~h/2的数据,线程2处理h/2 ~ h的数据。
dst:输出数据的每个通道数据指针
dstStride:输出数据的每个通道一行的字节数

int attribute_align_arg sws_scale(struct SwsContext *c,
                                  const uint8_t * const srcSlice[],
                                  const int srcStride[], int srcSliceY,
                                  int srcSliceH, uint8_t *const dst[],
                                  const int dstStride[])
{
    
    
    if (c->nb_slice_ctx)
        c = c->slice_ctx[0];

    return scale_internal(c, srcSlice, srcStride, srcSliceY, srcSliceH,
                          dst, dstStride, 0, c->dstH);
}

sws_freeContext::释放context
SwsContext *c : 传入一个SwsContext的地址

void sws_freeContext(SwsContext *c)

二、yuv转RGBA代码

#include <iostream>
#include <fstream>
#include<ctime>
using namespace std;

extern "C"{
    
    
//引用ffmpeg头文件
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}

//预处理指令导入库
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib, "swscale.lib")

#define YUV_FILE "gzm.yuv"
#define RGB_FILE "1280_720_gzm.rgba"

int main(int argc, char* argv[])
{
    
    
    //1920*1080 YUV转 RGBA 1920*1080
    int width = 1920;
    int height = 1080;
    int rgb_width = 1280;
    int rgb_height = 720;
    int yuv_linesize[3] = {
    
     width, width / 2, width / 2 };
    SwsContext* yuv2rgb = nullptr;

    //YUV  yyyyyy uuu vvv
    unsigned char* yuv[3] = {
    
     0 };
    yuv[0] = new unsigned char[width * height];
    yuv[1] = new unsigned char[width * height / 4];
    yuv[2] = new unsigned char[width * height / 4];

    //RGB交叉存储 rgba rbga
    unsigned char* rgba = new unsigned char[width * height * 4];
    int rgba_linesize = rgb_width * 4;

    ifstream ifs;
    ifs.open(YUV_FILE, ios::binary);
    if (!ifs) return -1;

    ofstream ofs;
    ofs.open(RGB_FILE, ios::binary);
    if (!ofs) return -1;
    
    time_t start_time = time(NULL);
    cout << "start " << endl;
    while (true) 
    {
    
    
        
        ifs.read((char*)yuv[0], width * height);
        ifs.read((char*)yuv[1], width * height / 4);
        ifs.read((char*)yuv[2], width * height / 4);

        if (ifs.gcount() == 0) break;

        //YUV转RGBA
        yuv2rgb = sws_getCachedContext(
            yuv2rgb,   
            width, height,  
            AV_PIX_FMT_YUV420P, 
            rgb_width, rgb_height,
            AV_PIX_FMT_RGBA,
            SWS_BICUBLIN   //双线性插值算法,选择支持变化的算法
            , 0, 0, 0);

        if (!yuv2rgb)
        {
    
    
            cerr << "sws_getCachedContext failed" << endl;
            return -1;
        }

        unsigned char* data[1];
        data[0] = rgba;
        int lines[1] = {
    
     rgba_linesize };
        int ret = sws_scale(yuv2rgb,
            yuv,
            yuv_linesize,
            0,
            height, //输入高度
            data,
            lines);

        //ofs.write((char*)rgba, rgb_width * rgb_height * 4);
    }
    cout << "end " << endl;
    time_t end_time = time(NULL);
    cout << end_time - start_time << "" << endl;

    delete yuv[0];
    delete yuv[1];
    delete yuv[2];
    delete rgba;
    ofs.close();
    ifs.close();

    return 0;
}

测试背景: 1920 * 1080 YUV420P 数据大小1.4G 转换成 1280 * 720 RBGA 格式
SWS_FAST_BILINEAR : 耗时 4s
SWS_BILINEAR: 耗时 5s
SWS_BICUBLIN: 耗时 5s

猜你喜欢

转载自blog.csdn.net/weixin_42764231/article/details/129888319