跳至主要內容

Image processing

LPrincess大约 18 分钟image

基于opencv课程设计,分两个部分,一个是根据要求将图像素材进行基础的函数处理,第二部分开发综合图像处理软件

第一部分 图像基础操作

1.1 返回图中的饮料瓶个数,选出容量少的那一瓶饮料,用绿色填充液体部分

(1)思路

操作一:返回饮料瓶个数

  • 加载灰度图像,并图像二值化处理,将非黑色像素设置成白色,能够很好的排除瓶子液体部分的干扰
  • 使用cv2.filter2D 和一个锐化核对图像进行锐化处理
  • 提取边缘:使用 cv2.Canny 提取图像的边缘
  • 寻找轮廓:使用 cv2.findContours 寻找边缘图像中的轮廓
  • 轮廓数组的长度就是拼字的个数

提取的边缘图像:

操作二:选出容量少的那一瓶饮料,用绿色填充液体部分

  • 图像预处理:将灰色的点变成白色,其余的点变成黑色 -> gray_image
  • 平滑图像:对gray先进行开运算再形态学闭运算
  • 寻找轮廓
  • 遍历轮廓,找到所有轮廓中最小矩形的最小h值以及对应的轮廓
  • 绘制绿色液体:利用cv2.cvtColor创建一个bgr图像,遍历最小h的轮廓内的所有像素值,除了黑色和白色部分,根据像素值调整设置不同的绿色

(2)实现

图1 提取边缘图像
图1 提取边缘图像
图2 gray_image
图2 gray_image
图3 result_image
图3 result_image

1.2 感兴趣区域提取与图像集校正 * 2

(1)思路:

  • 提取ROI:从提取从 (x, y) 开始,宽 w 高 h 的子图像
  • 定义透视变换的四个点
  • 对 ROI 进行透视变换并应用图像增强(锐化和高斯模糊)。
  • 返回增强后的图像和原始 ROI。

(2)实现

图4提取roi区域
图4提取roi区域
图5 结果图
图5 结果图
图6 roi图
图6 roi图
图7 结果图
图7 结果图

1.3 计算火柴数目1

(1)思路:

  • 复制原图img_caculator,遍历img_caculator像素进行自定义二值化处理,遍历每一个像素灰度值小于160的像素值设置为0
  • 边缘检测:使用Canny边缘检测算法提取边缘
  • 寻找轮廓,轮廓数组长度 = 火柴根的数量

(2) 实现

图8 img_caculator
图8 img_caculator
图9 边缘图像
图9 边缘图像

1.4 将花瓣作为前景提取出来

(1)思路:

  • 将图像从 BGR 颜色空间转换为 HSV 颜色空间
  • 定义花瓣颜色的 HSV 范围
  • 根据定义的花瓣 HSV 范围,生成掩码图像 mask。
  • 创建一个 5x5 的矩形结构元素 kernel,用于形态学操作。
  • 对掩码图像进行闭运算,填充小孔
  • 对掩码图像进行开运算,去除噪点。
  • 使用按位与操作从原始图像中提取花瓣区域。

(2) 实现

图10 mask
图10 mask
图11 提取花瓣
图11 提取花瓣

1.5 匹配模板

(1)思路:

  • 读取模板图和待匹配图像,获取模板图宽高
  • 创建模板的二值化掩膜,对模板图像 temp 进行二值化处理,生成掩膜 mask
  • 图像 img 中使用模板 temp 进行模板匹配,并使用掩膜 mask
  • 找到模板匹配结果中最小值和最大值及其对应的位置
  • 计算模板匹配区域的右下角坐标
  • 绘制匹配区域:在图像 img 上绘制一个矩形框,以标记匹配区域
图12 结果图
图12 结果图

1.6 提取出橙色的区域

(1)思路:

  • 将图像从 BGR 颜色空间转换为 HSV 颜色空间
  • 定义橙色的 HSV 颜色范围
  • 根据定义的橙色 HSV 范围,生成掩码图像 mask
  • 创建一个 10x10 的矩形结构元素 kernel,用于形态学操作。
  • 对掩码图像进行闭运算,填充小孔。
  • 对掩码图像进行开运算,去除噪点。
  • 在掩码图像中查找轮廓
  • 计算每个轮廓的面积,并找到面积最小的轮廓
  • 在新的黑色图像 img_new 上绘制面积最小的轮廓。
  • 使用按位与操作从原始图像中提取橙色区域
图14 提取橙色区域
图14 提取橙色区域

