1

I have a YCCK image on my input and I use libjpeg-turbo to first convert it to CMYK and then I manually convert CMYK to BGRA. I am doing this because I have read somewhere that direct conversion from YCCK to BGRA is not supported by libjpeg-turbo.

This is my current conversion algorithm:

auto const rowStride{ jpegDecompressWrapper.cinfo().output_width * jpegDecompressWrapper.cinfo().output_components };

JSAMPARRAY buffer{ ( *( cinfo ).mem->alloc_sarray )( reinterpret_cast< j_common_ptr >( &( jpegDecompressWrapper.cinfo() ) ), JPOOL_IMAGE, rowStride, 1 ) };

while ( cinfo.output_scanline < cinfo.output_height )
{
    jpeg_read_scanlines( &cinfo, buffer, 1 );
    for ( std::size_t i{ 0U }; i < rowStride / 4; ++i )
    {
        auto const c{ static_cast< std::uint8_t >( buffer[ 0 ][ i * 4U      ] ) };
        auto const m{ static_cast< std::uint8_t >( buffer[ 0 ][ i * 4U + 1U ] ) };
        auto const y{ static_cast< std::uint8_t >( buffer[ 0 ][ i * 4U + 2U ] ) };
        auto const k{ static_cast< std::uint8_t >( buffer[ 0 ][ i * 4U + 3U ] ) };

        // Convert CMYK to RGB
        auto const r{ static_cast< std::uint8_t >( ( ( 255 - c ) * ( 255 - k ) ) / 255 ) };
        auto const g{ static_cast< std::uint8_t >( ( ( 255 - m ) * ( 255 - k ) ) / 255 ) };
        auto const b{ static_cast< std::uint8_t >( ( ( 255 - y ) * ( 255 - k ) ) / 255 ) };

        // Assign RGB values to the same pixel
        pOutput[ i * 4U      ] = b;
        pOutput[ i * 4U + 1U ] = g;
        pOutput[ i * 4U + 2U ] = r;
        pOutput[ i * 4U + 3U ] = 255;
    }

    pOutput += rowStride;
}

It's pretty poor because I get too dark image on the output.

Does anyone know how can I improve my algorithm or if it's possible to directly convert YCCK to BGRA with libjpeg-turbo?

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
NutCracker
  • 11,485
  • 4
  • 44
  • 68
  • 1
    Converting between color-spaces is nontrivial: you also need to consider things like gamma correction, and the different kinds of RGB color-spaces (e.g. sRGB vs Adobe RGB vs Linear RGB). BTW, you should remove the `static_cast` to `uint8_t` because it means you'll lose precision: you should probably be using `double` to represent intermediate values. – Dai Jun 24 '23 at 07:07
  • See also: https://stackoverflow.com/questions/10566668/lossless-rgb-to-ycbcr-transformation – Dai Jun 24 '23 at 07:10
  • yes, i know it's non-trivial. Do you know if libjpeg does support this kind of conversion or not? – NutCracker Jun 24 '23 at 08:20
  • Which version of `libjpeg` are you using? – Dai Jun 24 '23 at 08:21
  • actually, i am using libjpeg-turbo, version 2.1.4 – NutCracker Jun 24 '23 at 08:23
  • 1
    and `static_cast`s will eventually be needed when assigning each component so i don't think that's a mistake in above code – NutCracker Jun 24 '23 at 08:35
  • 1
    Your arithmetic operations like `( ( 255 - c ) * ( 255 - k ) ) / 255` will be conducted over integral types, which truncate values and lose precision berfore the final values are obtained. At least give it a try first to eliminate that. – Dai Jun 24 '23 at 08:37
  • ¿What are the input ranges of cmyk components? The formula used assumes that they are in 0...255 range, but I suspect that may not be the case. 0...100 range is possible. – user7860670 Jun 24 '23 at 09:07
  • @Dai i get the same thing without casts – NutCracker Jun 24 '23 at 09:24
  • @user7860670 i am not sure but i can maybe check – NutCracker Jun 24 '23 at 09:24
  • Going from CMYK to RGB is non-trivial like Dai says -- there's not only no single "RGB", but also way more versions of CMYK (ink and paper differ). You would get much better luck if you just had an actual ICC library do the job. If you don't want to do that, just be informed that your current algorithm is basically the same as jpeg-turbo's cmyk.c, so play around with Levels in GIMP to figure out the correct gamma & ranges. – Mingye Wang Jun 25 '23 at 18:20
  • @MingyeWang so libjpeg-turbo doesn't do anything smarter than what i've written in the question? – NutCracker Jun 26 '23 at 11:37
  • @NutCracker correct, it doesn't! – Mingye Wang Jun 26 '23 at 15:33
  • disappointing :( – NutCracker Jun 26 '23 at 19:59

0 Answers0