利用cv2.calcHist函数计算出颜色直方图后,将其归一化后返回给调用函数。
有关extract_color_histogram方法更详细的描述,参阅这篇博客(
http://www.pyimagesearch.com/2016/08/08/k-nn-classifier-for-image-classification/)。
接着,我们从命令行解析参数,并初始化几个变量:
# import the necessary packages
from sklearn.preprocessing import LabelEncoder
from sklearn.svm import LinearSVC
from sklearn.metrics import classification_report
from sklearn.cross_validation import train_test_split
from imutils import paths
import numpy as np
import argparse
import imutils
import cv2
import os
def extract_color_histogram(image, bins=(8, 8, 8)):
# extract a 3D color histogram from the HSV color space using
# the supplied number of `bins` per channel
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1, 2], None, bins,
[0, 180, 0, 256, 0, 256])
# handle normalizing the histogram if we are using OpenCV 2.4.X
if imutils.is_cv2():
hist = cv2.normalize(hist)
# otherwise, perform "in place" normalization in OpenCV 3 (I
# personally hate the way this is done
else:
cv2.normalize(hist, hist)
# return the flattened histogram as the feature vector
return hist.flatten()
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required=True,
help="path to input dataset")
args = vars(ap.parse_args())
# grab the list of images that we'll be describing
print("[INFO] describing images...")
imagePaths = list(paths.list_images(args["dataset"]))
# initialize the data matrix and labels list
data = []
labels = []
33行到36行解析命令行参数。我们这里只需要一个简单的开关,—dataset是kaggle 猫vs狗数据集的路径。
然后我们将25000张图片保存的磁盘位置赋值给imagePaths,跟着初始化一个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 as the format: /path/to/dataset/{class}.{image_num}.jpg
image = cv2.imread(imagePath)
label = imagePath.split(os.path.sep)[-1].split(".")[0]
# extract a color histogram from the image, then update the
# data matrix and labels list
hist = extract_color_histogram(image)
data.append(hist)
labels.append(label)
# show an update every 1,000 images
if i > 0 and i % 1000 == 0:
print("[INFO] processed {}/{}".format(i, len(imagePaths)))
在47行,我们开始对输入的imagePaths进行遍历,对于每个imagePath,我们从磁盘中加载image,提取类别label,然后通过计算颜色直方图来量化图片。然后我们更新data和labels各自的列表。
目前,我们的labels是一个字符串列表,如“狗”或者“猫”。但是,很多scikit-learn中的机器学习算法倾向于将labels编码为整数,每个标签有一个唯一的数字。
使用LabelEncoder类可以很方便的将类别标签从字符串转变为整数:
# import the necessary packages
from sklearn.preprocessing import LabelEncoder
from sklearn.svm import LinearSVC
调用了 .fit_transform方法后,现在我们的labels表示为整数列表。
代码的最后一部分将数据划分为训练和测试两组、训练线性SVM、评估模型。
# partition the data into training and testing splits, using 75%
# of the data for training and the remaining 25% for testing
print("[INFO] constructing training/testing split...")
(trainData, testData, trainLabels, testLabels) = train_test_split(
np.array(data), labels, test_size=0.25, random_state=42)
# train the linear regression clasifier
print("[INFO] training Linear SVM classifier...")
model = LinearSVC()
model.fit(trainData, trainLabels)
# evaluate the classifier
print("[INFO] evaluating classifier...")
predictions = model.predict(testData)
print(classification_report(testLabels, predictions,
target_names=le.classes_))
70和71行构造了训练集和测试集。我们将75%的数据用于训练,剩下的25%用于测试。
我们将使用scikit-learn库实现的LinearSVC(75和76行)来训练线性SVM。
最后,80到82行评估我们的模型,显示一个格式整齐的报告,来说明模型的执行情况。
需要注意的一点是,我故意没有进行调参,只是为了让这个例子简单,容易理解。不过,既然提到了,我就把LinearSVC 分类器的调参作为练习留个读者。可以参考我以前的k-NN分类器调参博客。
评估线性分类器
为了测试我们的线性分类器,确保你已经下载了:
1. 本博客中的源代码,可以使用教程底部的“下载”部分。
2. kaggle 猫vs狗数据集
有了代码和数据集之后,你可以执行如下命令:
$ python linear_classifier.py --dataset kaggle_dogs_vs_cats
特征提取过程大约要花费1-3分钟不等,具体时间根据机器的速度。
之后,训练并评估我们的线性SVM: