面对不同的渲染算法,比如:PT、BDPT、PM、SPPM、Original MLT、PSSMLT、MMLT等等,对比不同渲染算法效果的常用方式是:相同时间下,渲染图片的质量(或者,渲染相同质量图片所需的时间)。
这里涉及到一个关键问题:怎么评估渲染图片的质量?
一般是,计算渲染图片和参考图片(标准图片)的MSE(均方误差)。
参考图片的选择要特别注意!!!
参考图片的选择要特别注意!!!
参考图片的选择要特别注意!!!
(小编正是在因为选错了参考图片然后被折磨得死去活来之后,才决定写这篇博文的)
举例说明。
假设我们要用MMLT渲染这么一个场景:
https://benedikt-bitterli.me/resources/
小编下载这个场景模型的原因是:其提供了参考图片(如果是本地自己用path tracing渲染一张参考图片的话,至少得好几十个小时吧)
具体怎么用PBRT-V3渲染这个场景,不是本篇文章的内容。此处默认已经完成渲染得到了一张渲染图片。
现在,是要根据渲染图片和参考图片的MSE判断渲染图片的质量。
下载的场景模型文件夹中包含两张参考图片,分别是:
TungstenRender.exr
TungstenRender.png
在不调节场景的几何参数时,对应的参考图片应该是来自TungstenRender.exr。
小编用的MSE程序是基于网络上找的一段python代码改的。
python貌似不能直接读取.exr文件,另外考虑到这个场景的描述文件中设置的渲染图片输出格式为.png,所以,我们将.exr格式的参考图片转换为.png格式。
Mac中的Preview软件是可以读取.exr格式的,而且可以直接将其转换为.png格式。但是,不要这么做,不要这么做,不要这么做!!!!!
这样操作得到的png参考图片和PBRT-V3产生的渲染图片是“不匹配”的,所以会导致MSE计算错误。
小编转换参考图片的具体步骤如下(敲黑板,划重点):
1,shift+cmd+4在屏幕上截一张640x360的图(考虑到:参考图片是1280x720分辨率,另外“截图”刚好是png格式),然后用Preview打开截图;
2,用Preview打开原.exr格式的参考图片,然后复制参考图片(ctrl+a,ctrl+c);
3,将1280x720参考图片粘贴到640x360的“截图”上(参考图片会自动缩小,然后调整位置使得参考图片刚好填在“截图”上)
4,Tools–>Adjust Size,将Resolution后面的数字由144改成72(此时“截图”图片即为1280x720分辨率png格式的参考图片了)
得到正确的参考图片之后,就可用如下代码计算MSE了。
(再次声明这段程序是基于网路上的python代码改的,原出处找不到了,抱歉)
import cv2
def clmap(v, k, upBound): # mul and clamp
val = v * k
if val > upBound:
return upBound
else:
return val
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_13_0_1280,720_m100_985s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_9_0_1280,720_670_9_7510s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_5_0_1280,720_750_9_7807s.png' #
inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_11_0_1280,720_800_9_7744s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_13_1_1280,720_m60_901s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_13_1_1280,720_m70_1191s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_5_1_1280,720_500_9_7898s_zlb3.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_9_1_1280,720_310_9_6843s_again,depth+2.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_9_2_1280,720_270_9_6645s_again,depth+2.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_9_2_1280,720_350_9_8416s_again,depth+2.png' #
inImage_2 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/living-room/living-room_17_18_1_reference.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-10_again,0_1280,720_m100_1446s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-10_again,0_1280,720_m140_2179s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-10_again,0_1280,720_m280_3805s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-10_again,1_1280,720_m50_1176s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-10_again,1_1280,720_m55_1290s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-10_again,1_1280,720_m60_1410s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-10_again,1_1280,720_m65_1520s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-10_again,1_1280,720_m70_1597s.png' #
#inImage_1 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-10_again,1_1280,720_m100_2321s.png' #
#inImage_2 = '/Users/libingzeng/CG/pbrt-v3/buildX/Debug/bathroom2/bathroom2-9_reference.png' #
dif_img = 'dif_' + inImage_1
img_1 = cv2.imread(inImage_1) # read as color image
img_2 = cv2.imread(inImage_2)
dif = img_1.copy()
show_dif = dif.copy() # dif image for show only
width = img_1.shape[0] # get width
height = img_1.shape[1] # get height
sum = 0
for i in range(width):
for j in range(height):
# dif[i, j] = [128,0,0] # b g r
# print img_1[i,j]-img_2[i,j]
#diff0 = float(img_1[i, j][0]) - float(img_2[i, j][0])
#diff1 = float(img_1[i, j][1]) - float(img_2[i, j][1])
#diff2 = float(img_1[i, j][2]) - float(img_2[i, j][2])
#diff = (abs(diff0) + abs(diff1) + abs(diff2)) / 3.0
y1 = img_1[i, j][2] * 0.212671 + img_1[i, j][1] * 0.715160 + img_1[i, j][0] * 0.072169
y2 = img_2[i, j][2] * 0.212671 + img_2[i, j][1] * 0.715160 + img_2[i, j][0] * 0.072169
diff = y1 - y2
diff0 = diff
sum = sum + diff ** 2
if diff0 < 0:
show_dif[i, j] = [0, clmap(abs(diff0), 10, 255), 0]
elif diff0 > 0:
show_dif[i, j] = [0, 0, clmap(abs(diff0), 10, 255)]
else:
show_dif[i, j] = [0]
dif[i, j] = abs(diff0)
mse = sum / (width * height)
print 'MSE: ', mse
print dif_img
print 'different data:'
print 'max : ', dif.max()
print 'min : ', dif.min()
print 'mean : ', dif.mean()
cv2.imwrite(dif_img, show_dif)
cv2.imshow('_dif', show_dif)
cv2.waitKey(0)
cv2.destroyAllWindows()
参考图片:
渲染图片:
从前面程序中可以看到,可视化的是第0通道(blue)的差异。
注意:
1,程序中默认通道次序是blue、green、red(和常见的rgb次序不一样)
2,一般求三个通道的MSE的方式是:分别求每个通道的MSE,然后平均。小编的求法是:对单像素点的三个通道的差异的绝对值求平均值,然后对这个平均值平方求和,最后再平均