机器学习算法可以从图像中识别出图案和物体。在机器学习算法应用于图像之前,通常需要先将原始图像转换成为算法可用的特征
开源计算机视觉库(OpenCV)可以来处理图像。
需要先安装opencv
安装命令
conda install --channel https://conda.anaconda.org/menpoopencv3
# 查看OpenCV的安装情况
import cv2
cv2.__version__
数据:链接:https://pan.baidu.com/s/1dL7D2XHWvr9pwW4UEf6AWw
提取码:ar5o
代码:链接:https://pan.baidu.com/s/1w9QE5TLrsyRPcq3jyBWNbQ
提取码:zfvf
准备工作做好后,就可以开始对图片数据的探索啦~
8.1 加载图像
对图像进行预处理前需要先将其导入程序 使用OpenCV的imread方法
调用imread方法时,程序会将图像数据转换为我们非常熟悉的数据类型–Numpy数组
%matplotlib inline
import cv2
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
import matplotlib.pyplot as plt
img = Image.open("./Python机器学习手册/plane.jpg") #填写自己的路径
plt.figure('image')
plt.imshow(img)
plt.show()
#将图像导入成灰度图
image = cv2.imread("./image/ML_Handbook/plane.jpg",cv2.IMREAD_GRAYSCALE) #切记路径中不能含有中文
#显示图像
plt.imshow(image,cmap="gray"),plt.axis("off")
plt.show()
#显示数据类型
type(image) #imread将图像数据转换成Numpy数组
# 矩阵中的元素与原图像中的像素点一一对应
#显示图像数据
image
# 图像的分辨率是3600*2270
#显示矩阵纬度
image.shape
# 灰度图:矩阵元素的值代表对应的灰度值。,灰度值在0(黑)到255(zip)之间变化。
#显示第一个像素点的像素值
image[0,0]
# 矩阵中包含三个值,分别对应蓝色、绿色、红色(BGR)
#以彩色模式加载图像
image_bgr = cv2.imread("./image/ML_Handbook/plane.jpg",cv2.IMREAD_COLOR)
#显示像素点
image_bgr[0,0]
# OpenCV默认使用BGR格式,但许多应用程序使用RGB格式,红色和蓝色对调了位置,要在matplotlib中正确显示OpenCV彩色图像,需要先将颜色转换为RGB格sample.db'
#转换为RGB格式
image_rgb = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2RGB)
#显示图像
plt.imshow(image_rgb),plt.axis("off")
plt.show()
8.2 保存图像
对图像进行预处理前需要对图片进行保存
opencv中的imwrite,该函数保存在指定的路径并会直接进行覆盖,不会输出一条错误消息或请求
import cv2
import numpy as np
from matplotlib import pyplot as plt
#以灰度图的格式导入图像
image =cv2.imread("./image/ML_Handbook/plane.jpg",cv2.IMREAD_GRAYSCALE)
# 保存图像
cv2.imwrite("./image/ML_Handbook/plane_new.jpg",image)
成功保存的时候会输出True
8.3 调整图像的大小
使用resize对图像调整大小
常见图像规格:32 x 32 64 x 64 96 x 96 256 x 256
1、原始图像的大小不一,但用作特征的图像需要相同的规格
2、当数据量巨大时,如果图像非常大会占据大量的内存
#加载图像
import cv2
import numpy as np
from matplotlib import pyplot as plt
#以灰度图格式导入图像
image = cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_GRAYSCALE)
#讲图片尺寸调整为50x50像素
image_50x50 = cv2.resize(image,(50,50))
#查看图像
plt.imshow(image_50x50,cmap="gray"),plt.axis("off")
plt.show()
8.4 裁剪图像
OpenCV将图像保存为矩阵,因此可以和方便的裁剪想要的部分
# 图像被编码成二维Numpy数组,通过数组切片可以方便的裁剪图片
#以灰度图格式加载图像
image =cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_ANYCOLOR)
#选择所有行和前128列
image_cropped = image[:,:128]#取所有的行,前128列
#显示图像
plt.imshow(image_cropped,cmap="gray"),plt.axis("off")
plt.show()
image1 = image[:100,:100] #截取前100行和前100列
plt.imshow(image1,cmap="gray"),plt.axis("off")
plt.show()
8.5 平滑处理图像
将每个像素的值变换为其相邻像素平均值
#image--图片的灰度图格式
#平滑处理图像
image_blurry = cv2.blur(image,(5,5)) #5x5的核对每个像素周围的值取平均值
#显示图像
plt.imshow(image_blurry,cmap="gray"),plt.axis("off")
plt.show()
# 100x100的核进行平滑操作
image_blurry = cv2.blur(image,(100,100))
#显示图像
plt.imshow(image_blurry,cmap="gray"),plt.axis("off")
plt.show()
# 核被广泛应用于 图像锐化到边缘检测的所有领域 ,本小节反复讨论核的处理,平滑核如下所示:
#创建核
kernel = np.ones((5,5))/25.0
#显示核
kernel
# 核中的中心元素是要处理的像素,其余像素是该像素的相邻像素。
# 由于所有像素具有相同的值(被归一化为1),因此每个元素对要处理的像素点有相同的权重
# filter2D在图像上手动应用核
#应用核
image_kernel = cv2.filter2D(image,-1,kernel)
#显示图像
plt.imshow(image_kernel,cmap="gray"),plt.xticks([]),plt.yticks([])
plt.show()
8.6 图像锐化
创建一个可以图出目标像素的核,使用filter2D将其应用于图像
#image-- 灰度图格式的图像
#创建核
kernel =np.array([[0,-1,0],[-1,5,-1],[0,-1,0]])
#锐化图像
image_shape = cv2.filter2D(image,-1,kernel)
#显示图像
plt.imshow(image_shape,cmap="gray"),plt.axis("off")
plt.show()
锐化的原理于与平滑相似,在平滑处理中,使用核来平均处理相邻像素的值;在锐化时,使用可以图出像素自身的核。锐化可以使图像的边缘更加突出。
8.7 提升对比度
直方图均衡是一种图像处理方法,可以使图像中的物体和形状更加图出。对于灰度图像,可以直接在图像应用OpenCV的equalizeHist
import cv2
import numpy as np
from matplotlib import pyplot as plt
#以灰度图的格式导入图像
image =cv2.imread("./image/ML_Handbook/plane.jpg",cv2.IMREAD_GRAYSCALE)
#增强图像
image_enhanced = cv2.equalizeHist(image)
#显示图像
plt.imshow(image_enhanced,cmap="gray"),plt.axis("off")
plt.show()
对彩色图像进行增强操作时,首先需要将其转换为YUV格式。其中Y表示亮度,U和V表示颜色。
转换后,可以将equalizeHist方法应用于此图像,然后将其转换回BGR或RGB
直方图–转换图像,使像素强度的分布范围更广
#加载图像
image_bgr = cv2.imread("./image/ML_Handbook/plane.jpg")
#转换YUV格式
image_yuv = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2YUV)
#对图像应用直方图
image_yuv[:,:,0] = cv2.equalizeHist(image_yuv[:,:,0])
#转换为RGB格式
image_rgb = cv2.cvtColor(image_yuv,cv2.COLOR_YUV2RGB)
#显示图像
plt.imshow(image_rgb),plt.axis("off")
plt.show()
8.8 颜色分离
OpenCV分离颜色,首先将图像转换成HSV格式;其次定义要分离的一系列值,然后为图像创建一个掩模;
最后使用bitwise_and方法将掩模应用于图像,并将图像转换为图像转换为所需的输出格式
# 将图像的颜色分离出来
import cv2
import numpy as np
from matplotlib import pyplot as plt
#以灰度图的格式导入图像
image_bgr =cv2.imread("./image/ML_Handbook/plane_256x256.jpg")
#将BGR转换成HSV格式
image_hsv = cv2.cvtColor(image_bgr , cv2.COLOR_BGR2HSV)
#定义HSV格式中蓝色分量的区间
lower_blue = np.array([50,100,50])
upper_blue = np.array([130,255,255])
#创建掩模
mask = cv2.inRange(image_hsv,lower_blue,upper_blue)
#应用掩模
image_bgr_masked = cv2.bitwise_and(image_bgr,image_bgr,mask=mask)
#从BGR格式转换成RGB格式
image_rgb = cv2.cvtColor(image_bgr_masked,cv2.COLOR_BGR2RGB)
#显示图像
plt.imshow(image_rgb),plt.axis("off")
plt.show()
#显示图像
plt.imshow(mask,cmap=“gray”),plt.axis(“off”)
plt.show()
8.9 图像二值化
将图像转换成只包含黑色和白色的简化版本
图像二值化是对图像进行阈值处理,强度大于某个阈值设置成白色,小于该阈值的设置成黑色。
还有一种更先进的办法:自适应闵值处理,一个像素的阈值由其他相邻像素的强度来决定的。
当图像中的不同区域的光照条件有差异是,可以体现这种方法的优势。
对图像进行阈值处理可以对图像去噪,最保留最重要的元素
import cv2
import numpy as np
from matplotlib import pyplot as plt
#以灰度图的格式导入图像
image_grey =cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_GRAYSCALE)
#应用自适应阈值处理
max_output_value = 255
neighborhood_size = 99
subtract_from_mean = 10
image_binaruzed = cv2.adaptiveThreshold(image_grey,
max_output_value, #确定输出像素的最大强度
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,#将像素的阈值设置为相邻像素强度的加权和,其权重由高斯窗口确定
cv2.THRESH_BINARY,
neighborhood_size,
subtract_from_mean)
#显示图像
plt.imshow(image_binaruzed,cmap="gray"),plt.axis("off")
plt.show()
# 可以将ADAPTIVE_THRESH_GAUSSIAN_C参数设置为cv2.ADAPTIVE_THRESH_MEAN_C,使用相邻像素的平均值作为阈值
#使用cv2.ADAPTIVE_THRESH_MEAN_C
image_mean_threshold = cv2.adaptiveThreshold(image_grey,
max_output_value,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY,
neighborhood_size,
subtract_from_mean)
#显示图像
plt.imshow(image_mean_threshold,cmap="gray"),plt.axis("off")
plt.show()
8.10 移除背景
提取图像中的前景图像
在所需的前景图像画一个矩形,然后运行GrabCut算法
import cv2
import numpy as np
from matplotlib import pyplot as plt
#以灰度图的格式导入图像
image_bgr =cv2.imread("./image/ML_Handbook/plane_256x256.jpg")
image_rgb=cv2.cvtColor(image_bgr,cv2.COLOR_BGR2RGB)
#矩形的值,左上角的X坐标,左上角的y坐标,宽,高
rectangle = (0,56,256,150)
#创建初始掩模
mask = np.zeros(image_rgb.shape[:2],np.uint8)
#创建grabCut函数需要的临时数组
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
#执行grabCut函数
cv2.grabCut(image_rgb,#图像
mask,#掩模
rectangle,#矩形
bgdModel,#背景的临时数组
fgdModel,#前景的临时数组
5, #迭代次数
cv2.GC_INIT_WITH_RECT)#使用定义的矩阵初始化
#创建一个掩模,将确定或很可能是背景的部分设置为0,其余部分设置为1
mask_2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
#将图像与掩模相乘以除去背景
image_rgb_nobg = image_rgb*mask_2[:,:,np.newaxis]
#显示图像
plt.imshow(image_rgb_nobg),plt.axis("off")
plt.show()
图像中仍然残留部分背景区域,可以手动将这些区域标记为背景,
但在显示中往往有数千张图像要处理,注意手动修改是不可行的,所以可以考虑接受“留有部分背景噪声”的图像
在上述的解决办法中,首先在包含前景的区域画一个矩形。GrabCut认为这个矩形之外的一切都是背景,并使用这些信息找出矩形内可能的背景。
算法最后会生成一个掩模,它可以标出确定为背景的区域,可能为背景的区域和前景区域。
#显示掩模
plt.imshow(mask,cmap='gray'),plt.axis("off")
plt.show()
# 黑色区域是所画矩形之外的区域,它被认为是背景。灰色区域是GrabCut认为的可能为背景的区域,白色区域则可能是前景图像
# 然后,用这个掩模生成一个新的掩模,新掩模会将黑色和灰色区域合并
#显示掩模(将第二个掩模应用到图像上)
plt.imshow(mask_2,cmap='gray'),plt.axis("off")
plt.show()
8.11 边缘检测
找出一张图片的边缘
使用Canny边缘检测等边缘检测技术
import cv2
import numpy as np
from matplotlib import pyplot as plt
#以灰度图的格式导入图像
image_gray =cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_GRAYSCALE)
#计算像素强度的中位数
median_intensity = np.median(image_gray)
#设置阈值
lower_threshold = int(max(0,(1.0-0.33)*median_intensity))
upper_threshold = int(min(0,(1.0+0.33)*median_intensity))
#应用Canny边缘检测器
image_canny = cv2.Canny(image_gray,lower_threshold,upper_threshold)
#显示图像
plt.imshow(image_canny,cmap="gray"),plt.axis("off")
plt.show()
边缘中包含了大量的信息。边缘检测可以帮助我们去除低信息含量的区域,并将信息含量最高的区域从原始图像中分离出来。
Sobel滤波器、Laplacian边缘检测器 、Canny边缘检测器
Canny边缘检测器需要两个参数来标识梯度的低阈值和高阈值。处于低阈值和高阈值之间的前在边缘像素被认为是弱边缘像素,而高于阈值的则被认为是强边缘像素。
OpenCV的Cannneighborhood_size方法需要用低阈值和高阈值做参数。
将两个阈值分别设置为低于和高于像素强度中位数一个标准偏差的值。如果在对整个图像集合运行Canny之前,先用一小部分图像试错,手动调整这个对阈值,通常会得到较好的效果。
8.12 角点检测
使用OPenCV实现啊的Harris角点检测器cornerHarris
import cv2
import numpy as np
from matplotlib import pyplot as plt
#以灰度图模式加载图像
image_bgr = cv2.imread("./image/ML_Handbook/plane_256x256.jpg")
image_gray = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2GRAY)
image_gray = np.float32(image_gray)
#设置角点显示器的参数
block_size = 2
aperture = 29
free_parameter = 0.04
#检测角点
detector_responses = cv2. cornerHarris(image_gray,
block_size,
aperture,
free_parameter)
#放大角点标志
detector_responses = cv2.dilate(detector_responses,None)
#只保留大于阈值的检测结果,并把它们标记成白色
threshold = 0.02
image_bgr[detector_responses > threshold * detector_responses.max()]=[255,255,255]
#转换成灰度图
image_gray = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2GRAY)
#显示图像
plt.imshow(image_gray,cmap="gray"),plt.axis("off")
plt.show()
Harris检测器检测两条边缘线条的交叉点的常用方法,角点中含有大量的信息。Harris检测器会寻找一个窗口(可以叫做邻域)整个窗口的微小移动会引发窗口内像素值的大幅变化。
cornerHarris保函了个重要的参数,可以用于控制检测到的边缘。参数block_size代表角点检测中窗口的尺寸,参数aperture代表Sobel算子的尺寸,参数free_parameter用于控制对角点检测的严格程度,这个值越大,可以识别的角点越平滑
# 输出是一张描绘了可能角点的灰度图像
#显示可能的角点
plt.imshow(detector_responses,cmap="gray"),plt.axis("off")
plt.show()
然后使用阈值筛选出最可能的角点,或者使用Shi-Tomasi角点检测器(goodFeaturesToTrack)确定一组固定数量的明显的角点,其工作方式与Harris检测器类似。goodFeaturesToTrack有3个主要参数:待检测角点的数量、角点的最差质量(从0到1)、角点间的最短欧式距离
#以灰度图模式加载图像
image_bgr = cv2.imread("./image/ML_Handbook/plane_256x256.jpg")
image_gray = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2GRAY)
#待检测角点的数量
corners_to_detect = 10
minimum_quality_score = 0.05
minmum_distance = 25
#检测角点
corners = cv2.goodFeaturesToTrack(image_gray,
corners_to_detect,
minimum_quality_score,
minmum_distance)
corners = np.float32(corners)
#在每个角点上画白圈
for corner in corners:
x,y = corner[0]
cv2.circle(image_bgr,(x,y),10,(255,255,255),-1)
#转换成灰度图
image_rgb = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2GRAY)
#显示图像
plt.imshow(image_rgb,cmap="gray"),plt.axis("off")
plt.show()
8.13 为机器学习创建特征
将图像转化成机器学习算法中可用的样本数据
# 使用numpy中的flatten方法将包含图像信息的多维数组转换成包含样本值的特征向量
import cv2
import numpy as np
from matplotlib import pyplot as plt
image = cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_GRAYSCALE)
#将图像尺寸转换成10x10
image_10x10 = cv2.resize(image,(10,10))
#将图像数据转换成一维数组
image_10x10.flatten()
# 图像使用像素点的网格来表示,如果是灰度图,则每个像素用一个值表示(即像素强度,白色为255,黑色为0).假设我们有一张10x10像素的图像:
plt.imshow(image_10x10,cmap="gray"),plt.axis("off")
plt.show()
# 在上述情况下,图像数据的纬度是10x10
image_10x10.shape
# 如果使用flatten方法将数据展开,会得到一个长度为100(10x10)的向量
image_10x10.flatten().shape
这就是图像的特征向量,它可以与其他图像的特征向量结合,生成可供机器学习算法使用的数据。
如果是彩色图像,每个像素就不是用一个值表示,而是多个值(最常见的是3个),分别表示每个通道(红,绿,蓝等)强度,这些色彩分两混合后可以表示对应像素点的颜色。因此,如果我们的10x10像素的图像是彩色的,每个样本将有300个特征值
#以彩色模式加载图像
image_color = cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_COLOR)
#将图像尺寸变换为10 x 10
image_color_10x10 = cv2.resize(image_color,(10,10))
#将图像数据转换成一维数组并显示数组纬度
image_color_10x10.flatten().shape
由于图像中的每个像素点都是一个特征,所以随着图像尺寸变大,特征的数量也快速增多,这是图像处理和计算机视觉中的一个主要挑战
image_256x256_gray = cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_GRAYSCALE)
#将图像转换成一维数组并显示数组维度
image_256x256_gray.flatten().shape
# 如果图像是彩色的,特征的数量还会进一步增加
image_256x256_color = cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_COLOR)
#将图像转换成一维数组并显示数组纬度
image_256x256_color.flatten().shape
如上述结果所示,即使是一张小小的彩色图片也有将近20万个特征,在我们训练模型时,这会产生问题,因为特征的数量可能远远超过样本的数量
这个问题将持续在后续对维度策略的思考,如何在减少特征数量的同时不丢失过多的信息
8.14 将颜色平均值编码成特征
获取一个基于图像颜色的特征
彩色图像中的每个像素都是用多个颜色通道,计算图像每种颜色通道的平均值,就可以得到代表该图像颜色均值的三种颜色特征
import cv2
import numpy as np
from matplotlib import pyplot as plt
image_bgr = cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_COLOR)
#计算每个通道的平均值
channels = cv2.mean(image_bgr)
#交换红色和蓝色通道,将图像从BGR格式转换成RGB格式
observation = np.array([(channels[2],channels[1],channels[0])])
#显示每个颜色通道得额平均值
observation
#显示图像
plt.imshow(observation),plt.axis("off")
plt.show()
8.15 将色彩直方图编码成特征
创建一组特征来表示图像中的颜色
import cv2
import numpy as np
from matplotlib import pyplot as plt
image_bgr = cv2.imread("./image/ML_Handbook/plane_256x256.jpg",cv2.IMREAD_COLOR)
image_rgb = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2RGB)
features=[]
# 为每一个颜色通道计算直方图
colors = ["r","g","b"]
#为每一个通道计算直方图并把它加入到特征列表中
for i,channel in enumerate(colors):
histogram = cv2.calcHist([image_rgb],
[i],
None,
[256],
[0,256])
features.extend(histogram)
#将样本的特征值展开成一维数组
observation = np.array(features).flatten()
#显示样本的前5个特征值
observation[0:5]
# 在RGB色彩模型中,每中颜色是由三个通道的值组合而成。每个通道可以取256个值中的一个。例如:图像左上角像素的通道值如下:
import pandas as pd
data=pd.Series([1,1,2,2,3,3,3,4,5])
# 显示直方图
data.hist(grid=False)
plt.show()
数据中有两个1,两个2,三个3,一个4,一个5.在直方图中每个竖条代表一个值在数据中出现的次数
可以将这种方法应用于每个颜色通道,这里的值不是5个,而是256个。x轴表示256个可能的通道值,y轴表示某个特定的通道在图像中出现的次数
#计算每个颜色通道的直方图
colors = ("r","g","b")
#对每个通道绘制直方图
for i,channel in enumerate(colors):
histogram = cv2.calcHist([image_bgr],
[i],
None,
[256],
[0,256])
plt.plot(histogram,color=channel)
plt.xlim([0,256])
#显示图
plt.show()
几乎没有像素的蓝色通道值正在0到180之间,许多像素的蓝色通道值在190到210之间,三个通道所有值的分布都在图中显示出来了。但是该图不仅仅是对数据的可视化,还显示了每个颜色通道的256个特征值,总共768个特征值,代表了图像中颜色的分布