0
点赞
收藏
分享

微信扫一扫

机器学习-09-图像处理02-PIL+numpy+OpenCV实践

总结

本系列是机器学习课程的系列课程,主要介绍机器学习中图像处理技术。

参考

【人工智能】Python+OpenCV图像处理(一篇全)

一文讲解方向梯度直方图(hog)

【杂谈】计算机视觉在人脸图像领域的十几个大的应用方向,你懂了几分?

开源地理空间基金会中文分会 Pillow (PIL Fork) 10.0.1 文档

python使用opencv对图像添加(高斯/椒盐/泊松/斑点)噪声

【OpenCV实战】简洁易懂的车牌号识别Python+OpenCV实现“超详解”(含代码)

本门课程的目标

完成一个特定行业的算法应用全过程:

懂业务+会选择合适的算法+数据处理+算法训练+算法调优+算法融合
+算法评估+持续调优+工程化接口实现

机器学习定义

图像数据

pip list

PIL模块

Image模块

PIL提供了大量常用的、基本的图像操作方法。本节将介绍几个图像处理中非常重要的模块。
(1)Image模块
Image模块中定义的Image类是PIL类库中最重要的类,实现了图像文件的读写、显示、颜色空间转换等方法。
Image类的open()方法接收图像文件路径为参数,用于读取图像文件。如果载入文件失败,则会引起一个IOError;载入成功则返回一个Image对象。例如,读取当前目录下文件名为“python.png”的图像:
在这里插入图片描述

#导入PIL类库中的Image模块
from PIL import Image
#加载图像数据到Image对象(Image对象的open()方法可以根据文件扩展名判断图像的格式)
im=Image.open('python.png')
# 利用Image对象的属性打印输出图像的类型、大小和模式:
print(im.format, im.size, im.mode)
# JPEG (256, 256) RGB
# PNG (369, 379) RGB

这里有三个属性,format识别图像的源格式,如果该文件不是从文件中读取的,则被置为 None 值;size是有两个元素的元组,其值为像素意义上的宽和高;mode表示颜色空间模式,定义了图像的类型和像素的位宽。
PIL支持如下模式:

读取的图像数据可以利用Image对象的show()方法进行显示:

im.show()
# 标准版本的show()方法不是很有效率,
# 因为Linux系统下,它先将图像保存为一个临时文件,然后使用xv进行显示。
# 如果没有安装xv,该函数甚至不能工作。Windows系统下该方法调用默认图片查看器打开图像。

输出为:
在这里插入图片描述

# 图像颜色空间转换可以使用convert()方法来实现,例如,将读取的im数据转换为灰度图像:
im_gray=im.convert('L')
im_gray.show()

输出为:
在这里插入图片描述

# 图像数据的保存使用save()方法:
#将im_gray保存为png图像文件,文件名为python_gray.png
im_gray.save('python_gray.png')
#Image对象的crop()方法可以从一副图像中裁剪指定的矩形区域,它接收包含四个元素的元组作为参数,
#各元素的值分别对应裁剪区域在原图像中的左上角和右下角位置的坐标,坐标系统的原点(0, 0)在图像的左上角:
#使用四元组(左,上,右,下)指定裁剪区域
box = (10, 10, 300, 300)
#裁剪图像区域
region = im.crop(box)
#保存裁剪的图像区域数据到图像文件
region.save('python_region.png')
region.show()

输出为:
在这里插入图片描述

#Image对象的transpose()方法通过传入参数Image.ROTATE_#(#代表旋转的角度,可以是90°、180°、270°)可以将图像数据进行旋转;
#也可以传入参数Image.FLIP_LEFT_RIGHT将图像做水平翻转,传入参数Image.FLIP_TOP_BOTTOM做垂直翻转等:
#将图像数据region旋转90°
region_90 = region.transpose(Image.ROTATE_90)
region_90.save('region_90.png')	#保存图像
region_90.show()

输出为:
在这里插入图片描述

# Image对象的rotate ()方法可以将图像数据进行任意角度的旋转:
#将图像数据region逆时针旋转30°
region_30 = region.rotate(30)
region_30.save('region_30.png')	#保存图像
region_30.show()

输出为:
在这里插入图片描述

需要注意的,粘贴的图像数据必须与粘贴区域具有相同的大小,但是,它们的颜色模式可以不同,
paste()方法在粘贴之前自动将粘贴的图像数据转换为与被粘贴图像相同的颜色模式。
读者可以通过将从灰度图像裁剪的区域粘贴在彩色图像,或者将从彩色图像裁剪的区域粘贴在灰度图像进行验证。

# Image对象的paste ()方法可以为图像对象在特定位置粘贴图像数据:
#创建图像对象im的拷贝
im_paste=im
#将region_90贴在图像对象im_paste中box对应的位置
im_paste.paste(region_90, box)
im_paste.show()	#显示图像
im_paste.save('python_region_90_paste.png')	#保存图像

输出为:
在这里插入图片描述

PIL可以对多波段图像的每个波段分别处理。
Image对象的getbands()方法可以获得每个波段的名称,split()方法将多波段图像分解为多个单波段图像,merge()方法可以按照用户指定的颜色模式和单波段图像数据的顺序,将它们组合成新的图像。

#需要注意的,粘贴的图像数据必须与粘贴区域具有相同的大小,
#但是,它们的颜色模式可以不同,
#paste()方法在粘贴之前自动将粘贴的图像数据转换为与被粘贴图像相同的颜色模式。
#读者可以通过将从灰度图像裁剪的区域粘贴在彩色图像,或者将从彩色图像裁剪的区域粘贴在灰度图像进行验证。
#PIL可以对多波段图像的每个波段分别处理。
#Image对象的getbands()方法可以获得每个波段的名称,
#split()方法将多波段图像分解为多个单波段图像,
#merge()方法可以按照用户指定的颜色模式和单波段图像数据的顺序,将它们组合成新的图像
#显示每个波段的名称
print(im.getbands())
# ('R', 'G', 'B')
#将RGB彩色图像对象im分解为三个单波段图像(红、绿、蓝)
r, g, b = im.split()
#显示每个波段图像
r.show(title='r')
# g.show(title='g')
# b.show(title='b')

输出为:
在这里插入图片描述

#按照RGB颜色模式,并将波段按蓝、绿、红顺序组合生成新的图像
im_bgr = Image.merge("RGB", (b, g, r))
im_bgr.show(title='BGR')

输出为:
在这里插入图片描述

#Image对象的point()方法用于对图像每个像素的值进行数值运算,由此可以完成图像反色、线性拉伸、归一化等。
from PIL import Image
im_gray=Image.open('python_gray.png')
#将图像数据im_gray进行反色处理
im_gray_inv = im_gray.point(lambda i: 255-i)
im_gray_inv.save('im_gray_inv.png') 	#保存图像
im_gray_inv.show()

输出为:
在这里插入图片描述

#图像的直方图用来表征该图像的像素值的分布情况。
#Image对象的histogram()方法将像素值的范围分成一定数目的小区间,统计落入每个小区间的像素值数目。
#以下代码可以生成一副图像的直方图:
from PIL import Image
from pylab import *
im=Image.open('lena.png')
im.show()

输出为:
在这里插入图片描述

imin = im.convert('L')
im_hist=imin.histogram()	#获得直方图数据
plot(im_hist)

输出为:
在这里插入图片描述

# 利用PIL模块,我们可以调用Image对象的resize()方法调整一副图像的大小,
#该方法接受一个表示新图像大小的元组为参数,返回原图像缩放后的拷贝:
im_half= im.resize((128, 128))	#将im调整为宽和高均为128像素
im_half.show()
# resize()方法中指定的图像宽度和高度也可以不一致,
#新图像的宽度或高度可以比原图像的大(对应图像放大)也可以比原图像的小(对应图像缩小)。
#给一张特别小的图像,当放大该图像时,通常会出现马赛克,我们希望把细节填充进来。
#resize()方法也可以指定两个参数:resize(size, filter) ,
#其中filter
#Image.Resampling.NEAREST (0), Image.Resampling.LANCZOS (1), Image.Resampling.BILINEAR (2), 
# Image.Resampling.BICUBIC (3), Image.Resampling.BOX (4) or Image.Resampling.HAMMING (5)。

输出为:
在这里插入图片描述

#将im调整为宽和高均为128像素
im_four= im_half.resize((1280, 1280),Image.Resampling.BILINEAR)	
im_four.show()

输出为:
在这里插入图片描述

ImageFilter模块

PIL提供的ImageFilter模块包含一组预先定义的滤波器,可以结合Image对象的filter()方法实现图像平滑和增强。
目前,该模块支持BLUR、CONTOUR、DETAIL、EDGE_ENHANCE、EDGE_ENHANCE_MORE、EMBOSS、FIND_EDGES、SMOOTH、SMOOTH_MORE、SHARPEN、GaussianBlur、RankFilter等。

from PIL import Image
from PIL import ImageFilter
boat = Image.open("lena.png")
boat_blur = boat.filter(ImageFilter.BLUR)		#模糊滤波
boat_blur.save('boat_blur.png')
boat_blur.show()

输出为:
在这里插入图片描述

boat_edge = boat.filter(ImageFilter.FIND_EDGES) 		#边缘检测
boat_edge.save('boat_edge.png')
boat_edge.show()

输出为:
在这里插入图片描述

boat_contour = boat.filter(ImageFilter.CONTOUR) 		#找轮廓
boat_contour.save('boat_contour.png')
boat_contour.show()

输出为:
在这里插入图片描述

boat_rank= boat.filter(ImageFilter.RankFilter(9,3))
boat_rank.save('boat_rank.png')		#每个像素取值为它的3*3邻域中第三大的像素值
boat_rank.show()

输出为:
在这里插入图片描述

ImageEnhance模块

PIL中更高级的图像增强可以借助ImageEnhance模块完成,例如,
ImageEnhance.Contrast()用于调整对比度,
ImageEnhance.Sharpness()用于图像锐化,
ImageEnhance.Color()用于图像颜色均衡,
ImageEnhance.Brightness()用于调整图像亮度等等。

from PIL import Image,ImageEnhance
im=Image.open('lena.png')
# ImageEnhance.Contrast()用于调整对比度
im_contrast = ImageEnhance.Contrast(im)
im_contrast.enhance(0.3).show()

输出为:
在这里插入图片描述

for i in [0.3,1.5,3]:
    temp=im_contrast.enhance(i)
    temp.save('boat_enhance'+str(i)+'.png')
    temp.show()

在这里插入图片描述

# ImageEnhance.Sharpness()用于图像锐化
im=Image.open('lena.png')
im_Sharpness = ImageEnhance.Sharpness(im)
im_Sharpness.enhance(0.3).show()

输出为:
在这里插入图片描述

Numpy图像数据分析示例

PIL提供了大量基本的图像处理模块和方法,但当我们需要完成一些高级图像处理任务,或者自定义一组图像处理流程时,还需要借助于其他模块。首先可以考虑的就是提供了向量、矩阵、方程组等操作方法的Numpy。
PIL模块读取的图像数据不能直接与整型、浮点型等数据类型进行运算,我们可以通过array()方法将图像数据转换成Numpy的数组对象,之后利用Numpy执行任意数学操作,完成一些复杂的图像处理流程。Numpy处理后的数据想要调用PIL提供的方法时,再利用Image对象的fromarray()方法创建图像实例。

from PIL import Image
from numpy import *
import numpy as np
boat=array(Image.open('lena.png'))
#对图像像素值进行二次多项式变换
boat_new=255.0*(boat/255.0)**2
#由boat_new创建图像实例
im_boat_new=Image.fromarray(np.uint8(boat_new))
#调研Image对象的show()方法显示图像
im_boat_new.show()

输出为:
在这里插入图片描述

import matplotlib.pylab as plt

image = plt.imread("lena.png")
plt.imshow(image)
# 依次表示数组的维度,形状,数组本身
print(image.ndim, image.shape, "\n",image[0:1])
plt.show()

输出为:
在这里插入图片描述

# 裁剪图像
# 加载图像后,我们可以对图像执行基本的裁剪操作。对于 NumPy,可以通过数组的切片操作来执行裁剪操作。
import matplotlib.pylab as plt
image = plt.imread("lena.png")
# 只是一个:表示这个维度保持不变,410: 表示从0~1280这个轴的index=410 处切一刀
crop_img = image[:, 410:, :]
print(image.shape, crop_img.shape)
plt.imshow(crop_img)
plt.show()

输出为:
在这里插入图片描述

# 分离颜色

# 因为我们知道每个图像都是由像素值组成的,这些像素值代表三个整数或小数,称为其颜色的 RGB 值。
# 为了将这些颜色的图像分开,我们需要拉出图像数组的正确切片。

import numpy as np
import matplotlib.pylab as plt

image = plt.imread("lena.png")
# 使用plot创建一个1行3列的尺寸为(12,8)的绘图区,相当每个是6英寸宽,4英寸高。
fig, axs = plt.subplots(nrows=1, ncols=3, figsize=(12, 8))
for c, ax in zip(range(3), axs):
    # 造一个与image数组一样结构,初始值为0的数组
    # 如果你图片数据值是0~255,类型设为unit8,如果是0~1,设置为float64
    rgb_img = np.zeros(image.shape, dtype="float64")
    # 读取image某一通道的值
    # RGB只拷贝C位,按循环依次取值为0,1,2
    rgb_img[:, :, c] = image[:, :, c]
    ax.imshow(rgb_img)
    ax.set_axis_off()
plt.show()

输出为:
在这里插入图片描述

# 转换

# 在此步骤中,我们将执行颜色转换。为此,我们可以将图像像素视为空间中的一个点。以这种方式处理图像像素使我们能够对色点进行转换。
# 旋转色点可以是上述语句的一个例子。
# 在这里,我们应用了 Numpy 的爱因斯坦符号函数,这是一种将旋转矩阵逐像素应用于图像的方法。
import numpy as np
import matplotlib.pylab as plt


def rotation_matrix(theta):
    return np.c_[
        [1, 0, 0],
        [0, np.cos(theta), -np.sin(theta)],
        [0, np.sin(theta), np.cos(theta)]
    ]


image = plt.imread("lena.png")
img_rot = np.einsum("ijk,lk->ijl", image, rotation_matrix(np.pi*0.5))
plt.imshow(img_rot)
plt.show()

输出为:
在这里插入图片描述

NumPy 中的 np.einsum 函数是一个强大的工具,它允许我们使用爱因斯坦求和约定来表达矩阵的乘法和其他数学操作。在 NumPy 中,爱因斯坦求和约定是通过简化多个索引的重复来简化张量的操作。
np.einsum 的调用 "ijk,lk->ijl" 中,我们有以下含义:

  • ijk 表示输入张量 image 的维度,其中 ijk 是索引,分别代表图像的高度、宽度和颜色通道数。
  • lk 表示第二个操作数 rotation_matrix(np.pi) 的维度,其中 lk 是索引,l 代表旋转矩阵的行数(这里是 3),k 代表旋转矩阵的列数(这里是 3)。
  • ijl 表示输出张量的维度,其中 ij 与输入图像的 ijk 相同,代表旋转后图像的高度和宽度,而 l 代表旋转矩阵的行数(这里是 3),因为输出图像的颜色通道数与输入图像相同。
    因此,np.einsum("ijk,lk->ijl", image, rotation_matrix(np.pi*2)) 这行代码的意义是将 image 中的每个像素(由 ijk 索引)与一个旋转矩阵(由 lk 索引)相乘,并生成一个新的图像,其中每个像素的颜色值是通过应用旋转矩阵得到的。这个操作在图像处理中称为像素级的矩阵乘法,它对应于图像的每个像素点在旋转后的空间位置上的颜色值。
    简而言之,这段代码将每个像素的颜色值乘以一个旋转矩阵,从而实现图像的旋转效果。
# 灰度转换

# 我们还可以使用 NumPy 将图像转换为灰度图像。通过取图像RGB值的加权平均值,我们可以执行此操作。
# 灰度值 = 0.2989 * R + 0.5870 * G + 0.1140 * B

import numpy as np
import matplotlib.pylab as plt
fig,ax = plt.subplots(1,2)

image = plt.imread("lena.png")
# plt.imshow(image)
rgb_weights = [0.1140, 0.5870,0.2989]
# image.shape # (759, 753, 3)
# 0.0876 0.22 0.10 = 
# print(image[0:10])
grayscale_image = np.dot(image[..., :3], rgb_weights)
print(grayscale_image.shape)
print(grayscale_image[0:10])

ax[0].imshow(grayscale_image,cmap='Greys')
ax[1].imshow(grayscale_image)
plt.show()

输出为:
在这里插入图片描述

修改下权重系数

# 灰度转换

# 我们还可以使用 NumPy 将图像转换为灰度图像。通过取图像RGB值的加权平均值,我们可以执行此操作。
# 灰度值 = 0.2989 * R + 0.5870 * G + 0.1140 * B

import numpy as np
import matplotlib.pylab as plt
fig,ax = plt.subplots(1,2)

image = plt.imread("lena.png")
# plt.imshow(image)
rgb_weights = [0.2989, 0.5870,0.1140]
# image.shape # (759, 753, 3)
# 0.0876 0.22 0.10 = 
# print(image[0:10])
grayscale_image = np.dot(image[..., :3], rgb_weights)
print(grayscale_image.shape)
print(grayscale_image[0:10])

ax[0].imshow(grayscale_image,cmap='Greys')
ax[1].imshow(grayscale_image)
plt.show()

输出为:

在这里插入图片描述

# 图像分割

# 这是我们分割图像不同区域的最常用的图像处理步骤之一。
#有多种方法可以做到这一点,例如前景和背景。
#例如,在本文中,我们将了解如何通过将图像转换为灰度并找到阈值来执行分割。
# 图像中高于阈值的像素位于一个区域中,而其他像素位于另一个区域中

import numpy as np
import matplotlib.pylab as plt

image = plt.imread("lena.png")
image = image*255


def simple_threshold(img, threshold=128):
    return ((img > threshold) * 255).astype("uint8")


thresholds = [100, 120, 128, 138, 150]

fig, axs = plt.subplots(nrows=1, ncols=len(thresholds), figsize=(20, 5))

rgb_weights = [0.2989, 0.5870, 0.1140]
gray_im = np.dot(image[..., :3], rgb_weights)

# gray_im = to_grayscale(image)

for t, ax in zip(thresholds, axs):
    ax.imshow(simple_threshold(gray_im, t), cmap='Greys')
    ax.set_title("Threshold: {}".format(t), fontsize=20)
    ax.set_axis_off()
plt.show()

输出为:
在这里插入图片描述

OpenCV

OpenCV 的全称是 Open Source Computer Vision Library, 是一个跨平台的计算机视觉库。OpenCV 是由英特尔公司发起并参与开发, 以 BSD 许可证授权发行, 可以在商业和研究领域中免费使用。 OpenCV 可用于开发实时的图像处理、 计算机视觉以及模式识别程序。 该程序库也可以使用英特尔公司的 IPP 进行加速处理。

# pip install opencv-python==4.9.0.80

一、图像处理基础知识

1、读取、显示、保存图像
img = cv2.imread(文件名,[,参数])

其中参数:
参数(1) cv2.IMREAD_UNCHANGED (图像不可变)
参数(2) cv2.IMREAD_GRAYSCALE (灰度图像)
参数(3) cv2.IMREAD_COLOR (读入彩色图像),这是默认参数,不填的话默认为这个
参数(4) cv2.COLOR_BGR2RGB (图像通道BGR转成RGB)

综合实例:

# 1)读取照片:
"""
OpenCV读取图像
"""
# 导包
import cv2
# 读取照片
img = cv2.imread("lena.png")  # 路径中不能有中文
# 显示图片
cv2.imshow("read_img", img)
# 输入毫秒值,传0就是无限等待
cv2.waitKey(3000)
# 释放内存,由于OpenCV底层是C++写的
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

# 3)OpenCV画图,对图片进行编辑
# OpenCV 的强大之处的一个体现就是其可以对图片进行任意编辑处理。下面的这个函数最后一个参数指定的就是画笔的大小。
"""
OpenCV画图,对图片进行编辑
"""
# 导包
import cv2
img = cv2.imread("lena.png")
# 左上角的坐标是(x,y),矩形的宽度和高度是(w,h)
x, y, w, h = 100, 100, 100, 100
# 绘制矩形
cv2.rectangle(img, (x, y, x + w, y + h), color=(255, 0, 0), thickness=2)
# 绘制圆
x, y, r = 200, 200, 100
cv2.circle(img, center=(x, y), radius=r, color=(255, 0, 0), thickness=2)
# 显示图片
cv2.imshow("rectangle_img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

2、获取图像属性

2.1形状- img.shape
通过shape关键字获取图像的形状,返回包含行数、列数、通道数的元祖。其中:
(1)灰度图像返回行数和列数;
(2)彩色图像返回行数、列数 和 通道数。

2.2像素数目- img.size
通过size关键字获取图像的像素数目,其中

(1)灰度图像返回 [行数 * 列数] ;
(2)彩色图像返回 [行数 * 列数 * 通道数] 。
2.3图像类型- img.dtype
通过 dtype 关键字获取图像的数据类型,通常返回uint8。
通过像素矩阵可以直接获取ROI区域,如img[200:400, 200:400]。

3、图像的通道拆分与合并

3.1 通道拆分- split()

OpenCV读取的彩色图像由B、G、R三原色组成,可以通过下面代码获取不同的通道。

方法一:

# -*- coding:utf-8 -*-

"""
b = img[:, :, 0]
g = img[:, :, 1]
r  = img[:, :, 2]
"""
import cv2
import numpy as np
 
# 读取图片
img = cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
 
# 拆分通道
b, g, r = cv2.split(img)
 
# 显示原始图像
cv2.imshow("B", b)
cv2.imshow("G", g)
cv2.imshow("R", r)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()
#注意:不要点击显示图像窗口右上角的X关闭,那样会导致图片关闭了,程序还在运行。
#直接按任意键就可

输出为:
在这里插入图片描述

3.2 通道合并- merge()

图像通道合并主要使用 merge() 函数实现。例如:

m = cv2.merge([b, g, r])
# -*- coding:utf-8 -*-
import cv2
import numpy as np
 
# 读取图片
img = cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
 
# 拆分通道
b, g, r = cv2.split(img)
 
# 合并通道
m = cv2.merge([b, g, r])
cv2.imshow("Merge", m)
 
# 等待显示
cv2.waitKey(3000)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

4、图像加法与融合运算 和 图像类型转换

4.1图像加法运算
(1)使用Numpy
在这里插入图片描述
在这里插入图片描述
(2)使用OpenCV—add()
在这里插入图片描述
对比试验:

(1) 两个彩色图像相加
代码如下所示:

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
# img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #取消注释就为转换为灰度图 
# result = 
img = cv2.resize(img, None, fx=0.5, fy=0.5)  # 缩放
test = img 
 
# 方法一:Numpy加法运算
result1 = img + test
 
# 方法二:OpenCV加法运算
result2 = cv2.add(img, test)
 
# 显示图像
cv2.imshow("original", img)
cv2.imshow("result1_Numpy", result1)
cv2.imshow("result2_OpenCv", result2)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

灰度图加法
在这里插入图片描述

4.2图像融合运算- addWeighted()

图像融合通常是指将2张或2张以上的图像信息融合到1张图像上,融合的图像含有更多的信息,能够更方便人们观察或计算机处理。如下图所示,将两张不清晰的图像融合得到更清晰的图。

在这里插入图片描述在这里插入图片描述
主要调用的函数是 addWeighted() 函数,方法如下:

dst = cv2.addWeighter(scr1, alpha, src2, beta, gamma)
dst = src1*alpha + src2*beta + gamma

其中,参数gamma不能省略。
注:两张融合的图像像素大小需要一致

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
src1 = cv2.imread('lena.png')
src2 = cv2.imread('shuji.jpg')

src1 = cv2.resize(src1, None, fx=0.5, fy=0.5)  # 缩放
src2 = cv2.resize(src2, (src1.shape[1],src1.shape[0]), interpolation=cv2.INTER_LINEAR)  # 缩放

print(type(src1),type(src2))
print(src1.shape,src2.shape)
# # 图像融合
result = cv2.addWeighted(src1, 1, src2, 1, 0)
 
# 显示图像
cv2.imshow("src1", src1)
cv2.imshow("src2", src2)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

1)设置参数1

# 图像融合
# result = cv2.addWeighted(src1, 1, src2, 1, 0)
result = cv2.addWeighted(src1, 0.8, src2, 0.2, 10)

运行结果如下:
在这里插入图片描述

(2)设置参数2

# 图像融合
# result = cv2.addWeighted(src1, 1, src2, 1, 0)
#result = cv2.addWeighted(src1, 0.8, src2, 0.2, 10)
result = cv2.addWeighted(src1, 0.2, src2, 0.8, 10)

运行结果如下:

在这里插入图片描述

4.3图像类型转换- cvtColor()
图像类型转换是指将一种类型转换为另一种类型,比如彩色图像转换为灰度图像、BGR图像转换为RGB图像。OPenCV提供了200多种不同类型之间的转换,其中最常用的包括3类,如下:

result = cv2.cvtColor(图像, 参数) 

其中,参数有以下三种:

图片灰度转换
OpenCV 中有数百种关于在不同色彩空间之间转换的方法。 当前, 在计算机视觉中有三种常用的色彩空间:灰度、BGR以及 HSV(Hue, Saturation, Value)。
灰度转换的作用就是: 转换成灰度的图片的计算强度得以降低。
灰度色彩空间是通过去除彩色信息来将其转换成灰阶, 灰度色彩空间对中间处理特别有效, 比如人脸识别。
BGR 及蓝、 绿、 红色彩空间, 每一个像素点都由一个三元数组来表示, 分别代表蓝、 绿、 红三种颜色。 网页开发者可能熟悉另一个与之相似的颜色空间: RGB 它们只是颜色顺序上不同。
HSV, H(Hue) 是色调, S(Saturation) 是饱和度, V(Value) 表示黑暗的程度(或光谱另一端的明亮程度)。
OpenCV图片灰度转换:

"""
OpenCV进行灰度转换
"""
import cv2
img = cv2.imread("lena.png")
cv2.imshow("BGR_IMG", img)
# 将图片转化为灰度
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 展示图片
cv2.imshow("gray_img", gray_img)
# 设置展示时间
cv2.waitKey(3000)
# 保存图片
cv2.imwrite("./gray_lena.jpg", gray_img)
# 释放内存
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

BRG通道转RGB通道

代码如下:


"""
OpenCV进行BGR2RGB
"""
import cv2
img = cv2.imread("lena.png")
cv2.imshow("BGR_IMG", img)
# 将图片转化为灰度
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 展示图片
cv2.imshow("BGR_img", img)
cv2.imshow("rgb_img", rgb_img)
# 设置展示时间
cv2.waitKey(5000)
# 保存图片
# cv2.imwrite("./rgb_lena.jpg", rgb_img)
# 释放内存
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

5、图像缩放、图像旋转、图像翻转 和 图像平移

5.1图像缩放- resize()
图像缩放主要调用 resize() 函数实现,具体如下:

result = cv2.resize(src, dsize[, result[. fx[, fy[, interpolation]]]])
'''
其中,参数
src 表示原始图像;
dsize 表示缩放大小;
fx和fy 也可以表示缩放大小倍数,他们两个(dsize或fx/fy)设置一个即可实现图像缩放。例如:
(1)result = cv2.resize(src, (160,160))
(2)result = cv2.resize(src, None, fx=0.5, fy=0.5)
'''

(1) cv2.resize(src, (200,100)) 设置的dsize是列数为200,行数为100

result = cv2.resize(src, (200,100))

代码如下:

import cv2
import numpy as np
 
# 读取图片
src = cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
 
# 图像缩放
# cv2.resize(src, (200,100)) 设置的dsize是列数为200,行数为100
result = cv2.resize(src, (200,100))
print (result.shape)
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

(2)可以获取 原始图像像素\times乘以缩放系数 进行图像变换;

result = cv2.resize(src, (int(cols * 0.6), int(rows * 1.2)))

代码如下所示:

import cv2
import numpy as np
 
# 读取图片
src = cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
rows, cols = src.shape[:2]
print(rows, cols)

# 图像缩放 dsize(列,行)
result = cv2.resize(src, (int(cols * 0.6), int(rows * 1.2)))
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

3)(fx,fy) 缩放倍数的方法对图像进行放大或缩小。

result = cv2.resize(src, None, fx=0.3, fy=0.3)

代码如下所示:

# encoding:utf-8
import cv2
import numpy as np
 
# 读取图片
src =cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
rows, cols = src.shape[:2]
print(rows, cols)

 
# 图像缩放
result = cv2.resize(src, None, fx=0.3, fy=0.3)
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述
5.2 图像旋转- getRotationMatrix2D(), warpAffine()
在这里插入图片描述
图像旋转主要调用getRotationMatrix2D() 函数和 warpAffine() 函数实现,绕图像的中心旋转,具体如下:

M = cv2.getRotationMatrix2D((cols/2, rows/2), 30, 1)

其中,参数分别为:旋转中心、旋转度数、scale缩放比例

rotated = cv2.warpAffine(src, M, (cols, rows))

其中,参数分别为:原始图像、旋转参数 和 原始图像宽高

(1)旋转30度

代码如下:

import cv2
import numpy as np
 
# 读取图片
src =  cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
 
# 原图的高、宽 以及通道数
rows, cols, channel = src.shape
 
# 绕图像的中心旋转
# 参数:旋转中心 旋转度数 scale
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 30, 1)
# M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 90, 1)
# 参数:原始图像 旋转参数 元素图像宽高
rotated = cv2.warpAffine(src, M, (cols, rows))
 
# 显示图像
cv2.imshow("src", src)
cv2.imshow("rotated", rotated)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

5.3 图像翻转- flip()

图像翻转在OpenCV中调用函数 flip() 实现,函数用法如下:

dst = cv2.flip(src, flipCode)
'''
其中,参数:
src 表示原始图像;
flipCode 表示翻转方向,如果flipCode为0,则以X轴为对称轴翻转,如果fliipCode>0则以Y轴为对称轴翻转,如果flipCode<0则在X轴、Y轴方向同时翻转。
'''

代码如下:(注意一个窗口多张图像的用法)

import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
src = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 
# 图像翻转
# 0以X轴为对称轴翻转 >0以Y轴为对称轴翻转 <0X轴Y轴翻转
img1 = cv2.flip(src, 0)
img2 = cv2.flip(src, 1)
img3 = cv2.flip(src, -1)
 
# 显示图形 (注意一个窗口多张图像的用法)
titles = ['Source', 'Image1', 'Image2', 'Image3']
images = [src, img1, img2, img3]
for i in range(4):
    plt.subplot(2, 2, i + 1)
    plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()
 
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

5.4 图像平移- warpAffine()

在这里插入图片描述
图像平移首先定义平移矩阵M,再调用 warpAffine() 函数实现平移,函数用法如下:

M = np.float32([[1, 0, x], [0, 1, y]])
shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))

代码如下:

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 
# 图像平移 下、上、右、左平移
M = np.float32([[1, 0, 0], [0, 1, 100]])
img1 = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
 
M = np.float32([[1, 0, 0], [0, 1, -100]])
img2 = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
 
M = np.float32([[1, 0, 100], [0, 1, 0]])
img3 = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
 
M = np.float32([[1, 0, -100], [0, 1, 0]])
img4 = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
 
# 显示图形
titles = ['Image1', 'Image2', 'Image3', 'Image4']
images = [img1, img2, img3, img4]
for i in range(4):
    plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

输出为:
在这里插入图片描述

二、图像处理进阶

1、图像阈值化-threshold()

灰度转换处理后的图像中,每个像素都只有一个灰度值,其大小表示明暗程度。二值化处理可以将图像中的像素划分为两类颜色,

Python OpenCV中提供了阈值函数 threshold() 实现二值化处理,其函数形式及参数如下图所示:

retval, dst = cv2.threshold(src, thresh, maxval, type)
'''
其中,参数:
retval:阈值        
dst: 处理结果
src,:原图像
thresh:阈值
maxval:最大值
type:类
'''

在这里插入图片描述
在这里插入图片描述对应OpenCV提供的五张图如下所示,第一张为原图,后面依次为:
二进制阈值化、反二进制阈值化、截断阈值化、反阈值化为0 和 阈值化为0 。
在这里插入图片描述
二值化处理广泛应用于各行各业,比如生物学中的细胞图分割、交通领域的车牌设别等。在文化应用领域中,通过二值化处理将所需民族文物图像转换为黑白两色图,从而为后面的图像识别提供更好的支撑作用。下图表示图像经过各种二值化处理算法后的结果,其中“BINARY”是最常见的黑白两色处理。

指定一个阈值thresh

总代码:

import cv2
import numpy as np
import matplotlib.pyplot as plt
 
#读取图像
img = cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)
lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
GrayImage=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
 
#阈值化处理
ret,thresh1=cv2.threshold(GrayImage,127,255,cv2.THRESH_BINARY)
ret,thresh2=cv2.threshold(GrayImage,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3=cv2.threshold(GrayImage,127,255,cv2.THRESH_TRUNC)
ret,thresh4=cv2.threshold(GrayImage,127,255,cv2.THRESH_TOZERO)
ret,thresh5=cv2.threshold(GrayImage,127,255,cv2.THRESH_TOZERO_INV)
 
#显示结果
titles = ['Gray Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [GrayImage, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

输出为:
在这里插入图片描述

2、图像平滑(均值滤波、中值滤波、高斯滤波)—空域滤波

图像增强是对图像进行处理,使其比原始图像更适合于特定的应用,它需要与实际应用相结合。对于图像的某些特征如边缘、轮廓、对比度等,图像增强是进行强调或锐化,以便于显示、观察或进一步分析与处理。图像增强主要是一个主观过程,而图像复原大部分是一个客观过程。图像增强的方法是因应用不同而不同的,研究内容包括:
在这里插入图片描述

图像平滑是一种区域增强的算法,平滑算法有邻域平均法、中指滤波、边界保持类滤波等。在图像产生、传输和复制过程中,常常会因为多方面原因而被噪声干扰或出现数据丢失,降低了图像的质量(某一像素,如果它与周围像素点相比有明显的不同,则该点被噪声所感染)。这就需要对图像进行一定的增强处理以减小这些缺陷带来的影响。

生成椒盐噪声

# encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
#读取图片
image = cv2.imread("lena.png", cv2.IMREAD_UNCHANGED)

#设置添加椒盐噪声的数目比例
s_vs_p = 0.01
#设置添加噪声图像像素的数目
amount = 0.01
noisy_img = np.copy(image)
#添加salt噪声
num_salt = np.ceil(amount * image.size * s_vs_p)
#设置添加噪声的坐标位置
coords = [np.random.randint(0,i - 1, int(num_salt)) for i in image.shape]
noisy_img[coords[0],coords[1],:] = [255,255,255]
#添加pepper噪声
# num_pepper = np.ceil(amount * image.size * (1. - s_vs_p))
#设置添加噪声的坐标位置
# coords = [np.random.randint(0,i - 1, int(num_pepper)) for i in image.shape]
# noisy_img[coords[0],coords[1],:] = [0,0,0]
# 如果去掉上面注释可以添加更多噪声


#保存图片
cv2.imwrite("noisy_lena.png",noisy_img)

# 显示图像
cv2.imshow("noisy_lena", noisy_img)
# 等待显示
cv2.waitKey(5000)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述
去掉注释的更多噪声点
在这里插入代码片

import cv2
import numpy as np
import matplotlib.pyplot as plt
 
# 读取图片
img = cv2.imread('lena.png')
noisy_img = cv2.imread('noisy_lena.png')
source = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
noisy_img = cv2.cvtColor(noisy_img, cv2.COLOR_BGR2RGB)

# 高斯滤波
result = cv2.GaussianBlur(noisy_img, (3, 3), 0) #可以更改核大小
# result = cv2.cvtColor(result, cv2.COLOR_BGR2RGB)
 
# 显示图形
titles = ['Source Image', 'noisy_img','GaussianBlur Image (3, 3)']
images = [source, noisy_img,result]

plt.figure(figsize=(30,10),dpi=300)
for i in range(3):
    plt.subplot(1, 3, i + 1)
    plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

输出为:

在这里插入图片描述

3、形态学处理–腐蚀、膨胀、开运算、闭运算、顶帽运算、黑帽运算

形态学运算主要针对的二值图像

import cv2
import numpy as np
 
#读取图片
src = cv2.imread('lena.png', cv2.IMREAD_UNCHANGED)
 
src=cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
 
#阈值化处理 ret为127 为二值化的结果
ret,src_1=cv2.threshold(src,127,255,cv2.THRESH_BINARY)

#设置卷积核
kernel = np.ones((7,7), np.uint8)
# kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
 
'''形态学6种操作'''
 
#腐蚀
fushi = cv2.erode(src_1, kernel) 
#膨胀
pengzhang = cv2.dilate(src_1, kernel)
#开运算
kai = cv2.morphologyEx(src_1, cv2.MORPH_OPEN, kernel)
#闭运算
bi = cv2.morphologyEx(src_1, cv2.MORPH_CLOSE, kernel)
#顶帽运算
tophat = cv2.morphologyEx(src_1, cv2.MORPH_TOPHAT, kernel)
#黑帽运算
blackhat = cv2.morphologyEx(src_1, cv2.MORPH_BLACKHAT, kernel)

 
# 显示图形
titles = ['Source Image','threshold Image', 'fushi','pengzhang','kai','bi','tophat','blackhat']
images = [src,src_1, fushi,pengzhang,kai,bi,tophat,blackhat]

plt.figure(figsize=(20,10),dpi=300)
for i in range(8):
    plt.subplot(2, 4, i + 1)
    plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

输出为:
在这里插入图片描述

卷积核的设置

4、灰度直方图

4.1 绘制灰度直方图

hist(数据源, 像素级)
'''
其中,参数:
数据源:必须是一维数组,通常需要通过函数 ravel() 拉直图像
像素级:一般是256,表示[0, 255]
函数 ravel() 将多维数组降为一维数组,其格式为:
一维数组 = 多维数组.ravel()
'''

在这里插入图片描述

import cv2
import numpy as np
import matplotlib.pyplot as plt
 
src = cv2.imread('lena.png')
cv2.imshow("src", src)
 
plt.hist(src.ravel(), 256)
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述
(2) opencv----calcHist()函数

hist = cv2.calcHist(images, channels, mask, histSize, ranges, accumulate)
'''
其中,参数:
 hist 表示直方图,返回的是一个二维数组;
 images 表示原始图像;
 channels 表示指定通道,通道编号需要用中括号括起,输入图像是灰度图像时,它的值为[0],
 彩色图像则为[0]、[1]、[2],分别表示B、G、R;
 mask 表示掩码图像,统计整副图像的直方图,设为None,统计图像的某一部分直方图时,需要掩码图像;
 histSize 表示BINS的数量,参数子集的数目,bins=3表示三个灰度级;
 ranges 表示像素值范围,例如[0, 255];
 accumulate 表示累计叠加标识,默认为false,如果被设置为true,则直方图在开始分配时不会被清零,该参数允许从多个对象中计算单个直方图,或者用于实时更新直方图;多个直方图的累积结果用于对一组图像的直方图计算。
'''
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
src = cv2.imread('lena.png')

cv2.imshow("src", src)

histb = cv2.calcHist([src], [0], None, [256], [0,255]) #掩摸处理,求的是掩摸区域的直方图
histg = cv2.calcHist([src], [1], None, [256], [0,255]) #不做掩膜处理,直接求整幅图(G分量灰度图)的直方图
histr = cv2.calcHist([src], [2], None, [256], [0,255])
# print(histr)

 
plt.plot(histb, color='b')
plt.plot(histg, color='g')
plt.plot(histr, color='r')
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

调整亮度和对比度
代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt
 
src_old = cv2.imread('lena.png')
cv2.imshow("src_old", src_old)

#定义alpha和beta
alpha = 1.5 #对比度控制
beta  = 10  #亮度控制

#调用convertScaleAbs函数
src = cv2.convertScaleAbs(src_old, alpha=alpha, beta=beta)
cv2.imshow("src", src)

histb = cv2.calcHist([src], [0], None, [256], [0,255]) #掩摸处理,求的是掩摸区域的直方图
histg = cv2.calcHist([src], [1], None, [256], [0,255]) #不做掩膜处理,直接求整幅图(G分量灰度图)的直方图
histr = cv2.calcHist([src], [2], None, [256], [0,255])
# print(histr)

 
plt.plot(histb, color='b')
plt.plot(histg, color='g')
plt.plot(histr, color='r')
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述
在这里插入图片描述

4.2直方图均衡化-equalizeHist()

dst=cv2.equalizeHist(src)
'''
其中,参数:
         dst 表示处理结果
         src 表示原始图像
'''

代码如下:

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('lena.png', cv2.IMREAD_GRAYSCALE)
equ = cv2.equalizeHist(img)
 

cv2.imshow("src", img)
cv2.imshow("result", equ)

plt.figure(figsize=(20,8))
fig,ax =plt.subplots(1,2)

ax[0].hist(img.ravel(), 256)
ax[0].set_xlim(0,255)
# plt.figure()
ax[1].hist(equ.ravel(), 256)
ax[1].set_xlim(0,255)
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述
在这里插入图片描述

代码如下:

#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('zxp.jpg', cv2.IMREAD_GRAYSCALE)
equ = cv2.equalizeHist(img)
 
plt.subplot(221),plt.imshow(img, 'gray'),plt.title('img'), plt.xticks([]),plt.yticks([])
plt.subplot(222),plt.imshow(equ, 'gray'),plt.title('equ'), plt.xticks([]),plt.yticks([])
plt.subplot(223),plt.hist(img.ravel(),256),plt.title('img_hist')
plt.subplot(224),plt.hist(equ.ravel(),256),plt.title('equ_hist')
 
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

matplotlib.pyplot.imshow() 函数
imshow() 函数用法如下所示,同样的,Pyhton下需要导入 matplotlib.pyplot 绘图包。

imshow(X, cmap=None)
'''
其中,参数:
X 表示要绘制的图像;
cmap 表示colormap,颜色图谱,默认为RGB(A)颜色空间:
    1)对于灰度图像,使用参数 “ cmap=plt.cm.gray ”;
    2)对于彩色图像,如果使用opencv读入的图像,默认空间为BRG,需要调整色彩空间为RGB。
'''

下面是分别使用函数读取灰度图像和彩色图像例子。

(1)灰度图像

import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('lena.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
plt.subplot(221),plt.imshow(img),plt.title('img'), plt.axis('off') #坐标轴关闭
plt.subplot(222),plt.imshow(img, cmap=plt.cm.gray),plt.title('img_cmap'), plt.axis('off')
 
plt.subplot(223),plt.imshow(img_gray),plt.title('img_gray'), plt.axis('off')
plt.subplot(224),plt.imshow(img_gray, cmap=plt.cm.gray),plt.title('img_gray_cmap'),plt.axis('off')#正确用法
 
plt.show()
 
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

(2)彩色图像

代码如下所示:

import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('lena.png')
b,g,r=cv2.split(img) #通道分割
img_RGB=cv2.merge([r,g,b])#通道组合
 
plt.subplot(121),plt.imshow(img),plt.title('img_BGR'), plt.axis('off') #坐标轴关闭
plt.subplot(122),plt.imshow(img_RGB),plt.title('img_RGB'), plt.axis('off')

输出为:
在这里插入图片描述

5、频域滤波—高通滤波、低通滤波

频率域图像增强首先通过傅里叶变换将图像从空间域转换为频率域,然后在频率域对图像进行处理,最后通过傅里叶反变换转换到空间域。频率域内的图像增强包括低通滤波、高通滤波、同态滤波等等。
5.1 傅里叶变换

在这里插入图片描述
在这里插入图片描述

5.2 傅里叶变换的实现

代码如下:

import cv2 
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('lena.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 实现傅里叶变换
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
result = 20*np.log(np.abs(fshift))
# 实现傅里叶逆变换


fshift_r = np.fft.ifftshift(fshift)
f_r = np.fft.ifft2(fshift_r)
result_r = np.abs(f_r)
# result = 20*np.log(np.abs(fshift))

plt.subplot(131)
plt.imshow(img, cmap = 'gray')
plt.title('original')
plt.axis('off')
plt.subplot(132)
plt.imshow(result, cmap = 'gray')
plt.title('result')
plt.axis('off')

plt.subplot(133)
plt.imshow(result_r, cmap = 'gray')
plt.title('result_r')
plt.axis('off')


# numpy.fft.ifft2(): 实现逆傅里叶变换,返回一个复数数组
# numpy.fft.ifftshift() :fftshift的逆函数,将低频信息由中心移到左上角
# iimg = np.abs(逆傅里叶变换的结果) : 将逆傅里叶变换的结果不能直接显示为图像,需要变为灰度值,显示为图像

plt.show()

输出为:
在这里插入图片描述

滤波:让低频通过的滤波器称为低通滤波器,反之为高通滤波器。

频域滤波:修改傅里叶变换以达到特殊目的,然后计算逆傅里叶变换(IDFT)返回到图像域(空域)。可以实现图像增强、图像去燥、边缘检测、特征提取、压缩、加密等等。
在这里插入图片描述
将低频的地方置为0
在这里插入图片描述

import cv2
import numpy as np 
import matplotlib.pyplot as plt
img = cv2.imread('lena.png', 0)

rows, cols = img.shape[:2]

f = np.fft.fft2(img)    #傅里叶变换
fshift = np.fft.fftshift(f)   #将低频移到中心
rows,cols = int(rows/2), int(cols/2)  #获取图像中心点
fshift[rows-30:rows+30, cols-30:cols+30] = 0  #将中心点周围30区域置为0,即将低频信息去掉,实现高通
                                            #滤波
ishift = np.fft.ifftshift(fshift)   #将低频移还原到左上角
iimg = np.fft.ifft2(ishift)   #逆傅里叶变换
iimg = np.abs(iimg)    #将逆傅里叶变换的结果转换为灰度值,显示为图像
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('original'), plt.axis('off')
plt.subplot(122), plt.imshow(iimg, cmap='gray')
plt.title('iimg'),plt.axis('off')
plt.show()

输出为:
在这里插入图片描述

(2)opencv实现–cv2.dft()

result = cv2.dft(原始图像, 转换标识)
'''
注意:
(1)返回结果是双通道的,第1个通道是结果的实数部分,第2个通道是结果的虚数部分
(2)输入的图像首先要转化成np.float32格式, 语法:np.float32(img)
(3)转换标识:flags = cv2.DFT_COMPLEX_OUTPUT, 输出是一个复数阵列
numpy.fft.fftshift():将低频分量转移到频谱中心
返回值 = cv2.magnitude(参数1, 参数2) : 将傅里叶变换结果转换为灰度值,进行显示
参数1:浮点型X坐标值,也就是实部
参数2:浮点型Y坐标值,也就是虚部
'''

代码示例:

import numpy as np 
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('lena.png', 0)
dft = cv2.dft(np.float32(img), flags = cv2.DFT_COMPLEX_OUTPUT)        #傅里叶变换
dftShift = np.fft.fftshift(dft)                                       #低频移到中心位置
result = 20*np.log(cv2.magnitude(dftShift[:,:,0], dftShift[:,:,1]))   #转换为灰度值,显示
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('original'), plt.axis('off')
plt.subplot(122),plt.imshow(result, cmap='gray')
plt.title('result'), plt.axis('off')
plt.show()

输出为:
在这里插入图片描述
opencv进行逆傅里叶变换
将经过傅里叶变换并处理的频域图像,进行逆傅里叶变换为原图

返回结果=cv2.idft(原始数据)
'''
返回结果:取决于原始数据的类型和大小
原始数据:实数或者复数均可
'''

代码如下:

import cv2 
import numpy as np
import matplotlib.pyplot as plt
o = cv2.imread('lena.png', 0)

# 傅里叶结果
dft = cv2.dft(np.float32(o), flags=cv2.DFT_COMPLEX_OUTPUT)  #傅里叶变换,flags不能省
dshift = np.fft.fftshift(dft)                         #低频从左上角移到中心
result = 20*np.log(cv2.magnitude(dftShift[:,:,0], dftShift[:,:,1]))   #转换为灰度值,显示
# 傅里叶逆变换
ishift = np.fft.ifftshift(dshift)                     #从中心移到左上角,还原
io = cv2.idft(ishift)                                 #逆傅里叶变换
io = cv2.magnitude(io[:,:,0], io[:,:,1])              #转换为灰度值, [0-255]
 
plt.subplot(131)
plt.imshow(o, cmap='gray')
plt.axis('off')
plt.title('original')

plt.subplot(132)
plt.imshow(result, cmap='gray')
plt.axis('off')
plt.title('fly')

plt.subplot(133)
plt.imshow(io, cmap='gray')
plt.axis('off')
plt.title('inverse')
plt.show()    

输出为:
在这里插入图片描述

6、图像梯度

函数实现

dst = cv2.Sobel(src, ddepth, dx, dy,  [ksize])
'''
dst:计算结果
src:原始图像
ddepth :处理结果图像深度
dx:x轴方向
dy:y轴方向
ksize:核大小
ddepth参数说明:
一般,通常直接设置为-1,表示让处理结果与原始图像保持一致
但是此处需要注意,不能直接置为-1
'''

举例说明

dst = cv2.converScaleAbs(src, [, alpha[, beta]])
'''
作用:把负数的值取绝对值,无论边界是正负,都能获取
dx dy参数说明
计算x方向梯度: dx=1,dy=0
计算y方向梯度:dx=0,dy=1
'''

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.2 scharr算子 --比sobel算子效果更好

函数实现:

dst = Scharr(src, ddpeth, dx, dy)
'''
src:原始图像
ddepth:处理结果图像深度
dx:x轴方向
dy:y轴方向
注意:
ddepth参数:一般在别的图像处理函数中,直接置为-1,表示处理成与原图像一样深度的图像,但是由于此函数处理过程中会出现负数,故与sobel算子一样,将它赋值为cv2.CV_64F
然后再转为unit8正数
'''
dst = Scharr(src, cv2.CV_64F, dx, dy)
dst = cv2.convertScaleAbs(dst)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

sobel和 scharr算子的比较
(1)大小是一样的,说明计算起来的工作量是一样的
但是scharr精确度更高,给了临近像素更高的权重
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

6.3 laplacian算子

拉普拉斯算子类似于二阶sobel导数。实际上,在OpenCV中通过调用sobel算子来计算拉普拉斯算子。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

函数实现:

dst = cv2.Laplacian(src, ddepth)
import cv2
import numpy as np
o = cv2.imread('lena.png', cv2.IMREAD_GRAYSCALE)
 
laplacian = cv2.Laplacian(o, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)  #转回uint8
 
cv2.imshow('original', o)
cv2.imshow('laplacian', laplacian)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

7、边缘检测

canny边缘检测

函数实现:

edges =  cv2.Canny(image, threshold1, threshold2)
'''
image: 原始图像
threshold1: 阈值1 minVal
threshold2: 阈值2  maxVal
'''

在这里插入图片描述
threshold值越小,细节越丰富

import cv2
import numpy as np
o = cv2.imread('lena.png', cv2.IMREAD_GRAYSCALE)
o = cv2.resize(o,None,fx=0.5,fy=0.5)

r = cv2.Canny(o,100,200)
# 下面是不同阈值处理结果,可以看出阈值越小细节越丰富
r1 = cv2.Canny(o,64,128)
# laplacian = cv2.Laplacian(o, cv2.CV_64F)
# laplacian = cv2.convertScaleAbs(laplacian)  #转回uint8
 
cv2.imshow('original', o)
cv2.imshow('c', r)
cv2.imshow('c1', r1)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出为:
在这里插入图片描述

8、图像金字塔

在这里插入图片描述
8.1 向下采样
代码为:

import cv2
import numpy as np
o = cv2.imread('lena.png')
# 依次变为原来的四分之一
r1 = cv2.pyrDown(o)
r2 = cv2.pyrDown(r1)
r3 = cv2.pyrDown(r2)
 
cv2.imshow('original', o)
cv2.imshow('PyrDown1', r1)
cv2.imshow('PyrDown2', r2)
cv2.imshow('PyrDown3', r3)
 
cv2.waitKey(0)
cv2.destoryAllWindows()

输出为:
在这里插入图片描述

8.2 向上采样
代码为:

import cv2
import numpy as np
o = cv2.imread('lena.png')
o = cv2.resize(o,None,fx=0.1,fy=0.1)
r1 = cv2.pyrUp(o)
r2 = cv2.pyrUp(r1)
r3 = cv2.pyrUp(r2)
 
cv2.imshow('original', o)
cv2.imshow('PyrUp1', r1)
cv2.imshow('PyrUp2', r2)
cv2.imshow('PyrUp3', r3)
 
cv2.waitKey(0)
cv2.destoryAllWindows()

输出为:
在这里插入图片描述

车牌识别

前面博客介绍了OpenCV图像处理的基础知识,本篇博客利用前面知识完成一个小项目——车牌号码识别。
简洁易懂的车牌号识别Python实现“超详解”(含代码)

1、整体思路

首先附上本次识别的图片:
在这里插入图片描述

基于OpenCV车牌号识别总体分为四个步骤:
(1)提取车牌位置,将车牌从图中分割出来;
(2)车牌字符的分割;
(3)通过模版匹配识别字符;
(4)将结果绘制在图片上显示出来。
与深度学习相比,传统图像处理的识别有好处又有坏处:
好处:不需要大量的数据集训练模型,通过形态学、边缘检测等操作提取特征
坏处:基于传统图像处理的图像识别代码的泛化性较低,当图像的角度,光照不同时,识别效果有时会不尽人意。

2、代码详解

为了方便观察每一步图片的变化,本次代码在Jupyter Notebook上编写,全部代码以上传(可直接运行)。
本次项目中会多次使用到图片显示和图片去噪灰度处理,所以首先定义了显示函数和高斯滤波灰度处理函数,方便后面的调用:

# 导入所需模块
import cv2
from matplotlib import pyplot as plt
import os
import numpy as np
# plt显示彩色图片
def plt_show0(img):
#cv2与plt的图像通道不同:cv2为[b,g,r];plt为[r, g, b]
    b,g,r = cv2.split(img)
    img = cv2.merge([r, g, b])
    plt.imshow(img)
    plt.show()
    
# plt显示灰度图片
def plt_show(img):
    plt.imshow(img,cmap='gray')
    plt.show()

# 图像去噪灰度处理
def gray_guss(image):
    image = cv2.GaussianBlur(image, (3, 3), 0)
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return gray_image
2.1提取车牌位置

对图片进行阈值化处理、边缘检测及形态学操作,根据得到的轮廓特征识别车牌的具体位置,将车牌分割出来。直接上代码及代码详解:

# 读取待检测图片
origin_image = cv2.imread('./refer1/icar.jpg')
# 复制一张图片,在复制图上进行图像操作,保留原图
image = origin_image.copy()
# 图像去噪灰度处理
gray_image = gray_guss(image)
# x方向上的边缘检测(增强边缘信息)
Sobel_x = cv2.Sobel(gray_image, cv2.CV_16S, 1, 0)
absX = cv2.convertScaleAbs(Sobel_x)
image = absX

# 图像阈值化操作——获得二值化图
ret, image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
# 显示灰度图像
plt_show(image)
# 形态学(从图像中提取对表达和描绘区域形状有意义的图像分量)——闭操作
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 10))
image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernelX,iterations = 1)
# 显示灰度图像
plt_show(image)

二值化图以及闭操作(闭合细小的连接,抑制暗细节)的结果如图所示:
输出为:
在这里插入图片描述

# 腐蚀(erode)和膨胀(dilate)
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 1))
kernelY = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 20))
#x方向进行闭操作(抑制暗细节)
image = cv2.dilate(image, kernelX)
image = cv2.erode(image, kernelX)
#y方向的开操作
image = cv2.erode(image, kernelY)
image = cv2.dilate(image, kernelY)
# 中值滤波(去噪)
image = cv2.medianBlur(image, 21)
# 显示灰度图像
plt_show(image)
# 获得轮廓
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

