参考:
百度百科:YUV
维基百科:YUV
YUV Colorspace:http://softpixel.com/~cwright/programming/colorspace/yuv/
YUV420P格式分析:https://my.oschina.net/u/589963/blog/167766
YUV
是一种颜色编码方法
Y
分量表示颜色的亮度(luminance
),单取出 Y
分量就是图像的灰度图;U
、V
分量表示颜色色度或者浓度(Chrominance
)
YUV
图像有两种编码格式:
packed formats
):Y
、U
、V
三通道像素值依次排列,即 Y0 U0 V0 Y1 U1 V1 ...
planar formats
):先排列 Y
分量的所有像素值,再排列 U
分量的,最后排列 V
分量的Note:平面模式适合采样(subsample
)
由于人眼对于亮度的敏感度远大于色度,所以减少 U
、V
分量并不会过多损害图像的质量,从而减少了数据量,达到压缩的目的
YUV444
表示图像完全采样,没有压缩,紧缩格式YUV420p
表示图像 2:1
的水平取样,垂直 2:1
采样,即每 4
个 Y
分量对应一个 U
、V
分量,平面模式Note:YUV420p
(有时简称为 YUV420
) 是常见的图像格式,接下来进行的 YUV
和 RGB
的转换操作都是针对 420p
格式的
YUV
和 RGB
之间的转换公式如下:
R = Y + 1.4075 * (V - 128)
G = Y - 0.3455 * (U - 128) - (0.7169 * (V - 128))
B = Y + 1.7790 * (U - 128)
Y = R * .299000 + G * .587000 + B * .114000
U = R * -.168736 + G * -.331264 + B * .500000 + 128
V = R * .500000 + G * -.418688 + B * -.081312 + 128
Note:假定各分量之间的取值范围均为 [0, 255]
主要内容
C++
:YUV420p
转 RGB
C++
:关键函数介绍Python
:YUV420p
转 RGB
方法一Python
:关键函数介绍Python
:YUV420p
转 RGB
方法二Python
:关键函数介绍C
:YUV420p
转 RGB
C
:关键函数介绍YUV
图片资源C++
:
YUV420p
转
RGB
首先读取 YUV
格式图片,是二进制文件;打开图像文件,按字节读取,并赋值到新建的图像中,其中,Y
分量图像大小和结果图像大小一致,U
、V
分量图像大小比结果图像小一倍;按顺序读取 Y
分量,再读取 U
、V
分量
vector readYUV(char* image_path, int width, int height) {
ifstream fin;
fin.open(image_path, ios::binary);
if (!fin.is_open()) {
cout << "open failed" << endl;
exit(0);
}
Mat img_Y = Mat::zeros(height, width, CV_8UC1);
char ch;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
fin.read(&ch, sizeof(ch));
img_Y.at(i, j) = (uchar)ch;
}
}
Mat img_U = Mat::zeros(height / 2, width / 2, CV_8UC1);
for (int i = 0; i < height / 2; i++) {
for (int j = 0; j < width / 2; j++) {
fin.read(&ch, sizeof(ch));
img_U.at(i, j) = (uchar)ch;
}
}
Mat img_V = Mat::zeros(height / 2, width / 2, CV_8UC1);
for (int i = 0; i < height / 2; i++) {
for (int j = 0; j < width / 2; j++) {
fin.read(&ch, sizeof(ch));
img_V.at(i, j) = (uchar)ch;
}
}
fin.close();
vector yuv;
yuv.push_back(img_Y);
yuv.push_back(img_U);
yuv.push_back(img_V);
return yuv;
}
得到从文件解析出来的各分量后,放大 U
、V
分量和 Y
分量同样大小:
Mat enlarge(Mat src) {
Mat enlarge = Mat::zeros(src.rows*2, src.cols*2, CV_8UC1);
resize(src, enlarge, Size(), 2, 2, INTER_CUBIC);
return enlarge;
}
合并 3
个通道分量:
Mat merge(vector channels) {
int width = channels.at(0).cols;
int height = channels.at(0).rows;
Mat yuv = Mat::zeros(height, width, CV_8UC3);
merge(channels, yuv);
return yuv;
}
将 YUV
图像转换为 RGB
图像(OpenCV
中格式为 BGR
):
Mat cvtYUV2BGR(Mat yuv) {
int width = yuv.cols;
int height = yuv.rows;
Mat bgr = Mat::zeros(height, width, CV_8UC3);
cvtColor(yuv, bgr, COLOR_YUV2BGR);
return bgr;
}
完整代码如下所示:
#include
#include
#include
using namespace std;
using namespace cv;
vector readYUV(char* image_path, int width, int height) {
ifstream fin;
fin.open(image_path, ios::binary);
if (!fin.is_open()) {
cout << "open failed" << endl;
exit(0);
}
Mat img_Y = Mat::zeros(height, width, CV_8UC1);
char ch;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
fin.read(&ch, sizeof(ch));
img_Y.at(i, j) = (uchar)ch;
}
}
Mat img_U = Mat::zeros(height / 2, width / 2, CV_8UC1);
for (int i = 0; i < height / 2; i++) {
for (int j = 0; j < width / 2; j++) {
fin.read(&ch, sizeof(ch));
img_U.at(i, j) = (uchar)ch;
}
}
Mat img_V = Mat::zeros(height / 2, width / 2, CV_8UC1);
for (int i = 0; i < height / 2; i++) {
for (int j = 0; j < width / 2; j++) {
fin.read(&ch, sizeof(ch));
img_V.at(i, j) = (uchar)ch;
}
}
fin.close();
vector yuv;
yuv.push_back(img_Y);
yuv.push_back(img_U);
yuv.push_back(img_V);
return yuv;
}
Mat enlarge(Mat src) {
Mat enlarge = Mat::zeros(src.rows*2, src.cols*2, CV_8UC1);
resize(src, enlarge, Size(), 2, 2, INTER_CUBIC);
return enlarge;
}
Mat merge(vector channels) {
int width = channels.at(0).cols;
int height = channels.at(0).rows;
Mat yuv = Mat::zeros(height, width, CV_8UC3);
merge(channels, yuv);
return yuv;
}
Mat cvtYUV2BGR(Mat yuv) {
int width = yuv.cols;
int height = yuv.rows;
Mat bgr = Mat::zeros(height, width, CV_8UC3);
cvtColor(yuv, bgr, COLOR_YUV2BGR);
return bgr;
}
int main(int argc, char* argv[]) {
int width = 640;
int height = 480;
//char* image_path = "c:\\yuv\\img_yuv";
char* image_path = "c:\\yuv\\jpgimage1_image_640_480.yuv";
vector temp = readYUV(image_path, width, height);
vector channels;
channels.push_back(temp.at(0));
channels.push_back(enlarge(temp.at(1)));
channels.push_back(enlarge(temp.at(2)));
Mat yuv = merge(channels);
Mat bgr = cvtYUV2BGR(yuv);
//Mat yuv = Mat::zeros(height, width, CV_8UC3);
//merge(channels, yuv);
//Mat bgr = Mat::zeros(height, width, CV_8UC3);
//cvtColor(yuv, bgr, COLOR_YUV2BGR);
imshow("img", bgr);
waitKey(0);
return 0;
}
C++
:关键函数介绍
fin.open(image_path, ios::binary);
参考:std::ifstream::open
原型:
void open (const char* filename, ios_base::openmode mode = ios_base::in);
参数介绍:
image_path
- filename
,指定打开的文件名
ios::binary
- mode
,操作文件的方式,ios::binary
表明已二进制模式操作而不是文本模式(Operations are performed in binary mode rather than text.
)
Mat img_Y = Mat::zeros(height, width, CV_8UC1);
参考:Mat::zeros
原型:
static MatExpr Mat::zeros(int rows, int cols, int type)
参数介绍:
height
- rows
,行数,即图像高
width
- cols
,列数,即图像宽
CV_8UC1
- type
,图像类型,此处为单通道无符号 8
位整数
fin.read(&ch, sizeof(ch));
参考:std::istream::read
原型:
istream& read (char* s, streamsize n);
参数介绍:
&ch
- s
,字符指针,保存提取的数据
sizeof(ch)
- n
,提取的字节数
img_Y.at(i, j) = (uchar)ch;
参考:Mat::at
原型:
template T& Mat::at(int i, int j)
函数功能:返回指定数组元素的索引
Python
:
YUV420p
转
RGB
在网上找到一个参考:
Python读取YUV
能够运行(import Image
改成 from PIL import Image
),不过他使用的是 PIL
模块,目前最常用的图像处理库是 OpenCV
在知乎上的一个讨论,给了我灵感:
Python如何正确读取YUV二进制文件(为数字)?
读取二进制图像文件并解析为 Y
,U
,V
单通道图像:
def read_YUV420(image_path, rows, cols):
"""
读取YUV文件,解析为Y, U, V图像
:param image_path: YUV图像路径
:param rows: 给定高
:param cols: 给定宽
:return: 列表,[Y, U, V]
"""
# create Y
gray = np.zeros((rows, cols), np.uint8)
print type(gray)
print gray.shape
# create U,V
img_U = np.zeros((rows / 2, cols / 2), np.uint8)
print type(img_U)
print img_U.shape
img_V = np.zeros((rows / 2, cols / 2), np.uint8)
print type(img_V)
print img_V.shape
with open(image_path, 'rb') as reader:
for i in xrange(rows):
for j in xrange(cols):
gray[i, j] = ord(reader.read(1))
for i in xrange(rows / 2):
for j in xrange(cols / 2):
img_U[i, j] = ord(reader.read(1))
for i in xrange(rows / 2):
for j in xrange(cols / 2):
img_V[i, j] = ord(reader.read(1))
return [gray, img_U, img_V]
合并单通道图像并转换为 RGB
图像:
def merge_YUV2RGB_v1(Y, U, V):
"""
转换YUV图像为RGB格式(放大U、V)
:param Y: Y分量图像
:param U: U分量图像
:param V: V分量图像
:return: RGB格式图像
"""
# Y分量图像比U、V分量图像大一倍,想要合并3个分量,需要先放大U、V分量和Y分量一样大小
enlarge_U = cv2.resize(U, (0, 0), fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
enlarge_V = cv2.resize(V, (0, 0), fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
# 合并YUV3通道
img_YUV = cv2.merge([Y, enlarge_U, enlarge_V])
dst = cv2.cvtColor(img_YUV, cv2.COLOR_YUV2BGR)
return dst
还有另一种方法
def merge_YUV2RGB_v2(Y, U, V):
"""
转换YUV图像为RGB格式(缩小Y)
:param Y: Y分量图像
:param U: U分量图像
:param V: V分量图像
:return: RGB格式图像
"""
rows, cols = Y.shape[:2]
# 先缩小Y分量,合并3通道,转换为RGB格式图像后,再放大至原来大小
shrink_Y = cv2.resize(Y, (cols / 2, rows / 2), interpolation=cv2.INTER_AREA)
# 合并YUV3通道
img_YUV = cv2.merge([shrink_Y, U, V])
dst = cv2.cvtColor(img_YUV, cv2.COLOR_YUV2BGR)
cv2.COLOR_YUV2BGR_I420
# 放大
enlarge_dst = cv2.resize(dst, (0, 0), fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
return enlarge_dst
完整代码如下:
# -*- coding: utf-8 -*-
import cv2
import numpy as np
def read_YUV420(image_path, rows, cols):
"""
读取YUV文件,解析为Y, U, V图像
:param image_path: YUV图像路径
:param rows: 给定高
:param cols: 给定宽
:return: 列表,[Y, U, V]
"""
# create Y
gray = np.zeros((rows, cols), np.uint8)
print type(gray)
print gray.shape
# create U,V
img_U = np.zeros((rows / 2, cols / 2), np.uint8)
print type(img_U)
print img_U.shape
img_V = np.zeros((rows / 2, cols / 2), np.uint8)
print type(img_V)
print img_V.shape
with open(image_path, 'rb') as reader:
for i in xrange(rows):
for j in xrange(cols):
gray[i, j] = ord(reader.read(1))
for i in xrange(rows / 2):
for j in xrange(cols / 2):
img_U[i, j] = ord(reader.read(1))
for i in xrange(rows / 2):
for j in xrange(cols / 2):
img_V[i, j] = ord(reader.read(1))
return [gray, img_U, img_V]
def merge_YUV2RGB_v1(Y, U, V):
"""
转换YUV图像为RGB格式(放大U、V)
:param Y: Y分量图像
:param U: U分量图像
:param V: V分量图像
:return: RGB格式图像
"""
# Y分量图像比U、V分量图像大一倍,想要合并3个分量,需要先放大U、V分量和Y分量一样大小
enlarge_U = cv2.resize(U, (0, 0), fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
enlarge_V = cv2.resize(V, (0, 0), fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
# 合并YUV3通道
img_YUV = cv2.merge([Y, enlarge_U, enlarge_V])
dst = cv2.cvtColor(img_YUV, cv2.COLOR_YUV2BGR)
return dst
def merge_YUV2RGB_v2(Y, U, V):
"""
转换YUV图像为RGB格式(缩小Y)
:param Y: Y分量图像
:param U: U分量图像
:param V: V分量图像
:return: RGB格式图像
"""
rows, cols = Y.shape[:2]
# 先缩小Y分量,合并3通道,转换为RGB格式图像后,再放大至原来大小
shrink_Y = cv2.resize(Y, (cols / 2, rows / 2), interpolation=cv2.INTER_AREA)
# 合并YUV3通道
img_YUV = cv2.merge([shrink_Y, U, V])
dst = cv2.cvtColor(img_YUV, cv2.COLOR_YUV2BGR)
cv2.COLOR_YUV2BGR_I420
# 放大
enlarge_dst = cv2.resize(dst, (0, 0), fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
return enlarge_dst
if __name__ == '__main__':
rows = 480
cols = 640
image_path = 'C:\\yuv\\jpgimage1_image_640_480.yuv'
Y, U, V = read_YUV420(image_path, rows, cols)
dst = merge_YUV2RGB_v1(Y, U, V)
cv2.imshow("dst", dst)
cv2.waitKey(0)
Python
:关键函数介绍
gray = np.zeros((rows, cols), np.uint8)
参考:numpy.zeros
原型:
numpy.zeros(shape, dtype=float, order='C')
(rows, cols)
- shape
,数组形状
np.uint8
- dtype
,默认是 float
,当前设置为 8
位无符号整数
with open(image_path, 'rb') as reader:
参考:[open(name[, mode[, buffering]])](https://docs.python.org/2.7/library/functions.html?highlight=open#open)
gray[i, j] = ord(reader.read(1))
参考:ord(c)
原型:ord(c)
函数功能:返回单个字符字符串的整数值
Python
:
YUV420p
转
RGB
方法二
之前的学习,让我又有了另一种更简单的方法
树莓派(Raspberry Pi)中PiCamera+OpenCV的使用
直接上程序:
# -*- coding: utf-8 -*-
import cv2
import numpy as np
if __name__ == '__main__':
rows = 240
cols = 320
with open("c:\\zhujian\\yuv\\img2_yuv", 'rb') as reader:
bin_y = reader.read(rows * cols)
print type(bin_y)
print len(bin_y)
num_y = np.fromstring(bin_y, np.uint8)
print type(num_y)
print num_y.shape
img_y = np.reshape(num_y, (rows, cols))
print type(img_y)
print img_y.shape
cv2.imshow("img", img_y)
cv2.waitKey(0)
bin_u = reader.read(rows * cols / 4)
num_u = np.fromstring(bin_u, np.uint8)
img_u = np.reshape(num_u, (rows / 2, cols / 2))
bin_v = reader.read(rows * cols / 4)
num_v = np.fromstring(bin_v, np.uint8)
img_v = np.reshape(num_v, (rows / 2, cols / 2))
enlarge_u = cv2.resize(img_u, dsize=(cols, rows), interpolation=cv2.INTER_CUBIC)
print enlarge_u.shape
enlarge_v = cv2.resize(img_v, dsize=(cols, rows), interpolation=cv2.INTER_CUBIC)
dst = cv2.merge([img_y, enlarge_u, enlarge_v])
bgr = cv2.cvtColor(dst, cv2.COLOR_YUV2BGR)
cv2.imshow("dst", bgr)
cv2.waitKey(0)
Python
:关键函数介绍
num_y = np.fromstring(bin_y, np.uint8)
参考:numpy.fromstring
原型:
numpy.fromstring(string, dtype=float, count=-1, sep='')
函数功能:转换一维包含二进制数据或者文本数据的数组(A new 1-D array initialized from raw binary or text data in a string.
)
img_y = np.reshape(num_y, (rows, cols))
参考:numpy.reshape
原型:
numpy.reshape(a, newshape, order='C')[source]
函数功能:转换数组形状大小,更改后数据变换参考链接(Gives a new shape to an array without changing its data.
)
C
:
YUV420p
转
RGB
使用 C
语言进行 YUV
图像的操作,和 C++
类似
Y
、U
、V
分量,读取二进制图像文件,写入图像中;U
、V
分量进行放大,合并 3
个分量为一个 3
通道YUV
图像为 RGB
图像代码如下:
#include
#include
#include
#include
#include
int main(int argc, char* argv[]) {
int width = 640;
int height = 480;
IplImage* img_Y = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
IplImage* img_U = cvCreateImage(cvSize(width / 2, height / 2), IPL_DEPTH_8U, 1);
IplImage* img_V = cvCreateImage(cvSize(width / 2, height / 2), IPL_DEPTH_8U, 1);
IplImage* enlarge_U = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
IplImage* enlarge_V = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
IplImage* YUV = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
IplImage* RGB = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
// 打开二进制图像文件
FILE* fp = NULL;
if ((fp = fopen("c:\\zhujian\\yuv\\jpgimage1_image_640_480.yuv", "rb")) == NULL) {
printf("Error: open file failed\n");
exit(0);
}
// 读取Y分量
for (int i = 0; i < height; i++) {
uchar* ptr = (uchar*)(img_Y->imageData + i*img_Y->widthStep);
for (int j = 0; j < width; j++) {
ptr[j] = (uchar)fgetc(fp);
}
}
// 读取U分量
for (int i = 0; i < height / 2; i++) {
uchar* ptr = (uchar*)(img_U->imageData + i*img_U->widthStep);
for (int j = 0; j < width / 2; j++) {
ptr[j] = (uchar)fgetc(fp);
}
}
// 读取V分量
for (int i = 0; i < height / 2; i++) {
uchar* ptr = (uchar*)(img_V->imageData + i*img_V->widthStep);
for (int j = 0; j < width / 2; j++) {
ptr[j] = (uchar)fgetc(fp);
}
}
// 读取文件结束后,关闭文件指针
fclose(fp);
// 放大U、V分量
cvResize(img_U, enlarge_U, CV_INTER_CUBIC);
cvResize(img_V, enlarge_V, CV_INTER_CUBIC);
// 合并Y、U、V分量为一个3通道YUV图像
cvMerge(img_Y, enlarge_U, enlarge_V, NULL, YUV);
// 转换YUV图像为RGB图像
cvCvtColor(YUV, RGB, CV_YUV2BGR);
// 显示图像
cvNamedWindow("img", CV_WINDOW_NORMAL);
cvShowImage("img", RGB);
cvWaitKey(0);
cvDestroyWindow("img");
cvReleaseImage(&RGB);
cvReleaseImage(&YUV);
cvReleaseImage(&enlarge_V);
cvReleaseImage(&enlarge_U);
cvReleaseImage(&img_V);
cvReleaseImage(&img_U);
cvReleaseImage(&img_Y);
return 0;
}
当前运行环境是 Win7+VS2013+OpenCV2.4.13
,C
版本的 OpenCV
需要在程序结束前消除掉 IplImage
结构体,明显感觉在这个部分速度慢了很多
C
:关键函数介绍
if ((fp = fopen("c:\\zhujian\\yuv\\jpgimage1_image_640_480.yuv", "rb")) == NULL) {
参考:fopen
原型:
FILE * fopen ( const char * filename, const char * mode );
ptr[j] = (uchar)fgetc(fp);
参考:fgetc
原型:
int fgetc ( FILE * stream );
函数功能:从流中读取一个字符(Get character from stream
)
YUV
图片资源
YUV420格式图片 和 视频 测试用
2张yuv格式图像