使用OpenCV-python对植物图片进行分类

文章目录

  • 图片分类的思路和需要加载的库函数说明
  • 核心代码分析
    • 数据预处理:文件处理和命名匹配
    • 数据预处理:特征提取和标签提取
    • 学习模型:数据划分和模型训练
    • 预测输出:利用训练好的模型进行图片分类
  • 完整代码附录和运行结果

图片分类的思路和需要加载的库函数说明

图片分类的基本思路如下

  1. 首先需要有一个标注好图片里面花朵名称的数据集,例如命名好的rose.png
  2. 特征提取,即提取图片中的重要特征(能区别于其他图片的特征),并把重要特征加工成可以训练的数据,因为每种花的颜色分布不同,这里的思路是计算图片的彩色直方图信息,作为图片的特征。
  3. 标签提取,即把图片标注加工成数值信息,这里的思路是把图片的字符串名字转换成唯一的数字序列。
  4. 挑选一个分类器模型,例如随机森林分类器,然后划分训练集和测试集进行训练,得到学习器,即分类模型。
  5. 用这个训练好的模型,对图片进行分离,即利用模型预测。

需要使用的库函数和功能说明

# 标签处理
from sklearn.preprocessing import LabelEncoder

# 随机森林分类器模型
from sklearn.ensemble import RandomForestClassifier

# 对数据集进行有效划分的方法,划分成训练集和测试集
# from sklearn.cross_validation import train_test_split
# 在python新版本中,train_test_split被移动到model_selection中
from sklearn.model_selection import train_test_split

# 分类器性能评估报告
from sklearn.metrics import classification_report

# 基本矩阵运算库
import numpy as np

# 文件操作模块,主要是进行文件搜索和通配符操作
import glob

# opencv函数库
import cv2 

核心代码分析

数据预处理:文件处理和命名匹配

glob函数使用参考:python glob model - 漩涡鸣人 - 博客园

# 加载所有的图片和蒙版
imagePath = "dataset/images"
maskPath  = "dataset/masks"
# 利用sorted函数实现名字的一一对应,对每个图片进行蒙版的加载
imagePaths = sorted(glob.glob(imagePath + "/*.png"))
maskPaths  = sorted(glob.glob(maskPath + "/*.png"))

数据预处理:特征提取和标签提取

# 计算直方图,返回的是均一化之后的彩色直方图数据
features = desc.describe(image,mask)
# 构造测试数据集的特征信息集合
data.append(features)
# 注意图片的命名格式为"image_crocus_0001.png",我们需要提取的"crocus"是倒数第二个
target.append(imagePath.split("_")[-2])
# 初始化一个打标签的对象
le = LabelEncoder()
# 对target进行数字化标签index(因为字符串这种特征是不能直接拿来训练的,需要转换成唯一的数值)
target = le.fit_transform(target)

标签处理:unique()fit_transform()的用法

target = ["happy","birthday","happy","wedding","happy","ending"]

unique对这串字符串进行去重:

targetNames = np.unique(target)
print(targetNames)
['birthday' 'ending' 'happy' 'wedding']

fit_transform最后算出每个字符串label对应的标签序号,此时brithdayendinghappywedding按照字母表顺序,分别对应0~3。

le = LabelEncoder()
target = le.fit_transform(target)
print(target)
[2 0 2 3 2 1]

学习模型:数据划分和模型训练

# 对数据进行训练集和测试集的划分,划分比例是7:3
(trainData,testData,trainTarget,testTarget) = \
    train_test_split(data,target,test_size=0.3,random_state=42)

# 初始化一个随机森林分类器模型
model = RandomForestClassifier(n_estimators=25,random_state=84)
# 用训练集训练这个分类器模型
model.fit(trainData,trainTarget)

# 用测试集合评估分类器性能,并打印出来
print(classification_report(testTarget, model.predict(testData),\
                            target_names=targetNames))

预测输出:利用训练好的模型进行图片分类

# 生成图片的特征(彩色直方图)
features = desc.describe(image,mask)
# 预测输出,从数字index转换成对应的字符串标签
flower = le.inverse_transform(model.predict([features]))[0]

完整代码附录和运行结果

