图像处理技术研究与实现——python+opencv
背景介绍
21世纪是一个充满信息的时代,图像作为人类感知世界的视觉基础,是人类获取信息、表达信息和传递信息的重要手段。处理图像,可以帮助我们更好地获取信息,也可以使我们更客观、准确地认识世界。
图像处理作为计算机视觉领域的重要分支,在各个行业中扮演着越来越重要的角色。从医疗诊断、自动驾驶、安防监控到人工智能领域的图像识别,图像处理无处不在。
OpenCV
- OpenCV是一个开源的计算机视觉库,它包含了丰富的图像处理和计算机视觉功能,支持多种操作系统(Linux、Windows、Mac OS)
- OpenCV用C++语言编写,它的主要接口也是C++语言。该库也有大量的Python, Java and MATLAB的接口
- 具有通用的图象/视频载入、保存和获取模块
- 具有基本的数字图象处理能力,如可进行滤波、边缘检测、角点检测、采样与差值、色彩转换、形态操作、直方图和图象金字塔等操作
本文所有代码都是基于Pyhon的OpenCV库,conda install opencv
有时候安装opencv得使用conda install -c conda-forge opencv 这个命令
不然可能会因为和python版本不兼容问题导致导入不了cv2
图像色彩转换
灰度图像生成
- 计算机中,图像由一个个像素组成
- 彩色图像有R,G,B三个颜色通道叠加而成
- 灰度图像只有一个颜色通道,显示范围从纯黑到纯白,0-255(8位灰度图,28)
如图所示,这是两张10×10像素的图

图像灰度化就是使彩色图像的R、G、B三个分量相等的过程,opencv内置了转换函数
# 将彩色图像转为灰度图
import cv2
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
image=cv2.imread('lena.png')
gray_image=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# OpenCV读取的图像默认是BGR格式(蓝、绿、红)
# 而matplotlib的imshow方法预期的是RGB格式
image_rgb=image[:,:,::-1]
fig, axs = plt.subplots(1, 2, figsize=(10, 5))
# axs[0].imshow(cv2.cvtColor(image,cv2.COLOR_BGR2RGB)) #所以这里需要将BGR转为RGB格式
axs[0].imshow(image_rgb) #这样也能转换
axs[0].set_title('Original Image')
axs[0].axis('off')
axs[1].imshow(gray_image,cmap='gray') # 灰度图需要指定颜色映射
axs[1].set_title('Grayscale Image')
axs[1].axis('off')
plt.tight_layout() #自动调整子图间距
plt.show()


图像二值化
图像二值化即将图像转换为只有黑白两种颜色,设定一个阈值,灰度值超过这个阈值的转换为白色,低于阈值的转为黑色
#生成黑白颜色图
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams["font.sans-serif"]=["SimHei"]
mpl.rcParams["axes.unicode_minus"]=False
image=cv2.imread("lena_gray.png")
"""固定阈值二值化"""
_, binary = cv2.threshold(image, 125, 255, cv2.THRESH_BINARY)
thresh=125
image[image>thresh]=255
image[image<=thresh]=0
plt.figure(figsize=(10, 5))
# 原始灰度图
plt.subplot(1, 2, 1)
plt.imshow(image, cmap='gray')
plt.title('原始灰度图')
plt.axis('off')
plt.subplot(1,2,2)
plt.imshow(binary)
plt.title('黑白图')
plt.axis('off')
plt.tight_layout()
plt.show()


灰度图彩色化
如何从灰度图生成彩色图?需要自定义颜色映射关系
下面代码展示几种经典的颜色映射关系
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
mpl.rcParams["font.sans-serif"]=["SimHei"]
mpl.rcParams["axes.unicode_minus"]=False
# 读取灰度图像
gray_image = plt.imread('lena_gray_512.tif') #cv2.imread('lena.png', cv2.IMREAD_GRAYSCALE)
# 创建子图
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
# 原始灰度图像
axs[0,0].imshow(gray_image, cmap='gray')
axs[0,0].set_title('原始灰度图')
axs[0,0].axis('off')
# 应用不同伪彩色映射
axs[0,1].imshow(gray_image, cmap='jet') # 彩虹色映射
axs[0,1].set_title('Jet 彩虹色映射')
axs[0,1].axis('off')
axs[1,0].imshow(gray_image, cmap='viridis') # 黄绿色映射
axs[1,0].set_title('Viridis 黄绿色映射')
axs[1,0].axis('off')
axs[1,1].imshow(gray_image, cmap='hot') # 热力图映射
axs[1,1].set_title('Hot 热力图映射')
axs[1,1].axis('off')
plt.tight_layout()
# plt.savefig('pseudo_color_comparison.png', dpi=120)
plt.show()


