此次图像分类器是用于猫狗分类,数据量比较小;分类器采用k-Nearest Neighbors(k-NN)方法。
从Kaggle Dogs vs. Cats挑战中抽取狗和猫图像各1000张。这个数据集包含2000个图片。
knnClassifier
│ ├── pycache
│ ├── animals
│ ├── dataset.py
│ ├── knn.py
│ ├── simpledatasetloader.py
│ └── simplepreprocessor.py
在开始之前,构建调整图像大小的图像预处理器,忽略纵横比:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-12-21 02:17:37
# @Author : Chen Cjv ([email protected])
# @Link : https://blog.csdn.net/weixin_44049128
# @Version : $Id$
import cv2 # OpenCV-Python库
class SimplePreprocessor:
# 使用像素区域关系进行重采样。它可能是图像抽取的首选方法,因为它会产生无云纹理的结果
def __init__(self, width, height, inter=cv2.INTER_AREA):
# 存储调整大小时使用的目标图像宽度,高度和插值方法
self.width = width
self.height = height
self.inter = inter
def preprocess(self, image):
# 将图像调整为固定大小,忽略纵横比
return cv2.resize(image, (self.width, self.height), interpolation=self.inter)
参数说明:
- width:调整大小后输入图像的目标宽度。
- height:调整大小后输入图像的目标高度。
- inter:一个可选参数,用于控制调整大小时使用的插值算法。(使用像素区域关系进行重采样。 它可能是图像抽取的首选方法,因为它会产生无云纹理的结果。)
函数preprocess
需要需要一个参数——我们要预处理的输入图像。通过cv2.resize
将图像调整为固定大小的宽度和高度来预处理图像,然后返回到调用函数。
定义好图像处理器之后,开始定义图像加载器:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-12-21 02:16:19
# @Author : Chen Cjv ([email protected])
# @Link : https://blog.csdn.net/weixin_44049128
# @Version : $Id$
import numpy as np
import cv2
import os
class SimpleDatasetLoader:
def __init__(self, preprocessors=None):
# 存储图像预处理器
self.preprocessors = preprocessors
# 如果预处理器为None,则将它们初始化为空列表
if self.preprocessors is None:
self.preprocessors = []
def load(self, imagePaths, verbose=-1):
# 初始化特征和标签列表
data = []
labels = []
# 循环输入图像
for (i, imagePath) in enumerate(imagePaths):
# 加载图像并提取类标签,假设我们的路径具有以下格式:
# /path/to/dataset/{class}/{image}.jpg
image = cv2.imread(imagePath)
label = imagePath.split(os.path.sep)[-2]
# 检查我们的预处理器是否不是None
if self.preprocessors is not None:
# 循环预处理器并将每个应用于图像
for p in self.preprocessors:
image = p.preprocess(image)
# 通过更新数据列表后跟标签,将处理后的图像视为“特征向量”
data.append(image)
labels.append(label)
# 显示每个'verbose'图像的更新
if verbose > 0 and i > 0 and (i + 1) % verbose == 0:
print("[INFO] processed {}/{}".format(
i + 1, len(imagePaths)))
# 返回数据和标签的元组
return (np.array(data), np.array(labels))
k-Nearest Neighbor 分类器是迄今为止最简单的机器学习和图像分类算法。事实上,它很简单,实际上并没有“学习”任何东西。相反,该算法直接依赖于特征向量之间的距离(在我们的例子中,是图像的原始RGB像素强度)
简而言之,k-NN算法通过在k个最近的例子中找到最常见的类来对未知数据点进行分类。 k个最接近的数据点中的每个数据点投票,并且具有最高投票数的类别获胜。如图1所示,对狗进行分类:
图1 狗的k-NN 分类
为了应用k-NN分类器,我们首先需要选择距离度量或相似度函数。 常见的选择
包括欧几里德距离(通常称为L2距离):
(1) d ( p , q ) = ∑ i = 1 N ( q i − p i ) 2 d(p, q)=\sqrt{\displaystyle{\sum_{i=1}^N(q_i-p_i)^2}}\tag{1} d(p,q)=i=1∑N(qi−pi)2(1)
但是,也可以使用其他距离指标,例如北京城市街区(通常称为L1距离):
(2) d ( p , q ) = ∑ i = 1 N ∣ q i − p i ∣ d(p, q)={\displaystyle{\sum_{i=1}^N|q_i-p_i|}}\tag{2} d(p,q)=i=1∑N∣qi−pi∣(2)
最流行的是采用:欧几里德距离。
下面是knn.py代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-12-21 11:12:19
# @Author : Chen Cjv ([email protected])
# @Link : https://blog.csdn.net/weixin_44049128
# @Version : $Id$
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from simplepreprocessor import SimplePreprocessor
from simpledatasetloader import SimpleDatasetLoader
from imutils import paths
import argparse
if __name__ == '__main__':
# 构造参数解析并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required=True,
help= "path to input dataset")
ap.add_argument("-k", "--neighbors", type=int, default=1,
help="# of nearest neighbors for classification")
ap.add_argument("-j", "--jobs", type=int, default=-1,
help="# of jobs for k-NN distance (-1 uses all available cores)")
args = vars(ap.parse_args())
# 抓住我们将要描述的图像列表
print("[INFO] loading images...")
imagePaths = list(paths.list_images(args["dataset"]))
# 初始化图像预处理器,从磁盘加载数据集,并重塑数据矩阵
sp = SimplePreprocessor(32, 32)
sdl = SimpleDatasetLoader(preprocessors=[sp])
(data, labels) = sdl.load(imagePaths, verbose=100)
data = data.reshape((data.shape[0], 3072))
# 显示有关图内存消耗的一些信息
print("[INFO] features matrix: {:.1f}MB".format(
data.nbytes / (1024 * 1000.0)))
# 将标签编码为整数
le = LabelEncoder()
labels = le.fit_transform(labels)
# 使用75%的数据进行训练并将剩余的25%用于测试,将数据划分为训练和测试分组
(trainX, testX, trainY, testY) = train_test_split(data, labels,
test_size=0.25, random_state=42)
# 在原始像素强度上训练和评估k-NN分类器
print("[INFO] evaluating k-NN classifier...")
model = KNeighborsClassifier(n_neighbors=3)
model.fit(trainX, trainY)
print(classification_report(testY, model.predict(testX),
target_names=le.classes_))
执行knn.py,需要三个参数:
输入以下命令,执行:
$ python knn.py --dataset ./animals
输出如下图2所示:
图2 结果显示平均识别准确率是54%,对猫的识别准确度 是55%,对狗是52%。结果可归因于:狗和猫可以具有非常相似的皮毛涂层,并且它们的涂层的颜色不能用于区分它们。
参考: