音視頻入門-10-使用libyuv對(duì)YUV數(shù)據(jù)進(jìn)行縮放碎浇、旋轉(zhuǎn)临谱、鏡像、裁剪奴璃、混合

* 音視頻入門文章目錄 *

libyuv

libyuv 是 Google 開源的實(shí)現(xiàn)各種 YUV 與 RGB 之間相互轉(zhuǎn)換悉默、旋轉(zhuǎn)、縮放等的庫(kù)苟穆。它是跨平臺(tái)的抄课,可在 Windows、Linux雳旅、Mac跟磨、Android 等操作系統(tǒng),x86攒盈、x64吱晒、arm 架構(gòu)上進(jìn)行編譯運(yùn)行,支持 SSE沦童、AVX仑濒、NEON 等 SIMD 指令加速。

準(zhǔn)備工作

一張圖片

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

image-demo-rainbow.png

FFmpeg 工具包

FFmpeg 工具下載

根據(jù)自己的系統(tǒng)墩瞳,下載 FFmpeg Static 工具包。

得到所需的 yuv420p 文件

將上面準(zhǔn)備的圖片轉(zhuǎn)換成 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 旋轉(zhuǎn)

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


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末氏豌,一起剝皮案震驚了整個(gè)濱河市喉酌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖泪电,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件般妙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡相速,警方通過(guò)查閱死者的電腦和手機(jī)碟渺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)突诬,“玉大人苫拍,你說(shuō)我怎么就攤上這事⊥叮” “怎么了绒极?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蔬捷。 經(jīng)常有香客問(wèn)我垄提,道長(zhǎng),這世上最難降的妖魔是什么周拐? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任铡俐,我火速辦了婚禮,結(jié)果婚禮上速妖,老公的妹妹穿的比我還像新娘。我一直安慰自己聪黎,他們只是感情好罕容,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稿饰,像睡著了一般锦秒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喉镰,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天旅择,我揣著相機(jī)與錄音,去河邊找鬼侣姆。 笑死生真,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捺宗。 我是一名探鬼主播柱蟀,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蚜厉!你這毒婦竟也來(lái)了长已?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎术瓮,沒(méi)想到半個(gè)月后康聂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胞四,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年恬汁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撬讽。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蕊连,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出游昼,到底是詐尸還是另有隱情甘苍,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布烘豌,位于F島的核電站载庭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏廊佩。R本人自食惡果不足惜囚聚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望标锄。 院中可真熱鬧顽铸,春花似錦、人聲如沸料皇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)践剂。三九已至鬼譬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逊脯,已是汗流浹背优质。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留军洼,地道東北人巩螃。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像匕争,于是被迫代替她去往敵國(guó)和親牺六。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容