音視頻入門-10-使用libyuv對YUV數據進行縮放、旋轉、鏡像、裁剪、混合

音視頻入門文章目錄

libyuv

libyuv 是 Google 開源的實現各種 YUV 與 RGB 之間相互轉換、旋轉、縮放等的庫。它是跨平臺的,可在 Windows、Linux、Mac、Android 等操作系統,x86、x64、arm 架構上進行編譯運行,支持 SSE、AVX、NEON 等 SIMD 指令加速。

準備工作

一張圖片

下載 rainbow-700x700.bmp BMP 圖片 或者 自己準備一張圖片(知道分辨率,如:700x700)

image-demo-rainbow.png

FFmpeg 工具包

FFmpeg 工具下載

根據自己的系統,下載 FFmpeg Static 工具包。

得到所需的 yuv420p 文件

將上面準備的圖片轉換成 YUV420P 格式:

ffmpeg -i rainbow.bmp -video_size 700x700 -pix_fmt yuv420p rainbow-yuv420p.yuv

查看 YUV 文件

ffplay -f rawvideo -pixel_format yuv420p -video_size 700x700 rainbow-yuv420p.yuv

libyuv 操作 YUV

YUV 裁剪

libyuv-yuv420p-clip.jpg

[rainbow-yuv420p.yuv] -> [rainbow-yuv420p-clip-x-y.yuv]

#include <stdio.h>
#include <stdint.h>
#include "libyuv.h"

void clip(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height, int cropX, int cropY, int cropWidth, int cropHeight) {
    ConvertToI420(
            srcYuvData,
            width*height*3/2,
            dstYuvData,
            cropWidth,
            dstYuvData+cropWidth*cropHeight,
            (cropWidth+1)/2,
            dstYuvData+cropWidth*cropHeight+((cropWidth+1)/2)*((cropHeight+1)/2),
            (cropWidth+1)/2,
            cropX,
            cropY,
            width,
            height,
            cropWidth,
            cropHeight,
            kRotate0,
            FOURCC_YU12);
}

int main() {
    uint32_t width = 700, height = 700;
    uint32_t clipWidth = 200, clipHeight = 200;
    uint8_t YUV[width*height*3/2];
    uint8_t YUV_CLIP[clipWidth*clipHeight*3/2];

    FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p.yuv", "rb");
    fread(YUV, sizeof(YUV), 1, yuv420pFile);

    clip(YUV, YUV_CLIP, width, height, 0, 0, clipWidth, clipHeight);

    FILE *yuvClipFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-clip-0-0.yuv", "wb");
    fwrite(YUV_CLIP, sizeof(YUV_CLIP), 1, yuvClipFile);

    fclose(yuvClipFile);
    fclose(yuv420pFile);
    return 0;
}

YUV 縮放

libyuv-yuv420p-scale.jpg

[rainbow-yuv420p.yuv] -> [rainbow-yuv420p-scale-X.yuv]

#include <stdio.h>
#include <stdint.h>
#include "libyuv.h"

void scale(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height, int dstWidth, int dstHeight) {
    I420Scale(
            srcYuvData,
            width,
            srcYuvData+width*height,
            (width+1)/2,
            srcYuvData+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            width,
            height,
            dstYuvData,
            dstWidth,
            dstYuvData+dstWidth*dstWidth,
            (dstWidth+1)/2,
            dstYuvData+dstWidth*dstHeight+((dstWidth+1)/2)*((dstHeight+1)/2),
            (dstWidth+1)/2,
            dstWidth,
            dstHeight,
            kFilterNone
            );
}

int main() {
    uint32_t width = 700, height = 700;
    uint32_t dstWidth = 100, dstHeight = 100;
    uint8_t YUV[width*height*3/2];
    uint8_t YUV_SCALE[dstWidth*dstHeight*3/2];

    FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p.yuv", "rb");
    fread(YUV, sizeof(YUV), 1, yuv420pFile);

    scale(YUV, YUV_SCALE, width, height, dstWidth, dstHeight);

    FILE *yuvScaleFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-scale-6.yuv", "wb");
    fwrite(YUV_SCALE, sizeof(YUV_SCALE), 1, yuvScaleFile);

    fclose(yuvScaleFile);
    fclose(yuv420pFile);
    return 0;
}

YUV 旋轉

