Kaggle-Plant Seedlings Classification-(多分类+CNN+图形处理)

发布于:2025-06-20 ⋅ 阅读:(20) ⋅ 点赞:(0)

Plant Seedlings Classification

题意:

就是给出很多图片,预测出图片中的小草是什么类型的。

数据处理:

1.去除图片背景,只保留物体。
2.图片数据加强。

建立模型:

1.CNN的Sequential(),多层卷积块和全连接层。
2.学习率降低回调函数。
3.查看混淆矩阵进一步考虑优化。

代码:
import cv2
from glob import glob
import numpy as np
from matplotlib import pyplot as plt
import math
import pandas as pd

# 设置图像缩放尺寸和随机种子
ScaleTo = 70  # 图像缩放尺寸
seed = 7  # 随机种子,保证结果可复现

print("读取训练图片" + "/"*70 + "\n")
path = '/kaggle/input/plant-seedlings-classification/train/*/*.png'  # 训练集路径
files = glob(path)  # 获取所有图像文件路径

trainImg = []  # 存储训练图像
trainLabel = []  # 存储训练标签
j = 1
num = len(files)

# 读取图像并调整大小,同时获取标签
for img in files:
    print(str(j) + "/" + str(num), end="\r")
    trainImg.append(cv2.resize(cv2.imread(img), (ScaleTo, ScaleTo)))  # 读取图像并调整大小
    trainLabel.append(img.split('/')[-2])  # 从路径中提取标签(文件夹名)
    j += 1

trainImg = np.asarray(trainImg)  # 转换为numpy数组
trainLabel = pd.DataFrame(trainLabel)  # 转换为DataFrame

# 显示前8张训练图像
for i in range(8):
    plt.subplot(2, 4, i + 1)
    plt.imshow(trainImg[i])

print("去掉图片背景,只保留物体本身" + "/"*70 + "\n")
clearTrainImg = []  # 存储去背景后的图像
examples = []
getEx = True

# 对每张图像进行背景去除处理
for img in trainImg:
    # 使用高斯模糊减少噪声
    blurImg = cv2.GaussianBlur(img, (5, 5), 0)

    # 转换为HSV颜色空间,便于颜色分割
    hsvImg = cv2.cvtColor(blurImg, cv2.COLOR_BGR2HSV)

    # 创建掩码,提取绿色植物区域
    lower_green = (25, 40, 50)  # 绿色下界
    upper_green = (75, 255, 255)  # 绿色上界
    mask = cv2.inRange(hsvImg, lower_green, upper_green)  # 创建二值掩码
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))  # 定义结构元素
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)  # 闭运算填充空洞

    # 创建布尔掩码
    bMask = mask > 0

    # 应用掩码,提取植物区域
    clear = np.zeros_like(img, np.uint8)  # 创建空白图像
    clear[bMask] = img[bMask]  # 仅保留掩码区域的像素

    clearTrainImg.append(clear)  # 保存去背景后的图像

    # 显示处理过程示例
    if getEx:
        plt.subplot(2, 3, 1); plt.imshow(img)  # 原始图像
        plt.subplot(2, 3, 2); plt.imshow(blurImg)  # 模糊图像
        plt.subplot(2, 3, 3); plt.imshow(hsvImg)  # HSV图像
        plt.subplot(2, 3, 4); plt.imshow(mask)  # 掩码
        plt.subplot(2, 3, 5); plt.imshow(bMask)  # 布尔掩码
        plt.subplot(2, 3, 6); plt.imshow(clear)  # 去背景图像
        getEx = False

clearTrainImg = np.asarray(clearTrainImg)  # 转换为numpy数组

# 显示前8张去背景后的图像
for i in range(8):
    plt.subplot(2, 4, i + 1)
    plt.imshow(clearTrainImg[i])

clearTrainImg = clearTrainImg / 255  # 归一化处理

print("处理训练数据集的label" + "/"*70 + "\n")
from tensorflow.keras.utils import to_categorical
from sklearn import preprocessing
import matplotlib.pyplot as plt

# 标签编码
le = preprocessing.LabelEncoder()
le.fit(trainLabel[0])
print("类别: " + str(le.classes_))  # 显示所有类别
encodeTrainLabels = le.transform(trainLabel[0])  # 将标签转换为数字

# 转换为one-hot编码
clearTrainLabel = to_categorical(encodeTrainLabels)
num_clases = clearTrainLabel.shape[1]
print("类别数量: " + str(num_clases))

# 可视化各类别样本数量分布
trainLabel[0].value_counts().plot(kind='bar')

print("分离训练和验证集" + "/"*70 + "\n")
from sklearn.model_selection import train_test_split

# 划分训练集和验证集,保持类别平衡
trainX, testX, trainY, testY = train_test_split(clearTrainImg, clearTrainLabel,
                                                test_size=0.1, random_state=seed,
                                                stratify = clearTrainLabel)

print("数据集加强" + "/"*70 + "\n")
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 创建数据增强器,增加训练样本多样性
datagen = ImageDataGenerator(
        rotation_range=180,  # 随机旋转角度范围
        zoom_range = 0.1,    # 随机缩放范围
        width_shift_range=0.1,  # 水平平移范围
        height_shift_range=0.1,  # 垂直平移范围
        horizontal_flip=True,  # 随机水平翻转
        vertical_flip=True  # 随机垂直翻转
    )
datagen.fit(trainX)  # 应用数据增强器

print("构造神经网络CNN model" + "/"*70 + "\n")
import numpy
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D, BatchNormalization

numpy.random.seed(seed)  # 固定随机种子

