When working with storage and network it is sometimes useful to know the estimated size of image. Should it be stored in SQL/NoSQL, locally, to CDN? Quite a few times have I tried to get a quick answer by Google, but its not that easy. So today I figured I’d write up a quick app, convert an image to some formats and post the results.
Image format and compression level impacts the size of an image.
The original image is a 16384×16384 332MB PNG image. I’m converting it to thumbnails of exponential starting at 8×8, 16×16, 32×32, .. up to 8192×8192. Once we hit 512 I keep thumbnails to this size so the table is readable. Once we hit 1024 I reuse the images from 1024 because there is little visible change. The image link is however linked to the resulting image.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Mime; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using Microsoft.Win32; namespace Tedd.ImageResizeSizeGenerator { class ImageResizeTest { private string _htmlFile; public void Do() { var InputImageFile = "TestImage.png"; var OutDir = "Converted"; var Sizes = new List<Size>(); Sizes.Clear(); //const int startSize = 8, endSize = 16384; const int startSize = 8, endSize = 8192; int addSize = startSize; while (addSize <= endSize) { Sizes.Add(new Size(addSize, addSize)); addSize *= 2; } //Sizes.Reverse(); var jpegQualities = new List<int>() { 1, 30, 60, 90, 100 }; if (!Directory.Exists(OutDir)) Directory.CreateDirectory(OutDir); var log = new StringBuilder(); var resultFile = Path.Combine(OutDir, "Result.txt"); _htmlFile = Path.Combine(OutDir, "Result.html"); var headerList = new List<string>() { "MegaPixels", "Width*Height", "Gif size", "PngSize" }; headerList.AddRange(jpegQualities.Select((q) => "Jpeg " + q + " size")); File.WriteAllText(resultFile, string.Join(",", headerList) + Environment.NewLine); File.WriteAllText(_htmlFile, "<body><head><title>Image sizes</title></head><body>"); File.AppendAllText(_htmlFile, "<table><tr><td>" + string.Join("</td><td>", headerList)); File.AppendAllText(_htmlFile, "</td></tr>"); foreach (var size in Sizes) { File.AppendAllText(_htmlFile, "<tr>"); var data = new List<string>(); var mp = (size.Width * size.Height) / 1024 / 1024; var filename = string.Format("{0}x{1}", (int)size.Width, (int)size.Height); filename = Path.Combine(OutDir, filename); data.Add(mp.ToString().Replace(",", ".")); data.Add(((int)size.Width).ToString()); data.Add(((int)size.Height).ToString()); File.AppendAllText(_htmlFile, string.Format("<td>{0:00.00}</td><td>{1}x{2}</td>", mp, size.Width, size.Height)); Debug.WriteLine(filename); var inputImage = new BitmapImage(); inputImage.BeginInit(); inputImage.UriSource = new Uri(InputImageFile, UriKind.Relative); inputImage.DecodePixelHeight = (int)size.Height; inputImage.DecodePixelWidth = (int)size.Width; inputImage.EndInit(); { // GIF var fn = filename + ".gif"; var fileSize = SaveImage(inputImage, fn, new GifBitmapEncoder(), size); data.Add(fileSize.ToString()); } { // PNG var fn = filename + ".png"; var fileSize = SaveImage(inputImage, fn, new PngBitmapEncoder(), size); data.Add(fileSize.ToString()); } // JPEG foreach (var quality in jpegQualities) { var fileSize = SaveImage(inputImage, filename + string.Format("_Q{0:000}", quality) + ".jpg", new JpegBitmapEncoder() { QualityLevel = quality }, size); data.Add(fileSize.ToString()); } inputImage = null; GC.Collect(10); File.AppendAllText(resultFile, string.Join(",", data) + Environment.NewLine); File.AppendAllText(_htmlFile, "<td></td></tr>\r\n"); } File.AppendAllText(_htmlFile, "</table></body></html>"); } private object SaveImage(BitmapImage inputImage, string filename, BitmapEncoder encoder, Size size) { Debug.WriteLine(filename); encoder.Frames.Add(BitmapFrame.Create(inputImage)); using (var filestream = new FileStream(filename, FileMode.Create)) encoder.Save(filestream); var fileInfo = new FileInfo(filename); var sizeStr = ""; // Above this size images become difficult to see if (size.Width > 128 || size.Height > 128) sizeStr = "width='128' height='128'"; var file = Path.GetFileName(filename); // Thumbs from this size and up looks the same so no point in forcing in huge pictures if (size.Width > 1024) filename.Replace(size.Width.ToString(), "1024"); File.AppendAllText(_htmlFile, string.Format("<td style='text-align: center; vertical-align: middle;'><a href='{0}' target='_blank'><img " + sizeStr + " src='{0}' /></a><br />{1}</td>", file, GetSizeReadable(fileInfo.Length))); return fileInfo.Length; } // Borowed from http://www.somacon.com/p576.php // Returns the human-readable file size for an arbitrary, 64-bit file size // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB" public static string GetSizeReadable(long i) { string sign = (i < 0 ? "-" : ""); double readable = (i < 0 ? -i : i); string suffix; if (i >= 0x1000000000000000) // Exabyte { suffix = "EB"; readable = (double)(i >> 50); } else if (i >= 0x4000000000000) // Petabyte { suffix = "PB"; readable = (double)(i >> 40); } else if (i >= 0x10000000000) // Terabyte { suffix = "TB"; readable = (double)(i >> 30); } else if (i >= 0x40000000) // Gigabyte { suffix = "GB"; readable = (double)(i >> 20); } else if (i >= 0x100000) // Megabyte { suffix = "MB"; readable = (double)(i >> 10); } else if (i >= 0x400) // Kilobyte { suffix = "KB"; readable = (double)i; } else { return i.ToString(sign + "0 B"); // Byte } readable = readable / 1024; return sign + readable.ToString("0.### ") + suffix; } } } |