libyuv-yuv420p-rotation.jpg

[rainbow-yuv420p.yuv] -> [rainbow-yuv420p-rotation-90.yuv]

#include <stdio.h>
#include <stdint.h>
#include "libyuv.h"

void rotation(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height) {
    I420Rotate(
            srcYuvData,
            width,
            srcYuvData+width*height,
            (width+1)/2,
            srcYuvData+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            dstYuvData,
            width,
            dstYuvData+width*height,
            (width+1)/2,
            dstYuvData+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            width,
            height,
            kRotate90
            );
}

int main() {
    uint32_t width = 700, height = 700;
    uint8_t YUV[width*height*3/2];
    uint8_t YUV_ROTATION[width*height*3/2];

    FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p.yuv", "rb");
    fread(YUV, sizeof(YUV), 1, yuv420pFile);

    rotation(YUV, YUV_ROTATION, width, height);

    FILE *yuvRotationFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-rotation-90.yuv", "wb");
    fwrite(YUV_ROTATION, sizeof(YUV_ROTATION), 1, yuvRotationFile);

    fclose(yuvRotationFile);
    fclose(yuv420pFile);
    return 0;
}

YUV 鏡像

libyuv-yuv420p-mirror.jpg

[rainbow-yuv420p-rotation-90.yuv] -> [rainbow-yuv420p-rotation-90-mirror.yuv]

#include <stdio.h>
#include <stdint.h>
#include "libyuv.h"

void mirror(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height) {
    I420Mirror(
            srcYuvData,
            width,
            srcYuvData+width*height,
            (width+1)/2,
            srcYuvData+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            dstYuvData,
            width,
            dstYuvData+width*height,
            (width+1)/2,
            dstYuvData+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            width,
            height
            );
}

int main() {
    uint32_t width = 700, height = 700;
    uint8_t YUV[width*height*3/2];
    uint8_t YUV_MIRROR[width*height*3/2];

    FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-rotation-90.yuv", "rb");
    fread(YUV, sizeof(YUV), 1, yuv420pFile);

    mirror(YUV, YUV_MIRROR, width, height);

    FILE *yuvMirrorFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-rotation-90-mirror.yuv", "wb");
    fwrite(YUV_MIRROR, sizeof(YUV_MIRROR), 1, yuvMirrorFile);

    fclose(yuvMirrorFile);
    fclose(yuv420pFile);
    return 0;
}

YUV 混合

libyuv-yuv420p-blend.jpg

[rainbow-yuv420p.yuv] -> [rainbow-yuv420p-blend.yuv]

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "libyuv.h"

void blend(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height) {
    uint8_t YUV_ROTATION[width*height*3/2];
    I420Rotate(
            srcYuvData,
            width,
            srcYuvData+width*height,
            (width+1)/2,
            srcYuvData+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            YUV_ROTATION,
            width,
            YUV_ROTATION+width*height,
            (width+1)/2,
            YUV_ROTATION+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            width,
            height,
            kRotate90
    );

    // 透明度
    uint8_t alpha[width*height];
    memset(alpha, 0X88, width*height);

    I420Blend(
            srcYuvData,
            width,
            srcYuvData+width*height,
            (width+1)/2,
            srcYuvData+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            YUV_ROTATION,
            width,
            YUV_ROTATION+width*height,
            (width+1)/2,
            YUV_ROTATION+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            alpha,
            width,
            dstYuvData,
            width,
            dstYuvData+width*height,
            (width+1)/2,
            dstYuvData+width*height+((width+1)/2)*((height+1)/2),
            (width+1)/2,
            width,
            height
            );
}

int main() {
    uint32_t width = 700, height = 700;
    uint8_t YUV[width*height*3/2];
    uint8_t YUV_BLEND[width*height*3/2];

    FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p.yuv", "rb");
    fread(YUV, sizeof(YUV), 1, yuv420pFile);

    blend(YUV, YUV_BLEND, width, height);

    FILE *yuvBlendFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-blend.yuv", "wb");
    fwrite(YUV_BLEND, sizeof(YUV_BLEND), 1, yuvBlendFile);

    fclose(yuvBlendFile);
    fclose(yuv420pFile);
    return 0;
}

代碼:
10-yuv-conversion-libyuv

參考資料:

libyuv/libyuv

內容有誤?聯繫作者:

聯繫作者


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章