水果和动物分类识别系统
1. 效果演示
水果和动物分类识别系统
2. 系统界面
3. 系统阐述
- 环境设置:首先,通过设置TF_CPP_MIN_LOG_LEVEL环境变量为3,来抑制TensorFlow在运行时输出的警告信息,以便于观察模型训练过程中的关键信息。
- 导入必要的库:导入TensorFlow库以及用于构建模型的Sequential、Conv2D、MaxPooling2D、Flatten和Dense等类。
- 构建CNN模型:使用Sequential模型构建一个简单的CNN结构,该结构包含两个卷积层、两个最大池化层、一个展平层和两个全连接层。模型的输出层有两个神经元,使用softmax激活函数,因为这是一个二分类问题。
- 编译模型:使用Adam优化器和交叉熵损失函数来编译模型,并指定准确率作为评估指标。
- 数据增强:使用ImageDataGenerator类来增强训练数据,通过随机剪切、缩放和水平翻转等操作来增加数据的多样性,提高模型的泛化能力。
- 准备数据:使用flow_from_directory方法从指定的目录中加载并预处理图像数据。这里假设数据集的目录结构是按照类别组织的,每个类别下有多个子目录,每个子目录包含该类别的图像。
- 训练模型:使用fit方法训练模型,通过生成器提供的数据进行训练。这里设置了60个epoch,并且每个epoch中使用生成器提供的数据数量作为训练步数。
- 保存模型:训练完成后,将模型保存为.h5文件,以便将来可以重新加载和使用。
- 用python的flask框架写一个界面,方便用户的使用的测试。
4. 分析与设计
1. 系统功能概述
数据预处理:对图像数据进行归一化处理,以及数据增强,如随机剪切、缩放和水平翻转。
模型构建:构建一个卷积神经网络(CNN),用于图像分类任务。
模型训练:使用增强后的数据训练CNN模型。
模型评估:评估训练好的模型在验证集上的性能。
模型保存:将训练好的模型保存为文件,以便未来使用。
2. 数据预处理
归一化:将图像数据的像素值缩放到0-1之间。
数据增强:通过随机剪切、缩放和水平翻转等操作增加数据多样性。
3. 模型构建
卷积层:使用3x3卷积核提取图像特征。
激活函数:使用ReLU激活函数。
池化层:使用2x2最大池化层减少特征图尺寸。
全连接层:使用128个神经元的全连接层。
输出层:使用2个神经元的输出层,使用softmax激活函数。
4. 模型训练
优化器:使用Adam优化器。
损失函数:使用交叉熵损失函数。
训练周期:训练60个epoch。
5. 模型评估
准确率:使用准确率作为评估指标。
6. 模型保存
保存格式:保存为.h5文件。
5. 编程实现:
1. 构建CNN模型:
def build_cnn_model(input_shape):
"""
构建一个卷积神经网络模型。
:param input_shape: 输入数据的形状,例如(64, 64, 3)。
:return: 构建好的模型。
"""
model = Sequential()
# 第一层卷积层,32个3x3的卷积核,激活函数为ReLU
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
# 第一层最大池化层,池化窗口大小为2x2
model.add(MaxPooling2D(pool_size=(2, 2)))
# 第二层卷积层,64个3x3的卷积核
model.add(Conv2D(64, (3, 3), activation='relu'))
# 第二层最大池化层,池化窗口大小为2x2
model.add(MaxPooling2D(pool_size=(2, 2)))
# 展平层,将多维数据展平为一维
model.add(Flatten())
# 第一层全连接层,128个神经元,激活函数为ReLU
model.add(Dense(128, activation='relu'))
# 输出层,2个神经元,激活函数为softmax(因为是二分类问题)
model.add(Dense(2, activation='softmax'))
return model
2. 编译模型:
def compile_model(model):
"""
编译模型,设置优化器、损失函数和评估指标。
:param model: 要编译的模型。
"""
# 使用Adam优化器,损失函数为交叉熵,评估指标为准确率
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
3. 准备数据增强生成器
def prepare_data_augmentation():
"""
准备数据增强生成器,进行图像预处理。
:return: 数据增强生成器。
"""
# 创建一个ImageDataGenerator实例,进行归一化、随机剪切、随机缩放和随机水平翻转
train_datagen = ImageDataGenerator(
rescale=1. / 255, # 归一化
shear_range=0.2, # 随机剪切变换
zoom_range=0.2, # 随机缩放变换
horizontal_flip=True # 随机水平翻转
)
return train_datagen
4. 准备数据生成器
def prepare_data_generator(train_datagen, data_dir, target_size, batch_size, class_mode):
"""
准备数据生成器,用于从指定目录加载图像数据。
:param train_datagen: 数据增强生成器。
:param data_dir: 数据集的根目录。
:param target_size: 图像大小调整为(target_size[0], target_size[1])。
:param batch_size: 每批处理的图像数量。
:param class_mode: 类别模式,'categorical'表示输出为one-hot编码的类别标签。
:return: 数据生成器。
"""
# 使用ImageDataGenerator的flow_from_directory方法创建数据生成器
train_generator = train_datagen.flow_from_directory(
data_dir, # 数据集的根目录
target_size=target_size, # 图像大小调整为64x64
batch_size=batch_size, # 每批图片数量
class_mode=class_mode # 类别模式为categorical
)
return train_generator
5. 训练模型:
def train_model(model, train_generator, epochs, steps_per_epoch):
"""
使用数据生成器训练模型。
:param model: 要训练的模型。
:param train_generator: 数据生成器。
:param epochs: 训练的轮数。
:param steps_per_epoch: 每个epoch中,使用生成器提供的数据数量作为训练步数。
"""
# 使用fit_generator方法训练模型
model.fit_generator(
train_generator, # 使用生成器提供的数据进行训练
steps_per_epoch=steps_per_epoch, # 每个epoch中,使用生成器提供的数据数量作为训练步数
epochs=epochs, # 训练60个epoch
verbose=1 # 设置verbose为1,表示在训练过程中打印进度条
)
6. 保存模型:
def save_model(model, filename):
"""
保存训练好的模型。
:param model: 要保存的模型。
:param filename: 保存模型的文件名。
"""
# 使用save方法保存模型
model.save(filename)
7. 主程序:
def main():
# 定义输入形状
input_shape = (64, 64, 3)
# 构建模型
model = build_cnn_model(input_shape)
# 编译模型
compile_model(model)
# 准备数据增强
train_datagen = prepare_data_augmentation()
# 准备数据生成器
train_generator = prepare_data_generator(train_datagen, 'FruitAnimal', (64, 64), 32, 'categorical')
# 训练模型
train_model(model, train_generator, 60, len(train_generator))
# 保存模型
save_model(model, 'fruit_animal_classifier.h5')
# 运行主程序
if __name__ == '__main__':
main()
测试与运行结果及其分析:
我发现后面要不停换照片来测试自己的模型是否可以分类,也就是模型是否训练好了,修改路径虽然也可以实现,但是有点麻烦,就写了一个网站,来上传图片,点击预测就可以调用模型来识别是啥东西了。
1. 模型预测的代码
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # 设置TensorFlow的日志级别为3,不显示警告信息
import tensorflow as tf
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import load_img, img_to_array
import numpy as np
def predict_image(img_path):
# 加载模型
model = load_model('fruit_animal_classifier.h5') # 加载之前保存的模型
# 预测单张图片
img = load_img(img_path, target_size=(64, 64)) # 加载图片并调整大小为64x64
img_array = img_to_array(img) # 将图片转换为数组
img_array = np.expand_dims(img_array, axis=0) / 255 # 增加一个维度并归一化图片数据
# 预测图片
prediction = model.predict(img_array) # 使用模型进行预测
# 解释预测结果
if prediction[0][0] > 0.5:
return "动物" # 如果预测结果大于0.5,则输出“动物”
else:
return "水果" # 否则输出“水果”
# 使用函数进行预测
img_path = 'fruit.png' # 替换为您的图片路径
result = predict_image(img_path)
print(result)
2. 前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>水果与动物分类识别系统</title>
<style>
body {
background-color: lightblue;
font-family: Arial, sans-serif;
text-align: center;
margin-top: 50px;
}
.upload-container {
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
border-radius: 5px;
margin-bottom: 20px;
}
.upload-button {
display: inline-block;
padding: 10px 20px;
color: white;
border: none;
cursor: pointer;
margin: 0 5px;
border-radius: 3px;
text-align: center;
}
.select-file {
background-color: #2196F3;
}
.upload-audio {
background-color: #4CAF50;
}
.audio-container {
text-align: center;
margin-top: 20px;
}
.file-type {
font-size: 12px;
color: #666;
}
h1 {
font-size: 2em; /* 加大一号字体 */
letter-spacing: 0.1em; /* 添加点空格 */
color: #333333;
}
</style>
</head>
<body>
<h1>水果与动物分类识别系统</h1>
<p class="file-type">只能上传图片</p>
<div class="upload-container">
<form action="/success" method="post" enctype="multipart/form-data">
<!-- 隐藏的文件输入框 -->
<input type="file" name="file" id="fileInput" style="display:none;" accept="image/*">
<button type="button" class="upload-button select-file" onclick="selectFile()">
选择图片
</button>
<button type="submit" class="upload-button upload-audio">开始检测</button>
</form>
</div>
{% if flag == 'fruit' %}
<img src="{{ img_url }}" alt="水果">
<br>
<img src="../static/fruit.png" alt="水果">
{% elif flag == 'animal' %}
<img src="{{ img_url }}" alt="动物">
<br>
<img src="../static/animal.png" alt="动物">
{% endif %}
<script>
function selectFile() {
var fileInput = document.getElementById('fileInput');
fileInput.click(); // 触发隐藏的文件输入框的点击事件
}
// 为隐藏的文件输入框绑定 onchange 事件
document.getElementById('fileInput').onchange = function () {
var file = this.files[0];
var imgContainer = document.querySelector('.image-container');
imgContainer.innerHTML = ''; // 清空之前的图片
if (file) {
var imgElem = document.createElement('img');
imgElem.src = URL.createObjectURL(file);
imgElem.classList.add('image');
imgContainer.appendChild(imgElem);
} else {
alert("请选择一个图片文件!");
}
};
// 为“开始检测”按钮绑定点击事件
document.querySelector('.upload-audio').onclick = function (event) {
var fileInput = document.getElementById('fileInput');
var file = fileInput.files[0];
if (!file) {
event.preventDefault(); // 阻止表单提交
alert("请选择一个图片文件!");
}
};
</script>
</body>
</html>
简单写了一哈前端,也就是两个按钮,一个上传图片,一个预测图片内容。
3. 后端代码
from flask import Flask, render_template, request
from use_model import predict_image
app = Flask(__name__)
# 设置请求体的最大大小为 16MB
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
@app.route('/')
def index():
return render_template('upload.html')
@app.route('/success', methods=['POST'])
def upload_file():
f = request.files['file']
# 保存文件到服务器
f_path = './static/images/' + f.filename
f.save(f_path)
# 假设我们已经通过某种方式识别了图片内容,并确定了 img_url
img_url = '../static/images/' + f.filename # 假设图片保存在服务器的 images 文件夹中
flag = predict_image(f_path)
print(flag)
return render_template('upload.html', flag=flag, img_url=img_url)
if __name__ == '__main__':
app.run(debug=True)
随便写了两个路由,来接收前端的表单提交的图片。
总结
水果和动物分类识别,感觉模型,不够完善,训练集的照片没有在更加复杂的环境,训练集也被处理过了,希望有时间可以更加完善,比如选择图片的时候,放在网站展示的时候,可以对图片进行裁剪,还有可以具体分类,是啥动物水果,可以具体分类,这是需要改进的方向。希望大家可以在我的帮本上进行学习和改进