Image conversion between YUV4:2:0 format and RGB format

Purpose

1. In the class, the teacher has given a sample program for converting RGB to YUV format. Read and understand the program.
2. Write a program to convert YUV to RGB. Use this program to convert the given experimental data into RGB files. And compare with the original RGB file, if there is an error, analyze the cause of the error.

The basic principle of the algorithm

In this experiment, the image range I used is [0,255]. The commonly used conversion formula for the value range of [0,255] is as follows.

RGB to YUV formula:

Y = 0.2990R + 0.5870G + 0.1140B

U = - 0.1684R - 0.3316G + 0.5B + 128

V = 0.5R - 0.4187G - 0.0813B + 128

Corresponding to YUV to RGB:

R = Y + 1.403(V - 128)

G = Y - 0.343 (U - 128) - 0.714 (V - 128)

B = Y + 1.770 (U-128)
The sampling method of YUV stream

The storage format of YUV code stream is actually closely related to its sampling method. There are three mainstream sampling methods:

  • YUV4:4:4: Indicates that a Y component corresponds to a set of UV components.
  • YUV4:2:2: means that two Y components share a set of UV components.
  • YUV4:1:1: is the 4:1 sampling of chromaticity in the horizontal direction.
  • YUV4:2:0: indicates that the four Y components share a set of UV components.

The following is a schematic diagram of the four sampling methodsInsert picture description here

This experiment is: YUV4:2:0 sampling method is: U component and V component are interlaced sampling, and the UV component is also interlaced sampling in its sampling line.
The rgb storage method is that
Insert picture description here
each pixel corresponds to three unsigned chars of bgr.
In order to consider the simplicity of the algorithm, the value used here is
to take yuv in the increasing order of u, v , then (y+256) uv, then y++, then yuv, then (y+256) uv, then y++, u++, v++. In this way, four groups form a circle.

Code part

Function part of the program:

void YUV2RGB(FILE* yuv,FILE* rgb,int width,int height)
{
    
    
	Init();
	int i = 0, j = 0;
	long shape = width * height;
	unsigned char* y, * u, * v, * rgb1, * auxrgb, * start;
	y = (unsigned char*)malloc(sizeof(unsigned char*) * shape);
	u = (unsigned char*)malloc(sizeof(unsigned char*) * shape / 4);
	v = (unsigned char*)malloc(sizeof(unsigned char*) * shape / 4);
	rgb1 = (unsigned char*)malloc(sizeof(unsigned char*) * shape * 3);//没必要将rgb分开,*3即可
	start = rgb1;//rgb1首指针
	fread(y, shape, sizeof(unsigned char), yuv);
	fread(u, shape / 4, sizeof(unsigned char), yuv);
	fread(v, shape / 4, sizeof(unsigned char), yuv);
	for (j = 0; j < height/2; j++)
	{
    
    
		auxrgb = rgb1 + 3 * width;//偶数行
		for(i=0;i<width/2;i++)
		{
    
    
			*rgb1++ = Rgu(*y + YUVRGB1403[*u]);
			*rgb1++ = Rgu(*y - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*rgb1++ = Rgu(*y + YUVRGB1770[*v]);

			*auxrgb++ = Rgu(*(width + y) + YUVRGB1403[*u]);
			*auxrgb++ = Rgu(*(width + y) - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*auxrgb++ = Rgu(*(width + y++) + YUVRGB1770[*v]);

			*rgb1++ = Rgu(*y + YUVRGB1403[*u]);
			*rgb1++ = Rgu(*y - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*rgb1++ = Rgu(*y + YUVRGB1770[*v]);

			*auxrgb++ = Rgu(*(width + y) + YUVRGB1403[*u]);
			*auxrgb++ = Rgu(*(width + y) - YUVRGB0343[*u++] - YUVRGB0714[*v]);
			*auxrgb++ = Rgu(*(width + y++) + YUVRGB1770[*v++]);
		}
		y += width; rgb1 += 3 * width;
	}

	if (shape * 3 == fwrite(start, sizeof(unsigned char*), shape * 3, rgb)) 
		printf("success");
	else printf("failed to write");
	free(start); 
	free(y - shape);
	free(u - shape / 4);
	free(v - shape / 4);
}

Lookup table part

static float YUVRGB1403[256], YUVRGB0343[256], YUVRGB0714[256], YUVRGB1770[256];

void Init()
{
    
    
	int i = 0;
	for (i; i < 256; i++)
	{
    
    
		YUVRGB1403[i] = 1.403 * (i - 128);
		YUVRGB0343[i] = 0.343 * (i - 128);
		YUVRGB0714[i] = 0.714 * (i - 128);
		YUVRGB1770[i] = 1.770 * (i - 128);
	}
}

main function

int main(int argc,char **argv)
{
    
    

	if (argc == 5)
	{
    
    
		FILE* yuv = fopen(argv[1], "rb");
		FILE* rgb = fopen(argv[2], "wb");
		YUV2RGB(yuv, rgb, atoi(argv[3]), atoi(argv[4]));
		fclose(yuv);
		fclose(rgb);
	}
	else printf("需要4个参数");
	return 0;
}

Here, the file path and image size are directly passed to *argv[], eliminating the need for judgment, and the code is more concise, at the cost of less rigor.
Complete code

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
static float YUVRGB1403[256], YUVRGB0343[256], YUVRGB0714[256], YUVRGB1770[256];

int main(int argc,char **argv)
{
    
    

	if (argc == 5)
	{
    
    
		FILE* yuv = fopen(argv[1], "rb");
		FILE* rgb = fopen(argv[2], "wb");
		YUV2RGB(yuv, rgb, atoi(argv[3]), atoi(argv[4]));
		fclose(yuv);
		fclose(rgb);
	}
	else printf("需要4个参数");
	return 0;
}
unsigned char Rgu(float a)
{
    
    
	if (a < 0)	return (unsigned char)0;
	else if (a > 255) return (unsigned char)255;
	else return (unsigned char)a;
}

void YUV2RGB(FILE* yuv,FILE* rgb,int width,int height)
{
    
    
	Init();
	int i = 0, j = 0;
	long shape = width * height;
	unsigned char* y, * u, * v, * rgb1, * auxrgb, * start;
	y = (unsigned char*)malloc(sizeof(unsigned char*) * shape);
	u = (unsigned char*)malloc(sizeof(unsigned char*) * shape / 4);
	v = (unsigned char*)malloc(sizeof(unsigned char*) * shape / 4);
	rgb1 = (unsigned char*)malloc(sizeof(unsigned char*) * shape * 3);//没必要将rgb分开,*3即可
	start = rgb1;//rgb1首指针
	fread(y, shape, sizeof(unsigned char), yuv);
	fread(u, shape / 4, sizeof(unsigned char), yuv);
	fread(v, shape / 4, sizeof(unsigned char), yuv);
	for (j = 0; j < height/2; j++)
	{
    
    
		auxrgb = rgb1 + 3 * width;//偶数行
		for(i=0;i<width/2;i++)
		{
    
    
			*rgb1++ = Rgu(*y + YUVRGB1403[*u]);
			*rgb1++ = Rgu(*y - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*rgb1++ = Rgu(*y + YUVRGB1770[*v]);

			*auxrgb++ = Rgu(*(width + y) + YUVRGB1403[*u]);
			*auxrgb++ = Rgu(*(width + y) - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*auxrgb++ = Rgu(*(width + y++) + YUVRGB1770[*v]);

			*rgb1++ = Rgu(*y + YUVRGB1403[*u]);
			*rgb1++ = Rgu(*y - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*rgb1++ = Rgu(*y + YUVRGB1770[*v]);

			*auxrgb++ = Rgu(*(width + y) + YUVRGB1403[*u]);
			*auxrgb++ = Rgu(*(width + y) - YUVRGB0343[*u++] - YUVRGB0714[*v]);
			*auxrgb++ = Rgu(*(width + y++) + YUVRGB1770[*v++]);
		}
		y += width; rgb1 += 3 * width;
	}

	if (shape * 3 == fwrite(start, sizeof(unsigned char*), shape * 3, rgb)) 
		printf("success");
	else printf("failed to write");
	free(start); 
	free(y - shape);
	free(u - shape / 4);
	free(v - shape / 4);
}
void Init()
{
    
    
	int i = 0;
	for (i; i < 256; i++)
	{
    
    
		YUVRGB1403[i] = 1.403 * (i - 128);
		YUVRGB0343[i] = 0.343 * (i - 128);
		YUVRGB0714[i] = 0.714 * (i - 128);
		YUVRGB1770[i] = 1.770 * (i - 128);
	}
}

Experimental result

rgb image Insert picture description hereafter conversion of original rgb imageInsert picture description here

Experimental results

After visual comparison,
it is found that the color is slightly deviated. This is because the sampling rate of the image chroma caused by the yuv4:2:0 format is low, the gradation of the chroma signal is lost, and the color transition is not "round" enough.

Guess you like

Origin blog.csdn.net/weixin_44218153/article/details/114947643