shimat / opencvsharp

OpenCV wrapper for .NET
Apache License 2.0
5.38k stars 1.15k forks source link

Compressed image with a fixed size #205

Closed reda24 closed 7 years ago

reda24 commented 8 years ago

Hi !

Here is the problem: I would like to know if it's possible to compress an image into a .jp2 file with a fixed size. Currently, i do something like this:

Function Compress:

Bitmap imgIn;
byte[] jp2Img;
// Instructions
Mat mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(imgIn);
Cv2.ImEncode(".jp2", mat, out jp2Img);
return jp2Img;

Main:

result = JP2Functions.Compress(loadedBitmap);
File.WriteAllBytes(@"C:\Pictures\res.jp2", result);

It works fine, but i would like to specify the size of the compressed image to the function ImEncode. I made a lot of research, I tried to use the optional parameters but it's only useful for the .png, .webp or .jpg files.

And i would like to know if it's possible to set a ROI on an image, and then compress it to have 2 parts with one compressed more than the other. I tried, but it didn't work.

shimat commented 8 years ago

As you say, probably there are no encoding flags for JPEG2000...

enum ImwriteFlags {
       IMWRITE_JPEG_QUALITY        = 1,  //!< For JPEG, it can be a quality from 0 to 100 (the higher is the better). Default value is 95.
       IMWRITE_JPEG_PROGRESSIVE    = 2,  //!< Enable JPEG features, 0 or 1, default is False.
       IMWRITE_JPEG_OPTIMIZE       = 3,  //!< Enable JPEG features, 0 or 1, default is False.
       IMWRITE_JPEG_RST_INTERVAL   = 4,  //!< JPEG restart interval, 0 - 65535, default is 0 - no restart.
       IMWRITE_JPEG_LUMA_QUALITY   = 5,  //!< Separate luma quality level, 0 - 100, default is 0 - don't use.
       IMWRITE_JPEG_CHROMA_QUALITY = 6,  //!< Separate chroma quality level, 0 - 100, default is 0 - don't use.
       IMWRITE_PNG_COMPRESSION     = 16, //!< For PNG, it can be the compression level from 0 to 9. A higher value means a smaller size and longer compression time. Default value is 3.
       IMWRITE_PNG_STRATEGY        = 17, //!< One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_DEFAULT.
       IMWRITE_PNG_BILEVEL         = 18, //!< Binary level PNG, 0 or 1, default is 0.
       IMWRITE_PXM_BINARY          = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1.
       IMWRITE_WEBP_QUALITY        = 64  //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used.
     };
shimat commented 8 years ago

You can use partial matrix instead of ROI.

var whole = OpenCvSharp.Extensions.BitmapConverter.ToMat(imgIn);
var part = whole[new Rect(100, 200, 50, 50)];
reda24 commented 8 years ago

Thanks you for answering me ! (and for adding the label too)

Are there other ways (other than using the encoding flags) to affect the compression rate ? (not only for the .jp2 files but in general)

I was thinking I could compress the image into a .png file for example to use the flags, and then convert it into the .jp2 format. Or maybe I could resize the output buffer (for the fixed size I want), but the problem is that the function ImEncode resize the buffer to fit the compressed image.

shimat commented 7 years ago

I can't think of any other ideas when you use only OpenCV. 😩 IMWRITE_WEBP_QUALITY may also help you.

This is a naive sample.

byte[] SizeLimitedCompress(Mat m, int byteLengthLimit)
{
    for (int level = 0; level <= 9; level ++)
    {
        byte[] encodedBytes = m.ToBytes(".png", 
            new mageEncodingParam(ImwriteFlags.PngCompression, level));
        if (encodedBytes.Length <= byteLengthLimit)
            return encodedBytes;
    }
    throw new Exception();
}