还可以自定义随机颜色映射关系
#为灰度图重新生成颜色
import cv2
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
import numpy as np
# image=cv2.imread('lena_gray.png')
gray_image = plt.imread('lena_gray_512.tif')
# 确保图像数据在0-1范围内(matplotlib要求)
if gray_image.dtype == np.uint8:
gray_image = gray_image.astype(np.float32) / 255.0
elif np.max(gray_image) > 1.0:
gray_image = gray_image / np.max(gray_image)
# 方法1:生成随机颜色映射(每个灰度值对应随机颜色)
def create_random_colormap(num_colors=256):
"""创建随机颜色映射表"""
# 生成随机RGB颜色(0-1范围)
random_colors = np.random.rand(num_colors, 3)
# 确保黑色和白色固定
random_colors[0] = [0, 0, 0] # 黑色
random_colors[-1] = [1, 1, 1] # 白色
return ListedColormap(random_colors)
# 方法2:生成平滑过渡的随机颜色映射
def create_smooth_random_colormap(num_colors=256):
"""创建平滑过渡的随机颜色映射"""
# 生成关键颜色点
key_points = np.random.rand(5, 3) # 5个随机RGB颜色点
# 在关键点之间插值
colormap_values = np.zeros((num_colors, 3))
for i in range(3): # 对R,G,B通道分别处理
colormap_values[:, i] = np.interp(
np.linspace(0, 1, num_colors),
np.linspace(0, 1, len(key_points)),
key_points[:, i]
)
# 确保黑色和白色固定
colormap_values[0] = [0, 0, 0] # 黑色
colormap_values[-1] = [1, 1, 1] # 白色
return ListedColormap(colormap_values)
random_cmap=create_random_colormap()
smooth_cmap = create_smooth_random_colormap()
# 生成随机 RGB 图像
rgb_image = np.random.rand(256, 3)
fig, axs = plt.subplots(1, 2, figsize=(10, 5))
# axs[0].imshow(cv2.cvtColor(image,cv2.COLOR_BGR2RGB))
axs[0].imshow(gray_image,cmap='gray')
axs[0].set_title('Original Image')
axs[0].axis('off')
axs[1].imshow(gray_image,cmap=smooth_cmap) # 灰度图需要指定颜色映射
axs[1].set_title('随机颜色映射')
axs[1].axis('off')
plt.tight_layout() #自动调整子图间距
plt.show()


图像滤波
- 图像滤波是一种去除图像噪声和干扰的技术,也可用来平滑图像
- 常见的噪声有两种,椒盐噪声和高斯噪声 ,椒盐噪声通常由图像传感器、传输信道或解码环节产生,是一种随机出现的白点或黑点(最大最小值),高斯噪声是一种符合正态分布的随机噪声,通常由传感器在拍摄过程中因光照不足、温度变化或电子干扰引起,无法避免。
下面是两张分别具有高斯噪声和椒盐噪声的图片


高斯滤波
高斯滤波是最常用的一种滤波方式,适合处理高斯噪声,原理这里就不多讲了,感兴趣的可以去自行了解
# 高斯滤波
import cv2
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams["font.sans-serif"]=["SimHei"]
mpl.rcParams["axes.unicode_minus"]=False
# 读取图像
img = cv2.imread('gaussian_noisy.jpg')
# 均值滤波 会使图像模糊化
# blurred=cv2.blur(img,(5,5))
# 高斯滤波
blurred = cv2.GaussianBlur(img, (5, 5), 0)
# 显示原始图像和滤波后的图像
# cv2.imshow('Original Image', img)
# cv2.imshow('Blurred Image', blurred)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.title('原始图')
plt.axis('off')
plt.subplot(1,2,2)
plt.imshow(cv2.cvtColor(blurred,cv2.COLOR_BGR2RGB))
plt.title('滤波图')
plt.axis('off')
plt.tight_layout()
plt.show()


均值滤波
均值滤波是最简单的滤波方式,就是每个像素取周围像素的平均值,均值滤波可以使图像模糊化以便得到感兴趣物体的粗略描述。
# 均值滤波
import cv2
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams["font.sans-serif"]=["SimHei"]
mpl.rcParams["axes.unicode_minus"]=False
# 读取图像
img = cv2.imread('gaussian_noisy.jpg')
# 均值滤波 会使图像模糊化
blurred=cv2.blur(img,(5,5))
# 高斯滤波
# blurred = cv2.GaussianBlur(img, (5, 5), 0)
# 显示原始图像和滤波后的图像
# cv2.imshow('Original Image', img)
# cv2.imshow('Blurred Image', blurred)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.title('原始图')
plt.axis('off')
plt.subplot(1,2,2)
plt.imshow(cv2.cvtColor(blurred,cv2.COLOR_BGR2RGB))
plt.title('滤波图')
plt.axis('off')
plt.tight_layout()
plt.show()


中值滤波
- 中值滤波是取周围像素排序后的中间值
- 因为取中间值不受极值影响,中值滤波很适合去除椒盐噪声
import cv2
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams["font.sans-serif"]=["SimHei"]
mpl.rcParams["axes.unicode_minus"]=False
# 读取图像
img = cv2.imread('salt_pepper_noisy.jpg')
# 均值滤波 会使图像模糊化
# blurred=cv2.blur(img,(5,5))
# 高斯滤波
# blurred = cv2.GaussianBlur(img, (5, 5), 0)
# 中值滤波
blurred=cv2.medianBlur(img,5)
# cv2.imshow('Original Image', img)
# cv2.imshow('Blurred Image', blurred)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.title('原始图')
plt.axis('off')
plt.subplot(1,2,2)
plt.imshow(cv2.cvtColor(blurred,cv2.COLOR_BGR2RGB))
plt.title('滤波图')
plt.axis('off')
plt.tight_layout()
plt.show()


