二、灰度变换
2.2 线性灰度变换
2.2.1灰度变换函数的选择
①根据问题,可以直接选择灰度变换函数 g(x,y) = T[f(x,y)]。
f (x,y)和g (x,y)分别是在任意点(x,y)处输入图像和输出图像的灰度值。
②对比度、灰度动态范围的扩展和压缩操作。
当区间内曲线斜率大于1时进行扩展(对比度拉伸、增大对比度):
斜率大于1意味着输出灰度值的增长速度快于输入灰度值的增长速度,这会导致灰度动态范围的扩展,即原本相近的灰度值在输出时会被拉开差距,从而增强了图像的对比度。此时,则应对该区间内的灰度值动态范围进行扩展(即对比度拉伸,目的是增大对比度)
当区间内曲线斜率小于1时进行压缩(对比度收缩、对比度降低):
斜率小于1意味着输出灰度值的增长速度慢于输入灰度值的增长速度,这会导致灰度动态范围的压缩,即原本差距较大的灰度值在输出时会被拉近,从而降低了图像的对比度。此时,则对该区间内的灰度值动态范围进行压缩(即对比度收缩,目的是降低对比度)
2.2.2线性灰度变换的一般表达式
g(x,y) =a*f(x,y)+b
2.2.3具有饱和处理的线性灰度变换
、 分别表示输入图像灰度值有效动态范围的下限值和上限值,、分别表示输出图像期望动态范围的下限值和上限值
图片来源于山东建筑大学计算机科学与技术学院宁阳老师。
根据图像可以明显的看出具有饱和处理的线性灰度变换会在变换过程中对超出范围的像素值进行饱和处理,即将超出最大值的像素值设置为最大值,将低于最小值的像素值设置为最小值。
为了避免极端灰度值对图像处理的影响,一个更好的策略是采用百分位数来确定和 这两个基准值。具体来说,我们可以选择一个灰度值作为 ,使得图像中仅有a%的像素灰度值小于或等于它,再选择一个灰度值作为 ,使得图像中仅有b%的像素灰度值大于或等于它。这样的选择方式能够有效地减小极端灰度值对处理结果的干扰。(a,b根据具体情况进行选择)
当输出图像的最小灰度值与最大灰度值之间的差值大于输入图像的基准灰度值与之间的差值时(函数斜率大于1),该操作被称为图像动态范围扩展,即对比度增大。相反,如果和之间的差值小于或等于和之间的差值(函数斜率小于1),则该操作被称为图像动态范围压缩,即对比度减小。
import matplotlib.pyplot as plt
import numpy as np
from skimage import io, exposure
# 定义一个函数,用于绘制图像的灰度直方图
def plot_gray_histogram(image, rows, cols, index):
plt.subplot(rows, cols, index) # 创建一个子图
histogram, bins, patch = plt.hist(image.ravel(), 256, histtype='bar', density=True) # 绘制直方图
plt.xlabel('灰度值') # 设置x轴标签
plt.ylabel('像素百分比') # 设置y轴标签
plt.axis([0, 255, 0, np.max(histogram)]) # 设置坐标轴范围
# 读取灰度图像
original_image = io.imread('saved_image.jpg') # 明确指定以灰度模式读入图像
def contrast_stretching(image, in_range=None):
if in_range is None:
in_range = 'image'
return exposure.rescale_intensity(image, in_range=in_range)
# 使用默认方法进行对比度拉伸
rescaled_image_minmax = contrast_stretching(original_image)
# 根据百分位数进行对比度拉伸
percentile_3, percentile_97 = np.percentile(original_image, (3, 97))
rescaled_image_percentile = contrast_stretching(original_image, in_range=(percentile_3, percentile_97))
# 显示结果
plt.figure(figsize=(18, 10)) # 创建一个新的图形,并设置更大的大小以增强显示效果
plt.gray() # 设置颜色映射为灰度
# 显示原始图像
plt.subplot(2, 3, 1) # 创建一个子图
plt.imshow(original_image, vmin=0, vmax=255) # 显示图像
plt.title('Original Image: slpn'); # 设置更具描述性的标题
plt.axis('off') # 关闭坐标轴
# 显示对比度拉伸后的图像(最小/最大)
plt.subplot(2, 3, 2) # 创建一个子图
plt.imshow(rescaled_image_minmax, vmin=0, vmax=255) # 显示图像
plt.title('Contrast Stretching (Min/Max)'); # 设置标题
plt.axis('off') # 关闭坐标轴
# 显示对比度拉伸后的图像(百分位数)
plt.subplot(2, 3, 3) # 创建一个子图
plt.imshow(rescaled_image_percentile, vmin=0, vmax=255) # 显示图像
plt.title('Contrast Stretching (Percentile)'); # 设置标题
plt.axis('off') # 关闭坐标轴
# 绘制灰度直方图
plot_gray_histogram(original_image, 2, 3, 4) # 绘制原始图像的灰度直方图
plot_gray_histogram(rescaled_image_minmax, 2, 3, 5) # 绘制对比度拉伸后图像的灰度直方图(最小/最大)
plot_gray_histogram(rescaled_image_percentile, 2, 3, 6) # 绘制对比度拉伸后图像的灰度直方图(百分位数)
plt.tight_layout() # 调整子图参数, 使之填充整个图像区域
plt.show() # 显示所有图形
2.2.3分段线性灰度变换
即在不同灰度的区间,对图像采用不同的灰度函数进行变换,从而实现更好的图像灰度处理
图片来源于山东建筑大学计算机科学与技术学院宁阳老师。
import numpy as np
import matplotlib.pyplot as plt
from skimage import io
def piecewise_linear_transform(image, x1, y1, x2, y2, x3, y3):
"""
应用分段线性灰度变换来增强图像的对比度。
参数:
image : 输入图像数组。
(x1, y1), (x2, y2), (x3, y3) : 控制点的输入和输出灰度值。
返回:
变换后的图像数组和查找表(LUT)。
"""
# 将图像转换为浮点数类型以便计算
image_float = image.astype(float)
# 初始化输出图像和查找表
output_image = np.zeros_like(image_float)
lut = np.zeros(256, dtype=np.uint8)
# 计算每段的斜率和截距
slope1 = (y2 - y1) / (x2 - x1)
intercept1 = y1 - slope1 * x1
slope2 = (y3 - y2) / (x3 - x2)
intercept2 = y2 - slope2 * x2
# 应用分段线性变换
mask1 = image_float <= x1
mask2 = (image_float > x1) & (image_float < x2)
mask3 = (image_float >= x2) & (image_float < x3)
mask4 = image_float >= x3
output_image[mask1] = y1
output_image[mask2] = slope1 * image_float[mask2] + intercept1
output_image[mask3] = slope2 * image_float[mask3] + intercept2
output_image[mask4] = y3
# 填充查找表
for i in range(256):
if i <= x1:
lut[i] = y1
elif x1 < i < x2:
lut[i] = int(slope1 * i + intercept1)
elif x2 <= i < x3:
lut[i] = int(slope2 * i + intercept2)
else:
lut[i] = y3
# 确保输出图像在0到255的范围内
output_image = np.clip(output_image, 0, 255)
# 转换为整数类型
output_image = output_image.astype(np.uint8)
return output_image, lut
# 读入一幅灰度图像
input_image = io.imread('saved_image.jpg')
# 确定控制点
control_points = [(100, 10), (180, 220), (230, 250)]
# 调用自定义分段线性灰度变换函数
transformed_image, lut = piecewise_linear_transform(input_image, *control_points[0], *control_points[1],
*control_points[2])
# 显示结果
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(input_image, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(transformed_image, cmap='gray')
plt.title('Transformed Image')
plt.axis('off')
plt.tight_layout()
plt.show()
2.3.4连续单独或同时调整图像的亮度和对比度
以下代码基于opencv实现了对图像亮度和对比度的连续调整
import cv2
import numpy as np
# 读取图像
image = cv2.imread('slpn.jpg')
# 调整亮度:增加或减少每个像素的值
# 增加亮度
bright_image = cv2.convertScaleAbs(image, alpha=1, beta=30)
# 调整对比度:改变alpha(对比度控制)的值
# 增加对比度
contrast_image = cv2.convertScaleAbs(image, alpha=1.5, beta=0)
# 缩小图像尺寸
scale_percent = 40 # 缩小到原来的40%
width = int(image.shape[1] * scale_percent / 100)
height = int(image.shape[0] * scale_percent / 100)
dim = (width, height)
resized_image = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
resized_bright_image = cv2.resize(bright_image, dim, interpolation=cv2.INTER_AREA)
resized_contrast_image = cv2.resize(contrast_image, dim, interpolation=cv2.INTER_AREA)
# 水平堆叠图像
stacked_image = np.hstack((resized_image, resized_bright_image, resized_contrast_image))
# 显示堆叠后的图像
cv2.imshow('Stacked Images', stacked_image)
# 等待键盘输入
cv2.waitKey(0)
cv2.destroyAllWindows()
# 调整亮度:增加或减少每个像素的值
# 增加亮度
bright_image = cv2.convertScaleAbs(image, alpha=1, beta=30)
alpha
: 对比度控制参数。设置为1,表示不改变对比度。
beta
: 亮度控制参数。这里设置为30,表示将每个像素的值增加30,从而增加图像的亮度。# 调整对比度:改变alpha(对比度控制)的值
# 增加对比度
contrast_image = cv2.convertScaleAbs(image, alpha=1.5, beta=0)
alpha
: 对比度控制参数。这里设置为1.5,表示增加对比度。
beta
: 亮度控制参数。这里设置为0,表示不改变亮度。
注,本人为在校学生,博客是边学边写的,主要是为了巩固知识,如有错误请积极指正。
“本文章中所使用的圣灵谱尼图片来源于网络,版权归属原作者所有。若您认为本文章/作品的使用侵犯了您的权益,请及时与我联系,我将尽快核实并删除相关内容。
本文的内容主要基于我对张运楚教授编著的《数字图像处理》一书的学习和理解。这本书深入浅出地介绍了数字图像处理的基本理论以及经典算法等,并且提供了丰富的示例代码和实际用例,极大地帮助了我学习图像处理知识。在此,我推荐大家阅读这本书,更加深入的学习有关图像处理的知识。