1.7 提取圆形区域

(1)思路:

  • 将图像 img2 转换为灰度图,并进行高斯模糊和二值化处理
  • 使用形态学操作去除噪点,创建一个椭圆形的结构元素,进行开运算,去除小的噪点
  • 通过膨胀操作确定背景区域,并进行边缘检测,膨胀操作扩大背景区域,使用 Canny 算法检测开运算后的图像边缘
  • 查找图像中的轮廓并绘制符合条件的轮廓,计算每个轮廓的最小包围圆和圆心:如果轮廓面积大于 4000/ 如果圆半径大于 5 且中心不在特定位置

(2)实现

图15 binary_image
图15 binary_image
图16 对比图
图16 对比图

1.8 图像分割2

(1)思路:

  • 复制一个图像
  • 应用卷积锐化滤波器[[-1,-1,-1], [-1,9,-1], [-1,-1,-1]]增强图像边缘
  • 使用 Canny 算法检测图像边缘,找到图像中的外部轮廓
  • 在图像上绘制检测到的轮廓
图17 对比图
图17 对比图

1.9 为花着色 + 为图上色

(1) 思路:

  • 将 BGR 颜色空间的图像转换为 HSV 颜色空间
  • 定义蓝色的 HSV 范围
  • 生成一个掩码,掩码中蓝色范围内的像素值为 255,其余为 0
  • (图)调整掩码中蓝色区域的 HSV 值,设置色调为 120(蓝色),饱和度为 150,并增加明度 50。保证像素值不超过 255。
  • (花)调整掩码中蓝色和绿色区域的 HSV 值
    • 设置蓝色区域的色调为 120(蓝色),饱和度为 150,并增加明度 50。
    • 设置绿色区域的色调为 60(绿色),饱和度为 150,并增加明度 50。
    • 保证像素值不超过 255。
  • 将 HSV 颜色空间的图像转换回 BGR 颜色空间
图18 花
图18 花
图19 图
图19 图

1.10 阈值分隔,并统计图中有多少颗药片

(1)思路:

  • 创建一个与输入图像大小相同的高斯低通滤波器,用于在频域内平滑图像
  • 将图像从空间域转换到频域,以便进行频域滤波
  • 中心化频谱,使得低频分量集中在频谱中心,这有助于后续滤波操作
  • 在频域中进行滤波,保留低频成分,抑制高频成分,从而平滑图像
  • 将频谱中心化的低频成分移回原位,以便进行逆傅里叶变换
  • 将图像从频域转换回空间域
  • 得到滤波后的图像,并转换为适合显示和处理的图像格式
  • 将图像中灰度值大于160的像素设置为255(白色),小于130的像素设置为0(黑色),其余像素也设置为255(白色),以提取出药片
  • 使用中值滤波去除图像中的噪点,平滑图像
  • 使用 Canny 边缘检测算法提取图像中的边缘
  • 找到图像中的药片轮廓
  • 遍历轮廓,计算每个轮廓的面积,并在原图上绘制轮廓,以蓝色显示
图20 阈值分割图像
图20 阈值分割图像
图21 结果图
图21 结果图

1.11 找出火柴中黑色的头

(1)思路:

  • 创建一个与输入图像大小相同的高斯低通滤波器,用于在频域内平滑图像
  • 计算图像的二维快速傅里叶变换(FFT),将图像从空间域转换到频域,以便进行频域滤波
  • 将频谱的低频成分移动到中心,中心化频谱,使得低频分量集中在频谱中心,这有助于后续滤波操作
  • 应用高斯低通滤波器,在频域中进行滤波,保留低频成分,抑制高频成分,从而平滑图像
  • 将频谱的低频成分移回原位,以便进行逆傅里叶变换
  • 计算图像的二维逆快速傅里叶变换(IFFT),将图像从频域转换回空间域
  • 取傅里叶变换结果的绝对值并转换为无符号8位整数类型,得到滤波后的图像,并转换为适合显示和处理的图像格式
  • 对滤波后的图像进行二值化处理,将图像中灰度值大于160的像素设置为255(白色),小于50的像素保留原图值,其余像素也设置为255(白色),以提取出火柴根
  • 对图像进行中值滤波,去除图像中的噪点,平滑图像
  • 使用 Canny 边缘检测算法提取图像中的边缘,检测图像中的边缘,以便在后续步骤中查找轮廓
  • 查找图像中的轮廓:找到图像中的火柴根轮廓,并输出检测到的火柴根数量
  • 遍历并绘制检测到的轮廓:遍历轮廓,计算每个轮廓的面积。面积小于30的轮廓忽略。绘制轮廓在原图上,以蓝色显示

(2)实现

图22 经过滤波处理后图像
图22 经过滤波处理后图像
图23 结果图
图23 结果图

1.12 指纹增强,闭合断开地方

(1)思路:

  • 对灰度图像进行直方图均衡化,增强图像对比度,使得图像的灰度级分布更加均匀,从而提高细节的可见性
  • 对图像进行高斯模糊,减少图像中的噪声,平滑图像
  • 对图像进行自适应阈值二值化,使得对象与背景区分开来
  • 对二值图像进行形态学闭运算,闭合图像中的小孔或细缝,填补对象内部的空洞
  • 使用 Canny 边缘检测算法提取图像中的边缘,检测图像中的边缘,使得对象的轮廓更加明显
  • 对边缘图像进行膨胀操作,使边缘变粗,进一步闭合对象边缘的细缝
  • 合并边缘图像和闭合图像,将边缘信息添加到闭合图像中,增强对象的轮廓
图24 equalized_image
图24 equalized_image
图25 close_image
图25 close_image
图26 out最终图
图26 out最终图

1.13 手写体识别0-9

(1)思路:

  • 使用嵌套循环读取文件夹中的图像文件,将每个图像的像素值存储在数组 a 中。
  • 初始化特征数组 feature,大小为 (100, 48, 48),其中每个特征值大小为原图像的 1/5。
  • 遍历每个图像,计算每个特征块(5x5)中白色像素(值为255)的数量,并存储在 feature 数组中
  • 将 feature 转换为二维数组 train,每行表示一个样本的特征向量。
  • 为每个样本设置标签,每个数字 0-9 各有 10 个样本。
  • 读取待识别图像,并计算其特征值。
  • 创建 KNN 模型并训练。
  • 预测待识别图像的类别。

图27-1 识别0!

图27-2识别1

图27-3识别2
图27-3识别2
图27-4识别3
图27-4识别3
图27-5识别4
图27-5识别4
图27-5识别5
图27-5识别5
图27-5识别6
图27-5识别6
图27-5识别7
图27-5识别7
图27-5识别8
图27-5识别8
图27-5识别9
图27-5识别9

第二部分 图像综合实践

2.1 应用简介

已实现功能:

  • 功能1:加载图片 —— 文件
  • 功能2:将图片向右边旋转 —— 文件
  • 功能3:将图片向左边旋转 —— 文件
  • 功能4:将图片灰度化 —— 灰度变换
  • 功能5: tool bar:文件,灰度变换,噪声与滤波,二维码
  • 功能6:裁剪功能(tool bar) 动态调整并生成新图显示在window框 —— 几何变换(文件)
  • 功能7:查看灰度直方图/彩色直方图/直方图均衡 —— 灰度变换
  • 功能8:增强对比度(tool bar)动态显示在图像框(只有线性) —— 灰度变换
  • 功能9:保存当前图像 —— 文件
  • 功能10:高斯噪声(tool bar)动态生成新图并显示在图像框 —— 噪声
  • 功能11:椒盐噪声(tool bar)动态生成新图并显示在图像框 —— 噪声
  • 功能12:高斯滤波(tool bar) 滑动条动态显示图像狂 —— 滤波

功能——二维码:

  1. 二维码识别(并能够框出已识别的)
  2. 二维码生成

2.2 核心方法及其参数说明

(1) 上传图片功能

  • 使用filedialog.askopenfilename()打开一个文件对话框,让用户选择要上传的图像文件,并返回所选文件的路径。

(2)按照合适比例显示在显示框中:

  • 将图像从BGR颜色空间转换为RGB颜色空间,OpenCV默认读取的图像是BGR格式,而PIL使用的是RGB格式。
  • 将NumPy数组转换为PIL图像对象,这样可以在Tkinter中使用。
  • 清除Canvas中的所有内容,为显示新图像腾出空间
  • 获取Canvas和图像的宽高比例因子,选择最小的比例因子以保持图像的纵横比进行缩放。
  • 根据计算的比例因子调整图像的大小,使用Lanczos重采样算法进行高质量的缩放。
  • 将调整大小后的PIL图像对象转换为Tkinter可用的图像对象。
  • 在Canvas中显示图像,使图像居中显示。
  • 保存图像对象到showpicture.image属性,以防止图像被垃圾回收机制回收。
  • 更新显示文件名的标签,使用os.path.basename(filename)获取文件的基本名称(不包括路径)。

