Название вопроса предполагает, что у вас есть два точных изображения для сравнения, и это делается тривиально. Теперь, если у вас есть похожие изображения для сравнения, это объясняет, почему вы не нашли полностью удовлетворительного ответа: нет метрики, применимой к каждой проблеме, которая дает ожидаемые результаты (обратите внимание, что ожидаемые результаты различаются в зависимости от приложения). Одна из проблем заключается в том, что трудно — в том смысле, что нет общего согласия — сравнивать изображения с несколькими полосами, например цветные изображения. Чтобы справиться с этим, я рассмотрю применение данной метрики в каждой полосе, и результатом этой метрики будет наименьшее результирующее значение. Это предполагает, что метрика имеет хорошо установленный диапазон, например [0, 1], и максимальное значение в этом диапазоне означает, что изображения идентичны (по данной метрике). И наоборот, минимальное значение означает, что изображения совершенно разные.
Итак, все, что я сделаю здесь, это дам вам две метрики. Один из них — SSIM, а другой я назову NRMSE (нормализация корня из среднеквадратичной ошибки). Я решил представить второй, потому что это очень простой метод, и его может быть достаточно для вашей проблемы.
Давайте начнем с примеров. Изображения расположены в следующем порядке: f = исходное изображение в формате PNG, g1 = JPEG с качеством 50 % f
(сделано с помощью convert f -quality 50 g
), g2 = качество JPEG 1 % f
, h = «осветленное» g2.
Результаты (округленные):
- NRMSE(f, g1) = 0,96
- NRMSE(f, g2) = 0,88
- NRMSE (f, ч) = 0,63
- SSIM(f, g1) = 0,98
- SSIM(f, g2) = 0,81
- SSIM(f, ч) = 0,55
В некотором смысле обе метрики хорошо обрабатывали модификации, но SSIM
оказался более разумным, сообщая о меньшем сходстве, когда изображения были фактически визуально различны, и сообщая о более высоком значении, когда изображения были визуально очень похожи. В следующем примере рассматривается цветное изображение (f = исходное изображение, а g = JPEG с качеством 5%).
- NRMSE (f, г) = 0,92
- SSIM(f,g) = 0,61
Таким образом, вам решать, какую метрику вы предпочитаете и пороговое значение для нее.
Теперь метрики. То, что я назвал NRMSE, это просто 1 — [RMSE / (maxval
— minval
)]. Где maxval
— максимальная интенсивность двух сравниваемых изображений и, соответственно, одинаковая для minval
. RMSE задается квадратным корнем MSE: sqrt[(sum(A - B) ** 2) / |A|], где |A| означает количество элементов в A. При этом максимальное значение, заданное RMSE, равно maxval
. Если вы хотите лучше понять значение MSE в изображениях, см., например, https://ece.uwaterloo.ca/~z70wang/publications/SPM09.pdf. Метрика SSIM (Structural SIMilarity) более сложная, и вы можете найти подробности в приведенной ранее ссылке. Чтобы легко применять метрики, рассмотрим следующий код:
import numpy
from scipy.signal import fftconvolve
def ssim(im1, im2, window, k=(0.01, 0.03), l=255):
"""See https://ece.uwaterloo.ca/~z70wang/research/ssim/"""
# Check if the window is smaller than the images.
for a, b in zip(window.shape, im1.shape):
if a > b:
return None, None
# Values in k must be positive according to the base implementation.
for ki in k:
if ki < 0:
return None, None
c1 = (k[0] * l) ** 2
c2 = (k[1] * l) ** 2
window = window/numpy.sum(window)
mu1 = fftconvolve(im1, window, mode='valid')
mu2 = fftconvolve(im2, window, mode='valid')
mu1_sq = mu1 * mu1
mu2_sq = mu2 * mu2
mu1_mu2 = mu1 * mu2
sigma1_sq = fftconvolve(im1 * im1, window, mode='valid') - mu1_sq
sigma2_sq = fftconvolve(im2 * im2, window, mode='valid') - mu2_sq
sigma12 = fftconvolve(im1 * im2, window, mode='valid') - mu1_mu2
if c1 > 0 and c2 > 0:
num = (2 * mu1_mu2 + c1) * (2 * sigma12 + c2)
den = (mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2)
ssim_map = num / den
else:
num1 = 2 * mu1_mu2 + c1
num2 = 2 * sigma12 + c2
den1 = mu1_sq + mu2_sq + c1
den2 = sigma1_sq + sigma2_sq + c2
ssim_map = numpy.ones(numpy.shape(mu1))
index = (den1 * den2) > 0
ssim_map[index] = (num1[index] * num2[index]) / (den1[index] * den2[index])
index = (den1 != 0) & (den2 == 0)
ssim_map[index] = num1[index] / den1[index]
mssim = ssim_map.mean()
return mssim, ssim_map
def nrmse(im1, im2):
a, b = im1.shape
rmse = numpy.sqrt(numpy.sum((im2 - im1) ** 2) / float(a * b))
max_val = max(numpy.max(im1), numpy.max(im2))
min_val = min(numpy.min(im1), numpy.min(im2))
return 1 - (rmse / (max_val - min_val))
if __name__ == "__main__":
import sys
from scipy.signal import gaussian
from PIL import Image
img1 = Image.open(sys.argv[1])
img2 = Image.open(sys.argv[2])
if img1.size != img2.size:
print "Error: images size differ"
raise SystemExit
# Create a 2d gaussian for the window parameter
win = numpy.array([gaussian(11, 1.5)])
win2d = win * (win.T)
num_metrics = 2
sim_index = [2 for _ in xrange(num_metrics)]
for band1, band2 in zip(img1.split(), img2.split()):
b1 = numpy.asarray(band1, dtype=numpy.double)
b2 = numpy.asarray(band2, dtype=numpy.double)
# SSIM
res, smap = ssim(b1, b2, win2d)
m = [res, nrmse(b1, b2)]
for i in xrange(num_metrics):
sim_index[i] = min(m[i], sim_index[i])
print "Result:", sim_index
Обратите внимание, что ssim
отказывается сравнивать изображения, когда заданное window
больше их. window
обычно очень маленький, по умолчанию 11x11, поэтому, если ваши изображения меньше этого размера, нет особой «структуры» (из названия метрики) для сравнения, и вам следует использовать что-то еще (например, другую функцию nrmse
) . Вероятно, есть лучший способ реализовать ssim
, так как в Matlab это работает намного быстрее.
person
mmgp
schedule
14.12.2012