### 無法將rgb轉換為YCbCr，且不丟失。

#### [英]Unable to convert rgb to YCbCr and back without loss

I need to write a program that will take pictures, transfer them to YCbCr format, manipulate the picture and then return the result back in RGB format. Before I write the manipulation I wrote the transformation from RGB to YCbCr and back and wanted to test that it works. On most pictures I've tested it on it works, and returns back a picture that is identical to the original. I have problems with 2 pictures, where the transformation seems to change some of the pixels. the pictures are (cropped areas from larger pictures):

The original:

The result:

The original:

The result:

I'm taking the values for the conversion matrices and vectors from this site, and I've tried all the 3 available options, all of them give similar "errors".

The code I use:

``````//transformation matrix from RGB to YCbCr
const double rgb2YCbCrMatrix[3][3] = {
{ 0.257,  0.504,  0.098},
{-0.148, -0.291,  0.439},
{ 0.439, -0.368, -0.071}
};
//transformation vector from RGB to YcBCr
const char rgb2YCbCrVector[3] = {
16,
128,
128
};
//transformation matrix from YCbCr to RGB
const double TCbCr2rgbMatrix[3][3] = {
{1.164,  0.000,  1.596},
{1.164, -0.392, -0.813},
{1.164,  2.017,  0.000}
};
//transformation vector from RGB to YcBCr
const char YCbCr2rgbVector[3] = {
-16,
-128,
-128};

//calculate the values of the Y, Cb and Cr channels, used the formula for HDTV, as described at http://www.equasys.de/colorconversion.html
for (int i = 0; i < height*width; i++) {
YChannel[i]  = redChannel[i]*rgb2YCbCrMatrix[0][0]+greenChannel[i]*rgb2YCbCrMatrix[0][1]+blueChannel[i]*rgb2YCbCrMatrix[0][2] + rgb2YCbCrVector[0];
CbChannel[i] = redChannel[i]*rgb2YCbCrMatrix[1][0]+greenChannel[i]*rgb2YCbCrMatrix[1][1]+blueChannel[i]*rgb2YCbCrMatrix[1][2] + rgb2YCbCrVector[1];
CrChannel[i] = redChannel[i]*rgb2YCbCrMatrix[2][0]+greenChannel[i]*rgb2YCbCrMatrix[2][1]+blueChannel[i]*rgb2YCbCrMatrix[2][2] + rgb2YCbCrVector[2];
}
//calculate the values of the RGB channels after the transformation, used the formula for HDTV, as described at http://www.equasys.de/colorconversion.html
for (int i = 0; i < height*width; i++) {
redChannel[i]   = (YChannel[i] + YCbCr2rgbVector[0])*TCbCr2rgbMatrix[0][0]+(CbChannel[i] + YCbCr2rgbVector[1])*TCbCr2rgbMatrix[0][1]+(CrChannel[i] + YCbCr2rgbVector[2])*TCbCr2rgbMatrix[0][2];
greenChannel[i] = (YChannel[i] + YCbCr2rgbVector[0])*TCbCr2rgbMatrix[1][0]+(CbChannel[i] + YCbCr2rgbVector[1])*TCbCr2rgbMatrix[1][1]+(CrChannel[i] + YCbCr2rgbVector[2])*TCbCr2rgbMatrix[1][2];
blueChannel[i]  = (YChannel[i] + YCbCr2rgbVector[0])*TCbCr2rgbMatrix[2][0]+(CbChannel[i] + YCbCr2rgbVector[1])*TCbCr2rgbMatrix[2][1]+(CrChannel[i] + YCbCr2rgbVector[2])*TCbCr2rgbMatrix[2][2];
}
``````

Where `redChannel`, `greenChannel` and `blueChannel` as well as `YChannel`, `CbChannel` and `CrChannel` are of `unsigned char *` type, and `height` and `width` are the dimensions of the picture. Why doesn't those pictures return the same, while all other tested pictures return perfectly fine.

p.s.

I've tested both pictures in Matlab using the commands `rgb2ycbcr` and `ycbcr2rgb`, and rounding them down with `uint8` and it gave me good results.

``````in=imread(pic);
YCbCr = uint8(rgb2ycbcr(in));
out = uint8(ycbcr2rgb(YCbCr));
imshow(out);
``````

## 2 个解决方案

### #1

1

There is no 1-to-1 conversion between YCbCr and RGB. Using the same range of values, there are colors in both color spaces that cannot be represented in the other.

YCbCr和RGB之間沒有1- 1的轉換。使用相同范圍的值，在兩個顏色空間中都有不能表示的顏色。

When you do a conversion in either direction, you need to clamp the values to the range.

For example, an RGB to Y conversion needs to have something like this for every component conversion:

`````` double RGBtoY (double R, double G, double B)
{
. . . .
if (result < 0.0)
result = 0.0 ;
else if (result > 255) // Or whatever maximum value
result = 255 ;
}
``````

Otherwise, you are going to get wrap around.

### #2

1

I observed that the glitched areas appear to be in areas that are likely either pure black or white.

Based on this, a guess: rgb2YCbCrVector has type char, for which the value '128' actually overflows to -128.

So in the case of r,g,b=0,0,0 you get Y,Cr,Cb=16,-128,-128 instead of 16,128,128. Then when converting back, the formula gives

``````redChannel = (16-16)*blah + (-128 - 128)*blah2 + (-128 - 128)*blah3
``````

Test program:

``````char a = 128;
char b = -128;
printf("%d", a + b);
``````

Output: -256

So when converting back, redChannel = 0*blah1 - 256*blah2 - 256*blah3

Which is clearly not 0 as you would originally expect.

I would consider storing the conversion vectors as float/double, or at least int. Maybe consider doing the same for the color channels too, though not sure that would make a difference.