for item in contours:
    rect = cv2.boundingRect(item)
    x = rect[0]
    y = rect[1]
    weight = rect[2]
    height = rect[3]
    # 根据轮廓的形状特点,确定车牌的轮廓位置并截取图像
    if (weight > (height * 3.5)) and (weight < (height * 4)):
        image = origin_image[y:y + height, x:x + weight]
        plt_show0(image)

输出为:
在这里插入图片描述

2.2车牌字符的分割
#车牌字符分割
# 图像去噪灰度处理
gray_image = gray_guss(image)
# 图像阈值化操作——获得二值化图   
ret, image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_OTSU)
plt_show(image)

#膨胀操作,使“苏”字膨胀为一个近似的整体,为分割做准备
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
image = cv2.dilate(image, kernel)
plt_show(image)

输出为:
在这里插入图片描述

# 查找轮廓
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
words = []
word_images = []
#对所有轮廓逐一操作
for item in contours:
    word = []
    rect = cv2.boundingRect(item)
    x = rect[0]
    y = rect[1]
    weight = rect[2]
    height = rect[3]
    word.append(x)
    word.append(y)
    word.append(weight)
    word.append(height)
    words.append(word)
# 排序,车牌号有顺序。words是一个嵌套列表
words = sorted(words,key=lambda s:s[0],reverse=False)
i = 0
#word中存放轮廓的起始点和宽高
for word in words:
    # 筛选字符的轮廓
    if (word[3] > (word[2] * 1.5)) and (word[3] < (word[2] * 3.5)) and (word[2] > 25):
        i = i+1
        splite_image = image[word[1]:word[1] + word[3], word[0]:word[0] + word[2]]
        word_images.append(splite_image)
        print(i)
