Marginal Musings

BMP and PNG Produce Identical Quality Metrics

Author
Shlomo Stept
Published
Updated
Not updated
Vision Research Notes on Image Processing

I almost wasted an afternoon on this. While building the evaluation pipeline for my PSNR research , I had a nagging worry: some benchmark datasets shipped as BMP, but I was storing everything as PNG. If the format introduced even tiny pixel differences, my whole comparison framework was suspect.

So I checked.

import numpy as np
from PIL import Image

img_bmp = np.array(Image.open("reference.bmp"))
img_png = np.array(Image.open("reference.png"))

print(np.array_equal(img_bmp, img_png))  # True

Bit-for-bit identical. PNG uses lossless compression — the key word being lossless — so decoded pixel values are exactly what went in. Not approximately. Exactly.

I ran the full verification anyway (because I’m the kind of person who needs to see the numbers before I’ll stop worrying):

from skimage.metrics import peak_signal_noise_ratio, structural_similarity

noisy = np.clip(
    img_bmp.astype(np.float32) + np.random.normal(0, 10, img_bmp.shape),
    0, 255
).astype(np.uint8)

psnr_bmp = peak_signal_noise_ratio(img_bmp, noisy)
psnr_png = peak_signal_noise_ratio(img_png, noisy)
ssim_bmp = structural_similarity(img_bmp, noisy, channel_axis=2)
ssim_png = structural_similarity(img_png, noisy, channel_axis=2)

print(f"PSNR — BMP ref: {psnr_bmp:.4f}, PNG ref: {psnr_png:.4f}")  # identical
print(f"SSIM — BMP ref: {ssim_bmp:.6f}, PNG ref: {ssim_png:.6f}")  # identical

Every decimal place matches. Obviously. The arrays are the same data.

One thing worth flagging: this only holds for lossless formats (BMP, PNG, uncompressed TIFF). The moment you touch JPEG or any lossy codec, all bets are off. JPEG re-encoding introduces quantization artifacts, so saving and reloading the same image changes pixel values. I’ve seen researchers chase “model regressions” for days that turned out to be JPEG re-encoding noise.

Use PNG. Same pixels as BMP, fraction of the file size. And verify your boring assumptions before you build a pipeline on top of them — the five minutes it takes is cheaper than the rabbit hole it prevents.