Lossless Encoding

x265 can encode HEVC bitstreams that are entirely lossless (the reconstructed images are bit-exact to the source images) by using the --lossless option. Lossless operation is theoretically simple. Rate control, by definition, is disabled and the encoder disables all quality metrics since they would only waste CPU cycles. Instead, x265 reports only a compression factor at the end of the encode.

In HEVC, lossless coding means bypassing both the DCT transforms and bypassing quantization (often referred to as transquant bypass). Normal predictions are still allowed, so the encoder will find optimal inter or intra predictions and then losslessly code the residual (with transquant bypass).

All --preset options are capable of generating lossless video streams, but in general the slower the preset the better the compression ratio (and the slower the encode). Here are some examples:

./x265 ../test-720p.y4m o.bin --preset ultrafast --lossless
... <snip> ...
encoded 721 frames in 238.38s (3.02 fps), 57457.94 kb/s

./x265 ../test-720p.y4m o.bin --preset faster --lossless
... <snip> ...
x265 [info]: lossless compression ratio 3.11::1
encoded 721 frames in 258.46s (2.79 fps), 56787.65 kb/s

./x265 ../test-720p.y4m o.bin --preset slow --lossless
... <snip> ...
x265 [info]: lossless compression ratio 3.36::1
encoded 721 frames in 576.73s (1.25 fps), 52668.25 kb/s

./x265 ../test-720p.y4m o.bin --preset veryslow --lossless
x265 [info]: lossless compression ratio 3.76::1
encoded 721 frames in 6298.22s (0.11 fps), 47008.65 kb/s


In HEVC, only QP=4 is truly lossless quantization, and thus when encoding losslesly x265 uses QP=4 internally in its RDO decisions.

Near-lossless Encoding

Near-lossless conditions are a quite a bit more interesting. Normal ABR rate control will allow one to scale the bitrate up to the point where quantization is entirely bypassed (QP <= 4), but even at this point there is a lot of SSIM left on the table because of the DCT transforms, which are not lossless:

./x265 ../test-720p.y4m o.bin --preset medium --bitrate 40000 --ssim
encoded 721 frames in 326.62s (2.21 fps), 39750.56 kb/s, SSIM Mean Y: 0.9990703 (30.317 dB)

./x265 ../test-720p.y4m o.bin --preset medium --bitrate 50000 --ssim
encoded 721 frames in 349.27s (2.06 fps), 44326.84 kb/s, SSIM Mean Y: 0.9994134 (32.316 dB)

./x265 ../test-720p.y4m o.bin --preset medium --bitrate 60000 --ssim
encoded 721 frames in 360.04s (2.00 fps), 45394.50 kb/s, SSIM Mean Y: 0.9994823 (32.859 dB)

For the encoder to get over this quality plateau, one must enable lossless coding at the CU level with --cu-lossless. It tells the encoder to evaluate trans-quant bypass as a coding option for each CU, and to pick the option with the best rate-distortion characteristics.

The --cu-lossless option is very expensive, computationally, and it only has a positive effect when the QP is extremely low, allowing RDO to spend a large amount of bits to make small improvements to quality. So this option should only be enabled when you are encoding near-lossless bitstreams:

./x265 ../test-720p.y4m o.bin --preset medium --bitrate 40000 --ssim --cu-lossless
encoded 721 frames in 500.51s (1.44 fps), 40017.10 kb/s, SSIM Mean Y: 0.9997790 (36.557 dB)

./x265 ../test-720p.y4m o.bin --preset medium --bitrate 50000 --ssim --cu-lossless
encoded 721 frames in 524.60s (1.37 fps), 46083.37 kb/s, SSIM Mean Y: 0.9999432 (42.456 dB)

./x265 ../test-720p.y4m o.bin --preset medium --bitrate 60000 --ssim --cu-lossless
encoded 721 frames in 523.63s (1.38 fps), 46552.92 kb/s, SSIM Mean Y: 0.9999489 (42.917 dB)


It is not unusual for bitrate to drop as you increase lossless coding. Having “perfectly coded” reference blocks reduces residual in later frames. It is quite possible for a near-lossless encode to spend more bits than a lossless encode.

