- ssim介紹
在比較兩幅圖像誤差或者相似度時,常用的衡量方法有MAE和MSE,
https://blog.csdn.net/u011875342/article/details/78036380
但是上述這兩種損失不足以表達人的視覺系統對圖像的直觀感受,有時候兩張圖片只是亮度不同,但是他們之間的MSE loss相差很大,而一副很模糊與另一幅很清晰的圖,他們的MSE loss可能反而很小。
而SSIM(Structural Similarity)結構相似度更能反映人類視覺系統對兩幅圖相似性的判斷。
具體理論和公式計算可見以下鏈接:
https://mp.weixin.qq.com/s/hGum0fXrKUHCoaInVVS3rQ
- ssim調用
目前可以從anaconda自帶的庫skimage庫中找到該函數,具體調用方法如下:
另外,也可以調用其他圖像相似度評價函數,如MSE,PSNR。
from skimage.measure import compare_ssim
from skimage.measure import compare_mse
from skimage measure import compare_psnr
這三個函數只默認支持 兩張圖片進行對比,無法進行批量比較。X,Y必須都爲數組。
另外,如果是X Y 都是彩色圖三色,即爲多通道的,則需要設置mutichannel=True;
import keras
from skimage.measure import compare_ssim
from skimage.measure import compare_mse
img1=keras.preprocessing.image.load_img(img_path1_1) #先通過keras讀取圖片,
img1_array=keras.preprocessing.image.img_to_array(img1)/255 #將圖片轉換爲數組
img2=keras.preprocessing.image.load_img(img_path2_2)
img2_array=keras.preprocessing.image.img_to_array(img2)/255
ssim=compare_ssim(img1_array,img2_array,multichannel=True)
mse=compare_mse(img1_array,img2_array)
print('ssim value:' ,ssim)
print('mse value:',mse)
ssim value: 0.5348699316504579
mse value: 0.030186621034944017
一般情況下,ssim在-1~1之間,數值越大代表相似度越高。
肉眼上兩張圖片相似度較高,但是ssim值卻不到0.6.說明 ssim可以很容易捕捉到兩幅圖像之間的差異。因此可以考慮使用ssim作爲圖像迴歸中的損失函數;
兩張圖片如下
詳細介紹可以參考次鏈接:https://www.jianshu.com/p/ca343a441e6d
- Tensor和數組之間的轉換
先插入一個題外話,目前tensorflow和keras等深度學習框架中,默認使用張量Tensor,雖然Tensor在概念上和我們常見的矩陣沒什麼區別,但是在深度模型運算中,稍不注意,就會出錯。
這裏對如何對 Tensor和數組進行轉化給與說明:
import tensorflow as tf
import numpy as np
#a爲數組
a=np.random.randint(0,10,(2,3))
print('type of a:',type(a))
print()
#將a轉化成Tensor
tensor_a=tf.convert_to_tensor(a)
print('type of tensor_a:',type(tensor_a))
print()
#將tensor轉化成數組
with tf.Session():
array_a=tensor_a.eval()
print('type of array_a:',type(array_a))
print()
輸出結果如下:
type of a: <class ‘numpy.ndarray’>
type of tensor_a: <class ‘tensorflow.python.framework.ops.Tensor’>
type of array_a: <class ‘numpy.ndarray’>
- ssim作爲損失函數
需要說明的是:ssim爲衡量圖片相似度的,數值在-1~1之間,數值越大,越相似。而訓練神經網絡的爲了使得損失函數越來越小,因此這裏選取 1-ssim作爲損失。
目前keras中不支持ssim作爲損失函數,但是tensorflow中有ssim函數
可以參看tensorflow文檔:
https://www.w3cschool.cn/tensorflow_python/tensorflow_python-c5xz2rsh.html 說明
https://www.w3cschool.cn/tensorflow_python/tensorflow_python-ats62pzl.html 源碼
如果想在keras中使用ssim爲損失函數,可以自己根據自己要求單獨寫,損失函數寫法可以參考我的另一篇博客,鏈接如下:
https://blog.csdn.net/weixin_43718675/article/details/89041352
我嘗試了兩種寫法,一種是使用skimage庫,一種是使用tensorflow
先查看keras中損失函數的輸入數據type和維度:
https://keras.io/zh/losses/
https://github.com/keras-team/keras/blob/master/keras/losses.py
如:
def mean_squared_error(y_true, y_pred):
return K.mean(K.square(y_pred - y_true), axis=-1)
這裏面默認的輸入和輸出都是 Tensor
因此自定義損失函數時候,需要保證輸入輸出都是張量
4.1 使用skimag庫
def my_ssim_loss(y_true,y_pred):
from keras import backend as K
import tensorflow as tf
print(type(y_pred))
print('y_shape:',y_true.shape)
with tf.Session():
y_pred=y_pred.eval()
y_true=y_true.eval()
# y_true=np.array(y_true)
# y_pred=np.array(y_pred)
print(type(y_pred))
print('y_shape:',y_true.shape)
shape=y_true.shape
if len(shape)==5:
batch=shape[0]
set_length=shape[1]
total_loss=np.zeros((batch,set_length,400,400))
for i in range(batch):
for j in range(set_length):
ssim_loss=1-compare_ssim(np.array(y_true[i,j]),np.array(y_pred[i,j]),multichannel=True)
total_loss[i,j,:,:] +=ssim_loss
# loss.append(ssim_loss)
# loss=np.array(loss).reshape(batch,set_lenth)
total_loss=tf.convert_to_tensor(total_loss)
print(type(total_loss))
print('total_loss_shape:',total_loss.shape)
return total_loss
由於使用了ConvLSTM2D, 因此輸入y爲5D的,使用MSE爲loss時候,損失爲4D的,爲了與mse_loss保持一致,這裏使用擴充,具體過程見以上代碼。雖然在類型和維度上保持了一致,可是在調用時,還是出錯了。
原因不太清楚。
4.2 使用tf.image.ssim
def tf_ssim_loss(y_true,y_pred):
import keras.backend as k
# total_loss=1-tf.image.ssim_multiscale(y_true,y_pred,max_val=1)
total_loss=1-tf.image.ssim(y_true,y_pred,max_val=1)
with tf.Session():
total_loss=1-total_loss.eval()
batch=total_loss.shape[0]
set_length=total_loss.shape[1]
loss=np.zeros((batch,set_length,400,400))
for i in range(batch):
for j in range(set_length):
loss[i,j]=total_loss[i,j]
loss=tf.convert_to_tensor(loss)
return loss
同樣爲了保持維度,還是出錯。
後來,我不人爲進行維度設置,而是使用tf默認的輸出loss維度,結果倒還正確了
可能上述出錯的原因在於:傳入的y_true等張量雖然是5D的,但是其每個維度的大小是佔位符placeholder,無法實體化,即無法進行類似batch=total_loss.shape[0]這樣的獲取維度具體信息的操作。
def tf_ssim_loss(y_true,y_pred):
total_loss=1-tf.image.ssim(y_true,y_pred,max_val=1)
return total_loss
model.compile(optimizer=opt,loss=tf_ssim_loss,metrics=['accuracy'])
這裏的total_loss的維度爲2D,即[batch, set_length ]而上述使用MSE時候,loss維度爲4D,即[batch, set_length, width,high]。
以上!