音视频入门-08-RGB&YUV

* 音视频入门文章目录 *

YUV & RGB 相互转换公式

YCbCr 的 Y 与 YUV 中的 Y 含义一致,Cb 和 Cr 与 UV 同样都指色彩,Cb 指蓝色色度,Cr 指红色色度,在应用上很广泛,JPEG、MPEG、DVD、摄影机、数字电视等皆采此一格式。
因此一般俗称的 YUV 大多是指 YCbCr 。

  • RGB to YUV(YCbCr)

RGB 范围 [0,255],Y 范围 [16,235] ,UV 范围 [16,239]。如果计算结果超过了取值范围要进行截取。

Y = 0.257*R + 0.504*G + 0.098*B + 16;
U = -0.148*R - 0.291*G + 0.439*B + 128;
V = 0.439*R - 0.368*G - 0.071*B + 128;
  • YUV(YCbCr)to RGB

RGB 范围 [0,255],Y 范围 [16,235] ,UV 范围 [16,239]。如果计算结果超过了取值范围要进行截取。

R = 1.164*(Y - 16) + 1.596*(V - 128);
G = 1.164*(Y - 16) - 0.813*(V - 128) - 0.391*(U - 128);
B = 1.164*(Y - 16) + 2.018*(U - 128);

FFmpeg 工具的辅助

FFmpeg 将 之前生成的彩虹图 BMP 转成 YUV 格式的文件、RGB 格式的文件

ffmpeg -i rainbow.bmp -video_size 700x700 -pix_fmt yuv444p rainbow-yuv444p.yuv
ffmpeg -i rainbow.bmp -video_size 700x700 -pix_fmt rgb24 rainbow-rgb24.rgb

FFplay 查看生成的 YUV 格式的文件、RGB 格式的文件

ffplay -f rawvideo -pix_fmt yuv444p -video_size 700x700 rainbow-yuv444p.yuv
ffplay -f rawvideo -pix_fmt rgb24 -video_size 700x700 rainbow-rgb24.rgb
ffmpeg-bmp-to-yuv444p-rgb24.jpg

用代码实现 RGB & YUV 转换

RGB -> YUV

将 FFmpeg 生成的 RGB24 格式转换成 YUV444P 格式。
[rainbow-rgb24.rgb] -> [rainbow-rgb24-to-yuv444p.yuv]

#include <stdio.h>
#include <string.h>
#include <math.h>

// 彩虹的七种颜色
u_int32_t rainbowColors[] = {
        0XFF0000, // 红
        0XFFA500, // 橙
        0XFFFF00, // 黄
        0X00FF00, // 绿
        0X007FFF, // 青
        0X0000FF, // 蓝
        0X8B00FF  // 紫
};

u_int8_t bound(u_int8_t start, int value, u_int8_t end) {
    if(value <= start) {
        return start;
    }
    if(value >= end) {
        return end;
    }
    return value;
}

void rgbToYuv(u_int8_t R, u_int8_t G, u_int8_t B, u_int8_t *Y, u_int8_t *U, u_int8_t *V) {
    int y_val = (int)round(0.257*R + 0.504*G + 0.098*B + 16);
    int u_val = (int)round(-0.148*R - 0.291*G + 0.439*B + 128);
    int v_val = (int)round(0.439*R - 0.368*G - 0.071*B + 128);
    *Y = bound(16, y_val, 235);
    *U = bound(16, u_val, 239);
    *V = bound(16, v_val, 239);
}


void rgb24ToYuv444p(const u_int8_t *rgb24Data, u_int8_t *yuv444pData, int width, int height) {

    int8_t yuv_y[width*height];
    int8_t yuv_u[width*height];
    int8_t yuv_v[width*height];

    for (int i = 0; i < width; ++i) {
        for (int j = 0; j < height; ++j) {
            u_int8_t Y, U, V;
            u_int8_t R, G, B;

            int currentRGBIndex = 3*(i*height+j);
            R = rgb24Data[currentRGBIndex];
            G = rgb24Data[currentRGBIndex+1];
            B = rgb24Data[currentRGBIndex+2];

            rgbToYuv(R, G, B, &Y, &U, &V);

            int currentYUVIndex = i*height+j;
            yuv_y[currentYUVIndex] = Y;
            yuv_u[currentYUVIndex] = U;
            yuv_v[currentYUVIndex] = V;
        }
    }
    
    memcpy(yuv444pData, yuv_y, sizeof(yuv_y));
    memcpy(yuv444pData + sizeof(yuv_y), yuv_u, sizeof(yuv_u));
    memcpy(yuv444pData + sizeof(yuv_y) + sizeof(yuv_u), yuv_v, sizeof(yuv_v));
    
}

