總算放假了,也有閑下來的功夫去寫一些博文了,接下來我會出一些關于音視頻的技術博文,
如果有小伙伴想學習音視頻的可以關注一下這位大佬的博客:https://blog.csdn.net/leixiaohua1020
閑話不多說,下面開始今天討論的主題,
一、YUV格式圖片的原理
我們看到一張照片,有亮度,有色度,YUV格式中的Y就代表亮度,U和V代表色度,不嚴謹地說,如果把一幀YUV影像中的U和V拿掉,那么這張圖片只剩下亮度,它就會變成一張黑白照,為什么這么說不嚴謹,這里暫時先保留這個問題,
二、YUV格式圖片資料的內部排列
YUV格式的圖片又有很多種排列方式,詳細的可以自行百度,這里不展開討論,
這里簡單列舉幾種
YUV420p有分I420和YV12
I420的內部排列
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
U U U U U U U U U U
V V V V V V V V V V
YV12的內部排列
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
V V V V V V V V V V
U U U U U U U U U U
YUV420sp有分NV12和NV21
NV12的內部排列
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
U V U V U V U V U V
U V U V U V U V U V
NV21的內部排列
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y
V U V U V U V U V U
V U V U V U V U V U
上述內容是一幀解析度為10*4的YUV影像的內部組成,通過觀察,很容易發現,YUV420格式的圖片中的 Y U V 比例是4:1:1,
所以他為什么不叫YUV411???我這里也不太清楚,
三、將一幀YUV影像變成黑白照(灰度圖)
了解了一幀YUV影像的內部組成,下面我們來編碼修改一幀YUV420p(這里默認I420)影像,將他變成黑白照片,
在編碼前,我們先考慮一下,先前我們說過,我們要把YUV影像的U和V資料拿掉,就能讓這張圖片變成黑白照(灰度圖),如果我們把U和V資料去掉,那么只剩下Y資料,打開圖片的時候選擇一定要選擇以“Y”格式打開,而不是“YUV420”格式打開,
下面上代碼
#include <stdio.h>
#include <stdlib.h>
#define outPutFileName "testGrey.y"
/*
para: fileName: 輸入圖片名稱
width: 寬
height: 高
*/
int testYuv420Grey(char *fileName, int width, int height)
{
FILE *fp;
FILE *fp1;
unsigned char *readBuf;
readBuf = (unsigned char *)malloc(width*height*3/2);
fp = fopen(fileName, "rb+");
fp1 = fopen(outPutFileName, "wb+");
/* read yuv file */
fread(readBuf, 1, width*height, fp);
/* create new file */
fwrite(readBuf, 1, width*height, fp1);
free(readBuf);
fclose(fp);
fclose(fp1);
return 0;
}
int main()
{
testYuv420Grey("lena_256x256_yuv420p.yuv", 256, 256);
return 0;
}
輸入圖片

輸出圖片

這里我只保存了Y資料,那么理論上來說,它不能叫做一幀YUV影像,所以我們不一定要去將U和V的資料拿掉,也可以修改U和V的值,按照我們的常識來看,將U和V的資料修改為0就代表無色,然而U、V是影像中的經過偏置處理的色度分量,在偏置處理前,它的取值范圍是-128-127,這時,把U和V資料修改為0代表無色,在偏置處理后,它的取值范圍變成了0-255,所以這時候需要取中間值,即128,所以我們下面要將U和V資料修改為128
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define outPutFileName "testGrey.yuv"
int testYuv420Grey(char *fileName, int width, int height)
{
FILE *fp;
FILE *fp1;
unsigned char *readBuf;
readBuf = (unsigned char *)malloc(width*height*3/2);
fp = fopen(fileName, "rb+");
fp1 = fopen(outPutFileName, "wb+");
/* read yuv file */
fread(readBuf, 1, width*height, fp);
/* memset U and V */
memset(readBuf+width*height, 128, width*height/2);
/* create new file */
fwrite(readBuf, 1, width*height*3/2, fp1);
free(readBuf);
fclose(fp);
fclose(fp1);
return 0;
}
int main()
{
testYuv420Grey("lena_256x256_yuv420p.yuv", 256, 256);
return 0;
}
原圖

修改后的圖片

四、將一幀YUV影像亮度減半
這個很簡單,只要把Y的值減半就行了
#include <stdio.h>
#include <stdlib.h>
#define outputFileName "halfy.yuv"
int simplest_yuv420_halfy(char *fileName, int width, int height)
{
FILE *fp1 = NULL;
FILE *fp2 = NULL;
unsigned char *readBuf = NULL;
unsigned char *point = NULL;
readBuf = (unsigned char *)malloc(width*height*3/2);
point = readBuf;
fp1 = fopen(fileName, "rb+");
fp2 = fopen(outputFileName, "wb+");
fread(readBuf, 1, width*height*3/2, fp1);
/* 亮度減半 */
for(int i = 0; i < width*height; i++)
{
*point = *point/2;
point++;
}
fwrite(readBuf, 1, width*height*3/2, fp2);
free(readBuf);
fclose(fp1);
fclose(fp2);
readBuf = NULL;
point = NULL;
fp1 = NULL;
fp2 = NULL;
return 0;
}
int main()
{
simplest_yuv420_halfy("lena_256x256_yuv420p.yuv", 256, 256);
}
原圖

修改后的圖片

五、自己制作一幀YUV420灰階測驗圖
U和V資料置為128,將Y資料遞增即可,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define output_file "grey.yuv"
/*
width : 輸出圖片的寬
height: 輸出圖片的高
num : 輸出灰階測驗圖條數
*/
#define yMax 255
int output_grey_pic(int width, int height, int num)
{
FILE *fp1 = NULL;
int newest_y_data = 0;
int y_distance = yMax / num;
int uv_data = 128;
long file_size = 0;
int output_width = width / num; // 間隔的寬
int y_data[64];
unsigned char *buffer;
file_size = height * width * 3 / 2;
buffer = (unsigned char *)malloc(file_size);
fp1 = fopen(output_file, "wb+");
for (int i = 0; i < num; i++)
{
y_data[i] = newest_y_data;
newest_y_data += y_distance;
printf("Y = %d U = %d V = %d\n", y_data[i], uv_data, uv_data);
}
/* 寫入y資料 */
memset(buffer, 0, file_size);
for (int h = 0; h < height; h++)
{
for (int cnt = 0; cnt < num; cnt++)
{
memset(buffer + cnt * output_width + h * width, y_data[cnt], output_width);
}
}
/* 寫入 U V 資料 */
memset(buffer + height * width, uv_data, height * width / 2);
/* 將快取寫入檔案 */
fwrite(buffer, 1, file_size, fp1);
free(buffer);
fclose(fp1);
return 0;
}
int main()
{
output_grey_pic(1920, 1080, 4);
}
效果如下

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/258526.html
標籤:其他