(3) 向左向右旋转

  • 使用NumPy的np.rot90()函数将图像数组顺时针旋转90度。np.rot90()函数默认情况下是逆时针旋转90度,需要调用三次或指定参数k=-1来实现顺时针旋转。

(4) 灰度化图片

  • 使用OpenCV的cvtColor函数将彩色图像转换为灰度图像
  • 将转换后的灰度图像(NumPy数组)转换为PIL图像对象,以便在Tkinter中使用。

(5) 动态裁剪图片

更新裁剪预览功能:

  • 从滑动条获取 x1、y1、x2 和 y2 的值,并进行合法性检查(例如:确保 x1 小于 x2,y1 小于 y2,且坐标在图像范围内)。
  • 根据指定的坐标裁剪图像。
  • 显示图像 引用裁剪功能:
  • 从滑动条获取 x1、y1、x2 和 y2 的值,并进行合法性检查。
  • 如果坐标合法,则裁剪图像,并将裁剪后的图像赋值给全局变量 img。
  • 销毁裁剪窗口。
  • 调用 update_main_window 函数更新主窗口中的图像显示。 更新主窗口功能(最终确定按钮时):
  • 保存图像对象到 showpicture.image 属性,以防止图像被垃圾回收机制回收。 裁剪窗口的创建于设置:
  • 创建一个新的 Toplevel 窗口,用于裁剪操作。
  • 设置裁剪窗口的标题和最小尺寸。
  • 创建四个滑动条,用于输入裁剪的坐标 x1、y1、x2 和 y2。
  • 每个滑动条都有一个对应的标签,并设置 command 参数为 update_crop,以便在滑动条值变化时实时更新裁剪预览。
  • x2 和 y2 滑动条的初始值设置为图像的宽度和高度。
  • 创建一个确认按钮,点击后调用 apply_crop 函数应用裁剪并更新主窗口中的图像显示。
  • 设置裁剪窗口的关闭事件处理,确保关闭窗口时正确销毁窗口对象。

(6) 查看直方图

彩色:

  • 使用 cv2.split 函数将彩色图像分为蓝色、绿色和红色三个通道。
  • 使用 cv2.calcHist 函数分别计算每个通道的直方图。 • [b]、[g]、[r]:输入的单通道图像。 • [0]:计算第0个通道的直方图。 • None:不使用掩码。 • [256]:直方图的bin数。 • [0, 256]:像素值的范围。
  • 使用 plt.subplots 创建一个包含三个子图的图形,并设置大小。
  • 分别绘制红色、绿色和蓝色通道的直方图。每个子图设置标题、X轴标签和Y轴标签。
  • 使用 plt.tight_layout 自动调整子图布局,使其不重叠。
  • 使用 plt.show 显示图形。

灰度图像直方图

  • 使用 cv2.calcHist 函数计算灰度图像的直方图。
  • 使用 plt.plot 绘制灰度图像的直方图。设置颜色为灰色。
  • 设置图形的标题、X轴标签和Y轴标签。
  • 使用 plt.show 显示图形。

(7) 保存图像

  • filename = filedialog.asksaveasfilename():弹出一个“保存文件”对话框,让用户选择保存文件的位置和文件名。filename 会保存用户选择的文件路径。如果用户取消对话框,filename 将为空。

(8) 直方图均质化

  • img_grey = cv2.equalizeHist(img):对灰度图像进行直方图均衡化处理。

(9) 动态调节对比度,亮度

  • a = alpha_slider.get() 和 b = beta_slider.get():获取滑动条的当前值。
  • contrast_img = cv2.convertScaleAbs(img, alpha=a, beta=b):使用 OpenCV 的 convertScaleAbs 函数调整图像的对比度和亮度。
  • 显示图像

创建对比度,亮度窗口:

  • contrast_window = Toplevel(window):创建一个新的顶级窗口,用于对比度和亮度调整。
  • contrast_window.title('Contrast'):设置窗口标题为“Contrast”。
  • contrast_window.minsize(300, 150):设置窗口的最小尺寸为 300x150 像素。
  • 创建和配置 alpha 滑动条:
    • alpha_label = Label(contrast_window, text="Alpha:"):创建一个标签,用于显示 "Alpha:"。
    • alpha_label.grid(row=0, column=0):将标签放置在窗口的第 0 行第 0 列。
    • alpha_slider = Scale(contrast_window, from_=0.1, to=3.0, resolution=0.1, orient=HORIZONTAL, command=update_contrast):创建一个水平滑动条,用于调整对比度,范围从 0.1 到 3.0,步长为 0.1,滑动条的值变化时调用 update_contrast 函数。
    • alpha_slider.set(1.0):设置滑动条的默认值为 1.0。
    • alpha_slider.grid(row=0, column=1):将滑动条放置在窗口的第 0 行第 1 列。
  • beta同alpha
  • update_contrast():在窗口初始化时调用 update_contrast 函数,显示初始图像。

