感兴趣可以加QQ群85486140,大家一起交流相互学习下!

文章目录

  • 一、色彩空间简介及一些参考文档
  • 二、YUV和RGB转换公式
  • 三、RGB和YUV420转换提前需要了解的知识
    • 1.YUV420内存布局
    • 2.RGB内存布局
    • 3.转换方式
  • 四、源代码执行效率对比
  • 五、编译过程中遇到的问题

一、色彩空间简介及一些参考文档

不同的色彩空间,颜色的表现形式不同(见http://colorizer.org/ 这个网站中简单介绍了各个色彩空间的模型。)。色彩空间中的颜色是可以相互转化的。我们常用的颜色空间有YUV和RGB色彩空间。

  • 色彩空间简介:http://colorizer.org/

  • 参考文章:1:一种简单的YUV转RGB的优化算法(之前微信推送的一篇文章)

  • 参考文章2:YUV <——> RGB 转换算法(此文章分析的非常全面,并有github源码仓库,优先推荐参考)

  • JPG转RGB格式的网站:https://convertio.co/zh/jpg-rgb/

二、YUV和RGB转换公式

如果是RGB24的话,则它们和YUV的对应公式如下所示:

  • 1.RGB转YUV

Y = 0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V = 0.615R - 0.515G - 0.100B

  • 2.YUV转RGB

R = Y + 1.14V
G = Y - 0.39U - 0.58V
B = Y + 2.03U

上面可以可以看到有浮点运算,这会导致耗时很长。下面的优化也是基于浮点运算,来的。

三、RGB和YUV420转换提前需要了解的知识

1.YUV420内存布局

                   W+--------------------+|Y0Y1Y2Y3...         ||...                 ||                    |   H|                    ||                    ||                    |+--------------------+|U0U1...   ||...       |   H/2|          |+----------+|V0V1...   ||...       |  H/2|          |+----------+w/2

2.RGB内存布局

虽然名字上叫RGB,但是排列的时候是按着BGR的顺序排列的,如下所示。

                        B                               G                               R+-----------------------------------------------------------------------------------------------+高字节 | B | B | B | B | B | B | B | B | G | G | G | G | G | G | G | G | R | R | R | R | R | R | R | R |   低字节+-----------------------------------------------------------------------------------------------+0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23

3.转换方式

转换的时候由于不是RGB24转YUV444(一对一),而是转成YUV420.所以必然有些数据时要舍弃的。下面即将处理的图片分辨率是320 x 240的大小,所以RGB24的格式就会产生320 * 240 *3 byte的数据 即230400,下图也能看到后缀为rgb图片的大小。对应YUV420图片大小则为320 * 240 * 1.5=115200 Byte的大小。同样下面实际生成的图片也是这么大。

RGB的存放方式如下所示,例如8*6像素的RGB图像,其数据分布式这样存放的。

           pixe0       pixe1      pixe2       pixe3       pixe4      pixe5        pixe6       pixe7+-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------+
Line0  | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | +-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------+
Line1  | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | +-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------+
Line2  | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | +-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------+
Line3  | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | +-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------+
Line4  | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | +-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------+
Line5  | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | B | G | R | +-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------+

那么由于UV是每4个点对应一组UV,则选择哪个点转换为U,那个点转换为V。针对这样的block选择每个block的左上角(pix0)像素转换成U,左下角(pix2)的转换成V,其实这4个点的像素都可以用来转换UV,只是大家都习惯这样来转。

|pix0|pix1|
|pix2|pix3|

其它网友YUV420和YUV444相互转换的的好博客:https://blog.csdn.net/guyuealian/article/details/82454945

四、源代码执行效率对比

VS2015源码包可以在这里下载:https://download.csdn.net/download/armwind/11589508
由于原始的转换过程中包含了浮点和乘法运算,所以需要优化算法。下面例程中也包含了优化前后的对比。

  • 1.源码
#include "stdafx.h"
#include "iostream"
#include<Windows.h>
#include <stdio.h>using namespace::std;static void RGBToYUV_v1(int R, int G, int B, int* Y, int* U, int* V) {*Y = 0.299*R + 0.587*G + 0.114*B;*U = -0.147*R - 0.289*G + 0.436*B;*V = 0.615*R - 0.515*G - 0.100*B;
}static void YUVToRGB_v1(int Y, int U, int V, int* R, int* G, int* B) {*R = Y + 1.14*V;*G = Y - 0.39*U - 0.58*V;*B = Y + 2.03*U;
}static void RGBToYUV(int Red, int Green, int Blue, int* Y, int* U, int* V){*Y = ((Red << 6) + (Red << 3) + (Red << 2) + Red + (Green << 7) + (Green << 4) + (Green << 2) + (Green << 1) + (Blue << 4) + (Blue << 3) + (Blue << 2) + Blue) >> 8;*U = (-((Red << 5) + (Red << 2) + (Red << 1)) - ((Green << 6) + (Green << 3) + (Green << 1)) + ((Blue << 6) + (Blue << 5) + (Blue << 4))) >> 8;*V = ((Red << 7) + (Red << 4) + (Red << 3) + (Red << 2) + (Red << 1) - ((Green << 7) + (Green << 2)) - ((Blue << 4) + (Blue << 3) + (Blue << 1))) >> 8;
}static void YUVToRGB(int Y, int U, int V, int* Red, int* Green, int* Blue)
{*Red = ((Y << 8) + ((V << 8) + (V << 5) + (V << 2))) >> 8;*Green = ((Y << 8) - ((U << 6) + (U << 5) + (U << 2)) - ((V << 7) + (V << 4) + (V << 2) + V)) >> 8;*Blue = ((Y << 8) + (U << 9) + (U << 3)) >> 8;
}
/*********************只不包含浮点运算********************/
//如果使用原始的转换出来的话,会出现偏色问题,下面在原始的基础上又做了微调,可以正常显示。
#define RGB2Y(r,g,b) \((unsigned char)((66 * r + 129 * g + 25 * b + 128) >> 8) + 16)
//  (((r << 6) + (r << 3) + (r << 2) + r + (g << 7) + (g << 4) + (g << 2) + (g << 1) + (b << 4) + (b << 3) + (b << 2) + b) >> 8)#define RGB2U(r,g,b) \((unsigned char)((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128)
//  ((-((r << 5) + (r << 2) + (r << 1)) - ((g << 6) + (g << 3) + (g << 1)) + ((b << 6) + (b << 5) + (b << 4))) >> 8)#define RGB2V(r,g,b) \((unsigned char)((112 * r - 94 * g - 18 * b + 128) >> 8) + 128)
//  (((r << 7) + (r << 4) + (r << 3) + (r << 2) + (r << 1) - ((g << 7) + (g << 2)) - ((b << 4) + (b << 3) + (b << 1))) >> 8)/**********************不包含浮点和乘法运算*****************/
#define RGB2Y_SHIFT(r,g,b) \((unsigned char)((((r << 6) + (r << 3) + (r << 2) + r + (g << 7) + (g << 4) + (g << 2) + (g << 1) + (b << 4) + (b << 3) + (b << 2) + b) + 128) >> 8) + 16)#define RGB2U_SHIFT(r,g,b) \((unsigned char)(((-((r << 5) + (r << 2) + (r << 1)) - ((g << 6) + (g << 3) + (g << 1)) + ((b << 6) + (b << 5) + (b << 4))) + 128) >> 8) + 128)#define RGB2V_SHIFT(r,g,b) \((unsigned char)(((r << 7) + (r << 4) + (r << 3) + (r << 2) + (r << 1) - ((g << 7) + (g << 2)) - ((b << 4) + (b << 3) + (b << 1)) + 128) >> 8) + 128)/*************************包含浮点和乘法运算,没有优化**************/#define RGB2Y_FLOAT(r,g,b) \((unsigned char)(0.299*r + 0.587*g + 0.114*b))#define RGB2U_FLOAT(r,g,b) \((unsigned char)(-0.147*r - 0.289*g + 0.436*b))#define RGB2V_FLOAT(r,g,b) \((unsigned char)(0.615*r - 0.515*g - 0.100*b))void rgb2yuv420_multip(unsigned char *rgb_buf, unsigned char *yuv_buf, int width, int heigh) {if (!rgb_buf || !yuv_buf || !width || !heigh) {printf("invalid param\n");return;}unsigned char *y = yuv_buf;unsigned char *u = yuv_buf + width*heigh;unsigned char *v = u + width*heigh/4;for (int r = 0; r < heigh; r++ ) {for (int c = 0; c < width; c++) {int index = r*width + c;*y++ = RGB2Y(rgb_buf[index * 3 + 2], rgb_buf[index * 3 + 1], rgb_buf[index * 3]);if((r%2==0)&&(c%2 ==0)) //偶数行,偶数列获取U*u++ = RGB2U(rgb_buf[index * 3 + 2], rgb_buf[index * 3 + 1], rgb_buf[index * 3]);else if(c%2 == 0) //奇数行,偶数列获取V*v++ = RGB2V(rgb_buf[index * 3 + 2], rgb_buf[index * 3 + 1], rgb_buf[index * 3]);}}}void rgb2yuv420_shift(unsigned char *rgb_buf, unsigned char *yuv_buf, int width, int heigh) {if (!rgb_buf || !yuv_buf || !width || !heigh) {printf("invalid param\n");return;}unsigned char *y = yuv_buf;unsigned char *u = yuv_buf + width*heigh;unsigned char *v = u + width*heigh/4;for (int r = 0; r < heigh; r++ ) {for (int c = 0; c < width; c++) {int index = r*width + c;*y++ = RGB2Y(rgb_buf[index * 3 + 2], rgb_buf[index * 3 + 1], rgb_buf[index * 3]);if((r%2==0)&&(c%2 ==0)) //偶数行,偶数列获取U*u++ = RGB2U(rgb_buf[index * 3 + 2], rgb_buf[index * 3 + 1], rgb_buf[index * 3]);else if(c%2 == 0) //奇数行,偶数列获取V*v++ = RGB2V(rgb_buf[index * 3 + 2], rgb_buf[index * 3 + 1], rgb_buf[index * 3]);}}}void rgb2yuv420_float(unsigned char *rgb_buf, unsigned char *yuv_buf, int width, int heigh) {if (!rgb_buf || !yuv_buf || !width || !heigh) {printf("invalid param\n");return;}unsigned char *y = yuv_buf;unsigned char *u = yuv_buf + width*heigh;unsigned char *v = u + width*heigh/4;for (int r = 0; r < heigh; r++ ) {for (int c = 0; c < width; c++) {int index = r*width + c;*y++ = RGB2Y_FLOAT(rgb_buf[index * 3 + 2], rgb_buf[index * 3 + 1], rgb_buf[index * 3]);if((r%2==0)&&(c%2 ==0)) //偶数行,偶数列获取U*u++ = RGB2U_FLOAT(rgb_buf[index * 3 + 2], rgb_buf[index * 3 + 1], rgb_buf[index * 3]);else if(c%2 == 0) //奇数行,偶数列获取V*v++ = RGB2V_FLOAT(rgb_buf[index * 3 + 2], rgb_buf[index * 3 + 1], rgb_buf[index * 3]);}}}int main(int arc, const char ** argv)
{int ret = 0;FILE *pFile = NULL,*pFile_out = NULL;int size = 0;int width = 0, heigh = 0;char path[] = "D:\\vs2015_project\\yuv22rgb\\girl320x240.yuv";unsigned char * rgb_buf = NULL;unsigned char *yuv_buf = nullptr;cout << "arc:" << arc << endl;if (arc == 1) {cout << "please input parameters!" << endl;return -1;}sscanf(argv[2],"%d", &width);sscanf(argv[3],"%d", &heigh);printf("file name:%s,width:%d,heigh:%d\n", argv[1], width, heigh);//cout << "file name:" << argv[1] << endl;pFile = fopen(argv[1], "rb");if (NULL == pFile) {cout << "open file failed!" << endl;}else {fseek(pFile, 0, SEEK_END);size = ftell(pFile);fseek(pFile, 0, SEEK_SET);cout << "file size is " << size << endl;}rgb_buf = (unsigned char *)malloc(size);if (NULL == rgb_buf) {cout << "malloc buffer failed, return" << endl;fclose(pFile);return -1;}int r_size = fread(rgb_buf, 1, size, pFile);if (r_size != size) {cout << "read error" << endl;ret = -1;goto release;}int yuv_size = width * heigh * 1.5;yuv_buf = (unsigned char*)malloc(yuv_size);if (NULL == yuv_buf)goto release;LARGE_INTEGER t1, t2,t3,t4;LARGE_INTEGER f;QueryPerformanceFrequency(&f); //得到CPU频率QueryPerformanceCounter(&t1); //得到开始时间rgb2yuv420_multip(rgb_buf, yuv_buf, width, heigh);QueryPerformanceCounter(&t2); //得到优化了浮点运算的计算时间double total1_time = (t2.QuadPart - t1.QuadPart) / (double)f.QuadPart; //单位秒rgb2yuv420_shift(rgb_buf, yuv_buf, width, heigh);QueryPerformanceCounter(&t3); //得到优化浮点和乘法运算之后结束时间double total2_time = (t3.QuadPart - t2.QuadPart) / (double)f.QuadPart; //单位秒rgb2yuv420_float(rgb_buf, yuv_buf, width, heigh);QueryPerformanceCounter(&t4); //原始没有优化的图像double total3_time_float = (t4.QuadPart - t3.QuadPart) / (double)f.QuadPart; //单位秒printf("shift_time:%f s,multip_time:%f s,time_float:%f s\n", total2_time,total1_time,total3_time_float);pFile_out = fopen(path, "wb+");if (pFile_out == nullptr) {printf("null fd\n");goto release;}printf("yuv_size:%d\n", yuv_size);int w_count = fwrite(yuv_buf, 1, yuv_size, pFile_out);printf("write_count:%d\n", w_count);fclose(pFile_out);release:if (rgb_buf) {free(rgb_buf);rgb_buf = nullptr;}if (yuv_buf) {free(yuv_buf);yuv_buf = nullptr;}fclose(pFile);return 0;
}

运行结果如下所示:

D:\vs2015_project\yuv22rgb\yuv22rgb\Debug>yuv22rgb.exe …\girl320x240.rgb 320 240
arc:4
file name:…\girl320x240.rgb,width:320,heigh:240
file size is 230400
shift_time:0.000367 s,multip_time:0.000366 s,time_float:0.000593 s
yuv_size:115200
write_count:115200

从上面能够发现在同样的运行环境下,处理时间优化前后差异很大,其中优化移位操作,优化的时间不是很大,不知道跑在arm架构上时间会是怎么样的,后面会尝试一下。

优化项 时间(单位S)
没有任何优化 0.000593
优化浮点计算 0.000367
优化浮点和乘法计算 0.000366
  • 2.CPU硬件信息
  • 3.图片对比,下图左边是原始的图片,右边是转换之后的YUV,可以发现转换之后的图片有点偏红,不过色调基本一致。

五、编译过程中遇到的问题

  • 1.读取文件识别
    打开文件时应该加上“rb"模式
  • 2.写入文件有问题(写入文件的大小和预期不一致)
    写入文件时应该使用”wb+"
  • 3.图像偏色
    uv反色或者公式有问题

图像处理-RGB24转YUV420遇到的坑以及执行效率对比相关推荐

  1. RGB24 To Yuv420 C语言实现

    RGB24 To Yuv420 C语言实现(非SIMD版本) 以下代码来自 libyuv #include <stdint.h>//下面三个函数为RGB-->>Yuv420的公 ...

  2. Jenkins小坑之执行Shell

    title: Jenkins小坑之执行Shell tags: Jenkins ExecuteBash daemonize descriptors categories: ci date: 2017-1 ...

  3. win32截屏并rgb24转yuv420

    2019独角兽企业重金招聘Python工程师标准>>> void ScreenCap(void* buf, int w, int h) {HWND hDesk = GetDeskto ...

  4. 颜色转换rgb24 to yuv420

    void CVideoEncoder::RGB24ToYUV420(int Width,int Height,uint8_t* RgbBuffer,uint8_t*YuvBuffer) { uint8 ...

  5. RGB24 To Yuv420 C语言 +汇编实现(windows平台)

    以下代码来自libyuv #include <stdint.h> #include <stdlib.h> #include <string.h>#define IS ...

  6. 关于Qtdesigner中图像处理的一些踩过的坑:进程已结束,退出代码 -1073740791 (0xC0000409)

    今天本来想实现的是ui粗略是这个样子的: 就是当我上传图像时,是这个样子: 然后当我点击舌体分割的时候,第二个label可以用第一个label的图像数据,但是我发现Qtlabel中没有类似getPix ...

  7. 【坑】执行Consumer的时候发生java.net.UnknownHostException错误

    [时间]: 2016/4/8 17:30 [问题]: kafka执行Consumer实例的时候,发生了一下错误. kafka配置文件server.properties如下: zookeeper配置文件 ...

  8. 踩坑录·执行go命令报错“cannot load ‘xxx’”

    环境 操作系统: macOS10.15.4 golang版本: 1.13.8 (采用homebrew安装) 问题描述 写了一个打印出hello world的go文件,在使用go run hello.g ...

  9. 【opencv图像处理】图片的读取——cv2.imgread各种读取方式对比

    以下实验基于python==3.6.opencv-python==4.1.0.25.imutils==0.5.2 图片的读取 cv2.imread 该方法第一个参数为文件名filename(文件路径+ ...

  10. 视频图像数据处理八:将rgb视频图像转换为yuv420格式视频图像

    文章目录 转换公式 需要注意 函数代码 测试用例 下载 本文介绍了将rgb视频图像转换为yuv420格式视频图像的方法,附有详细的代码和图像示例.文中rgb24和yuv420文件需要使用yuv/rgb ...

最新文章

  1. cookie的作用域
  2. 智源大会“人工智能伦理、治理与可持续发展论坛”超5000人线上参会,专家敦促全球多边协作...
  3. linux安装python虚拟环境_linux环境下安装python虚拟环境及注意事项
  4. 财付通接口(asp)
  5. HT For Web 拓扑图背景设置
  6. 前端:CSS/09/行内框架,CSS简介,CSS选择器,组合选择器,CSS注释,CSS尺寸属性,CSS字体属性,CSS文本属性
  7. 生产活动目录不宜做快照,克隆,直接备份VMDK;
  8. iso12233测试方法_ISO12233分辨率图像测试卡使用时拍摄方法
  9. Dubbo的服务暴露过程
  10. 蓝字冲销是什么意思_会计记账,贷方红字,贷方蓝字什么意思
  11. cisco设备与基本操作
  12. opencv 图像人物识别
  13. 74cms|骑士cms|开源招聘系统,目录结构
  14. excel批量重命名工作表
  15. 如何实现超大文件(60G)传输给别人?
  16. 北邮实验:ARM实验板移植Linux操作系统,LCD显示汉字
  17. 基于DSP/BIoS设备驱动模型的视频驱动程序开发
  18. 什么是有理数和无理数?
  19. express hot-reload
  20. 免费的网上商城商品管理系统

热门文章

  1. JMH(java代码的微基准测试)入门和汇总
  2. VS+Qt应用开发-设置鼠标光标
  3. 数学建模-Logistic模型
  4. Win10家庭版安装VMware虚拟机-开启时出现蓝屏的问题
  5. 系统架构设计师-考试大纲
  6. Basic 语言发展史
  7. 如何解决Flash CS6打开后闪退的问题
  8. 服务器 python cant open file_QQ炫舞转服系统-QQ炫舞官方网站-腾讯游戏
  9. xcode 怎么调用midi开发录音_直播_个人工作室入门_1K-2k价位录音编曲声卡推荐
  10. 极品五笔输入法2009_考场指南!2020年注会机考计算器使用指南及输入法切换