YUV2RGB Opencv

来源:互联网 发布:java香港空间 编辑:程序博客网 时间:2024/06/04 18:15

YUV2RGB OpenCV(C/C++/Python)

参考:

百度百科:YUV
维基百科:YUV
YUV Colorspace:http://softpixel.com/~cwright/programming/colorspace/yuv/
YUV420P格式分析:https://my.oschina.net/u/589963/blog/167766


YUV是一种颜色编码方法

Y 分量表示颜色的亮度(luminance),单取出 Y 分量就是图像的灰度图;UV 分量表示颜色色度或者浓度(Chrominance

YUV 图像有两种编码格式:

  • 紧缩格式(packed formats):YUV 三通道像素值依次排列,即 Y0 U0 V0 Y1 U1 V1 ...
  • 平面格式(planar formats):先排列 Y 分量的所有像素值,再排列 U 分量的,最后排列 V 分量的

Note:平面模式适合采样(subsample

由于人眼对于亮度的敏感度远大于色度,所以减少 UV 分量并不会过多损害图像的质量,从而减少了数据量,达到压缩的目的

  • YUV444 表示图像完全采样,没有压缩,紧缩格式
  • YUV420p 表示图像 2:1 的水平取样,垂直 2:1 采样,即每 4Y 分量对应一个 UV 分量,平面模式

Note:YUV420p(有时简称为 YUV420) 是常见的图像格式,接下来进行的 YUVRGB 的转换操作都是针对 420p 格式的

YUVRGB 之间的转换公式如下:

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 *  .114000U = R * -.168736 + G * -.331264 + B *  .500000 + 128V = R *  .500000 + G * -.418688 + B * -.081312 + 128

Note:假定各分量之间的取值范围均为 [0, 255]


主要内容

  1. C++YUV420pRGB
  2. C++:关键函数介绍
  3. PythonYUV420pRGB 方法一
  4. Python:关键函数介绍
  5. PythonYUV420pRGB 方法二
  6. Python:关键函数介绍
  7. CYUV420pRGB
  8. C:关键函数介绍
  9. YUV 图片资源

C++YUV420pRGB

  • 首先读取 YUV 格式图片,是二进制文件;打开图像文件,按字节读取,并赋值到新建的图像中,其中,Y 分量图像大小和结果图像大小一致,UV 分量图像大小比结果图像小一倍;按顺序读取 Y 分量,再读取 UV 分量

    vector<Mat> 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<uchar>(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<uchar>(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<uchar>(i, j) = (uchar)ch;        }    }    fin.close();    vector<Mat> yuv;    yuv.push_back(img_Y);    yuv.push_back(img_U);    yuv.push_back(img_V);    return yuv;}
  • 得到从文件解析出来的各分量后,放大 UV 分量和 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<Mat> 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 <iostream>#include <fstream>#include <opencv2\opencv.hpp>using namespace std;using namespace cv;vector<Mat> 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<uchar>(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<uchar>(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<uchar>(i, j) = (uchar)ch;        }    }    fin.close();    vector<Mat> 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<Mat> 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<Mat> temp = readYUV(image_path, width, height);    vector<Mat> 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<uchar>(i, j) = (uchar)ch;

参考:Mat::at

原型:

template<typename T> T& Mat::at(int i, int j)

函数功能:返回指定数组元素的索引


PythonYUV420pRGB

在网上找到一个参考:

Python读取YUV

能够运行(import Image 改成 from PIL import Image),不过他使用的是 PIL 模块,目前最常用的图像处理库是 OpenCV

在知乎上的一个讨论,给了我灵感:

Python如何正确读取YUV二进制文件(为数字)?

  • 读取二进制图像文件并解析为 YUV 单通道图像:

    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 cv2import numpy as npdef 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 dstdef 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_dstif __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)

函数功能:返回单个字符字符串的整数值


PythonYUV420pRGB 方法二

之前的学习,让我又有了另一种更简单的方法

树莓派(Raspberry Pi)中PiCamera+OpenCV的使用

直接上程序:

# -*- coding: utf-8 -*-import cv2import numpy as npif __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.


CYUV420pRGB

使用 C 语言进行 YUV 图像的操作,和 C++ 类似

  • 首先就是对图像文件进行读取,新建空白的 YUV 分量,读取二进制图像文件,写入图像中;
  • 在对 UV 分量进行放大,合并 3 个分量为一个 3 通道
  • 转换 YUV 图像为 RGB 图像

代码如下:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <cv.h>#include <highgui.h>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.13C 版本的 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格式图像

0 0