Skip to content

yuv2gray() / luv2gray() (and other yuv2* / luv2* conversions) seem to ignore the intermediate BGR conversion. Is this intended? #81

Closed

Description

@omadson

I checked the documentation and searched the repository for the relevant color-conversion behavior. I could not find any documentation or existing implementation pattern indicating that these conversions are intentionally performed directly on the original YUV/LUV values.

Please confirm you have the latest versions of caer, numpy, opencv-contrib-python, and h5py prior to reporting a bug (delete one): YES (caer from dev, latest numpy and opencv-contrib-python from PyPI.)

Describe the bug

Hi! While reading caer/color/_yuv.py I noticed something that looks like a bug, but I'd like to confirm it's not intended before opening a PR.

The derived conversions compute a BGR intermediate and then discard it, converting the original YUV array as if it were BGR:

# caer/color/_yuv.py, yuv2gray()
img = yuv2bgr(tens)
img = bgr2gray(tens)   # <- uses `tens` (YUV data), not `img` (BGR)
return to_tensor(img, cspace="gray")

The same pattern appears in yuv2hls, yuv2hsv, yuv2lab, yuv2luv and, in caer/color/_luv.py, in luv2gray, luv2hls, luv2hsv, luv2lab, luv2yuv. Since caer.to_gray(), to_hsv(), to_hls(), to_lab(), to_yuv() and to_luv() call these functions, they are affected too for YUV/LUV inputs.

For comparison, caer/color/_hsv.py does pass the intermediate along (bgr = hsv2bgr(tens), then img = bgr2gray(bgr)), and its results match OpenCV.

To Reproduce

This uses the bundled sunrise image, so no external files are needed:

import cv2 as cv
import numpy as np
import caer

bgr = caer.data.sunrise(rgb=False)

# YUV -> Grayscale
yuv = caer.to_tensor(cv.cvtColor(bgr, cv.COLOR_BGR2YUV), cspace="yuv")
expected = cv.cvtColor(cv.cvtColor(yuv, cv.COLOR_YUV2BGR), cv.COLOR_BGR2GRAY)
print("yuv2gray matches OpenCV:", np.array_equal(caer.yuv2gray(yuv), expected))
print("to_gray(yuv) matches OpenCV:", np.array_equal(caer.to_gray(yuv), expected))
print("yuv2gray equals bgr2gray on the raw YUV data:", np.array_equal(caer.yuv2gray(yuv), caer.bgr2gray(yuv)))

# HSV -> Grayscale, for comparison
hsv = caer.to_tensor(cv.cvtColor(bgr, cv.COLOR_BGR2HSV), cspace="hsv")
expected = cv.cvtColor(cv.cvtColor(hsv, cv.COLOR_HSV2BGR), cv.COLOR_BGR2GRAY)
print("hsv2gray matches OpenCV:", np.array_equal(caer.hsv2gray(hsv), expected))

Output:

yuv2gray matches OpenCV: False
to_gray(yuv) matches OpenCV: False
yuv2gray equals bgr2gray on the raw YUV data: True
hsv2gray matches OpenCV: True

Expected behavior

yuv2gray(yuv) should equal converting YUV -> BGR -> Gray (as hsv2gray does for HSV). Instead it equals bgr2gray applied directly to the YUV values. The same applies to the other nine functions listed above.

Error with full stack trace

No exception is raised; the functions return silently wrong pixel values.

Additional context

  • caer: dev branch at c313af56 (version 2.1.1)
  • Python: 3.12.12
  • numpy: 2.5.3
  • opencv-contrib-python: 5.0.0
  • OS: Linux (Fedora 43, x86_64)

The existing tests in tests/color/test_yuv.py and tests/color/test_luv.py only check the shape and the cspace flag, which is probably why this wasn't caught.

If this is indeed unintended, I'd be happy to open a PR against dev with the fix (passing img instead of tens in those 10 places) and tests that compare the results against OpenCV.

Activity

  1. jasmcaus commented on Sep 22, 2026

    @jasmcaus
    Owner

    It's been a while since I've looked at this, but I suspect you might be correct. I don't believe at the time opencv had a way to convert from yuv -> gray directly w/o an intermediate representation back when I was working on this project. I could be wrong though. Either way, if you can get a working PR in, I'd be happy to merge this in. Good catch!

  2. omadson commented on Sep 22, 2026

    @omadson
    ContributorAuthor

    Hi @jasmcaus, thanks for the reply.

    I tested it and just opened a PR (#82).

    Feel free to continue the conversation there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions