Intel® Integrated Performance Primitives
Deliberate problems developing high-performance vision, signal, security, and storage applications.

how to encode the JPEG line by line(row by row)

Sathish_S_
Beginner
1,492 Views

Hello All,

Actually, I have implmented the JPEG encoding using Intel IPP libs . Its working for the small images and its not working for the large images. I understood that the problem is with , I am providing the whole Pixel data at a time. But , the large images having the size 15000X15000, 85000X85000 in these cases it will not allocate memory and so its failing . 

Now, I am thinking that I need provide the pixel data row by row. But, How to provide the data row by row and how to write the data to output file row by row.

could anyone help me in this regard.

Regards,

sathish

0 Kudos
20 Replies
Vladimir_D_Intel1
1,470 Views

Hello Sathish,

it should be possible if you change encoder to process image by one MCU row at time. Note, size of MCU depend on sampling factors (it is jpeg encoder parameter).

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,469 Views

Hello Vladimir,

Thank you for the information. I wrote like this for the single band images U8 bit data

int nDU = (((8 & 255) + 7) / 8);
int LineStep = width * 1 * nDU;

   //Construct and init the jpeg encoder
    UIC::JPEGEncoder uicEncoder;
    ExcStatus resStatus = ExcStatusOk;
    uicEncoder.Init();

    //Attach the output stream with output buffer to the encoder
    UIC::BaseStream::TStatus tStatus;
    CStdFileOutput outputFile;

JCOLOR dstClr = m_bGrayScale ? JC_GRAY : JC_YCBCR;

for (long y = 0; y < height; ++y)
    {
        ERASTER_NS::Boundary boundary(0, y, width, y + 1);
        dataSource->Read(0, boundary, pb);

        Epxm_PixelRectStack* prs = pb->asPixelRectStack();

        tStatus = outputFile.Open(m_pchOfName);
        if (!BaseStream::IsOk(tStatus))
            return false;

        resStatus = uicEncoder.AttachStream(outputFile);
        if (resStatus != ExcStatusOk)
            return false;

        UIC::Image srcImg;

        UIC::ImageSamplingGeometry samplingGeometry;
        samplingGeometry.SetRefGridRect(UIC::Rect(UIC::Point(0, 0), RectSize(width, height)));
        samplingGeometry.ReAlloc(1);
        samplingGeometry.SetEnumSampling(UIC::S444);

UIC::ImageDataOrder imageDataOrder;
        imageDataOrder.ReAlloc(UIC::Interleaved, 1);
        imageDataOrder.PixelStep()[0] = 1;
        imageDataOrder.LineStep()[0] = LineStep;
        imageDataOrder.SetDataType(T8u);

        UIC::ImageDataPtr dataPtr;
        dataPtr.p8u = (UINT8*)(prs->datalayer[0]->data);//Pixel data
        srcImg.Buffer().Attach(&dataPtr, imageDataOrder, samplingGeometry);

        srcImg.ColorSpec().ReAlloc(1);
        UIC::ImageEnumColorSpace srcClrSpace = m_bGrayScale ? UIC::Grayscale : UIC::RGB;
        srcImg.ColorSpec().SetColorSpecMethod(UIC::Enumerated);
        srcImg.ColorSpec().SetComponentToColorMap(UIC::Direct);
        srcImg.ColorSpec().SetEnumColorSpace(srcClrSpace);

        for (int i = 0; i < 1; i++)
        {
            srcImg.ColorSpec().DataRange().SetAsRange8u(255);
        }

resStatus = uicEncoder.AttachImage(srcImg);
        if (resStatus != ExcStatusOk)
            return false;


        JSS sampling = JS_444;
        //Setting the parameters
        resStatus = uicEncoder.SetParams(JPEG_BASELINE, dstClr, sampling, 0, 0, 75);
        if (resStatus != ExcStatusOk)
            return false;

            //Encode the image to the output File
            resStatus = uicEncoder.WriteHeader();
            if (resStatus != ExcStatusOk)
                return false;

            resStatus = uicEncoder.WriteData();
            if (resStatus != ExcStatusOk)
                return false;

}

Jpegenc.cpp:

  m_mcuWidth  = (m_jpeg_sampling == JS_444) ?  8 : 16;
  m_mcuHeight = (m_jpeg_sampling == JS_411) ? 16 :  8;

  m_numxMCU = (m_src.width  + (m_mcuWidth  - 1)) / m_mcuWidth;
  m_numyMCU = (m_src.height + (m_mcuHeight - 1)) / m_mcuHeight;

 

JERRCODE CJPEGEncoder::EncodeScanBaseline(void)

{

 while(curr_row < m_numyMCU)
    {
#ifdef _OPENMP
#pragma omp critical
    {
#endif
      curr_row = i;
      i++;

 if(curr_row < m_numyMCU)
      {
#ifdef __TIMING__
        c0 = ippGetCpuClocks();
#endif
        jerr = ColorConvert(curr_row,thread_id);
#ifdef __TIMING__
        c1 = ippGetCpuClocks();
        m_clk_cc += c1 - c0;
#endif

#ifdef __TIMING__
        c0 = ippGetCpuClocks();
#endif
        DownSampling(curr_row,thread_id);
#ifdef __TIMING__

//some code here

}

curr_row++;

}

}

Does it correct?. if not could you please made the necessary changes.

I have one more thing i,.e how to write the data into output file row by row. 

could you please help me.

Regards,

sathish.

0 Kudos
Vladimir_D_Intel1
1,469 Views

Hi Sathish,

basically, jpeg encoding consist from several operations:

1. color conversion (optional)

2. subsampling (optional)

3. level shift (mandatory)

4. forward DCT transform (mandatory)

5. quantization (optional)

6. huffman entropy encoding (mandatory)

For you, to implement row-by-row encoding approach for 8u single channel (band) image, it is needed to modify ipp jpeg encoder in such way that:

1) convert and level-shift 8 image scan lines from Ipp8u to Ipp16s data type. Store converted values to intermediate buffer of (8 * image_width * sizeof(Ipp16s)) size. Note, you may need to extend buffer in order to keep integer number of 8x8 blocks (this will be needed on the next stages)

2) perform forward DCT transform on 8x8 blocks in this intermediate buffer. it is possible to do in place operations

3) perform quantization (if needed) on 8x8 blocks of DCT coefficients

4) perform huffman encoding on 8x8 blocks of quantized DCT coefficients. Store the resulting bits of compressed output from ipp huffman encoding function to another intermediate buffer. Note, it is not possible to compute exact size of this buffer, so you 'll need to use some rough estimation. Usually, 1.5 * input_size will work.

5) flush compressed bits for row of compressed 8x8 blocks to file

6) repeat these steps till all image scan lines processed

Take attention to form compliant jpeg file:

each jpeg file must start from SOI marker and end up with EOI marker. In between these markers other jpeg markers may appears as needed. Usual structure of jpeg file for DCT based hffman entropy coded compression process will look like:

SOI
SOF0
DQT
DHT
SOS
<compressed bits>
EOI

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,469 Views

Thank you so much Vladimir.

Does this algorithm(steps mentioned by you) works for the three band images also?. if it is not, could you please provide me the necessary steps those should work for both single band and three band images. Actually, I need to implement the row by row mechanism for both single band image and three band images. 

One more thing , is the size of the output file will be same if we encode using row by row and using all a time?. 

could you please confirm.

Regards,

sathish.

0 Kudos
Vladimir_D_Intel1
1,469 Views

Sathish,

of course, jpeg file size will not be affected by the way you process the data. You may need to learn some details of jpeg specification in order to correctly implement three-band subsampled images (also you may look at ipp jpeg codec implementation to see an example implementation). The basic thing is that JPEG codec process image by MCU (which is minimally coded unit, according to jpeg spec terms). The MCU consist from a number of DU (data units), which are 8x8 blocks. The size of MCU depend on subsamping factors, for example, for sampling
444 (which means no subsampling used) MCU consist from 1 DU (which is one 8x8 block) per color band
422 (which means subsampling color bands by factor of 2 in horizontal direction) MCU consist from 2 DU for Y plane, 1 DU for Cb and 1 DU for Cr planes, what gives 4 8x8 blocks in total
411 (which means subsampling color bands by factor of 2 in both horizontal and vertical directions) MCU consist from 4 DU for Y plane, 1 DU for Cb and 1 DU for Cr planes, what gives 6 8x8 blocks in total. 

There is specific scan order over MCU 8x8 blocks, which may slightly differ depending on number of color bands and subsampling factors (you may also use sample ipp jpeg codec implementation to see how it may be implemented in jpeg specification compliant way).

So, for three-band images the differences in steps described above is that:

1) color-convertion will provide you three bands of converted pixels. It is better to keep them in separate planes, i.e. take image buffer in format BGR, BGR, BGR, ... and get intermediate buffer of format YYYY, CbCbCbCb, CrCrCr .. For 444 and 422 sampling factors you need 8 image scan lines and for 411 sampling factor you need 16 image scan lines in order to form MCU

2) subsampling step only make sense for images with more than one color band. Usually, Y plane stays not subsampled (because, according to human visual perception model, most of image information contains in luminance component) and Cb/Cr planes may be subsampled to improve compression rate without significant loss in image visual quality. Usually Cb/Cr planes subsampled in factor of 2 in horizontal direction (which called as 422 sampling) or both vertical and horizontal directions (which in ipp jpeg historically called 411 sampling, but more correct name is 420 - which used in video codecs). So, after subsampling (for example 411) you should form three buffers Y-plane with the same size as it was after color convertion step and subCb/subCr buffers with size twice less in both vertical and horizontal directions. For example, for 16x16 source image this will give you 16x16 Y plane, 8x8 Cb and 8x8 Cr planes

3) level-shift is simple subtraction of 128 (for 8u input data) to form signed representation from unsigned input data, it may be combined with convertion from 8u to 16s data type. Level shift done independently on all 8x8 block in subsampled buffer. So, after this step you will have Ipp16s buffer of 8x8 block in three planes.

Now, all you need is process MCU from forward DCT, quantization and huffman coding). As I mention before, jpeg spec defines specific scan order over DU in MCU, depending on sampling factors, I'd better refer you to jpeg spec or ipp sample for details. Once defined processing compliant to jpeg spec it does not depend on how do you process image, at a whole or in MCU by MCU, or in MCU row by MCU row fashion. Hope, this helps.

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,469 Views

Vladimir,

I set the  m_mcuWidth = 1;

  m_mcuHeight = 1;

One thing , how to set the SetRefGridRect , earlier I gave like this samplingGeometry.SetRefGridRect(UIC::Rect(UIC::Point(0, 0), RectSize(width,height)));

 

I am thinking like this 

for (long y = 0; y < height; ++y)
    {

UIC::ImageSamplingGeometry samplingGeometry;
        samplingGeometry.SetRefGridRect(UIC::Rect(UIC::Point(0, y), RectSize(width, y + 1)));
        samplingGeometry.ReAlloc(1);
        samplingGeometry.SetEnumSampling(UIC::S444);

}

does it correct?. Please confirm

I am using the JSS sampling = JS_444;

Regards,

sathish.

0 Kudos
Vladimir_D_Intel1
1,469 Views

Sorry, I'm not able to dive into details on code level with enough confidence at this time (did not work with UIC for several years).

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,470 Views

Hi Vladimir,

One last thing I have i,.e how to store the output data into JPEG file . Earlier, at a whole we are attaching like this

CStdFileOutput outputFile;
    tStatus = outputFile.Open(m_pchOfName); 

resStatus = uicEncoder.AttachStream(outputFile);

But, for row by row how should we write the data to to output file.

Regards,

sathish

0 Kudos
Vladimir_D_Intel1
1,470 Views

Hi Sathish,