int main() {
    int width = 700, height = 700;
    u_int8_t yuv444pData[width*height*3];
    u_int8_t rgb24Data[width*height*3];
    
    FILE *rgb24File = fopen("/Users/staff/Desktop/rainbow-rgb24.rgb", "rb");
    fread(rgb24Data, sizeof(rgb24Data), 1, rgb24File);
    
    rgb24ToYuv444p(rgb24Data, yuv444pData, width, height);

    FILE *yuv444pFile = fopen("/Users/ff/Desktop/rainbow-rgb24-to-yuv444p.yuv", "wb");
    fwrite(yuv444pData, sizeof(yuv444pData), 1, yuv444pFile);
    
    fclose(rgb24File);
    fclose(yuv444pFile);
    return 0;
}
ffplay -f rawvideo -pix_fmt rgb24 -video_size 700x700 rainbow-rgb24.rgb
ffplay -f rawvideo -pix_fmt yuv444p -video_size 700x700 rainbow-rgb24-to-yuv444p.yuv
rgb24-to-yuv444p.jpg

YUV -> RGB

将 FFmpeg 生成的 YUV444P 格式转换成 RGB24 格式。
[rainbow-yuv444p.yuv] -> [rainbow-yuv444p-to-rgb24.rgb]

#include <stdio.h>
#include <math.h>


u_int8_t bound(u_int8_t start, int value, u_int8_t end) {
    if(value <= start) {
        return start;
    }
    if(value >= end) {
        return end;
    }
    return value;
}


void yuv444pToRGB(u_int8_t *yuv444pData,u_int8_t *rgb24Data, int width, int height) {
    u_int8_t *srcY = yuv444pData, *srcU = srcY + width * height, *srcV = srcU + width * height;

    for(int i = 0 ; i < height ; i ++) {
        for (int j = 0; j < width; j++) {
            int currentYUVIndex = i * height + j;
            u_int8_t Y = srcY[currentYUVIndex], U = srcU[currentYUVIndex], V = srcV[currentYUVIndex];

            int r_val = (int)round(1.164*(Y-16)+1.596*(V-128));
            int g_val = (int)round(1.164*(Y-16)-0.813*(V-128)-0.391*(U-128));
            int b_val = (int)round(1.164*(Y-16)+2.018*(U-128));

            int currentRGBIndex = 3*(i * width + j);
            rgb24Data[currentRGBIndex] = bound(0, r_val, 255);
            rgb24Data[currentRGBIndex+1] = bound(0, g_val, 255);
            rgb24Data[currentRGBIndex+2] = bound(0, b_val, 255);
        }
    }
}

int main() {
    int width = 700, height = 700;
    u_int8_t yuv444pData[width*height*3];
    u_int8_t rgb24Data[width*height*3];

    FILE *yuv444pFile = fopen("/Users/staff/Desktop/rainbow-yuv444p.yuv", "rb");
    fread(yuv444pData, sizeof(yuv444pData), 1, yuv444pFile);

     yuv444pToRGB(yuv444pData, rgb24Data, width, height);

    FILE *rgb24File = fopen("/Users/staff/Desktop/rainbow-yuv444p-to-rgb24.rgb", "wb");
    fwrite(rgb24Data, sizeof(rgb24Data), 1, rgb24File);

    fclose(yuv444pFile);
    fclose(rgb24File);
    return 0;
}
ffplay -f rawvideo -pix_fmt yuv444p -video_size 700x700 rainbow-yuv444p.yuv
ffplay -f rawvideo -pix_fmt rgb24 -video_size 700x700 rainbow-yuv444p-to-rgb24.rgb
yuv444p-to-rgb24.jpg

使用色彩丰富的图片验证

准备一张手机拍摄的 Disney.png

ffmpeg -i Disney.png -video_size 700x700 -pix_fmt yuv444p Disney-yuv444p.yuv
ffmpeg -i Disney.png -video_size 700x700 -pix_fmt rgb24 Disney-rgb24.rgb
ffplay -f rawvideo -pix_fmt yuv444p -video_size 700x700 Disney-yuv444p.yuv
ffplay -f rawvideo -pix_fmt rgb24 -video_size 700x700 Disney-rgb24.rgb
Disney-demo.jpg

使用上面的代码进行转换后得到 Disney-rgb24-to-yuv444p.yuv Disney-yuv444p-to-rgb24.rgb

Disney-demo-result.jpg

Congratulations!


代码:
08-rgb-to-yuv

参考资料:

YUV 格式与 RGB 格式的相互转换公式及C++ 代码

Color Conversion

videolan-YUV

博客目录-技术-视频处理

YUV格式学习:YUV420P、YV12、NV12、NV21格式转换成RGB24

图像RGB2YUV与YUV2RGB格式互转介绍

YUV转RGB的优化算法

YUV 格式与 RGB 格式的相互转换公式及C++ 代码

YUV420与YUV444互转,YUV420与YUV444读取和保存,YUV的显示和播放功能

YCbCr与YUV的区别

维基百科-YUV

维基百科-YCbCr


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,961评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,444评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,009评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,082评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,101评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,271评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,738评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,395评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,539评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,434评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,481评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,160评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,749评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,816评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,038评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,548评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,140评论 2 341

推荐阅读更多精彩内容