PSNR/MSE 两张图片的计算
PSNR/MSE calculation for two images
我想编写一个函数来获取两个图像参考和编码,并评估每个分量(R、G、B、Y、Cb、Cr)的 (R)MSE 和 PSNR。为此,我提取所有组件,然后转换 RGB -> YCbCr。我想在不使用内置函数的情况下计算 (R)MSE 和 PSNR。
import os, sys, subprocess, csv, datetime
from PIL import Image
############ Functions Definitions ############
# Extracts the values of the R, G, B components of each pixel in the input file and calculates the Y, Cb, Cr components returning a dictionary having a key tuple with the coordinates of
the pixes and values the values of each R, G, B, Y, Cb, Cr components
def rgb_calc(ref_file):
img = Image.open(ref_file)
width, height = img.size
print(width)
print(height)
rgb_dict = {}
for x in range (width):
for y in range(height):
r, g, b = img.load()[x, y]
lum = 0.299 * r + 0.587 * g + 0.114 * b
cb = 128 - 0.168736 * r - 0.331264 * g + 0.5 * b
cr = 128 + 0.5 * r - 0.418688 * g - 0.081312 * b
print("X {} Y {} R {} G {} B {} Y {} Cb {} Cr {}".format(x, y, r, g, b, lum, cb, cr))
rgb_dict[(x, y)] = (r, g, b, lum, cb, cr)
return rgb_dict
############ MAIN FUNCTION ############
r_img = sys.argv[1]
p_img = sys.argv[2]
ref_img = Image.open(r_img)
proc_img = Image.open(p_img)
resolution_ref = ref_img.size
resolution_proc = proc_img.size
if resolution_ref == resolution_proc:
ycbcr_ref = rgb_calc(r_img)
ycbcr_proc = rgb_calc(proc_img)
else:
exit(0)
我想编写一个新函数并最终输出每个分量的平均 PSNR 和整个图像的平均值。
有没有办法加快我的进程?
目前,img.load()
每张 8Mpx 图像大约需要 10-11 秒,创建字典需要 6 秒。因此仅提取这些值并创建两个字典就已经花费了 32 秒。
首先,在循环外做img.load()
!
def rgb_calc(ref_file):
img = Image.open(ref_file)
width, height = img.size
print(width)
print(height)
rgb_dict = {}
rgb = img.load()
for x in range(width):
for y in range(height):
r, g, b = rgb[x, y]
lum = 0.299 * r + 0.587 * g + 0.114 * b
cb = 128 - 0.168736 * r - 0.331264 * g + 0.5 * b
cr = 128 + 0.5 * r - 0.418688 * g - 0.081312 * b
rgb_dict[(x, y)] = (r, g, b, lum, cb, cr)
return rgb_dict
但这仅仅是个开始。接下来我要做的(但我不是专家!)是使用一个 numpy 数组而不是一个由 (x, y) 索引的字典。
编辑
我尝试使用 numpy ndarray(N 维数组)来加快速度,但被卡住了,所以问了一个具体的问题,并得到了解决答案(15 倍加速!):
根据您的需要进行调整,并修复了原始代码的一些细节:
import numpy as np
from PIL import Image
def get_rgbycbcr(img: Image.Image):
R, G, B = np.array(img).transpose(2, 0, 1)[:3] # ignore alpha if present
Y = 0.299 * R + 0.587 * G + 0.114 * B
Cb = 128 - 0.168736 * R - 0.331264 * G + 0.5 * B
Cr = 128 + 0.5 * R - 0.418688 * G - 0.081312 * B
return np.array([R, G, B, Y, Cb, Cr], dtype=float).transpose(2, 1, 0)
r_img = sys.argv[1]
p_img = sys.argv[2]
ref_img = Image.open(r_img)
proc_img = Image.open(p_img)
resolution_ref = ref_img.size
resolution_proc = proc_img.size
if resolution_ref == resolution_proc:
ycbcr_ref = get_ycbcr(ref_img)
ycbcr_proc = get_ycbcr(proc_img)
else:
exit(0)
你现在剩下的是一个形状为 (width, height, 6)
的 numpy 数组。我不认为你需要那里的原始 RGB 数据(你可以随时从图像中获取它)——你可以更改代码,将 6 减少到 3,以防万一。您可以索引,例如 ycbcr_ref
,如下所示:ycbcr_ref[x, y]
并获得长度为 6 的列表,其中包含与存储在字典中的元组中相同的数据。但是你可以提取切片,特别是沿着这个长度为 6 的“轴”(numpy 术语),并对它们进行操作,比如
y_mean = ycbcr_ref[:, :, 3].mean()
绝对值得学习如何使用numpy!
我将在一个细节上为您提供帮助:除非您另有说明,否则 numpy 会首先存储变化索引(AKA 轴)最慢的数据,最后存储变化最快的数据。由于图像是按行存储的,除非您执行 transpose()
,否则读入 numpy 的图像必须像 arr[y, x]
一样被索引。转置将洗牌轴。在您的情况下,您有 3 个轴编号为 0、1、2。例如,.transpose(1, 0, 2)
将交换 x 和 y,而 .transpose(2, 0, 1)
将使像素通道成为“外部”(变化最慢的)索引。
我想编写一个函数来获取两个图像参考和编码,并评估每个分量(R、G、B、Y、Cb、Cr)的 (R)MSE 和 PSNR。为此,我提取所有组件,然后转换 RGB -> YCbCr。我想在不使用内置函数的情况下计算 (R)MSE 和 PSNR。
import os, sys, subprocess, csv, datetime
from PIL import Image
############ Functions Definitions ############
# Extracts the values of the R, G, B components of each pixel in the input file and calculates the Y, Cb, Cr components returning a dictionary having a key tuple with the coordinates of
the pixes and values the values of each R, G, B, Y, Cb, Cr components
def rgb_calc(ref_file):
img = Image.open(ref_file)
width, height = img.size
print(width)
print(height)
rgb_dict = {}
for x in range (width):
for y in range(height):
r, g, b = img.load()[x, y]
lum = 0.299 * r + 0.587 * g + 0.114 * b
cb = 128 - 0.168736 * r - 0.331264 * g + 0.5 * b
cr = 128 + 0.5 * r - 0.418688 * g - 0.081312 * b
print("X {} Y {} R {} G {} B {} Y {} Cb {} Cr {}".format(x, y, r, g, b, lum, cb, cr))
rgb_dict[(x, y)] = (r, g, b, lum, cb, cr)
return rgb_dict
############ MAIN FUNCTION ############
r_img = sys.argv[1]
p_img = sys.argv[2]
ref_img = Image.open(r_img)
proc_img = Image.open(p_img)
resolution_ref = ref_img.size
resolution_proc = proc_img.size
if resolution_ref == resolution_proc:
ycbcr_ref = rgb_calc(r_img)
ycbcr_proc = rgb_calc(proc_img)
else:
exit(0)
我想编写一个新函数并最终输出每个分量的平均 PSNR 和整个图像的平均值。
有没有办法加快我的进程?
目前,img.load()
每张 8Mpx 图像大约需要 10-11 秒,创建字典需要 6 秒。因此仅提取这些值并创建两个字典就已经花费了 32 秒。
首先,在循环外做img.load()
!
def rgb_calc(ref_file):
img = Image.open(ref_file)
width, height = img.size
print(width)
print(height)
rgb_dict = {}
rgb = img.load()
for x in range(width):
for y in range(height):
r, g, b = rgb[x, y]
lum = 0.299 * r + 0.587 * g + 0.114 * b
cb = 128 - 0.168736 * r - 0.331264 * g + 0.5 * b
cr = 128 + 0.5 * r - 0.418688 * g - 0.081312 * b
rgb_dict[(x, y)] = (r, g, b, lum, cb, cr)
return rgb_dict
但这仅仅是个开始。接下来我要做的(但我不是专家!)是使用一个 numpy 数组而不是一个由 (x, y) 索引的字典。
编辑
我尝试使用 numpy ndarray(N 维数组)来加快速度,但被卡住了,所以问了一个具体的问题,并得到了解决答案(15 倍加速!):
根据您的需要进行调整,并修复了原始代码的一些细节:
import numpy as np
from PIL import Image
def get_rgbycbcr(img: Image.Image):
R, G, B = np.array(img).transpose(2, 0, 1)[:3] # ignore alpha if present
Y = 0.299 * R + 0.587 * G + 0.114 * B
Cb = 128 - 0.168736 * R - 0.331264 * G + 0.5 * B
Cr = 128 + 0.5 * R - 0.418688 * G - 0.081312 * B
return np.array([R, G, B, Y, Cb, Cr], dtype=float).transpose(2, 1, 0)
r_img = sys.argv[1]
p_img = sys.argv[2]
ref_img = Image.open(r_img)
proc_img = Image.open(p_img)
resolution_ref = ref_img.size
resolution_proc = proc_img.size
if resolution_ref == resolution_proc:
ycbcr_ref = get_ycbcr(ref_img)
ycbcr_proc = get_ycbcr(proc_img)
else:
exit(0)
你现在剩下的是一个形状为 (width, height, 6)
的 numpy 数组。我不认为你需要那里的原始 RGB 数据(你可以随时从图像中获取它)——你可以更改代码,将 6 减少到 3,以防万一。您可以索引,例如 ycbcr_ref
,如下所示:ycbcr_ref[x, y]
并获得长度为 6 的列表,其中包含与存储在字典中的元组中相同的数据。但是你可以提取切片,特别是沿着这个长度为 6 的“轴”(numpy 术语),并对它们进行操作,比如
y_mean = ycbcr_ref[:, :, 3].mean()
绝对值得学习如何使用numpy!
我将在一个细节上为您提供帮助:除非您另有说明,否则 numpy 会首先存储变化索引(AKA 轴)最慢的数据,最后存储变化最快的数据。由于图像是按行存储的,除非您执行 transpose()
,否则读入 numpy 的图像必须像 arr[y, x]
一样被索引。转置将洗牌轴。在您的情况下,您有 3 个轴编号为 0、1、2。例如,.transpose(1, 0, 2)
将交换 x 和 y,而 .transpose(2, 0, 1)
将使像素通道成为“外部”(变化最慢的)索引。