obviously, you should write pieces of data in file append mode. This probably also not implemented in UIC classes.

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,470 Views

Thanks Vladimir.

you mentioned some steps to perform JPEG encoding row by row . In those steps the last step was 

" repeat these steps till all image scan lines processed"

If the input images is 128 X 128 means , we need to repeat those steps to height of that images i,.e here 128 times. 

 is it correct?. Could you please confirm.

Regards,

sathish.

0 Kudos
Vladimir_D_Intel1
1,470 Views

Hi Sathish,

not exactly. you need to divide input image to rows of MCUs. Each MCU row will require 8 or 16 input image scanlines, for the case of 128x128 input image and 444 subsampling factor, you need to process 128 / 8 = 16 rows of MCU (an so perform described steps 16 times).

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,470 Views

Hi Vladimir,

Actually, I am getting the pixel data row by row . If  we need to loop through 16 times means , we need to combine the data for every 8 rows and put it to ImageDataPtr. If this is the case , is there any predefined methods to combine it?.

UIC::ImageDataPtr dataPtr;
    dataPtr.p8u = pPixelData;//Pixel data

could you please confirm.

Regards,

sathish.

0 Kudos
Vladimir_D_Intel1
1,470 Views

Sathish,

I'm not aware of such methods in UIC, most probably you have to modify UIC source in order to achieve desired behaviour.

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,470 Views

Hello Vladimir,

Finally, I am able to encode the JPEG row by row i,.e 8 image scan lines per one MCU row. 

Actually, in the ColorConvert() method we have code like this(From IPP samples)

pSrc8u  =                   m_src.p.Data8u[0]  + nMCURow * m_mcuHeight * srcStep;

Here m_src.p.Data8u[0] is the whole pixel data, so here we need to place our data i,.e 8 rows pixel data. 

Thank you so much for your suggestions. 

Regards,

sathish.

0 Kudos
Vladimir_D_Intel1
1,470 Views

Hi Sathish,

thanks, I'm glad that you find solution.

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,470 Views

Hello Vladimir,

Everything is good now I am able to encode the 50000x50000 images and the output JPEG also 50kx50k but when I am trying for 70000X70000 the output JPEG is created with 4464x4464 . I am not sure why it is behaving like this. Is there any limitation that the output JPEG file size should not be >65X65?. 

Could you please comment.

Regards,

sathish.

0 Kudos
Vladimir_D_Intel1
1,470 Views

Hi Sathish,

that's right, according to the JPEG specification variables representing image size in SOF (start of frame) marker are limitied by 16 bits.

sof.jpg

You may want to consider some tiled meta format or other compression technique for huge images, for example jpeg2000 or jpeg-xr do not limit image size by 16 bits.

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,470 Views

Hello Vladimir,

Thank you for the information.

I have one last question , Actually To made it work for row by row mechanism I need to change in the jpegenc.cpp file which is in sample UIC code. 

May I use the sample UIC code in my project and may I edit the UIC sample code?.

I purchased the Intel IPP 8.2 update 1 license recently( three weeks ago).

could you please confirm.

Regards,

sathish

0 Kudos
Vladimir_D_Intel1
1,470 Views

Hi Sathish,

you should check ipp sample license document (should be part of sample package you downloaded) for details on legal usage of sample code.

Regards,
  Vladimir

0 Kudos
Sathish_S_
Beginner
1,324 Views

Hi Vladimir,

In the license agreement, mentioned the following 

"A.    Subject to all of the terms and conditions of this Agreement, Intel Corporation ("Intel") grants to you a non-exclusive, non-assignable, copyright license to use the Materials. 

B.    Subject to all of the terms and conditions of this Agreement, Intel grants to you a non-exclusive, non-assignable copyright license to modify the Materials, or any portions thereof, that are (i) provided in Source Code form or, (ii) are defined as Redistributables and are provided in text form."

I have a license and that is not a evaluation. So, I can use and modify those materials.

Thank you.

Regards,

sathish.

 

0 Kudos
Reply