[BUG]: ColorConvert YUV -> YUV bit-depth conversion to 16u puts neutral chroma at 32800 instead of 32768
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 78/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- cpp
- Área
- backend, computer-vision
Línea de trabajo
Lee color_conversion_operations.hpp alrededor de las líneas 186–189 y compara la conversión de profundidad dentro del mismo espacio de color con el tratamiento del punto medio en las líneas 117 y 142. Reproduce la conversión YUV neutra del issue y luego revisa 01_color_conversion/image_conversion_example.cu para encontrar la comprobación de regresión propuesta. Se considera terminado cuando U y V neutros se convierten a 32768 para entradas 8u y 10u y la prueba pasa.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Describe the bug.
ColorConvert<CS, CS, InDepth, OutDepth> with a YUV color space and only a bit-depth change scales U and V by the depth factor about zero, the same way it scales Y. Chroma is centered on the midpoint, so for 8u -> 16u (factor 256.246) and 10u -> 16u (factor 64.06) neutral chroma 128 / 512 becomes 32800. The 16-bit neutral value is 32768. That is the value used by midpoint_gray<bpp_16u> and by range_values<bit_depth::bpp_16u>.chroma_mid(). It is also what the YUV -> RGB path subtracts.
As a result, gray stops being gray when it goes through 16-bit YUV:
- 8u YUV -> 16u RGB in one step: R = G = B = 32800
- 8u YUV -> 16u YUV -> 16u RGB: R=32844 G=32766 B=32855
I expected U = V = 32768 in the 16-bit YUV output, and the same RGB from both paths.
The YUV <-> RGB paths already handle the midpoint (color_conversion_operations.hpp:117 subtracts midpoint_gray<InD>, :142 adds midpoint_gray<OutD>). Only the same-color-space branch (:186-189, b *= depth_scale; c *= depth_scale;) does not. 8u <-> 10u is unaffected because ×4 maps 128 to 512 exactly.
Minimum reproducible example
// nvcc -std=c++17 -arch=sm_103a --expt-relaxed-constexpr -DARCH=1030 -I<NPPDx>/include repro.cu
#include <nppdx.hpp>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace nppdx;
constexpr int Arch = ARCH, W = 16, H = 16;
template<typename In, typename Op1, typename Op2, typename Out>
__global__ void kernel(const uint8_t* in, uint8_t* out, int w, int h) {
float regs[In::elements_per_thread];
In().execute(in, regs, w, h);
Op1().execute(regs, w, h);
Op2().execute(regs, w, h);
Out().execute(regs, out, w, h);
}
template<packing_format F>
using Ingest = decltype(InputOutput<input_output_direction::ingest>() + InputFormat<F>() + TileSize<16, 16>() + SM<Arch>() + Block());
template<packing_format F>
using Exgest = decltype(InputOutput<input_output_direction::exgest>() + OutputFormat<F>() + TileSize<16, 16>() + SM<Arch>() + Block());
template<color_space I, color_space O, bit_depth DI, bit_depth DO>
using CC = decltype(Function<function::color_convert>() + ColorConvert<I, O, DI, DO>() + TileSize<16, 16>() + SM<Arch>() + Block());
using CS = color_space; using BD = bit_depth;
template<typename In, typename A, typename B, typename Out>
std::vector<uint8_t> run(const std::vector<uint8_t>& h_in, size_t out_bytes) {
uint8_t *d_in, *d_out;
cudaMalloc(&d_in, h_in.size()); cudaMalloc(&d_out, out_bytes);
cudaMemcpy(d_in, h_in.data(), h_in.size(), cudaMemcpyHostToDevice);
kernel<In, A, B, Out><<<In::calculate_grid_dim(W, H), In::block_dim>>>(d_in, d_out, W, H);
std::vector<uint8_t> h_out(out_bytes);
cudaMemcpy(h_out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost);
cudaFree(d_in); cudaFree(d_out);
return h_out;
}
uint16_t u16(const std::vector<uint8_t>& b, size_t i) { uint16_t v; std::memcpy(&v, b.data() + 2 * i, 2); return v; }
int main() {
std::vector<uint8_t> gray(3 * W * H, 128); // YUV444P 8-bit, Y = U = V = 128
using Depth = CC<CS::yuv_bt601, CS::yuv_bt601, BD::bpp_8u, BD::bpp_16u>;
using Nop16 = CC<CS::yuv_bt601, CS::yuv_bt601, BD::bpp_16u, BD::bpp_16u>;
using Nop8 = CC<CS::yuv_bt601, CS::yuv_bt601, BD::bpp_8u, BD::bpp_8u>;
auto p216 = run<Ingest<packing_format::yuv444p>, Depth, Nop16, Exgest<packing_format::p216>>(gray, W * H * 4);
printf("YUV 8u -> YUV 16u (P216): Y=%u U=%u V=%u\n", u16(p216, 0), u16(p216, W * H), u16(p216, W * H + 1));
auto one = run<Ingest<packing_format::yuv444p>, CC<CS::yuv_bt601, CS::rgb, BD::bpp_8u, BD::bpp_16u>, Nop8,
Exgest<packing_format::rgb16>>(gray, W * H * 6);
auto two = run<Ingest<packing_format::yuv444p>, Depth, CC<CS::yuv_bt601, CS::rgb, BD::bpp_16u, BD::bpp_16u>,
Exgest<packing_format::rgb16>>(gray, W * H * 6);
printf("YUV 8u -> RGB 16u : R=%u G=%u B=%u\n", u16(one, 0), u16(one, 1), u16(one, 2));
printf("YUV 8u -> YUV 16u -> RGB 16u: R=%u G=%u B=%u\n", u16(two, 0), u16(two, 1), u16(two, 2));
}
Environment details
NPPDx main @ e7efdc1 (v0.1.2)
nvcc 13.3 (V13.3.73), gcc 13.3, -arch=sm_103a, x86_64 Linux: the repro compiles.
The output below comes from running the same device code under a CPU emulation of the kernel
(no GPU was free when this was checked), not from a GPU run.
Relevant log output
YUV 8u -> YUV 16u (P216): Y=32800 U=32800 V=32800
YUV 8u -> RGB 16u : R=32800 G=32800 B=32800
YUV 8u -> YUV 16u -> RGB 16u: R=32844 G=32766 B=32855
Other/Misc.
The 10u -> 16u case behaves the same way (512 -> 32800).
A possible fix: in the same-color-space branch of convert<>, rescale U and V about the midpoint for YUV, and leave Y and RGB as they are.
b = (b - midpoint_gray<InputBitDepth>::value) * depth_scale + midpoint_gray<OutputBitDepth>::value;
With that change the outputs are U = V = 32768 and R = G = B = 32800 for both paths. RGB depth conversion is unchanged.
The change keeps the existing depth factor (256.246 / 64.06) and only re-centres chroma on the midpoint, so legal 8-bit chroma 16 / 240 maps to about 4068 / 61468 (today about 4100 / 61499). That matches what the 8u YUV -> 16u RGB path does today. The regression check only looks at the midpoint, so it would also pass if you prefer a ×2^(n-8) depth mapping for YUV instead.
A fix with a regression test is ready. The test is a check in 01_color_conversion/image_conversion_example.cu: it converts neutral YUV444P/YUV444P10 to P216 and expects 32768. I can open a pull request if that is welcome.
Check for duplicates
- I have searched the open bugs/issues and have found no duplicates for this bug report
This report was drafted with Claude Code (claude-opus-5-5); the output above is from the CPU-emulation run described.
- Lenguaje dominante
- C++
- Estrellas
- 18
- Forks
- 6
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
bazel-contrib/rules_go#4784 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
documentation kind: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
TUIs: Fix TUI CMakesAbiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
RMIT-Rover-Team/equinox-2#145 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
eunomia-bpf/bpftime#779 ·
Los mantenedores suelen responder en 1 día