print(words)

for i,j in enumerate(word_images):  
    plt.subplot(1,7,i+1)
    plt.imshow(word_images[i],cmap='gray')
plt.show()

输出为:
在这里插入图片描述

2.3模板匹配识别字符

模板匹配是一个机械性的流程,所以把机械性的操作设定为函数。

#模版匹配
# 准备模板(template[0-9]为数字模板;)
template = ['0','1','2','3','4','5','6','7','8','9',
            'A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z',
            '藏','川','鄂','甘','赣','贵','桂','黑','沪','吉','冀','津','晋','京','辽','鲁','蒙','闽','宁',
            '青','琼','陕','苏','皖','湘','新','渝','豫','粤','云','浙']

# 读取一个文件夹下的所有图片,输入参数是文件名,返回模板文件地址列表
def read_directory(directory_name):
    referImg_list = []
    for filename in os.listdir(directory_name):
        referImg_list.append(directory_name + "/" + filename)
    return referImg_list

# 获得中文模板列表(只匹配车牌的第一个字符)
def get_chinese_words_list():
    chinese_words_list = []
    for i in range(34,64):
        #将模板存放在字典中
        c_word = read_directory('./refer1/'+ template[i])
        chinese_words_list.append(c_word)
    return chinese_words_list
chinese_words_list = get_chinese_words_list()


