使用C++实现YUV格式图像与RGB格式图像之间相互转换
使用C++實現YUV格式圖像與RGB格式圖像之間相互轉換
- 一、RGB與YUV轉換公式
- 1、RGB轉YUV
- 1)RGB轉換亮度與色差信號公試:
- 2)歸一化為YUV的轉化公試為:
- 2、YUV轉RGB
- 二、利用命令參數實現讀取文件的簡單化安全化
- 三、使用C++實現YUV格式圖像轉換為RGB格式圖像
- (一)、YUV格式到RGB格式的轉換(yuv2rgb.cpp)
- 1、實現YUV轉RGB公式
- 2、圖像數據的讀入
- 3、將U、V分量擴充到256*256
- 4、將YUV轉化為RGB
- (二)主函數(main.cpp)
- (三)結果演示
- (四)整體代碼
- 1、main:
- 2、yuv2rgb.cpp
- 3、yuv2rgb.h
- 四、使用C++實現RGB格式圖像轉換為YUV格式圖像
- 1、main.cpp
- 2、rgb2yuv.cpp
- 3、rgb2yuv.h
- 4、結果呈現
- 五、歸納總結
一、RGB與YUV轉換公式
1、RGB轉YUV
1)RGB轉換亮度與色差信號公試:
Y=0.299R+0.587G+0.114B
R-Y=0.701R-0.587G-0.114B
B-Y=-0.299R-0.587G+0.866B
2)歸一化為YUV的轉化公試為:
Y=0.299R+0.587G+0.114B
U=-0.168R-0.332G+0.5B
V=0.5R-0.419G-0.081B
2、YUV轉RGB
根據RGB轉YUV轉換公式,我們可以計算出YUV轉化為RGB的公試:
R=Y+1.40 (V-28)
G=Y-0.344 (U-128)-0.714(V-128)
B=Y+1.772*(U-128)
二、利用命令參數實現讀取文件的簡單化安全化
通過選擇“項目”-“yuv2rgb屬性”進入yuv2rgb屬性頁
在屬性頁中選擇“配置屬性”-“調試”進入調試器界面
在命令參數中按順序依次輸入值(文件名稱、參數等)并以“ ”(空格)隔開,這些值會依次排放在argv[1]-argv[n]之中,需要調用的時候直接賦值即可:
并在工作目錄選擇文件所在位置即可并通過已下代碼即可使文件讀取更加安全,文件路徑不會再在代碼中展現:
yuvFile = fopen(yuvFileName, "rb");rgbFile = fopen(rgbFileName, "wb");三、使用C++實現YUV格式圖像轉換為RGB格式圖像
實現這一共功能一共分為三個部分,主體實現(main1.cpp),yuv到rgb的轉換(yuv2rgb.cpp),yuv到rgb的頭文件(yuv2rgb.h);
其中最為重要的是yuv到rgb轉換功能的實現,即yuv2rgb.cpp,因此下面我先簡述yuv2rgb.cpp的具體實驗過程
(一)、YUV格式到RGB格式的轉換(yuv2rgb.cpp)
1、實現YUV轉RGB公式
因為圖像大小為256*256,若在轉換時對每個像素以此進行轉換公式的計算,則計算量過大,所以可以先建立一個轉換公式,計算0-255像數值的轉換,并將其存在數組中,這樣在后續的使用中可以直接調用其對應的值不用再一一計算,大大提高了程序運行效率。根據轉換公試,具體實現如下:
void InitLookupTable() {int i;for (i = 0; i < 256; i++) RGBYUV14075[i] = (float)1.4075 * (i-128);for (i = 0; i < 256; i++) RGBYUV03455[i] = (float)0.3455 * (i - 128);for (i = 0; i < 256; i++) RGBYUV07169[i] = (float)0.7169 * (i - 128);for (i = 0; i < 256; i++) RGBYUV17790[i] = (float)1.7790 * (i - 128);}2、圖像數據的讀入
因為YUV格式圖像分辨率為256* 256,所以Y分配空間為256* 256,U、V分配空間為256*256/4:
y_buffer = (unsigned char*)malloc(size* sizeof(unsigned char));u_buffer = (unsigned char*)malloc(size* sizeof(unsigned char) / 4);v_buffer = (unsigned char*)malloc(size* sizeof(unsigned char) / 4);sub_u_buf = (unsigned char*)malloc(size* sizeof(unsigned char));sub_v_buf = (unsigned char*)malloc(size* sizeof(unsigned char));yuv_buffer = (unsigned char*)bmp;y = y_buffer;u = u_buffer;v = v_buffer;其中y_buffer、u_buffer、v_buffer為存放Y、U、V數據的指針,y,u,v為讀入數據的中間過渡傳遞指針;
因為RGB格式圖像的R、G、B分量都為256256大小,所以需要將,原本為256256/4的U、V分量擴大以此才方便計算,所以設置sub_u_buf、sub_v_buf兩個為256*256大小的指針以此來進行擴充存放。
因為YUV格式圖像為按照全部像素Y數據塊、U數據塊、V數據塊依次存放,且為4:2:0采樣,所以讀取方式為YYYY…,U…,V…:
3、將U、V分量擴充到256*256
因為RGB格式圖像的R、G、B分量都為256256大小,所以需要將原本為256256/4的U、V分量擴大以此才方便計算,這里設置sub_u_buf、sub_v_buf兩個為256*256大小的指針以此來進行擴充存放。
我選用將U、V的值直接分配給他最近鄰的4個值的方法(如圖所示,將原本左上角位置的a直接賦值給其正下方,右方,與右下方三個值
| a | a |
)以此來實現對U、V分量的擴充。具體實現如下:
for (i = 0; i < y_dim / 2; i ++) {pu1 = sub_u_buf + i * 2 * x_dim;pu2 = sub_u_buf + (i * 2 + 1) * x_dim;pv1 = sub_v_buf + i * 2 * x_dim;pv2 = sub_v_buf + (i * 2 + 1) * x_dim;for (j = 0; j < x_dim / 2;j++) {*pu1 = *(u_buffer + j + i * x_dim / 2);*pu2 = *pu1;pu1++;pu2++;*pu1 = *(u_buffer + j + i * x_dim / 2);*pu2 = *pu1;pu1++;pu2++;*pv1 = *(v_buffer + j + i * x_dim / 2);*pv2 = *pv1;pv1++;pv2++;*pv1 = *(v_buffer + j + i * x_dim / 2);*pv2 = *pv1;pv1++;pv2++;}}4、將YUV轉化為RGB
因為所用的讀圖工具(gbmp)為倒著讀圖中的數據,所以需要設計兩種方式的轉化,來實現正數據存放與倒數據存放;
并且因為在上面公式數組中計算存放的數值為“float”型,而傳入圖像所需的數據是“unsigned char”型,所以將“float”型數據強制轉換為“unsigned char”是可能會出現數據溢出(通過轉換公式得到的R、G、B分量值可能得到負值或大于255的值)所以需要將溢出的值規定為不溢出的值,我將負值=0,大于255的值=255。這樣既可解決數值溢出問題,所以需要另設“float”類型參數sub_r、sub_g、sub_b來作為中間參數;
具體實現方法如下:
(二)主函數(main.cpp)
主函數調用YUV2RGB函數功能即可算出R、G、B的值,所以在主函數中,我們需要關注的是如何將R、G、B三分量的值傳入RGB格式文件中。
RGB格式文件按每個像素BGR分量以此存儲,所以讀取方式為b,g,r,b,g,r…;因此我們需要將計算得出的RGB值以此進行傳入;
具體實現如下:
(三)結果演示
原圖像:
因為如上文所說,讀圖軟件(bgmp)為倒著讀圖像,所以結果分兩種情況演示
當flip = TRUE時:輸出圖像為倒像,并且因為數值溢出后經過調整,使顏色也略微有偏差。
當flip = FALSE時:輸出圖像為正像,并且因為數值溢出后經過調整,使顏色也略微有偏差。
(四)整體代碼
1、main:
#include <stdio.h> #include <stdlib.h> #include <malloc.h> #include "yuv2rgb.h"#define u_int8_t unsigned __int8 #define u_int unsigned __int32 #define u_int32_t unsigned __int32 #define FALSE false #define TRUE trueint main(int argc, char** argv) {u_int frameWidth = 0; u_int frameHeight = 0;bool flip = FALSE; unsigned int i;char* rgbFileName = NULL;char* yuvFileName = NULL;FILE* rgbFile = NULL;FILE* yuvFile = NULL;u_int8_t* yuvBuf = NULL;u_int8_t* rBuf = NULL;u_int8_t* gBuf = NULL;u_int8_t* bBuf = NULL;u_int32_t videoFramesWritten = 0;yuvFileName = argv[1];rgbFileName = argv[2];frameWidth = atoi(argv[3]);frameHeight = atoi(argv[4]);yuvFile = fopen(yuvFileName, "rb");if (yuvFile == NULL){printf("cannot find yuv file\n");exit(1);}else{printf("The input rgb file is %s\n", yuvFileName);}rgbFile = fopen(rgbFileName, "wb");if (rgbFile == NULL){printf("cannot find rgb file\n");exit(1);}else{printf("The output yuv file is %s\n", rgbFileName);}yuvBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3 / 2);rBuf = (u_int8_t*)malloc(frameWidth * frameHeight);gBuf = (u_int8_t*)malloc(frameWidth * frameHeight);bBuf = (u_int8_t*)malloc(frameWidth * frameHeight);if (yuvBuf == NULL || rBuf == NULL || gBuf == NULL || bBuf == NULL){printf("no enought memory\n");exit(1);}while (fread(yuvBuf, 1, frameWidth*frameHeight* 3/2, yuvFile)){if (YUV2RGB(frameWidth, frameHeight, yuvBuf, rBuf, gBuf, bBuf, flip)){printf("error");return 0;}for (i = 0; i < frameWidth * frameHeight; i++){fwrite(bBuf+i, 1, 1, rgbFile);fwrite(gBuf+i, 1, 1, rgbFile);fwrite(rBuf+i, 1, 1, rgbFile);}printf("\r...%d", ++videoFramesWritten);}printf("\n%u %ux%u video frames written\n",videoFramesWritten, frameWidth, frameHeight);fclose(rgbFile);fclose(yuvFile);return(0);}2、yuv2rgb.cpp
#include "stdlib.h" #include "yuv2rgb.h"static float RGBYUV14075[256], RGBYUV03455[256], RGBYUV07169[256], RGBYUV17790[256];int YUV2RGB(int x_dim, int y_dim, void* bmp, void* r_out, void* g_out, void* b_out, int flip) {static int init_done = 0;long i, j, size;unsigned char* r, * g, * b;unsigned char* y, * u, * v;unsigned char* pu1, * pu2,* pv1, * pv2,* psu,* psv,* psy;unsigned char* y_buffer, *u_buffer, *v_buffer, *yuv_buffer;unsigned char *sub_u_buf, *sub_v_buf;float sub_r,sub_g,sub_b;if (init_done == 0){InitLookupTable();init_done = 1;}if ((x_dim % 2) || (y_dim % 2)) return 1;size = x_dim * y_dim;y_buffer = (unsigned char*)malloc(size* sizeof(unsigned char));u_buffer = (unsigned char*)malloc(size* sizeof(unsigned char) / 4);v_buffer = (unsigned char*)malloc(size* sizeof(unsigned char) / 4);sub_u_buf = (unsigned char*)malloc(size* sizeof(unsigned char));sub_v_buf = (unsigned char*)malloc(size* sizeof(unsigned char));yuv_buffer = (unsigned char*)bmp;y = y_buffer;u = u_buffer;v = v_buffer;r = (unsigned char*)r_out;g = (unsigned char*)g_out;b = (unsigned char*)b_out;for (i = 0; i < size; i ++) {*y = *(yuv_buffer + i);y++;}for (i = size; i < size * 5 / 4; i++) {*u = *(yuv_buffer + i);u++;}for (i = size * 5 / 4; i < size * 3 / 2; i ++) {*v = *(yuv_buffer + i);v++;}for (i = 0; i < y_dim / 2; i ++) {pu1 = sub_u_buf + i * 2 * x_dim;pu2 = sub_u_buf + (i * 2 + 1) * x_dim;pv1 = sub_v_buf + i * 2 * x_dim;pv2 = sub_v_buf + (i * 2 + 1) * x_dim;for (j = 0; j < x_dim / 2;j++) {*pu1 = *(u_buffer + j + i * x_dim / 2);*pu2 = *pu1;pu1++;pu2++;*pu1 = *(u_buffer + j + i * x_dim / 2);*pu2 = *pu1;pu1++;pu2++;*pv1 = *(v_buffer + j + i * x_dim / 2);*pv2 = *pv1;pv1++;pv2++;*pv1 = *(v_buffer + j + i * x_dim / 2);*pv2 = *pv1;pv1++;pv2++;}}if (flip){for (i = 0; i < size; i++){sub_r = *(y_buffer+i) + RGBYUV14075[*(sub_v_buf+i)];sub_g = *(y_buffer+i) - RGBYUV03455[*(sub_u_buf+i)] - RGBYUV07169[*(sub_v_buf+i)];sub_b = *(y_buffer+i) + RGBYUV17790[*(sub_u_buf+i)];if (sub_r < 0) sub_r = 0;if (sub_r > 255) sub_r = 255;if (sub_g < 0) sub_g = 0;if (sub_g > 255) sub_g = 255;if (sub_b < 0) sub_b = 0;if (sub_b > 255) sub_b = 255;*r = (unsigned char)sub_r;*g = (unsigned char)sub_g;*b = (unsigned char)sub_b;r++;g++;b++;}}else {for (j = 0; j < y_dim; j++) {psy = y_buffer + (y_dim - 1 - j) * x_dim;psu = sub_u_buf + (y_dim - 1 - j) * x_dim;psv = sub_v_buf+ (y_dim - 1 - j) * x_dim;for (i = 0; i < x_dim; i++) {sub_r = *(psy+i) + RGBYUV14075[*(psv+i)];sub_g = *(psy+i) - RGBYUV03455[*(psu+i)]- RGBYUV07169[*(psv+i)];sub_b = *(psy+i) + RGBYUV17790[*(psu+i)];if (sub_r < 0) sub_r = 0;if (sub_r > 255) sub_r = 255;if (sub_g < 0) sub_g = 0;if (sub_g > 255) sub_g = 255;if (sub_b < 0) sub_b = 0;if (sub_b > 255) sub_b = 255;*r = (unsigned char)(sub_r);*g = (unsigned char)(sub_g);*b = (unsigned char)(sub_b);r++;g++;b++;}}}free(y_buffer);free(u_buffer);free(v_buffer);free(sub_u_buf);free(sub_v_buf);return 0; }void InitLookupTable() {int i;for (i = 0; i < 256; i++) RGBYUV14075[i] = (float)1.4075 * (i-128);for (i = 0; i < 256; i++) RGBYUV03455[i] = (float)0.3455 * (i - 128);for (i = 0; i < 256; i++) RGBYUV07169[i] = (float)0.7169 * (i - 128);for (i = 0; i < 256; i++) RGBYUV17790[i] = (float)1.7790 * (i - 128);}3、yuv2rgb.h
int YUV2RGB(int x_dim, int y_dim, void* bmp, void* r_out, void* g_out, void* b_out, int flip);void InitLookupTable();四、使用C++實現RGB格式圖像轉換為YUV格式圖像
因為和YUV格式圖像轉RGB格式圖像思路相近,所以這里只給出完整代碼:
1、main.cpp
#include <stdio.h> #include <stdlib.h> #include <malloc.h> #include "rgb2yuv.h"#define u_int8_t unsigned __int8 #define u_int unsigned __int32 #define u_int32_t unsigned __int32 #define FALSE false #define TRUE trueint main(int argc, char** argv) {u_int frameWidth = 352; u_int frameHeight = 240; bool flip = TRUE; unsigned int i;char* rgbFileName = NULL;char* yuvFileName = NULL;FILE* rgbFile = NULL;FILE* yuvFile = NULL;u_int8_t* rgbBuf = NULL;u_int8_t* yBuf = NULL;u_int8_t* uBuf = NULL;u_int8_t* vBuf = NULL;u_int32_t videoFramesWritten = 0;rgbFileName = argv[1];yuvFileName = argv[2];frameWidth = atoi(argv[3]);frameHeight = atoi(argv[4]);rgbFile = fopen(rgbFileName, "rb");if (rgbFile == NULL){printf("cannot find rgb file\n");exit(1);}else{printf("The input rgb file is %s\n", rgbFileName);}yuvFile = fopen(yuvFileName, "wb");if (yuvFile == NULL){printf("cannot find yuv file\n");exit(1);}else{printf("The output yuv file is %s\n", yuvFileName);}rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);if (rgbBuf == NULL || yBuf == NULL || uBuf == NULL || vBuf == NULL){printf("no enought memory\n");exit(1);}while (fread(rgbBuf, 1, frameWidth * frameHeight * 3, rgbFile)) {if(RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip)){printf("error");return 0;}for (i = 0; i < frameWidth*frameHeight; i++){if (yBuf[i] < 16) yBuf[i] = 16;if (yBuf[i] > 235) yBuf[i] = 235;}for (i = 0; i < frameWidth*frameHeight/4; i++){if (uBuf[i] < 16) uBuf[i] = 16;if (uBuf[i] > 240) uBuf[i] = 240;if (vBuf[i] < 16) vBuf[i] = 16;if (vBuf[i] > 240) vBuf[i] = 240;}fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);printf("\r...%d", ++videoFramesWritten);}printf("\n%u %ux%u video frames written\n", videoFramesWritten, frameWidth, frameHeight);/* cleanup */fclose(rgbFile);fclose(yuvFile);return(0); }2、rgb2yuv.cpp
#include "stdlib.h" #include "rgb2yuv.h"static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256]; static float RGBYUV01684[256], RGBYUV03316[256]; static float RGBYUV04187[256], RGBYUV00813[256];int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip) {static int init_done = 0;long i, j, size;unsigned char *r, *g, *b;unsigned char *y, *u, *v;unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;unsigned char *y_buffer, *u_buffer, *v_buffer;unsigned char *sub_u_buf, *sub_v_buf;if (init_done == 0){InitLookupTable();init_done = 1;}if ((x_dim % 2) || (y_dim % 2)) return 1;size = x_dim * y_dim;y_buffer = (unsigned char *)y_out;sub_u_buf = (unsigned char *)u_out;sub_v_buf = (unsigned char *)v_out;u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));if (!(u_buffer && v_buffer)){if (u_buffer) free(u_buffer);if (v_buffer) free(v_buffer);return 2;}b = (unsigned char *)bmp;y = y_buffer;u = u_buffer;v = v_buffer;if (!flip) {for (j = 0; j < y_dim; j ++){y = y_buffer + (y_dim - j - 1) * x_dim;u = u_buffer + (y_dim - j - 1) * x_dim;v = v_buffer + (y_dim - j - 1) * x_dim;for (i = 0; i < x_dim; i ++) {g = b + 1;r = b + 2;*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);b += 3;y ++;u ++;v ++;}}} else {for (i = 0; i < size; i++){g = b + 1;r = b + 2;*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);b += 3;y ++;u ++;v ++;}}for (j = 0; j < y_dim/2; j ++){psu = sub_u_buf + j * x_dim / 2;psv = sub_v_buf + j * x_dim / 2;pu1 = u_buffer + 2 * j * x_dim;pu2 = u_buffer + (2 * j + 1) * x_dim;pv1 = v_buffer + 2 * j * x_dim;pv2 = v_buffer + (2 * j + 1) * x_dim;for (i = 0; i < x_dim/2; i ++){*psu = (*pu1 + *(pu1+1) + *pu2 + *(pu2+1)) / 4;*psv = (*pv1 + *(pv1+1) + *pv2 + *(pv2+1)) / 4;psu ++;psv ++;pu1 += 2;pu2 += 2;pv1 += 2;pv2 += 2;}}free(u_buffer);free(v_buffer);return 0; }void InitLookupTable() {int i;for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i; }3、rgb2yuv.h
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip);void InitLookupTable();4、結果呈現
原圖:
輸出圖像:顏色因為整理數值溢出問題所以略有區別
五、歸納總結
通過此次實驗熟悉了兩種圖像格式之間的轉換,并且熟悉鞏固了C++語言的使用,為后續進一步學習數據壓縮打下了基礎。
總結
以上是生活随笔為你收集整理的使用C++实现YUV格式图像与RGB格式图像之间相互转换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Exp8 web基础
- 下一篇: 使用C++实现多张BMP图片转换为YUV