libjpeg.net icon indicating copy to clipboard operation
libjpeg.net copied to clipboard

Support for chroma subsampling other than 4:2:0

Open FelixKras opened this issue 5 years ago • 6 comments

I have tried compressing a raw 24 bpp image represented by an byte array, I have tried both the JpegImage API and the low level jpeg_compress_struct structure, I even tried changing the internal jpeg_set_colorspace_SET_COMP() values i either get 4:2:0 chroma image or "Not implemented exception" my input colorspace is RGB my output colorspace is YcBcR

FelixKras avatar Jul 29 '20 14:07 FelixKras

Could you please provide a sample code and image to reproduce the issue?

shibaev avatar Jul 30 '20 04:07 shibaev

Here's the code:

int[] LumTable = new int[]
            {
                16, 11, 10, 16, 24, 40, 51, 61,
                12, 12, 14, 19, 26, 58, 60, 55,
                14, 13, 16, 24, 40, 57, 69, 56,
                14, 17, 22, 29, 51, 87, 80, 62,
                18, 22, 37, 56, 68, 109, 103, 77,
                24, 35, 55, 64, 81, 104, 113, 92,
                49, 64, 78, 87, 103, 121, 120, 101,
                72, 92, 95, 98, 112, 100, 103, 99
            };
            int[] ChromaTable = new int[]
            {
                255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 255, 255, 255
            };


            try
            {
                Bitmap bmp = new Bitmap(@"c:\temp\chromatestInput.jpg");
                int width = bmp.Width;
                int height = bmp.Height;
                int bpp = 3; //bytes per pixel
                byte[] testImage = new byte[height * width * bpp];
                BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite,
                    bmp.PixelFormat);
                Marshal.Copy(bmData.Scan0, testImage, 0, testImage.Length);
                bmp.UnlockBits(bmData);

                int quality = 100;

                using (var fs = new FileStream(@"c:\temp\chromatestOutput.jpg", FileMode.Create))
                {
                    BitMiracle.LibJpeg.Classic.jpeg_error_mgr err = new jpeg_error_mgr();
                    BitMiracle.LibJpeg.Classic.jpeg_compress_struct cmpStr = new jpeg_compress_struct(err);
                    cmpStr.jpeg_stdio_dest(fs);
                    cmpStr.Image_width = width;
                    cmpStr.Image_height = height;
                    cmpStr.Input_components = bpp;

                    cmpStr.jpeg_set_defaults();
                    cmpStr.In_color_space = J_COLOR_SPACE.JCS_RGB;
                    cmpStr.jpeg_set_colorspace(J_COLOR_SPACE.JCS_YCbCr);
                    cmpStr.jpeg_set_quality(quality, false);
                    cmpStr.Optimize_coding = false;
                    cmpStr.jpeg_add_quant_table(0, LumTable, 100, true);
                    cmpStr.jpeg_add_quant_table(1, ChromaTable, 100, true);
                    cmpStr.Dct_method = J_DCT_METHOD.JDCT_IFAST;


                    byte[][] rowlines = new byte[1][];
                    rowlines[0] = new byte[cmpStr.Image_width * bpp];
                    cmpStr.jpeg_start_compress(true);
                    for (int i = 0; i < height && cmpStr.Next_scanline < cmpStr.Image_height; i++)
                    {
                        Buffer.BlockCopy(testImage,i*cmpStr.Image_width*bpp,rowlines[0]
                            ,0,cmpStr.Image_width*bpp);
                        cmpStr.jpeg_write_scanlines(rowlines, 1);
                    }
                    cmpStr.jpeg_finish_compress();
                }
            }
            catch (Exception exception)
            {
                System.Console.WriteLine(exception);
                throw;
            }

and the test file (input) the code generates an output file chromaTestInput

FelixKras avatar Jul 30 '20 08:07 FelixKras

@shibaev Any progress?

FelixKras avatar Aug 24 '20 13:08 FelixKras

Did you attach the chromatestInput.jpg? It looks that the only attachment https://user-images.githubusercontent.com/42567361/88899292-8ba3a700-d256-11ea-9060-0719a61e9ada.jpg corresponds to chromatestOutput.jpg.

Also please clarify what is expected result for you? I.e. what is wrong with the https://user-images.githubusercontent.com/42567361/88899292-8ba3a700-d256-11ea-9060-0719a61e9ada.jpg image.

shibaev avatar Aug 25 '20 08:08 shibaev

The attached image is an input image (it was meant to appear a line above) the code generates an output (not attached, it is generated by the code) the expected result is a 4:2:2 chroma subsampling (or any other than 4:2:0)

FelixKras avatar Aug 26 '20 11:08 FelixKras

Thank you for your explanation. Currently, LibJpeg.Net only supports 2x2 (4:2:0) and 1x1 (4:4:4) subsampling levels. You can produce 1x1 output using these lines:

var comp0 = cmpStr.Component_info[0];
comp0.V_samp_factor = 1;
comp0.H_samp_factor = 1;

Other variations are currently not implemented. Look at jpeg_fdct_*x* methods in /LibJpeg/Classic/Internal/jpeg_forward_dct.cs

However, the original libjpeg supports other subsampling levels. You can look at the latest version from http://www.ijg.org/.

Unfortunately, this feature has a low priority for us. We would appreciate a pull request that:

  1. Migrates logic for some or all missing jpeg_fdct_*x* methods from the original libjpeg to our managed version. I cannot guarantee that migration of jpeg_fdct_*x* is enough, so be ready to migrate some other related code.
  2. Includes tests covering the migrated code

shibaev avatar Sep 01 '20 07:09 shibaev