Flip an image

Original

EAST SIDEPRINT WORKS

Mirrored

EAST SIDEPRINT WORKS
The same sign, read out in reverse order along one axis.

Exactness

How a JPEG is flipped without losing anything

A JPEG is not a grid of pixels; it is a grid of 8 × 8 blocks of numbers. Mirror the blocks and a few of their numbers' signs, and the picture is flipped without being decoded — with one catch at the edge.

Last checked

What is inside a JPEG

A JPEG encoder cuts the picture into 8 × 8 squares and describes each square not as 64 pixel values but as 64 frequency coefficients: how much flat colour, how much left-to-right change, how much top-to-bottom change, and so on up to the finest detail. Those coefficients are rounded — that is where JPEG loses quality — and packed tightly with Huffman codes. Colour is usually stored at half resolution, so the blocks are grouped into units of 16 × 16 pixels (or 16 × 8, or 8 × 8 for a file with full-resolution colour or none).

Decoding reverses all of that and produces pixels. Re-encoding a flipped picture rounds the coefficients a second time: a small, real loss on top of the first, and the reason a JPEG flipped and saved repeatedly gets visibly worse.

Flipping the blocks instead

A block reflected left to right has the same flat colour and the same top-to-bottom detail; only its left-to-right detail runs the other way, which is the same as changing the sign of the coefficients for odd horizontal frequencies. So a horizontal flip can be written entirely in the compressed file: move each block to its mirrored column, negate half of its numbers, and pack them again. No number is rounded, because no number is computed — each one in the new file is one from the old file, possibly with its sign changed. Top to bottom works the same way with rows.

This is what jpegtran -flip has done for decades, and what this site’s JPEG route does in the tab. The Huffman tables are rebuilt for the new order, because neighbouring blocks have changed and the old tables may have no code for some new differences; rebuilt tables usually make the file a few hundred bytes smaller.

The edge strip

Blocks move only as whole units. A 4000 px wide photo in 16 px units is exactly 250 units, so every column has a partner on the other side. A 4001 px wide photo has a 1 px strip on the right that belongs to a unit with no partner: moving it would put a partial unit in the middle of the picture, which the format cannot express.

ABCDEFGHIJKLpartial strip
Four whole columns of units and a partial strip.
ABCDEFGHIJKLpartial strip
Flipped: whole columns reversed and mirrored; the strip stays where it was.

There are three honest answers, and the tool offers all of them. Leave the strip where it is — every pixel survives, but that strip is not flipped. Trim it off before flipping — the rest is perfect, and the strip is gone for good. Or use the tag route, below. A “perfect” flip, with no strip, is possible only when the size is a whole number of units on the axis you flip.

Three ways a flipped JPEG is written

  1. Re-encode. Decode, flip the pixels, compress again — at the source’s own quality where it can be read. Works for every file and every viewer; costs a generation. Never called lossless here.
  2. Tag. Change the sixteen-bit EXIF orientation value so viewers show the picture mirrored, and copy everything else byte for byte. Same size, nothing lost — but a viewer that ignores the tag shows it unflipped, and some websites strip the tag.
  3. Blocks. Move the coefficients as above. Every viewer sees the flip, nothing is rounded, and the saved file is read back to confirm that no coefficient changed. Needs a baseline JPEG (what cameras and phones write) and a whole number of units, or a choice about the edge.

Measured, on real sizes

A 4032 × 3024 phone photo with 4:2:0 colour is 252 × 189 units of 16 px — whole on both axes — and holds 285,768 blocks: 190,512 for brightness and 47,628 for each colour channel. Flipping it moves every block and changes no coefficient. A 4000 × 3000 photo flips perfectly left to right (250 units) and leaves an 8 px strip top to bottom (187½ units). After any block flip, a browser decoding the file may differ from a decoded-then-flipped copy by a level or two in subsampled colour near block edges; that is the decoder’s smoothing, not the file, and the Check reports it as measured.

Related