双边滤波
双边滤波是一种可以保持图像边缘清晰 的去噪方法,和高斯滤波类似,也是通过计算像素和领域像素的加权平均实现的,不同的是,双边滤波的权重矩阵权值由两部分组成,空间距离和色彩差异,距离越远,权重越小,色彩差别越大,权重越小。
# 双边滤波 保持边缘清晰的同时减少图片中的噪声
import cv2
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams["font.sans-serif"]=["SimHei"]
mpl.rcParams["axes.unicode_minus"]=False
# 读取图像
# img = cv2.imread('cat.4038.jpg')
img = cv2.imread('gaussian_noisy.jpg')
# 均值滤波 会使图像模糊化
# blurred=cv2.blur(img,(5,5))
# 高斯滤波
# blurred = cv2.GaussianBlur(img, (5, 5), 0)
# 中值滤波
# blurred=cv2.medianBlur(img,5)
blurred=cv2.bilateralFilter(img,9,75,75)
# cv2.imshow('Original Image', img)
# cv2.imshow('Blurred Image', blurred)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.title('原始图')
plt.axis('off')
plt.subplot(1,2,2)
plt.imshow(cv2.cvtColor(blurred,cv2.COLOR_BGR2RGB))
plt.title('滤波图')
plt.axis('off')
plt.tight_layout()
plt.show()


图像增强
非锐化掩蔽
非锐化掩蔽是图像锐化中最专业、最常用的方法之一,可拆解为三个步骤:
创建模糊版本:用高斯模糊创建原始图像的模糊版本
提取高频细节:原始图像减去模糊图像得到高频细节
加权混合:将原始图像与0.5倍的高频细节相加
import cv2
import numpy as np
from matplotlib import pyplot as plt
# 1. 读取彩色图像
image = cv2.imread('lena.png') # 读取BGR格式图像
# 2. 创建锐化卷积核(拉普拉斯增强核)
# sharpening_kernel = np.array([
# [0, -1, 0],
# [-1, 5, -1], # 中心值5 = 1(保留原像素) + 4(增强系数)
# [0, -1, 0]
# ])
# 3. 应用卷积(进行拉普拉斯锐化)
blurred = cv2.GaussianBlur(image, (0,0), 3)
sharpened_image = cv2.addWeighted(image, 1.5, blurred, -0.5, 0)
# sharpened_image = cv2.filter2D(image, -1, sharpening_kernel)
# 4. 显示结果
plt.figure(figsize=(10, 5))
# 原始图像(需转换为RGB显示)
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.axis('off')
# 锐化结果(需转换为RGB显示)
plt.subplot(1, 2, 2)
plt.title('Sharpened Image')
plt.imshow(cv2.cvtColor(sharpened_image, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.tight_layout()
plt.show()


限制对比度自适应直方图均衡化CLAHE
是传统直方图均衡化HE的改进版本,可拆解为四个步骤
- 局部处理:将输入图像划分为许多小矩形区域tiles
- 局部直方图均衡化:对每个独立的tile执行直方图均衡化
- 对比度限制:每个tile直方图设置裁剪限制度
- 插值:计算像素的最终灰度值
import cv2
import numpy as np
import matplotlib.pyplot as plt
def clahe_enhancement(img, clip_limit=2.0, grid_size=(8,8)):
if len(img.shape) == 2: #检测是否为灰度图
clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=grid_size)
return clahe.apply(img)
else:
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) #LAB颜色空间,亮度(L)颜色(A,B)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=grid_size)
l_clahe = clahe.apply(l) #只增强亮度通道 避免颜色失真
return cv2.cvtColor(cv2.merge([l_clahe, a, b]), cv2.COLOR_LAB2BGR)
image=cv2.imread('lena.png')
# # 对比度增强
# alpha=1.2
# enhanced_image=np.clip(alpha*image,0,255).astype(np.uint8)
# # 亮度调整
# beta=50
# adjusted_image=cv2.add(enhanced_image,beta)
# # 饱和度增强
# hsv_image=cv2.cvtColor(adjusted_image,cv2.COLOR_BGR2HSV)
# hsv_image[:,:,1]=1.5*hsv_image[:,:,1]
# saturated_image=cv2.cvtColor(hsv_image,cv2.COLOR_HSV2BGR)
enhanced = clahe_enhancement(image, clip_limit=2.0, grid_size=(8,8))
plt.figure(figsize=(10, 5))
plt.subplot(1,2,1)
plt.title('Original Image')
plt.imshow(cv2.cvtColor(image,cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.subplot(1,2,2)
plt.title('clahe Image')
plt.imshow(cv2.cvtColor(enhanced,cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.tight_layout()
plt.show()


总结
本文只探讨了图像处理常见的一些基本技术,对一些复杂的图像处理算法未做研究,望诸君见谅。
