简介
镜头阴影(Lens Shading)是摄影和成像过程中常见的问题,特别是在使用广角镜头时。镜头阴影会导致图像从中心到边缘的亮度逐渐减弱,影响图像的均匀性。为了解决这个问题,我们可以使用基于 Lagrange 插值的镜头阴影校正(Lens Shading Correction, LSC)技术。本文将详细介绍如何使用 Python 和 OpenCV 实现这一过程。
步骤解析
1. 读取图像
首先,我们需要读取待处理的图像。这里我们使用 OpenCV 读取图像,并进行简单的错误处理,以确保文件路径正确。
import cv2
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import lagrange
filePath = 'images/lsc.bmp'
num_rings = 16
# 读取图像
image = cv2.imread(filePath)
if image is None:
raise FileNotFoundError(f"未找到图像文件: {filePath}")
2. 计算环形增益
为了校正镜头阴影,我们需要计算图像从中心到边缘的环形平均值。我们将图像分成若干同心环,并计算每个环内的平均像素值。然后,对这些平均值进行归一化处理,以确保增益值从中心向外平滑过渡.
def calculate_lsc_gain_circular(image, num_rings):
height, width, _ = image.shape
center_x, center_y = width // 2, height // 2
max_radius = np.sqrt(center_x**2 + center_y**2)
# 分离颜色通道
image_r = image[:, :, 2] # OpenCV 读取图像是 BGR 顺序,所以红色通道在最后
image_g = image[:, :, 1]
image_b = image[:, :, 0]
# 初始化存储环形平均值的数组
ring_means_r = np.zeros(num_rings)
ring_means_g = np.zeros(num_rings)
ring_means_b = np.zeros(num_rings)
# 计算每个环的平均值
y, x = np.ogrid[:height, :width]
distance_from_center = np.sqrt((x - center_x)**2 + (y - center_y)**2)
for r in range(num_rings):
inner_radius = r * max_radius / num_rings
outer_radius = (r + 1) * max_radius / num_rings
mask = (distance_from_center >= inner_radius) & (distance_from_center < outer_radius)
ring_means_r[r] = np.mean(image_r[mask])
ring_means_g[r] = np.mean(image_g[mask])
ring_means_b[r] = np.mean(image_b[mask])
# 计算增益
ring_means_r = ring_means_r[0] / ring_means_r
ring_means_g = ring_means_g[0] / ring_means_g
ring_means_b = ring_means_b[0] / ring_means_b
return ring_means_r, ring_means_g, ring_means_b, center_x, center_y, max_radius / num_rings
3. Lagrange 插值
我们使用 Lagrange 插值多项式来平滑计算从中心到边缘的增益变化。这可以确保校正后的图像亮度过渡自然
def lagrange_interpolate(x, poly):
return poly(x)
4. 应用 LSC 校正
我们将计算得到的增益应用于原始图像。对于每个像素,我们根据其到图像中心的距离,使用预计算的 Lagrange 插值多项式计算增益值,并对像素进行校正。
def apply_lsc_correction_circular(image, ring_means_r, ring_means_g, ring_means_b, center_x, center_y, radius_step):
height, width, _ = image.shape
max_radius = radius_step * len(ring_means_r)
corrected_image = np.zeros_like(image, dtype=np.float32)
# 生成半径数组
radius_array = np.linspace(0, max_radius, len(ring_means_r))
# 预计算多项式
poly_r = lagrange(radius_array, ring_means_r)
poly_g = lagrange(radius_array, ring_means_g)
poly_b = lagrange(radius_array, ring_means_b)
# 打印多项式系数用于调试
print("Lagrange Polynomial Coefficients for R Channel:", poly_r)
print("Lagrange Polynomial Coefficients for G Channel:", poly_g)
print("Lagrange Polynomial Coefficients for B Channel:", poly_b)
# 计算所有像素到中心的距离
y, x = np.indices((height, width))
distance_from_center = np.sqrt((x - center_x)**2 + (y - center_y)**2)
# 确保半径不超过最大半径
distance_from_center = np.clip(distance_from_center, 0, max_radius)
# 使用预计算的多项式进行插值
rGain = lagrange_interpolate(distance_from_center, poly_r)
gGain = lagrange_interpolate(distance_from_center, poly_g)
bGain = lagrange_interpolate(distance_from_center, poly_b)
# 应用增益校正
corrected_image[:, :, 2] = image[:, :, 2] * rGain
corrected_image[:, :, 1] = image[:, :, 1] * gGain
corrected_image[:, :, 0] = image[:, :, 0] * bGain
return np.clip(corrected_image, 0, 255).astype(np.uint8)
完整代码
import cv2
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import lagrange
def calculate_lsc_gain_circular(image, num_rings):
height, width, _ = image.shape
center_x, center_y = width // 2, height // 2
max_radius = np.sqrt(center_x ** 2 + center_y ** 2)
# 分离颜色通道
image_r = image[:, :, 2] # OpenCV 读取图像是 BGR 顺序,所以红色通道在最后
image_g = image[:, :, 1]
image_b = image[:, :, 0]
# 初始化存储环形平均值的数组
ring_means_r = np.zeros(num_rings)
ring_means_g = np.zeros(num_rings)
ring_means_b = np.zeros(num_rings)
# 计算每个环的平均值
y, x = np.ogrid[:height, :width]
distance_from_center = np.sqrt((x - center_x) ** 2 + (y - center_y) ** 2)
for r in range(num_rings):
inner_radius = r * max_radius / num_rings
outer_radius = (r + 1) * max_radius / num_rings
mask = (distance_from_center >= inner_radius) & (distance_from_center < outer_radius)
ring_means_r[r] = np.mean(image_r[mask])
ring_means_g[r] = np.mean(image_g[mask])
ring_means_b[r] = np.mean(image_b[mask])
# 计算增益
ring_means_r = ring_means_r[0] / ring_means_r
ring_means_g = ring_means_g[0] / ring_means_g
ring_means_b = ring_means_b[0] / ring_means_b
return ring_means_r, ring_means_g, ring_means_b, center_x, center_y, max_radius / num_rings
def lagrange_interpolate(x, poly):
return poly(x)
def apply_lsc_correction_circular(image, ring_means_r, ring_means_g, ring_means_b, center_x, center_y, radius_step):
height, width, _ = image.shape
max_radius = radius_step * len(ring_means_r)
corrected_image = np.zeros_like(image, dtype=np.float32)
# 生成半径数组
radius_array = np.linspace(0, max_radius, len(ring_means_r))
# 预计算多项式
poly_r = lagrange(radius_array, ring_means_r)
poly_g = lagrange(radius_array, ring_means_g)
poly_b = lagrange(radius_array, ring_means_b)
# 打印多项式系数用于调试
print("Lagrange Polynomial Coefficients for R Channel:", poly_r)
print("Lagrange Polynomial Coefficients for G Channel:", poly_g)
print("Lagrange Polynomial Coefficients for B Channel:", poly_b)
# 计算所有像素到中心的距离
y, x = np.indices((height, width))
distance_from_center = np.sqrt((x - center_x) ** 2 + (y - center_y) ** 2)
# 确保半径不超过最大半径
distance_from_center = np.clip(distance_from_center, 0, max_radius)
# 使用预计算的多项式进行插值
rGain = lagrange_interpolate(distance_from_center, poly_r)
gGain = lagrange_interpolate(distance_from_center, poly_g)
bGain = lagrange_interpolate(distance_from_center, poly_b)
# 应用增益校正
corrected_image[:, :, 2] = image[:, :, 2] * rGain
corrected_image[:, :, 1] = image[:, :, 1] * gGain
corrected_image[:, :, 0] = image[:, :, 0] * bGain
return np.clip(corrected_image, 0, 255).astype(np.uint8)
if __name__ == "__main__":
filePath = '../images/LSC.png'
num_rings = 16
# 读取图像
image = cv2.imread(filePath)
if image is None:
raise FileNotFoundError(f"未找到图像文件: {filePath}")
# 计算 LSC 增益
ring_means_r, ring_means_g, ring_means_b, center_x, center_y, radius_step = calculate_lsc_gain_circular(image,
num_rings)
# 打印每个环的平均值用于调试
print("Normalized Ring Means R:", ring_means_r)
print("Normalized Ring Means G:", ring_means_g)
print("Normalized Ring Means B:", ring_means_b)
# 应用 LSC 校正
corrected_image = apply_lsc_correction_circular(image, ring_means_r, ring_means_g, ring_means_b, center_x, center_y,
radius_step)
# 显示图像
plt.figure(figsize=(12, 6))
plt.subplot(121)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('原始图像')
plt.subplot(122)
plt.imshow(cv2.cvtColor(corrected_image, cv2.COLOR_BGR2RGB))
plt.title('校正后图像')
plt.show()
总结
在镜头阴影校正中,我们选择使用 Lagrange 插值是因为它能够在已知点之间提供高精度的平滑过渡,适合处理图像中的亮度渐变问题。然而,Lagrange 插值在处理大量插值点时可能会出现精度下降的情况,因此在实际应用中需要根据具体需求选择合适的插值算法。