像素是图像最基础的构成要素,每一张图像都是由像素集合组成。
如果我们将图像当作一个网格,则每一小块是由单个像素组成,如下图:
上图的分辨率为1000 * 750,意味着有1000像素宽,750像素高。可以将一张图像看作一个多维矩阵,此时矩阵为1000列(宽)* 750行(高),在图像中一共有1000 * 750 = 750,000个像素组成。
大多数像素在两种情况作为表示:
1.灰度/单通道
2.颜色
在灰度图中,每个像素的值在【0,255】的范围之间,其中0对应黑色,而255当作白色。在0到255之间的值是不同程度的灰色,靠近0越来越黑,靠近255的越来越白。如下图:
在RGB的颜色通道中,像素则代表了3个值的列表,一个对应红色,一个对应绿色,一个对应蓝色。同样的,每个颜色通道取值也在范围【0,255】中,0代表不表示当前颜色,255代表全部表示当前颜色。
给定红绿蓝三种颜色的值,可以将他们以(红,绿,蓝)的格式组合成一个元组,这个元组代表着RGB通道中的图片。
在运用中,我们可以用矩阵表示法将RGB图片组成3个独立的W * H的矩阵,通过将RGB三个通道的W * H矩阵结合,可以得到一个多维矩阵(W * H * D),其中D代表深度或者通道的个数(对于RGB通道,D = 3)。
例如,使用OpenCV库和cv2的imread函数来读取图片,并展示它的维度:
import cv2
image = cv2.imread("example.png")
print(image.shape)
cv2.imshow("Image", image)
cv2.waitKey(0)
利用上面一段代码可以得到输入图片的维度(W * H * D):
(731, 1024, 3)
同样我们可以指定图片中某一维度的坐标,得到RGB的值,如下:
1 (b, g, r) = image[20, 100] # accesses pixel at x=100, y=20
2 (b, g, r) = image[75, 25] # accesses pixel at x=25, y=75
3 (b, g, r) = image[90, 85] # accesses pixel at x=85, y=90
注意:实际在OpenCV中取出的并非RGB而是BGR(由于历史原因),另外坐标第一维通常是Y,第二维时X。
关于图像的尺寸变换:
通常在图像处理中,会对图像加以压缩或者伸展,但是图像本身具有长宽比,如果不以图像本身的长宽比处理,会造成图像畸变或者扭曲。对于卷积神经网络而言,通常以固定尺寸作为输入,如:32*32,64*64,224*224,227*227,256*256。这就要求我们要对图像进行预处理,将尺寸变换为合适大小。
此时有两种方案:1,不考虑图像本身的长宽比,直接进行尺寸变换;
2,根据图像本身的长宽比变换到最小维度,取图像中心区域的需要识别的一块。实际在应用中,视情况而定,对于一些数据集可以忽视长宽比。而在另外的一些数据集中,第二种会表现出较好的效果。
图像分类的核心是将一个标签分配给一个预定义的图像的任务,目标就是获取这个输入图像并从我们类别中给它分配一个标签,即给一个W * H的RGB三通道图像,根据这W * H * 3 = N 个像素得出如何正确分类图像的内容。
数据集:一组图片
数据点:每张图片
图像分类面临的挑战
1.视角变化
2.尺度变化
3.遮挡变化
4.变形(曲变等)
5.光照变化
6.背景噪声
7.同类间变化(如沙发椅、艺术椅和办公椅等同类之间的变化)
由于图像分类中存在以上并不止以上的挑战,所以图像分类的关键是:
要始终考虑分类器的范围
除非你是有多年经验的图像分类专家,你也不一定有能力构建能把“整个厨房里所有的物体”准确识别的分类器。但是如果把问题框定在某一范围内,比如识别火炉和冰箱,还是很有可能完成的。
ImageNet是图像分类算法的事实上的标准基准数据集,它包含了我们日常生活中遇到的1000个对象,而这个数据集仍然被研究者们积极地使用,试图将最先进的技术推向深入学习的前沿。
图像分类步骤
1.获取数据集
构建深度学习网络的第一个组件是收集我们的初始数据集。我们需要图像本身以及与每个图像相关的标签。这些标签应该来自有限的类别,例如:类别=狗,猫,熊猫。
此外,每个类别的图像数量应该近似一致(即:,每个类别的例子数量相同)。如果我们的猫图像数量是狗图像的两倍,而熊猫图像的数量是猫图像的五倍,那么我们的分类器就会自然地偏向于过度拟合这些重量级的类别。
2.数据集划分
一般分为:训练集和测试集,两者保持独立。
在机器学习中常见交叉验证。训练和测试集的常见分割大小包括66:6%和33:3%、75%和25%以及90%和10%。
对于网络中超参数的调节,可以设置验证集来对网络超参数进行调节。
3.训练网络
考虑到我们的训练集,我们现在可以训练我们的网络。这里的目标是让我们的网络学习如何识别标签数据中的每一个类别。当模型出错时,它会从错误中吸取教训并自我完善。
4.评估
最后,我们需要评估我们经过训练的网络。对于我们测试集中的每一个图像,我们将它们呈现给网络,并要求它预测图像的标签是什么。然后,我们对测试集中的图像的模型进行对比。
最后,将这些模型预测与我们测试集的地面真相标签进行比较。从那里,我们可以计算出我们的分类器得到正确的预测的数量,并计算诸如precision、recall和f-measure这样的聚合报告,这些报告用于量化我们整个网络的性能。
需要注意的是:
在传统的基于特征的图像分类方法中,实际上在步骤2和步骤3之间插入了一个步骤,这一步就是特征提取。在这一阶段,我们应用诸如HOG、LBPs等手工工程算法来量化基于我们想要编码的图像的某一特定组件的图像的内容(形状、颜色、纹理)。考虑到这些特征,我们接着训练分类器并对其进行评估。
在构建卷积神经网络时,我们实际上可以跳过特征提取步骤。这是因为CNNs是端到端的模型。我们将原始输入数据(像素)呈现给网络。然后,网络在其隐藏层中学习过滤器,可以用来区分对象类。然后,网络的输出是在类标签上的概率分布。
图像分类最重要的指标:泛化能力。即一个网络对一个不作为其训练或测试数据一部分的图像的类标签进行一般化和正确预测的能力。
机器学习算法,如k-NN, SVMs,甚至卷积神经网络都要求数据集中的所有图像具有固定的特征向量大小。在图像的情况下,这个要求意味着我们的图像必须经过预处理和缩放,以达到相同的宽度和高度。
有许多方法可以实现这种大小调整和缩放,从更高级的方法,比如尊重原始图像的宽比和缩放图像,到忽略宽高比的简单方法,简单地将宽度和高度压缩到所需的尺寸。确切地说,你应该使用哪种方法取决于你的变化因素的复杂性,在某些情况下,忽略长宽比很好;在其他情况下,您将希望保持长宽比。
1.加载图片
def load(self,imagePaths,verbose = 1):
'''
initialize the list of feat
'''
data = []
labels = []
#loop over the input images
for(i,imagePath) in enumerate(imagePaths):
'''
load the image and extract the class label assuming
that our path has the following format:
/path/to/dataset/{class}/{image}.jpg
'''
image = cv2.imread(imagePath)
label = imagePath.split(os.path.sep)[-2]
加载图片需要用到图片所在的路径,其中:
enumerate()说明
enumerate()是python的内置函数
enumerate在字典上是枚举、列举的意思
对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值
enumerate多用于在for循环中得到计数
例如对于一个seq,得到:
(0, seq[0]), (1, seq[1]), (2, seq[2])