将jpeg图像的大小调整为指定大小

jbo*_*ins 5 c# jpeg image file image-size

这是将图像缩小到指定的较小尺寸的功能代码.但它有几件不好的事情:

  • 它很慢
  • 它可以在获得缩放图像之前进行多次迭代
  • 每次必须确定它必须将整个图像加载到memoryStream中的大小

我想改进它.有没有办法获得更好的初始估计来排除这么多次迭代?我错了吗?我创建它的原因是接受任何未知大小的图像并将其缩放到一定的大小.这样可以更好地规划存储需求.当您缩放到某个高度/宽度时,图像大小可能会因我们的需要而变化太大.

您将需要一个参考System.Drawing的参考.

    //Scale down the image till it fits the given file size.
    public static Image ScaleDownToKb(Image img, long targetKilobytes, long quality)
    {
        //DateTime start = DateTime.Now;
        //DateTime end;

        float h, w;
        float halfFactor = 100; // halves itself each iteration
        float testPerc = 100;
        var direction = -1;
        long lastSize = 0;
        var iteration = 0;
        var origH = img.Height;
        var origW = img.Width;

        // if already below target, just return the image
        var size = GetImageFileSizeBytes(img, 250000, quality);
        if (size < targetKilobytes * 1024)
        {
            //end = DateTime.Now;
            //Console.WriteLine("================ DONE.  ITERATIONS: " + iteration + " " + end.Subtract(start));
            return img;
        }

        while (true)
        {
            iteration++;

            halfFactor /= 2;
            testPerc += halfFactor * direction;

            h = origH * testPerc / 100;
            w = origW * testPerc / 100;

            var test = ScaleImage(img, (int)w, (int)h);
            size = GetImageFileSizeBytes(test, 50000, quality);

            var byteTarg = targetKilobytes * 1024;
            //Console.WriteLine(iteration + ": " + halfFactor + "% (" + testPerc + ") " + size + " " + byteTarg);

            if ((Math.Abs(byteTarg - size) / (double)byteTarg) < .1  ||  size == lastSize  ||  iteration > 15 /* safety measure */)
            {
                //end = DateTime.Now;
                //Console.WriteLine("================ DONE.  ITERATIONS: " + iteration + " " + end.Subtract(start));
                return test;
            }

            if (size > targetKilobytes * 1024)
            {
                direction = -1;
            }
            else
            {
                direction = 1;
            }

            lastSize = size;
        }
    }

    public static long GetImageFileSizeBytes(Image image, int estimatedSize, long quality)
    {
        long jpegByteSize;
        using (var ms = new MemoryStream(estimatedSize))
        {
            SaveJpeg(image, ms, quality);
            jpegByteSize = ms.Length;
        }
        return jpegByteSize;
    }

    public static void SaveJpeg(Image image, MemoryStream ms, long quality)
    {
        ((Bitmap)image).Save(ms, FindEncoder(ImageFormat.Jpeg), GetEncoderParams(quality));
    }

    public static void SaveJpeg(Image image, string filename, long quality)
    {
        ((Bitmap)image).Save(filename, FindEncoder(ImageFormat.Jpeg), GetEncoderParams(quality));
    }

    public static ImageCodecInfo FindEncoder(ImageFormat format)
    {

        if (format == null)
            throw new ArgumentNullException("format");

        foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
        {
            if (codec.FormatID.Equals(format.Guid))
            {
                return codec;
            }
        }

        return null;
    }

    public static EncoderParameters GetEncoderParams(long quality)
    {
        System.Drawing.Imaging.Encoder encoder = System.Drawing.Imaging.Encoder.Quality;
        //Encoder encoder = new Encoder(ImageFormat.Jpeg.Guid);
        EncoderParameters eparams = new EncoderParameters(1);
        EncoderParameter eparam = new EncoderParameter(encoder, quality);
        eparams.Param[0] = eparam;
        return eparams;
    }

    //Scale an image to a given width and height.
    public static Image ScaleImage(Image img, int outW, int outH)
    {
        Bitmap outImg = new Bitmap(outW, outH, img.PixelFormat);
        outImg.SetResolution(img.HorizontalResolution, img.VerticalResolution);
        Graphics graphics = Graphics.FromImage(outImg);
        graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        graphics.DrawImage(img, new Rectangle(0, 0, outW, outH), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
        graphics.Dispose();

        return outImg;
    }
Run Code Online (Sandbox Code Playgroud)

调用此方法将创建第二个图像,其大小与请求的值相近:

        var image = Image.FromFile(@"C:\Temp\test.jpg");
        var scaled = ScaleDownToKb(image, 250, 80);
        SaveJpeg(scaled, @"C:\Temp\test_REDUCED.jpg", 80);
Run Code Online (Sandbox Code Playgroud)

对于这个具体的例子:

  • 原始文件大小:628 kB
  • 请求的文件大小:250 kB
  • 缩放文件大小:238 kB

xpd*_*pda 0

不要对每个图像进行一组缓慢的迭代,而是使用一些具有代表性的图像进行测试,并获得一个分辨率,该分辨率将为您提供所需的平均文件大小。然后一直使用该分辨率。