| page.title=Reducing Image Download Sizes |
| page.metaDescription=Improve network performance by optimizing image size. |
| |
| meta.tags="performance" |
| page.tags="performance" |
| |
| @jd:body |
| |
| <div id="qv-wrapper"> |
| <div id="qv"> |
| |
| <h2>In this document</h2> |
| <ol> |
| |
| <li> |
| <a href="#uif">Understanding Image Formats</a> |
| <ul> |
| <li><a href="#png">PNG</a></li> |
| <li><a href="#jpg">JPG</a></li> |
| <li><a href="#webp">WebP</a></li> |
| </ul> |
| </li> |
| <li> |
| <a href="#sf">Selecting a Format</a></li> |
| <li><a href="#doqv">Determining Optimal Quality Values</a> |
| <ul> |
| <li><a href="#sv">Scalar Values (JPG, WebP only)</a></li> |
| <li><a href="#butter">Butteraugli</a></li> |
| </ul> |
| </li> |
| <li><a href="#sizes">Serving Sizes</a></li> |
| </ol> |
| </div> |
| </div> |
| |
| <p> |
| Most download traffic consists of images. As a result, the smaller you can make |
| your downloadable images, the better a network experience your app can provide |
| for users. This page provides guidance on making image files smaller and more |
| network-friendly. |
| </p> |
| |
| <h2 id="uif">Understanding Image Formats</h2> |
| |
| <p>Android apps typically use images that are in one or more of the following file |
| formats: PNG, JPG, and WebP. For each of these formats, there are steps you can |
| take to reduce image sizes. |
| </p> |
| |
| <h3 id="png">PNG</h3> |
| |
| <p> |
| A key to making your PNG files smaller is reducing the number of unique |
| colors used in each row of pixels that comprises the image. By using fewer |
| colors, you improve the compression potential at all of the other stages of |
| the pipeline. |
| </p> |
| |
| <p> |
| Reducing the number of unique colors makes a significant difference because PNG |
| compression effectiveness is partly a function of the degree to which |
| horizontally adjacent pixel colors vary. Thus, reducing the number of unique |
| colors in each row of your PNG images can help in reducing their file sizes. |
| </p> |
| |
| <p> |
| When deciding whether to pursue this strategy, you should keep in mind that |
| reducing the number of unique colors effectively amounts to applying a lossy |
| encoding stage to the image. However, an encoding tool may not be a good |
| judge of how bad a seemingly small error looks to the human eye. Therefore, |
| you should perform this work manually in order to help ensure |
| the right balance between efficient compression and acceptable image quality. |
| </p> |
| |
| <p> |
| There are two particularly useful approaches you can take: striving for indexed |
| formats, and applying vector quantization. |
| </p> |
| |
| |
| <h4 id="strive">Strive for indexed formats</h4> |
| |
| <p> |
| Any attempt at color reduction should start with trying to optimize your colors |
| so that you can use the INDEXED format when exporting the image as a PNG. The |
| INDEXED color mode works by choosing the best 256 colors to use, and replacing |
| all pixel values with indices into that color palette. The result is a |
| reduction from 16 million (potential) colors to only 256 colors: from 3 (without |
| transparency) or 4 (with transparency) bytes per pixel to 1 byte per pixel. |
| This change is a significant first-step file size reduction. |
| </p> |
| |
| <p> |
| Figure 1 shows shows an image and its indexed variant. |
| </p> |
| |
| <img src="{@docRoot}topic/performance/images/beforeafterindexed.png"> |
| <p class="img-caption"> |
| Figure 1. An image before and after conversion to the INDEXED format. |
| </p> |
| |
| |
| <p> |
| Figure 2 shows the color palette for the image in Figure 1: |
| </p> |
| |
| <img src="{@docRoot}topic/performance/images/palette.png"> |
| <p class="img-caption"> |
| Figure 2. The color palette for the image in Figure 1. |
| </p> |
| |
| <p> |
| Representing your image as a paletted image goes a long way toward |
| significantly improving the file size, so it's worth investigating if the |
| majority of your images can be converted. |
| </p> |
| |
| <p> |
| Of course, not every image can be accurately represented with only 256 colors. |
| Some images, for example, might need 257, 310, 512, or 912 colors to |
| look correct. In such cases, vector quantization can also be helpful. |
| </p> |
| |
| <h4 id="vq">Vector quantization</h4> |
| |
| <p> |
| The process of creating an indexed image may be better described as vector |
| quantization (VQ). VQ serves as a rounding process for multidimensional |
| numbers. In this process, all the colors in your image get grouped based upon |
| their similarity. For a given group, all colors in that group are replaced by a |
| single <em>center point</em> value, which minimizes error for colors in that |
| cell (or "site" if you're using the Voronoi terminology). In Figure 3, |
| the green dots represent input colors, and the red dots are the center points |
| that replace the input colors. Each cell is bounded by blue lines. |
| </p> |
| |
| <img src="{@docRoot}topic/performance/images/vq.gif"> |
| <p class="img-caption"> |
| Figure 3. Applying vector quantization to the colors in an image. |
| </p> |
| |
| <p> |
| The result of applying VQ to an image reduces the number of unique colors, |
| replacing each group of colors with a single color that's "pretty close" |
| in visual quality. |
| </p> |
| |
| <p> |
| This technique also allows you to define the maximum number of unique colors in |
| your image. For example, Figure 4 shows the a parrot head in 16.7 million colors |
| (24 bits per pixel, or bpp) alongside a version that only allows only |
| 16 (3 bpp) unique colors to be used. |
| </p> |
| |
| <img src="{@docRoot}topic/performance/images/parrot.png"> |
| <p class="img-caption"> |
| Figure 4. Image before and after application of vector quantification. |
| </p> |
| |
| <p> |
| Immediately, you can see that there's a loss of quality; most of the gradient |
| colors have been replaced, imparting a banding effect to the image. This image |
| needs more than 16 unique colors. |
| </p> |
| |
| <p> |
| Setting up a VQ step in your pipeline can help you get a better sense of the |
| true number of unique colors that your image uses, and can help you reduce them |
| significantly. There are a number of readily available tools that you can use |
| to help you implement this technique. |
| </p> |
| |
| <h3 id="jpg">JPG</h3> |
| |
| <p> |
| If you are using JPG images, there are several small changes you can make that |
| potentially provide significant file-size savings. These include: |
| </p> |
| |
| <ul> |
| <li> |
| Producing a smaller file size through different encoding methods (without |
| impacting quality). |
| </li> |
| |
| <li> |
| Adjusting quality slightly in order to yield better compression. |
| </li> |
| </ul> |
| |
| <p>Pursuing these strategies can often net you file-size reductions of up to |
| 25%. |
| </p> |
| |
| <p> |
| When choosing tools, remember that photo exporting tools can |
| insert unnecessary metadata, such as GPS information, into your images. At |
| a minimum, try to leverage existing tools to help strip out this information |
| from your files. |
| </p> |
| |
| <h3 id="webp">WebP</h3> |
| |
| <p> |
| WebP is a newer image format supported from Android 4.2.1 (API level 17). This |
| format provides superior lossless and lossy compression for images on the web. |
| Using WebP, developers can create smaller, richer images. WebP lossless image |
| files are, on average, |
| <a href="https://developers.google.com/speed/webp/docs/webp_lossless_alpha_study#conclusions"> |
| 26% smaller</a> than PNGs. These image files also support |
| transparency (also known as alpha channel) at a cost of just |
| <a href="https://developers.google.com/speed/webp/docs/webp_lossless_alpha_study#results"> |
| 22% more</a> bytes. |
| </p> |
| |
| <p> |
| WebP lossy images are |
| <a href="https://developers.google.com/speed/webp/docs/webp_study#experiment_1_webp_vs_jpeg_at_equal_ssim_index"> |
| 25-34% smaller</a> than comparable JPG images at equivalent |
| <a href="https://en.wikipedia.org/wiki/Structural_similarity">SSIM</a> |
| quality indices. For cases when lossy RGB compression is acceptable, lossy |
| WebP also supports transparency, typically producing file sizes 3 times smaller |
| than PNG. |
| </p> |
| |
| <p> |
| For more information about WebP, visit the |
| <a href="https://developers.google.com/speed/webp/">WebP site</a>. |
| </p> |
| |
| <h2 id="sf">Selecting a Format</h2> |
| |
| <p> |
| Different image formats are suitable for different types of images. JPG and PNG |
| have very different compression processes, and they produce quite different |
| results. |
| </p> |
| |
| <p> |
| The decision between PNG and JPG often comes down to the complexity of the |
| image itself. Figure 5 shows two images that come out quite differently |
| depending on which compression scheme the developer applies. The image on the |
| left has many small details, and thus compresses more efficiently with JPG. The |
| image on the right, with runs of the same color, compresses more efficiently |
| with PNG. |
| </p> |
| |
| <img src="{@docRoot}topic/performance/images/comparison.png"> |
| <p class="img-caption"> |
| Figure 5. Suitable cases for JPG vs. PNG |
| </p> |
| |
| |
| <p> |
| WebP as a format can support both lossy and lossless modes, making it an ideal |
| replacement for both PNG and JPG. The only thing to keep in mind is that it |
| only has native support on devices running Android 4.2.1 (API level 17) and |
| higher. Fortunately, the large |
| <a |
| href="https://developer.android.com/about/dashboards/index.html#Platform"> |
| majority of devices</a> satisfy that requirement. |
| </p> |
| |
| <p> |
| Figure 6 provides a simple visualization to help you decide which compression |
| scheme to use. |
| </p> |
| |
| <img src="{@docRoot}topic/performance/images/decisions.png"> |
| <p class="img-caption"> |
| Figure 6. Deciding on a compression scheme |
| </p> |
| |
| <h2 id="doqv">Determining Optimal Quality Values</h2> |
| |
| <p> |
| There are several techniques you can use to achieve the right balance between |
| compression and image quality. One technique uses scalar values and therefore |
| only works for JPG and WebP. The other technique takes advantage of the |
| Butteraugli library, and is usable for all image formats. |
| </p> |
| |
| <h3 id="sv">Scalar values (JPG and WebP only)</h3> |
| |
| <p> |
| The power of JPG and WebP comes from the fact that you can use a scalar value |
| to balance quality against file size. The trick is finding out what the correct |
| quality value is for your image. Too low a quality level produces a small file |
| at the cost of image quality. Too high a quality level increases file size |
| without providing a noticeable benefit to the user. |
| </p> |
| |
| <p> |
| The most straightforward solution is to pick some non-maximum value, and use |
| that value. However, be aware that the quality value affects every image |
| differently. While a quality of 75%, for example, may look fine on most images, |
| there may be some cases do not fare as well. You should make sure to test your |
| chosen maximum value against a representative sample of images. Also, make |
| sure to perform all of your tests against the original images, and not on |
| compressed versions. |
| </p> |
| |
| <p> |
| For large media applications that upload and re-send millions of JPGs a day, |
| hand-tuning for each asset is impractical. You might address this challenge by |
| specifying several different quality levels, according to image category. For |
| example, you might set 35% as the quality setting for thumbnails, since a |
| smaller image hides more compression artifacts. |
| </p> |
| |
| <h3 id="butter">Butteraugli</h4> |
| |
| <p> |
| The Butteraugli project is a library to test an image's Psychovisual Error |
| Threshold: the point at which a viewer starts to notice image degradation. In |
| other words, this project attempts to quantify how distorted your compressed |
| image is. |
| </p> |
| |
| <p> |
| Butteraugli allows you to define a goal for visual quality, and then run PNG, |
| JPG, WebP lossy, and WebP lossless compressions. You can then choose the image |
| that is the best balance of file size and Butteraugli level. Figure 7 shows an |
| example of how Butteraugli was used to find the minimal JPG quality level |
| before the visual distortion was high enough for a user could perceive a |
| problem; the result is a roughly 65% reduction in file size. |
| </p> |
| |
| <img src="{@docRoot}topic/performance/images/moarparrots.png"> |
| <p class="img-caption"> |
| Figure 7. An image before and after application of Butteraugli technology. |
| </p> |
| |
| <p> |
| Butteraugli allows you to proceed based on either output or input. That is, you |
| can look for the lowest quality setting before a user perceives noticeable |
| distortion in the resulting image, or you can iteratively set image-distortion |
| levels to learn their associated quality levels. |
| </p> |
| |
| <h2 id="sizes">Serving Sizes</h2> |
| |
| <p> |
| It is tempting to keep only a single resolution of an image on a server. When a |
| device accesses the image, the server serves it at that one resolution and |
| leaves downscaling to the device. |
| </p> |
| |
| <p> |
| This solution is convenient for the developer, but potentially painful for the |
| user, because the solution forces the user to download much more data than they |
| need. |
| |
| You should instead store multiple sizes of images, and serve the size that is |
| most appropriate for a particular use case. For example, for a thumbnail, |
| serving an actual thumbnail image instead of serving and downscaling a |
| full-size version consumes much less network bandwidth |
| </p> |
| |
| </p> |
| This approach is good for download speed, and is less costly for users who may |
| be using limited or metered data plans. Proceeding like this also results in |
| the image's taking less space on the device and in main memory. In the |
| case of large images, such as 4K ones, this approach also saves the device |
| from having to resize images before loading them. |
| </p> |
| |
| <p> |
| Implementing this approach requires that you have a backend image service to |
| provide images at various resolutions with proper caching. There are existing |
| services that can provide help with this task. For example, |
| <a href="https://cloud.google.com/appengine/">App Engine</a> comes |
| with image resizing functionality already installed. |
| </p> |