# 构建CNN模型
model = Sequential()

# 第一层卷积块
model.add(Conv2D(filters=64, kernel_size=(5, 5), input_shape=(ScaleTo, ScaleTo, 3), activation='relu'))
model.add(BatchNormalization(axis=3))  # 批量归一化
model.add(Conv2D(filters=64, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D((2, 2)))  # 最大池化
model.add(BatchNormalization(axis=3))
model.add(Dropout(0.1))  # Dropout正则化

# 第二层卷积块
model.add(Conv2D(filters=128, kernel_size=(5, 5), activation='relu'))
model.add(BatchNormalization(axis=3))
model.add(Conv2D(filters=128, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization(axis=3))
model.add(Dropout(0.1))

# 第三层卷积块
model.add(Conv2D(filters=256, kernel_size=(5, 5), activation='relu'))
model.add(BatchNormalization(axis=3))
model.add(Conv2D(filters=256, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization(axis=3))
model.add(Dropout(0.1))

# 全连接层
model.add(Flatten())  # 展平多维数据
model.add(Dense(256, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # 较高的Dropout率防止过拟合

model.add(Dense(256, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))

model.add(Dense(num_clases, activation='softmax'))  # 输出层,使用softmax激活函数

model.summary()  # 显示模型结构

# 编译模型
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

print("训练model" + "/"*70 + "\n")

from keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, CSVLogger

# 学习率降低回调函数
learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc',
                                            patience=3,  # 等待3个epoch没有改善
                                            verbose=1,   # 显示更新信息
                                            factor=0.4,  # 学习率降低因子
                                            min_lr=0.00001)  # 最小学习率

callbacks_list = [learning_rate_reduction]  # 回调函数列表

# 训练模型
hist = model.fit(datagen.flow(trainX, trainY, batch_size=75),
                 epochs=35, validation_data=(testX, testY),
                 steps_per_epoch=trainX.shape[0], callbacks=callbacks_list)

print("混淆矩阵" + "/"*70 + "\n")
from sklearn.metrics import confusion_matrix
import itertools

# 绘制混淆矩阵函数
def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    fig = plt.figure(figsize=(10, 10))
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=90)
    plt.yticks(tick_marks, classes)

    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]

    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('真实标签')
    plt.xlabel('预测标签')

# 预测验证集
predY = model.predict(testX)
predYClasses = np.argmax(predY, axis=1)
trueY = np.argmax(testY, axis=1)

# 计算混淆矩阵
confusionMTX = confusion_matrix(trueY, predYClasses)

# 绘制混淆矩阵
plot_confusion_matrix(confusionMTX, classes=le.classes_)

print("处理测试集" + "/"*70 + "\n")
path = '/kaggle/input/plant-seedlings-classification/test/*.png'  # 测试集路径
files = glob(path)

testImg = []  # 存储测试图像
testId = []  # 存储测试图像ID
j = 1
num = len(files)

# 读取测试图像
for img in files:
    print("读取图像: " + str(j) + "/" + str(num), end='\r')
    testId.append(img.split('/')[-1])  # 提取图像ID
    testImg.append(cv2.resize(cv2.imread(img), (ScaleTo, ScaleTo)))  # 调整图像大小
    j += 1

testImg = np.asarray(testImg)  # 转换为numpy数组

# 显示前8张测试图像
for i in range(8):
    plt.subplot(2, 4, i + 1)
    plt.imshow(testImg[i])

# 对测试图像进行背景去除处理(与训练集相同)
clearTestImg = []
examples = []
getEx = True

for img in testImg:
    # 使用高斯模糊
    blurImg = cv2.GaussianBlur(img, (5, 5), 0)

    # 转换为HSV颜色空间
    hsvImg = cv2.cvtColor(blurImg, cv2.COLOR_BGR2HSV)

    # 创建掩码(绿色植物区域)
    lower_green = (25, 40, 50)
    upper_green = (75, 255, 255)
    mask = cv2.inRange(hsvImg, lower_green, upper_green)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

    # 创建布尔掩码
    bMask = mask > 0

    # 应用掩码,提取植物区域
    clear = np.zeros_like(img, np.uint8)
    clear[bMask] = img[bMask]

    clearTestImg.append(clear)  # 保存去背景后的图像

    # 显示处理示例
    if getEx:
        plt.subplot(2, 3, 1); plt.imshow(img)  # 原始图像
        plt.subplot(2, 3, 2); plt.imshow(blurImg)  # 模糊图像
        plt.subplot(2, 3, 3); plt.imshow(hsvImg)  # HSV图像
        plt.subplot(2, 3, 4); plt.imshow(mask)  # 掩码
        plt.subplot(2, 3, 5); plt.imshow(bMask)  # 布尔掩码
        plt.subplot(2, 3, 6); plt.imshow(clear)  # 去背景图像
        getEx = False

clearTestImg = np.asarray(clearTestImg)  # 转换为numpy数组

# 显示前8张去背景后的测试图像
for i in range(8):
    plt.subplot(2, 4, i + 1)
    plt.imshow(clearTestImg[i])

clearTestImg = clearTestImg / 255  # 归一化处理

# 预测测试集
pred = model.predict(clearTestImg)

# 处理预测结果
predNum = np.argmax(pred, axis=1)  # 获取最大概率的类别索引
predStr = le.classes_[predNum]  # 将索引转换为类别名称

# 保存结果到CSV文件
res = {'file': testId, 'species': predStr}
res = pd.DataFrame(res)
res.to_csv("/kaggle/working/res.csv", index=False)

网站公告

今日签到

点亮在社区的每一天
去签到