Enabling psycho-visual rate distortion will improve lossless coding. --psy-rd influences the RDO decisions in favor of energy (detail) preservation over bit cost and results in more blocks being losslessly coded. Our psy-rd feature is not yet assembly optimized, so this makes the encodes run even slower:

./x265 ../test-720p.y4m o.bin --preset medium --bitrate 40000 --ssim --cu-lossless --psy-rd 1.0
encoded 721 frames in 581.83s (1.24 fps), 40112.15 kb/s, SSIM Mean Y: 0.9998632 (38.638 dB)

./x265 ../test-720p.y4m o.bin --preset medium --bitrate 50000 --ssim --cu-lossless --psy-rd 1.0
encoded 721 frames in 587.54s (1.23 fps), 46284.55 kb/s, SSIM Mean Y: 0.9999663 (44.721 dB)

./x265 ../test-720p.y4m o.bin --preset medium --bitrate 60000 --ssim --cu-lossless --psy-rd 1.0
encoded 721 frames in 592.93s (1.22 fps), 46839.51 kb/s, SSIM Mean Y: 0.9999707 (45.334 dB)

--cu-lossless will also be more effective at slower presets which perform RDO at more levels and thus may find smaller blocks that would benefit from lossless coding:

./x265 ../test-720p.y4m o.bin --preset veryslow --bitrate 40000 --ssim --cu-lossless
encoded 721 frames in 12969.25s (0.06 fps), 37331.96 kb/s, SSIM Mean Y: 0.9998108 (37.231 dB)

./x265 ../test-720p.y4m o.bin --preset veryslow --bitrate 50000 --ssim --cu-lossless
encoded 721 frames in 46217.84s (0.05 fps), 42976.28 kb/s, SSIM Mean Y: 0.9999482 (42.856 dB)

./x265 ../test-720p.y4m o.bin --preset veryslow --bitrate 60000 --ssim --cu-lossless
encoded 721 frames in 13738.17s (0.05 fps), 43864.21 kb/s, SSIM Mean Y: 0.9999633 (44.348 dB)

And with psy-rd and a slow preset together, very high SSIMs are possible:

./x265 ../test-720p.y4m o.bin --preset veryslow --bitrate 40000 --ssim --cu-lossless --psy-rd 1.0
encoded 721 frames in 11675.81s (0.06 fps), 37819.45 kb/s, SSIM Mean Y: 0.9999181 (40.867 dB)

./x265 ../test-720p.y4m o.bin --preset veryslow --bitrate 50000 --ssim --cu-lossless --psy-rd 1.0
encoded 721 frames in 12414.56s (0.06 fps), 42815.75 kb/s, SSIM Mean Y: 0.9999758 (46.168 dB)

./x265 ../test-720p.y4m o.bin --preset veryslow --bitrate 60000 --ssim --cu-lossless --psy-rd 1.0
encoded 721 frames in 11684.89s (0.06 fps), 43324.48 kb/s, SSIM Mean Y: 0.9999793 (46.835 dB)

It’s important to note in the end that it is easier (less work) for the encoder to encode the video losslessly than it is to encode it near-losslessly. If the encoder knows up front the encode must be lossless, it does not need to evaluate any lossy coding methods. The encoder only needs to find the most efficient prediction for each block and then entropy code the residual.

It is not feasible for --cu-lossless to turn itself on when the encoder determines it is encoding a near-lossless bitstream (ie: when rate control nearly disables all quantization) because the feature requires a flag to be enabled in the stream headers. At the time the stream headers are being coded we do not know whether --cu-lossless would be a help or a hinder. If very few or no blocks end up being coded as lossless, then having the feature enabled is a net loss in compression efficiency because it adds a flag that must be coded for every CU. So ignoring even the performance aspects of the feature, it can be a compression loss if enabled without being used. So it is up to the user to only enable this feature when they are coding at near-lossless quality.

Transform Skip

A somewhat related feature, --tskip tells the encoder to evaluate transform-skip (bypass DCT but with quantization still enabled) when coding small 4x4 transform blocks. This feature is intended to improve the coding efficiency of screen content (aka: text on a screen) and is not really intended for lossless coding. This feature should only be enabled if the content has a lot of very sharp edges in it, and is mostly unrelated to lossless coding.