Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Converting RGB to YCrCb #17370

Closed
csyuhao opened this issue May 25, 2020 · 8 comments
Closed

Converting RGB to YCrCb #17370

csyuhao opened this issue May 25, 2020 · 8 comments
Labels
incomplete question (invalid tracker) ask questions and other "no action" items here: https://forum.opencv.org

Comments

@csyuhao
Copy link

csyuhao commented May 25, 2020

System information (version)
  • OpenCV => 4.2.0
  • Operating System / Platform => Windows 64 Bit 2004
  • Compiler => None
Detailed description

I have a problem when I convert the color space from RGB to YCrCb. Accroding to RGB ↔ YCrCb JPEG (or YCC), I write a function named rgb2ycrcb.

def rgb2ycrcb(im):
    y = 0.299 * im[:, :, 0:1] + 0.587 * im[:, :, 1:2] + 0.114 * im[:, :, 2:3]
    cr = (im[:, :, 0:1] - y) * 0.713
    cb = (im[:, :, 2:3] - y) * 0.564
    ycrcb = np.concatenate([y, cr, cb], axis=2)
    ycrcb[:, :, [1, 2]] += 128
    return np.uint8(ycrcb)
Steps to reproduce

I use a picture named xxx.png to test this function. The code works as follows:

    img = cv2.imread(r'xxx.png')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    img_ycrcb_1 = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
    img_ycrcb_2 = rgb2ycrcb(img)

    print(img_ycrcb_1[:, :, 0].max(), img_ycrcb_1[:, :, 1].max(), img_ycrcb_1[:, :, 2].max())
    print(img_ycrcb_2[:, :, 0].max(), img_ycrcb_2[:, :, 1].max(), img_ycrcb_2[:, :, 2].max())
    print(np.abs(img_ycrcb_1[:, :, 0] - img_ycrcb_2[:, :, 0]).sum())

But the output is:

255 153 136
255 153 135
1828

You can see the output of cvtColor is much different with the rgb2ycrcb. Are there some mistakes in my function rgb2ycrcb?

Issue submission checklist

I checked the problem with documentation, FAQ, open issues, answers.opencv.org, Stack Overflow, etc and have not found solution.

@dkurt
Copy link
Member

dkurt commented May 25, 2020

@YUHAOOOO, OpenCV probably uses integer arithmetic. Make sure that np.uint8(ycrcb) rounds values as expected. Try to find exact RGB and YCrCb values which are different.

@csyuhao
Copy link
Author

csyuhao commented May 26, 2020

@dkurt, thanks a lot.

I do some experiments try to test np.uint8(ycrcb). I change the rgb2ycrcb to return ycrcb values before rounding, which is as follows.

def rgb2ycrcb(im):
    y = 0.299 * im[:, :, 0:1] + 0.587 * im[:, :, 1:2] + 0.114 * im[:, :, 2:3]
    cr = (im[:, :, 0:1] - y) * 0.713
    cb = (im[:, :, 2:3] - y) * 0.564
    ycrcb = np.concatenate([y, cr, cb], axis=2)
    ycrcb[:, :, [1, 2]] += 128.0
    return np.uint8(ycrcb), ycrcb

And the test code is also changed to check where the difference between the value output by cvtColor and the value output by rgb2ycrcb is. The size of xxx.png is 64 * 64 * 3. The code is as follows:

    img = cv2.imread(r'xxx.png')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    img_ycrcb_1 = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
    img_ycrcb_2, ycrcb = rgb2ycrcb(img)

    for i in range(0, 64):
        for j in range(0, 64):
            for c in range(0, 3):
                if img_ycrcb_1[i, j, c] != img_ycrcb_2[i, j, c]:
                    print('The value output by cvtColor: {}, The value output by rgb2ycrcb: {}, The value before rounding: {}, The location:[{}, {}, {}]'.format( img_ycrcb_1[i, j, c], img_ycrcb_2[i, j, c], ycrcb[i, j, c], i, j, c))

There are some outputs, which make me confused.

The value output by cvtColor: 129, The value output by rgb2ycrcb: 128, The value before rounding: 128.33106800000002, The location:[59, 59, 2]
The value output by cvtColor: 133, The value output by rgb2ycrcb: 132, The value before rounding: 132.961767, The location:[63, 51, 1]
The value output by cvtColor: 132, The value output by rgb2ycrcb: 131, The value before rounding: 131.474449, The location:[63, 53, 1]
The value output by cvtColor: 131, The value output by rgb2ycrcb: 130, The value before rounding: 130.812072, The location:[63, 54, 1]
The value output by cvtColor: 124, The value output by rgb2ycrcb: 123, The value before rounding: 123.996164, The location:[63, 55, 2]

Some outputs including the 1st line, the 3rd line are strange. In these cases, Rouding rule can not work.

@dkurt
Copy link
Member

dkurt commented May 26, 2020

Please print the source RGB pixel as well.

@csyuhao
Copy link
Author

csyuhao commented May 26, 2020

The source value is as follows (RGB tuples):

The source value is [255 254 255], The value output by cvtColor: 129, The value output by rgb2ycrcb: 128, The value before rounding: 128.33106800000002, The location:[59, 59, 2]
The source value is [156 149 131], The value output by cvtColor: 133, The value output by rgb2ycrcb: 132, The value before rounding: 132.961767, The location:[63, 51, 1]
The source value is [158 153 141], The value output by cvtColor: 132, The value output by rgb2ycrcb: 131, The value before rounding: 131.474449, The location:[63, 53, 1]
The source value is [140 136 126], The value output by cvtColor: 131, The value output by rgb2ycrcb: 130, The value before rounding: 130.812072, The location:[63, 54, 1]
The source value is [116 113 106], The value output by cvtColor: 124, The value output by rgb2ycrcb: 123, The value before rounding: 123.996164, The location:[63, 55, 2]

The corresponding test code is:

    img = cv2.imread(r'xxx.png')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    img_ycrcb_1 = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
    img_ycrcb_2, ycrcb = rgb2ycrcb(img)
    for i in range(0, 64):
        for j in range(0, 64):
            for c in range(0, 3):
                if img_ycrcb_1[i, j, c] != img_ycrcb_2[i, j, c]:
                    print('The source value is {}, The value output by cvtColor: {}, The value output by rgb2ycrcb: {}, The value before rounding: {}, The location:[{}, {}, {}]'.format(img[i, j, :], img_ycrcb_1[i, j, c], img_ycrcb_2[i, j, c], ycrcb[i, j, c], i, j, c))

@alalek
Copy link
Member

alalek commented May 26, 2020

There are floating point versions of cvtColor implementations (need to change input type and change range to 0..1).
Try to use them instead of integer-based implementation.

Value difference 1 is acceptable due to rounding errors.

@csyuhao
Copy link
Author

csyuhao commented May 26, 2020

When I use the floating point version, it is much better than integer-based implementation. I am just curious why it happens in a same computer. Is it due to design difference between Python and C++ in rounding rule?

@dkurt
Copy link
Member

dkurt commented May 26, 2020

@YUHAOOOO, because you just compare different implementations. Inside OpenCV, for uin8 input different algorithm is used comparing to fp32 input.

@csyuhao
Copy link
Author

csyuhao commented May 26, 2020

Thanks a lot.

@csyuhao csyuhao closed this as completed May 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
incomplete question (invalid tracker) ask questions and other "no action" items here: https://forum.opencv.org
Projects
None yet
Development

No branches or pull requests

3 participants