A pixel counts as changed when any of its red, green or blue values differs by more than the tolerance. At zero you see every difference, including the invisible ones that JPEG compression introduces. Around 10 ignores compression noise and shows real edits. Raise it further to find only substantial changes.
from PIL import Image, ImageChops
a = Image.open("before.png").convert("RGB")
b = Image.open("after.png").convert("RGB")
diff = ImageChops.difference(a, b)
box = diff.getbbox()
if box is None:
print("identical")
else:
print("they differ inside", box)
diff.save("diff.png")
getbbox() returning None means the two are pixel for pixel identical, which is the quickest test there is.
from PIL import Image, ImageChops
diff = ImageChops.difference(a, b).convert("L")
mask = diff.point(lambda v: 255 if v > 10 else 0)
changed = sum(mask.point(lambda v: v // 255).getdata())
print(changed, "of", a.width * a.height, "pixels differ")
from PIL import ImageStat
stat = ImageStat.Stat(ImageChops.difference(a, b))
print(stat.mean)
print(stat.rms)
Root mean square across the whole image is the usual single number for "how different are these", and it is what most automated comparisons threshold on.