from sklearn.preprocessing import LabelEncoder
from sklearn.ensemble import RandomForestClassifier
# from sklearn.cross_validation import train_test_split
# 在python新版本中,train_test_split被移动到model_selection中
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
import numpy as np
import glob
import cv2 

# 定义一个RGB直方图的类和方法
class RGBHistogram:
    def __init__(self,bins):
        self.bins = bins
    # 定义对图片特征的描述为彩色直方图
    def describe(self,image,mask=None):
        # 计算三个通道的直方图
        hist=cv2.calcHist([image],[0,1,2],mask,self.bins,[0,256,0,256,0,256])
        # 直方图均一化,减少图片亮度和对比度信息的干扰
        cv2.normalize(hist,hist)
        return hist.flatten()

# 加载所有的图片和蒙版
imagePath = "dataset/images"
maskPath  = "dataset/masks"
# 利用sorted函数实现名字的一一对应,对每个图片进行蒙版的加载
imagePaths = sorted(glob.glob(imagePath + "/*.png"))
maskPaths  = sorted(glob.glob(maskPath + "/*.png"))

# data保存彩色直方图信息,保存测试数据集的feature
data = []
# target保存图片名称的字符串信息,相当于测试数据的label
target = []

# 实例化一个计算RGB直方图的对象,类似于一个操作句柄
desc = RGBHistogram([8,8,8])

# 数据预处理,把照片信息转换成可训练的数据
for (imagePath,maskPath) in zip(imagePaths,maskPaths):
    # 读取照片和蒙版
    image = cv2.imread(imagePath)
    mask  = cv2.imread(maskPath)
    # 虽然蒙版已经是黑白的了,但要确保图片格式也是灰度表示的
    mask  = cv2.cvtColor(mask,cv2.COLOR_BGR2GRAY)
    # 计算直方图,返回的是均一化之后的彩色直方图数据
    features = desc.describe(image,mask)
    # 构造测试数据集的特征信息集合
    data.append(features)
    # 注意图片的命名格式为"image_crocus_0001.png",我们需要提取的"crocus"是倒数第二个
    target.append(imagePath.split("_")[-2])

# 这时候的target里面保存的是字符串,并不是具体的数值,还不能进行学习
# 对名字进行去重处理,注意target本身没变,targetNames是去重之后的标签名
targetNames = np.unique(target)
# 初始化一个打标签的对象
le = LabelEncoder()
# 对target进行数字化标签index(因为字符串这种特征是不能直接拿来训练的,需要转换成唯一的数值)
target = le.fit_transform(target)

######################### 至此,数据预处理已经全部完成 ##############################

# 对数据进行训练集和测试集的划分,划分比例是7:3
(trainData,testData,trainTarget,testTarget) = \
    train_test_split(data,target,test_size=0.3,random_state=42)

# 初始化一个随机森林分类器模型
model = RandomForestClassifier(n_estimators=25,random_state=84)
# 用训练集训练这个分类器模型
model.fit(trainData,trainTarget)

# 用测试集合评估分类器性能,并打印出来
print(classification_report(testTarget, model.predict(testData),\
                            target_names=targetNames))

######################### 至此,训练已经全部完成 ##############################

# 随机抽取10个案例进行测试
for i in np.random.choice(np.arange(0,len(imagePaths)),10):
    imagePath = imagePaths[i]
    maskPath  = maskPaths[i]
    image = cv2.imread(imagePath)
    mask  = cv2.imread(maskPath)
    mask  = cv2.cvtColor(mask,cv2.COLOR_BGR2GRAY)
    
    # 生成特征(彩色直方图)
    features = desc.describe(image,mask)
    # 预测输出,从数字index转换成对应的字符串标签
    flower = le.inverse_transform(model.predict([features]))[0]
    print(imagePath)
    print("According to RFT, this flower is {}".format(flower))
    cv2.imshow("predicted to be {}".format(flower.upper()),image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
cv2.destroyAllWindows()

学习器的性能如下

 				precision    recall  f1-score   support

      crocus       0.92      1.00      0.96        12
       daisy       0.88      0.93      0.90        15
       pansy       1.00      0.85      0.92        20
   sunflower       0.96      1.00      0.98        24

    accuracy                           0.94        71
   macro avg       0.94      0.95      0.94        71
weighted avg       0.95      0.94      0.94        71


预测输出效果:

你可能感兴趣的:(OpenCV)