# 获得英文模板列表(只匹配车牌的第二个字符)
def get_eng_words_list():
    eng_words_list = []
    for i in range(10,34):
        e_word = read_directory('./refer1/'+ template[i])
        eng_words_list.append(e_word)
    return eng_words_list
eng_words_list = get_eng_words_list()


# 获得英文和数字模板列表(匹配车牌后面的字符)
def get_eng_num_words_list():
    eng_num_words_list = []
    for i in range(0,34):
        word = read_directory('./refer1/'+ template[i])
        eng_num_words_list.append(word)
    return eng_num_words_list
eng_num_words_list = get_eng_num_words_list()


# 读取一个模板地址与图片进行匹配,返回得分
def template_score(template,image):
    #将模板进行格式转换
    template_img=cv2.imdecode(np.fromfile(template,dtype=np.uint8),1)
    template_img = cv2.cvtColor(template_img, cv2.COLOR_RGB2GRAY)
    #模板图像阈值化处理——获得黑白图
    ret, template_img = cv2.threshold(template_img, 0, 255, cv2.THRESH_OTSU)
#     height, width = template_img.shape
#     image_ = image.copy()
#     image_ = cv2.resize(image_, (width, height))
    image_ = image.copy()
    #获得待检测图片的尺寸
    height, width = image_.shape
    # 将模板resize至与图像一样大小
    template_img = cv2.resize(template_img, (width, height))
    # 模板匹配,返回匹配得分
    result = cv2.matchTemplate(image_, template_img, cv2.TM_CCOEFF)
    return result[0][0]


# 对分割得到的字符逐一匹配
def template_matching(word_images):
    results = []
    for index,word_image in enumerate(word_images):
        if index==0:
            best_score = []
            for chinese_words in chinese_words_list:
                score = []
                for chinese_word in chinese_words:
                    result = template_score(chinese_word,word_image)
                    score.append(result)
                best_score.append(max(score))
            i = best_score.index(max(best_score))
            # print(template[34+i])
            r = template[34+i]
            results.append(r)
            continue
        if index==1:
            best_score = []
            for eng_word_list in eng_words_list:
                score = []
                for eng_word in eng_word_list:
                    result = template_score(eng_word,word_image)
                    score.append(result)
                best_score.append(max(score))
            i = best_score.index(max(best_score))
            # print(template[10+i])
            r = template[10+i]
            results.append(r)
            continue
        else:
            best_score = []
            for eng_num_word_list in eng_num_words_list:
                score = []
                for eng_num_word in eng_num_word_list:
                    result = template_score(eng_num_word,word_image)
                    score.append(result)
                best_score.append(max(score))
            i = best_score.index(max(best_score))
            # print(template[i])
            r = template[i]
            results.append(r)
            continue
    return results


word_images_ = word_images.copy()
# 调用函数获得结果
result = template_matching(word_images_)
print(result)
# "".join(result)函数将列表转换为拼接好的字符串,方便结果显示
print( "".join(result))

输出为:
在这里插入图片描述
最后,利用PIL库将结果绘制在原图上,

from PIL import ImageFont, ImageDraw, Image

height,weight = origin_image.shape[0:2]
print(height)
print(weight)

image_1 = origin_image.copy()
cv2.rectangle(image_1, (int(0.2*weight), int(0.75*height)), (int(weight*0.9), int(height*0.95)), (0, 255, 0), 5)

#设置需要显示的字体
fontpath = "./refer1/simhei.ttf"
font = ImageFont.truetype(fontpath,64)
img_pil = Image.fromarray(image_1)
draw = ImageDraw.Draw(img_pil)
#绘制文字信息
draw.text((int(0.2*weight)+25, int(0.75*height)),  "".join(result), font = font, fill = (255, 255, 0))
# draw.text((int(0.2*weight)+25, int(0.75*height)),  "".join(result),fill = (255, 255, 0))
bk_img = np.array(img_pil)
print(result)
print( "".join(result))
plt_show0(bk_img)

获得的最终输出图片如下:
在这里插入图片描述

大功告成!!!!!

3、总结

(一) 、OpenCV的车牌号码识别一共分为四步走:
1–提取车牌位置,将车牌从图中分割出来;
2–车牌字符的分割;
3–通过模版匹配识别字符;
4–将结果绘制在图片上显示出来。
(二)、图像处理的识别泛化性较低,对图片的角度光照有要求,所以要理解图像处理每一步的作用,根据自己图像的特点调整参数,更改操作顺序等等,以达到最好的效果。
(三)、车牌号识别的模板连接如下,需要的可以下载,有了模板就可以识别自己的图片了
链接:https://pan.baidu.com/s/1QBjy7c0klv_PBUwJjA8ynA
提取码:v53d
(四)、完整代码已上传SCDN点击查看

针对完全没有基础的同学们
1.确定机器学习的应用领域有哪些
2.查找机器学习的算法应用有哪些
3.确定想要研究的领域极其对应的算法
4.通过招聘网站和论文等确定具体的技术
5.了解业务流程,查找数据
6.复现经典算法
7.持续优化,并尝试与对应企业人员沟通心得
8.企业给出反馈

举报

相关推荐

0 条评论