RGB、YUV420p、YUV420sp之间的互相转换(C++)

关于RGB、YUV介绍可以参考 RGB 与YUV颜色模型及存储格式

1.covert RGB to NV21

void encodeYUV420sp(unsigned  char* yuv420sp, unsigned char* rgb, int width, int height)
{
	if (yuv420sp == NULL || rgb == NULL)
		return;
	int frameSize = width*height;
	int yIndex = 0;
	int uvIndex = frameSize;

	int R, G, B, Y, U, V;
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			B = rgb[(i * width + j) * 3 + 0];
			G = rgb[(i * width + j) * 3 + 1];
			R = rgb[(i * width + j) * 3 + 2];
			
			//RGB to YUV
			Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
			U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
			V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;

			yuv420sp[yIndex++] = (unsigned char)((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
			if (i % 2 == 0 && j % 2 == 0)
			{
				yuv420sp[uvIndex++] = (unsigned char)((V < 0) ? 0 : ((V > 255) ? 255 : V));
				yuv420sp[uvIndex++] = (unsigned char)((U < 0) ? 0 : ((U > 255) ? 255 : U));
			}
		}
	}
}

2.covert RGB to YU12(I420)

void rgb_to_yuv420p(unsigned  char* yuv420p, unsigned char* rgb, int width, int height)
{
	if (yuv420p == NULL || rgb == NULL)
		return;
	int frameSize = width*height;
	int chromaSize = frameSize / 4;

	int yIndex = 0;
	int uIndex = frameSize;
	int vIndex = frameSize + chromaSize;

	int R, G, B, Y, U, V;
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			B = rgb[(i * width + j) * 3 + 0];
			G = rgb[(i * width + j) * 3 + 1];
			R = rgb[(i * width + j) * 3 + 2];

			//RGB to YUV
			Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
			U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
			V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;

			yuv420p[yIndex++] = (unsigned char)((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
			if (i % 2 == 0 && j % 2 == 0)
			{
				yuv420p[uIndex++] = (unsigned char)((U < 0) ? 0 : ((U > 255) ? 255 : U));
				yuv420p[vIndex++] = (unsigned char)((V < 0) ? 0 : ((V > 255) ? 255 : V));
			}
		}
	}
}

3.covert NV21 to YU12(I420)

void yuv420sp_to_yuv420p(unsigned char* yuv420p,  unsigned char* yuv420sp, int width, int height) {
	if (yuv420sp == NULL || yuv420p == NULL)
		return;
	int framesize = width * height;
	int i = 0, j = 0;
	//copy Y
	for (i = 0; i < framesize; i++) {
		*(yuv420p + i) = *(yuv420sp + i);
	}
	//copy U
	i = 0;
	for (j = 1; j < framesize / 2; j += 2) {
		*(yuv420p + (i + framesize)) = *(yuv420sp + (j + framesize));
		i++;
	}
	//copy V
	i = 0;
	for (j = 0; j < framesize / 2; j += 2) {
		*(yuv420p + (i + framesize * 5 / 4)) = *(yuv420sp + (j + framesize));
		i++;
	}
}

YUV其他存储格式的转换把上面的函数在UV赋值时变通一下即可。

测试

测试图片:
RGB、YUV420p、YUV420sp之间的互相转换(C++)_第1张图片

#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;

// show image
void showImage(String windowName, Mat img, int flag = 1)
{
	namedWindow(windowName, flag);//0: CV_WINDOW_NORMAL 1: WINDOW_AUTOSIZE
	imshow(windowName, img);
	waitKey(0);
	//destroyWindow(windowName);
}

int main()
{
	Mat img_eye = imread("Sharingan_1.jpg");
	showImage("img_eye", img_eye);

	int height = img_eye.rows;
	int width = img_eye.cols;

    unsigned char* img_eye_bgr_data = (unsigned char*)malloc(height*width * 3 * sizeof(unsigned char));
	for (int i = 0; i < height; i++)
	{
		unsigned char* current_row = img_eye.ptr(i);
		for (int j = 0; j < width; j++)
		{
			img_eye_bgr_data[(i * width + j) * 3 + 0] = current_row[j * 3 + 0];//B
			img_eye_bgr_data[(i * width + j) * 3 + 1] = current_row[j * 3 + 1];//G
			img_eye_bgr_data[(i * width + j) * 3 + 2] = current_row[j * 3 + 2];//R
		}
	}
	
	// 1. RGB covert to NV21
	unsigned char* img_eye_nv21_data = (unsigned char*)malloc(height*width * 3 / 2 * sizeof(unsigned char));
	encodeYUV420sp(img_eye_nv21_data, img_eye_bgr_data, width, height);
	Mat img_eye_nv21(height * 3 / 2, width, CV_8UC1, img_eye_nv21_data);
	// test 
	Mat img_BGR(height, width, CV_8UC3);
	cvtColor(img_eye_nv21, img_BGR, CV_YUV2BGR_NV21);//CV_YUV420sp2BGR
	showImage("img_BGR", img_BGR);
	
	// 2. RGB covert to YU12
	//类似,略去,使用CV_YUV2BGR_I420)或者CV_YUV2BGR_IYUV

	// 3. NV21 covert to YU12(I420)
        //类似,略去,

	return 0;
}

提示:如果把YUV420p的YU12和YV12或者被YUV420sp的NV12和NV21搞颠倒了,图片的红色和蓝色会颠倒。

类似如下:
RGB、YUV420p、YUV420sp之间的互相转换(C++)_第2张图片

你可能感兴趣的:(数字图像处理)