您的当前位置:首页正文

使用 FFmpeg 的 libavutil 库来处理图像数据,对图像进行缩放和改变像素格式

2024-11-11 来源:个人技术集锦
#include <stdio.h>
#include <libavutil/imgutils.h>

int main() {
    const char *inputFileName = "input.jpg"; // 输入图片文件名
    const char *outputFileName = "output.jpg"; // 输出图片文件名
    int srcWidth, srcHeight, dstWidth, dstHeight; // 图像宽度和高度
    uint8_t *srcData[4], *dstData[4]; // 输入和输出图像数据指针
    int srcLinesize[4], dstLinesize[4]; // 输入和输出图像数据步长

    // 注册 FFmpeg 所有组件
    av_register_all();

    // 打开输入文件
    FILE *inputFile = fopen(inputFileName, "rb");
    if (!inputFile) {
        printf("无法打开输入文件\n");
        return -1;
    }

    // 读取输入图片的宽度和高度 //文件指针的移动只是为了获取文件大小
    fseek(inputFile, 0, SEEK_END);  //第二第三个参数是一对,偏移量是相对于定位方式而言的。负为左,0当前,正为右方
    int fileSize = ftell(inputFile);   //返回一个字节数。
    fseek(inputFile, 0, SEEK_SET);     //还原指针。

    uint8_t *fileBuffer = (uint8_t *)av_malloc(fileSize);
    fread(fileBuffer, 1, fileSize, inputFile);                  //把整个图像读入缓冲区了。

    fclose(inputFile);

    // 获取输入图片的宽度和高度
    AVFrame *pFrame = av_frame_alloc();
    AVFrame *pFrameRGB = av_frame_alloc();

	//第一个参数存了各个分量的数据,第二个分量存了,步长。最后一个参数的对此方式,1表示不进行内存对齐,四五六参数,分别表示输入图像的像素格式,宽度和高度。
	//这个方法将,图像数据填充到 AVFrame 结构体中。
    int ret = av_image_fill_arrays(pFrame->data, pFrame->linesize, fileBuffer,
                                   AV_PIX_FMT_RGB24, srcWidth, srcHeight, 1);
    if (ret < 0) {
        printf("无法填充输入图像数据\n");
        av_free(fileBuffer);
        av_frame_free(&pFrame);
        av_frame_free(&pFrameRGB);
        return -1;
    }

    srcWidth = pFrame->width;
    srcHeight = pFrame->height;

    // 设置输出图像的宽度和高度(缩放后的大小)
    dstWidth = srcWidth / 2;
    dstHeight = srcHeight / 2;

    // 分配输出图像数据缓冲区    //最后的参数也是对齐方式。
    int dstBufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, dstWidth, dstHeight, 1);
    uint8_t *dstBuffer = (uint8_t *)av_malloc(dstBufferSize);

    // 获取输入和输出图像数据指针和步长
    av_image_fill_pointers(srcData, AV_PIX_FMT_RGB24, srcHeight, pFrame->data, pFrame->linesize);    //真实 填充了。
    av_image_fill_pointers(dstData, AV_PIX_FMT_RGB24, dstHeight, dstBuffer, dstWidth * 3);    //填充了空

    // 创建图像转换上下文
    struct SwsContext *swsContext = sws_getContext(srcWidth, srcHeight, AV_PIX_FMT_RGB24,
                                                   dstWidth, dstHeight, AV_PIX_FMT_RGB24,
                                                   SWS_BILINEAR, NULL, NULL, NULL);
    if (!swsContext) {
        printf("图像转换上下文创建失败\n");
        av_free(dstBuffer);
        av_free(fileBuffer);
        av_frame_free(&pFrame);
        av_frame_free(&pFrameRGB);
        return -1;
    }

    // 进行图像缩放和转换
    //转换上下文,源,源的步长,源的起始行号,需要处理源的行数,目标,目标步长。
    sws_scale(swsContext, srcData, pFrame->linesize, 0, srcHeight, dstData, dstLinesize);

    // 保存输出图像到文件
    FILE *outputFile = fopen(outputFileName, "wb");
    if (!outputFile) {
        printf("无法打开输出文件\n");
        av_free(dstBuffer);
        av_free(fileBuffer);
        av_frame_free(&pFrame);
        av_frame_free(&pFrameRGB);
        sws_freeContext(swsContext);
        return -1;
    }

    //fwrite(dstBuffer, 1, dstBufferSize, outputFile);
	// 写入图像数据到输出文件
	for (int i = 0; i < dstHeight; i++) {
	    fwrite(dstData[0] + i * dstLinesize[0], 1, dstWidth * 3, outputFile);
	}
    fclose(outputFile);

    // 释放资源
    av_free(dstBuffer);
    av_free(fileBuffer);
    av_frame_free(&pFrame);
    av_frame_free(&pFrameRGB);
    sws_freeContext(swsContext);

    return 0;
}

**

以上是一个简单完整例子的代码

**

假设有一个 RGB24 格式的图像,图像宽度为 640 像素,高度为 480 像素。那么步长数组可以定义为:

int linesize[3] = {640 * 3, 0, 0};

在这个例子中,linesize[0] 表示 R 分量的步长,它等于图像宽度乘以 3,因为 RGB24 格式每个像素有 3 个分量(R、G、B),每个分量占据一个字节。

linesize[1] 和 linesize[2] 分别表示 G 和 B 分量的步长,因为在 RGB24 格式中,G 和 B 分量紧随 R 分量存储,它们的步长可以设为 0,表示它们与 R 分量存储在同一行内。

这样,通过 linesize 数组,可以正确地访问图像数据中的每个像素和分量。例如,要访问图像中第 i 行第 j 列的像素的 R 分量,可以使用以下代码:

uint8_t r = frame->data[0][i * linesize[0] + j * 3];

其中,frame 是 AVFrame 结构体,frame->data[0] 表示 R 分量的指针数组,i * linesize[0] 表示第 i 行的偏移量,j * 3 表示第 j 列的偏移量。通过这样的方式,可以准确地访问图像中的像素数据。

要访问图像中第 i 行第 j 列的像素的 G分量:

uint8_t r = frame->data[1][i * linesize[0] + j * 3];

以此类推。

显示全文