The cause for the difference is most likely hinted in the caption of the referenced images (emphasis by me):
Example luma PSNR values for a cjpeg compressed image at various quality levels.
The PSNR is not calculated for the whole RGB image like you did, but only for the image's luma ("brightness").
Also, I even couldn't reproduce the 29.4363...
from your question. So, here's my code for comparison:
import cv2
import numpy as np
from skimage import io # Only needed for web grabbing images, use cv2.imread for local images
def psnr(image1, image2):
# OpenCV
print('OpenCV PSNR: ', cv2.PSNR(image1, image2))
# Own implementation
mse = np.mean((image1.astype(np.float64) / 255 - image2.astype(np.float64) / 255) ** 2)
print('Own implementation: ', 10 * np.log10(1. / mse))
def luma(image):
return (0.299 * image[:, :, 2] + 0.587 * image[:, :, 1] + 0.114 * image[:, :, 0]).astype(np.uint8)
# return (0.2126 * image[:, :, 2] + 0.7152 * image[:, :, 1] + 0.0722 * image[:, :, 0]).astype(np.uint8)
# return (0.212 * image[:, :, 2] + 0.701 * image[:, :, 1] + 0.087 * image[:, :, 0]).astype(np.uint8)
# Calculate PSNR on referenced images
img1 = cv2.cvtColor(io.imread('https://upload.wikimedia.org/wikipedia/commons/d/d3/PSNR-example-base.png'), cv2.COLOR_RGB2BGR)
img2 = cv2.cvtColor(io.imread('https://upload.wikimedia.org/wikipedia/commons/2/2a/PSNR-example-comp-90.jpg'), cv2.COLOR_RGB2BGR)
psnr(img1, img2)
# Calculate luma PSNR on referenced images
psnr(luma(img1), luma(img2))
Output:
OpenCV PSNR: 39.021537956442224
Own implementation: 39.02153795644222
OpenCV PSNR: 44.79892614734474
Own implementation: 44.79892614734474
For me, the PSNR of the RGB image is way higher than the one you report. But, the PSNR for the luma does quite match the given value, although it's not exactly the same. I tested several luma calculations, none gave the exact result – but since it's not mentioned in the referenced example, how the luma was calculated, trying to reproduce the exact values is meaningless anyway.
Hope that helps!