(10) 动态添加高斯噪声 / 调节高斯滤波

高斯噪声:

  • mean = mean_slider.get() 和 sigma = sigma_slider.get():获取滑动条的当前值,分别用于设置高斯噪声的均值和标准差。
  • gauss = np.random.normal(mean, sigma, img.shape[:2]):生成与图像大小相同的高斯噪声矩阵。
  • noisy_img = np.clip(img + gauss, a_min=0, a_max=255).astype(np.uint8):将生成的高斯噪声添加到图像中,并将结果限制在 0 到 255 范围内,然后转换为 8 位无符号整数类型。
  • 显示图像

高斯滤波:

  • 从滑块获取均值(sigma)和核大小(ksize)的值。
  • 核大小必须为奇数,如果滑块返回的值为偶数,则将其加1。
  • 使用OpenCV的GaussianBlur函数对图像应用高斯模糊。
  • 显示图像

(11) 动态添加椒盐噪声

  • salt = salt_slider.get() / 100.0 和 pepper = pepper_slider.get() / 100.0:从滑动条中获取盐和胡椒噪声的比例,并将其从百分比转换为小数形式。
  • h, w = img.shape[:2]:获取图像的高度和宽度。
  • img_sp = np.copy(img):创建图像的副本,以便在其上添加噪声。

添加盐噪声:

  • num_salt = np.ceil(salt * h * w):计算需要添加的盐噪声像素数量。
  • coords = [np.random.randint(0, i - 1, int(num_salt)) for i in img.shape]:生成随机坐标,用于确定噪声位置。
  • img_sp[coords[0], coords[1], :] = 255:将指定位置的像素值设置为 255(白色),即盐噪声。

添加胡椒噪声:

  • num_pepper = np.ceil(pepper * h * w):计算需要添加的胡椒噪声像素数量。
  • coords = [np.random.randint(0, i - 1, int(num_pepper)) for i in img.shape]:生成随机坐标,用于确定噪声位置。
  • img_sp[coords[0], coords[1], :] = 0:将指定位置的像素值设置为 0(黑色),即胡椒噪声。
  • 显示图像

(12) 二维码——二维码生成与解码

生成二维码:

  • data = qr_entry.get():从输入框 qr_entry 获取用户输入的内容。
  • qr = qrcode.QRCode(...):创建一个二维码对象,配置参数如下: • version=1:设置二维码版本为1,版本从1到40,数字越大,二维码尺寸越大,能存储的数据越多。 • error_correction=qrcode.constants.ERROR_CORRECT_L:设置错误纠正等级为L(最低级别),能纠正约7%的错误。 • box_size=10:设置二维码每个模块的大小为10像素。 • border=4:设置二维码边框的宽度为4个模块。
  • qr.add_data(data):向二维码对象添加用户输入的数据。
  • qr.make(fit=True):生成二维码图案,fit=True表示根据数据量调整二维码大小。
  • qr_img = qr.make_image(fill='black', back_color='white'):生成二维码图像,黑色填充模块,白色背景。
  • qr_img.show():在默认图像查看器中显示生成的二维码。
  • qr_window.destroy():关闭输入窗口。

二维码解码:

  • 创建一个OpenCV的二维码检测器对象detector。
  • 使用detectAndDecode方法检测图像中的二维码,返回解码数据data、顶点坐标数组vertices_array以及二值化的二维码图像binary_qrcode。
  • 使用PyZbar库的decode方法检测图像中的二维码,返回所有检测到的二维码列表QRCodes。
  • 遍历每个检测到的二维码,并打印其信息。
  • 将二维码的数据解码为字符串形式并打印。
  • 获取二维码的顶点坐标数组并转换为NumPy数组,用于绘制边框。
  • 使用OpenCV的polylines方法在图像上绘制二维码的边框,颜色为绿色,线宽为5。
  • 获取二维码的矩形坐标并在图像上绘制解码后的字符串数据。
  • 显示图像
上次编辑于:
贡献者: L-mj0