0

I'm exploring the replacement of opencv's cv2.cvtColor(yuv, cv2.COLOR_YUV2RGB_YUYV) and cv2.cvtColor(y8, cv2.COLOR_GRAY2RGB) functions (due to licensing/legal issues). I found skimage.color (scikit) convert_colorspace(..., 'yuv', 'rgb') function, but it is not working as a drop-in replacement. The best I can do is a monochrome magenta image, because most of the converted green values drop below zero and most of the blue is above 1, causing clipping on both ends (Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).). I'm not 100% sure of the data input format; I know it's identified as "YUV422" and it reads 16 bits per pixel (split into two bytes and put into a two-column array dimension). All evidence, though, comports with YUYV (a.k.a. YUY2). I tried converting it to YUV444 per https://en.wikipedia.org/wiki/YUV#Y%E2%80%B2UV422_to_RGB888_conversion -- specifically by expanding my array a dimension and copying u and v to each row ([y1 u v] and [y2 u v]) instead of y1 and u on one row and y2 and v in the next row -- and that gives me the best results, such as they are, but I need full color. I have normalized my input values to a float between 0 and 1 (from 0-255 integer bytes), and I've verified that the inputs are in range, but I'm mystified why the outputs are negative for green and above 1 for blue. I am new to this color stuff and I'm trying to learn what I can, but I'm kind of groping in the dark.

ObExample:

>>> import numpy as np
>>> from skimage import color
>>> input = np.array([[[0.21176471, 0.48235294, 0.4627451 ], [0.2, 0.48235294, 0.4627451 ], [0.2, 0.48235294, 0.4627451 ]]])
>>> color.convert_colorspace(input, 'yuv', 'rgb')
array([[[ 0.73923999, -0.2472721 ,  1.19193572],
        [ 0.72747528, -0.25903681,  1.18017101],
        [ 0.72747528, -0.25903681,  1.18017101]]])
>>> color.yuv2rgb(input)
array([[[ 0.73923999, -0.2472721 ,  1.19193572],
        [ 0.72747528, -0.25903681,  1.18017101],
        [ 0.72747528, -0.25903681,  1.18017101]]])
>>> 

Note how all the greens are negative and the blues above 1.

Questions to ponder:

  1. What input format does convert_colorspace() expect? So far I've ascertained it's floating point values between 0 and 1, but there are many Y'UV layouts; it does take exactly three values, which comports with YUV444, so that's what I used.

  2. Is there a better replacement for opencv for converting between color spaces?

ADDENDUM #1:

Following @MarkRansom's hint, I found a page which said:

As I understand it, YUV has its roots in analog TV with normalized ranges like

Y[0 .... 1.0]

U[-0.436....+0.436]

V[-0.615 .... +0.615]

So I changed my code to normalize the Y to 0-1 by dividing my 255.0, and the U and V I normalized by dividing by the same and subtracting 0.5. This gave nearly acceptable results so I'm on the right track, but I need to tweak this because there are still a handful of out-of-range values, so it's clear my adjustment is not quite correct yet. I tried subtracting 16 from the Ys and 128 from the Us and Vs before dividing by 256.0 (this time), inspired by Wikipedia YUV page, and this yields zero blue values above one, but a lot more greens below zero compared to the previous solution.

Vercingatorix
  • 1,838
  • 1
  • 13
  • 22
  • There are two different definitions of YUV ranges a full scale one from 0-255 and another with some headroom on the top and bottom. – Mark Ransom Apr 20 '22 at 22:26
  • Thanks, @MarkRansom, that permitted me significant progress. I've updated the question with my additional results. – Vercingatorix Apr 21 '22 at 15:40
  • I thought this question was familiar, it's very similar to https://stackoverflow.com/q/7041172/5987 and I think you'll find this link helpful: [Color Conversion](https://web.archive.org/web/20100122105857/http://www.equasys.de/colorconversion.html). – Mark Ransom Apr 22 '22 at 03:31

0 Answers0