4

I've got 100's (maybe 1000's) of products with 10-30 images of each product coming to an online store I've put together. I need to optimize the images' file sizes as much as possible without loosing image quality.

I haven't used jpegtran, jpegoptim, or any other jpeg optimizer directly but I have noticed that punypng shrinks file sizes down about 4-6% on the larger jpeg images LOSSLESSLY.

Meta data is already stripped from the images during upload (via jumpoader) so that is not an option/problem anymore.

Is there any way to get one of the jpeg optimizers to run from C# code?

Note: I'm using shared Godaddy hosting with IIS7 and .Net 3.5

Luke Francl
  • 31,028
  • 18
  • 69
  • 91
David Murdoch
  • 87,823
  • 39
  • 148
  • 191

5 Answers5

3

It might be 7 years too late, but I came across this question while trying to solve this problem. I eventually managed to do it and this is the solution. For PNG you first need to install nQuant using NuGet.

include:

using System.Web.Hosting;
using System.IO;
using System.Diagnostics;
using nQuant;
using System.Drawing;
using System.Drawing.Imaging;

Methods:

    public void optimizeImages()
    {
        string folder = 
            Path.Combine(HostingEnvironment.ApplicationPhysicalPath, @"assets\temp");
        var files = Directory.EnumerateFiles(folder);

        foreach (var file in files)
        {
            switch (Path.GetExtension(file).ToLower())
            {
                case ".jpg":
                case ".jpeg":
                    optimizeJPEG(file);
                    break;
                case ".png":
                    optimizePNG(file);
                    break;
            }

        }

    }

    private void optimizeJPEG(string file)
    {
        string pathToExe = HostingEnvironment.MapPath("~\\adminassets\\exe\\") + "jpegtran.exe";

        var proc = new Process
        {
            StartInfo =
            {
                Arguments = "-optimize \"" + file + "\" \"" + file + "\"",
                FileName = pathToExe,
                UseShellExecute = false,
                CreateNoWindow = false,
                WindowStyle = ProcessWindowStyle.Hidden,
                RedirectStandardError = true,
                RedirectStandardOutput = true
            }
        };

        Process jpegTranProcess = proc;

        jpegTranProcess.Start();
        jpegTranProcess.WaitForExit();
    }

    private void optimizePNG(string file)
    {
        string tempFile = Path.GetDirectoryName(file) + @"\temp-" + Path.GetFileName(file);
        int alphaTransparency = 10;
        int alphaFader = 70;
        var quantizer = new WuQuantizer();
        using (var bitmap = new Bitmap(file))
        {
            using (var quantized = quantizer.QuantizeImage(bitmap, alphaTransparency, alphaFader))
            {
                quantized.Save(tempFile, ImageFormat.Png);

            }

        }
        System.IO.File.Delete(file);
        System.IO.File.Move(tempFile, file);
    }

It will take all files from /assets/temp folder and optimize jpegs and PNG. I followed this question for the png part. The jpeg part I scraped from several sources. Including PicJam and Image Optimizer. The way I use it is by uploading all files from the user to the temp folder, running this method, uploading the files to azure blob storage, and deleting the local files. I downloaded jpegtran here.

Community
  • 1
  • 1
Shiran Dror
  • 1,472
  • 1
  • 23
  • 36
2

If you don't like to mess with temporary files, I'd advise to use C++/CLI.

Create a C++/CLI dll project in visual studio. Create one static managed class, and define the functions as you want to use them from C#:

public ref class JpegTools
{
public:
     static array<byte>^ Optimize(array<byte>^ input)
};

These functions you define can be directly called from C#, and you can implement them with all that C++ offers.

array^ corresponds to a C# byte array. You'll need to use pin_ptr<> to pin the byte array in memory, so you can pass on the data to the unmanaged Jpeg helper function of your choice. C++/CLI has ample support for marshalling managed types to native types. You can also allocate new array with gc_new, to return CLI compatible types. If you have to marshall strings from C# to C++ as part of this excercise, use Mfc/Atl's CString type.

You can statically link all the jpeg code into the dll. A C++ dll can be mixed pure native and C++/CLI code. In our C++/CLI projects, typically only the interface source files know about CLI types, all the rest work with with C++ types.

There's some overhead work to get going this way, but the upside is that your code is compile-time typechecked, and all the dealings with unmanged code and memory are dealt with on the C++ side. It actually works so well that I used C++/CLI to unit test native C++ code almost directly with NUnit.

Good luck!

  • 1
    You lost me. I can't even figure out how to compile jpegoptim or or jpegtran by themselves. Whats the reason software like this isn't provide as a downloadable dll? – David Murdoch Oct 14 '09 at 15:08
  • Could be that the toolkits try to be platform independent. Dll's are windows-only. I agree having a dll would be convenient, but if there was one, you would probably not need our help, right? If you have no C/C++ experience, its maybe wiser to run the .exe from a progrem as suggested below. –  Oct 24 '09 at 21:56
  • 1
    @user180326 Would you be willing to provide a code example? The answer sounds interesting, but without code it's limitedly instructive for those who it would help most - those who have not bridged C# and C++ before. – Chris Moschini Oct 28 '14 at 20:16
1

I would batch process the images before uploading them to your web server, rather then try to process them while serving them. This will lead to less load on the web server and let you use any match image processing tools you wish.

Ian Ringrose
  • 51,220
  • 55
  • 213
  • 317
  • I need four sizes for each image (thumb, small, medium, and large). Right now I just have to upload a image and all four sizes are created for me by the server. Batch processing the images manually then uploading won't work (er, it will be too much work). I will need a server-side solution. Thanks for your input. – David Murdoch Oct 12 '09 at 13:38
  • Or a tool that does the image processing then uploads them. The processing should be done at upload time anyway not when the image is served. – Ian Ringrose Oct 12 '09 at 13:42
  • This is a web-app; all work will need to be done after uploading. The processing IS currently and will be done at upload-time. I serve only static image files...no server processing needed. :o) – David Murdoch Oct 12 '09 at 14:07
1

I'm sure that I'm totally late to answer this question, but recently I have faced on lossless jpeg optimization problem and haven't found any suitable C# implementation of jpegtran utility. So, I have decided to implement by myself routines for lossless jpeg size reducing based on C wrapper of modified jpegtran, which you can find here. It comes, that similar realization with use of pure .Net LibJpeg.NET is far more slower than C wrapped solution, so, I haven't included it to the repo. Using of wrapper is quite simple,

if (JpegTools.Transform(jpeg, out byte[] optimized, copy: false, optimize: true))
{
  //do smth useful 
}
else
{
  //report on error or use original jpeg
}

Hope, someone will find it useful.

kenoma
  • 11
  • 2
-1

Why not call punypng.com with Process.Start()? There is no reason why you .net code can't run external programs, provided the processing is done at the time of uploading (rather then when serving the images)

E.g.

  • upload into a "upload" folder,
  • have a windows services that watches for new files in the “upload” folder
  • when you get a new file, start punypng.com to process it and put the output into the correct image folder.
Ian Ringrose
  • 51,220
  • 55
  • 213
  • 317
  • 2
    http://www.punypng.com is a website that lets you upload images (jpeg, gif, animated gif, and png[8,24, & 32]) to be analyzed and then compressed/optimized. It then returns a download link for the smaller-in-file-size images. – David Murdoch Oct 12 '09 at 14:11