blob: 7fe5594ca89968ccf77f66e0004fae699cb7855f [file] [log] [blame]
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>