news 2026/10/1 17:24:57

基于机器学习的农作物病虫害识别系统:从叶片照片到分类部署全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于机器学习的农作物病虫害识别系统:从叶片照片到分类部署全流程

简介:这份资源是一套基于机器学习实现的农作物病虫害识别系统,面向Python人工智能方向的毕业设计、课程设计或期末大作业人群,提供可直接运行的源码与配套数据集。项目覆盖了从数据收集预处理、病虫害标签标注、特征提取,到卷积神经网络与支持向量机等模型训练评估、系统部署和实时识别等完整工作流;代码含详细注释,模块划分清晰,新手也能看懂关键逻辑,方便二次开发与答辩讲解。压缩包共477个文件,约82MB,主要包含Python脚本、图片样本、HTML前端页面、JS/CSS等界面与交互资源,另有模型权重文件与SQLite数据库,构成可展示可运行的前后端闭环。目前已有664人学习使用,适合需要快速搭建高分毕设项目或系统学习图像分类实战流程的读者。

1. 基于机器学习实现的农作物病虫害识别系统:从叶片照片到分类结果的完整链路

种过地或者去过田间的人都知道,作物叶片一出问题,找到农技员确认病害种类往往要等很久,而且很多病害在早期长得非常像,靠肉眼很难判断。这个基于机器学习实现的农作物病虫害识别系统,把这件事做成了一套能跑的代码:数据整理、图像预处理、特征提取、模型训练与评估,最后还带一个可以直接调用的识别接口。下载下来之后,图片是按类别分好目录的,标签已经写在文件夹名里,不需要再用标注工具手工框选,按流程跑就能完整复现识别效果。它适合两类人:一类是正在准备 Python 人工智能方向毕业设计、课程设计的学生,因为这套流程和论文的章节几乎是逐一对应的;另一类是已经在做图像分类、想找一个完整参考实现快速迁移到其他识别场景的工程师。

2. 数据准备与标注:把杂乱图片整理成模型能吃的输入

2.1 图片目录即标签:省掉手工标注的第一步

这个项目的数据集组织方式是标准的"文件夹即标签":每个子目录的名字就是类别名,目录下所有图片都属于这一类。公开数据集里最常见的命名是 Tomato___Late_blight、Potato___Early_blight 这种"作物___病害"格式,也有直接用中文名的。好处很直接:不需要额外的 CSV 或 XML 标注文件,遍历目录就能把标签拿全,也天然避免了标注错位的问题。

我拿到数据集的第一件事,是先写一小段脚本把整个目录扫描一遍,生成一个包含图片路径、类别名、类别编号的表格。后续的划分、训练、评估全部基于这张表操作,不要一边训练一边去翻目录。

import os import pandas as pd dataset_dir = "dataset/PlantVillage" class_names = sorted(os.listdir(dataset_dir)) label_map = {name: idx for idx, name in enumerate(class_names)} records = [] for class_name in class_names: class_dir = os.path.join(dataset_dir, class_name) if not os.path.isdir(class_dir): continue for img_name in os.listdir(class_dir): if img_name.lower().endswith((".jpg", ".jpeg", ".png")): records.append({ "image_path": os.path.join(class_dir, img_name), "label": class_name, "label_id": label_map[class_name] }) df = pd.DataFrame(records) print("样本总数:", len(df)) print(df["label"].value_counts())

这段代码先把类别列表按字母排序后生成 label_map,保证每个类别的编号稳定。然后逐类别扫描子目录,只收 .jpg、.jpeg、.png 三种常见格式。最后的 value_counts 会输出每个类别的样本数,一眼就能看出数据集平不平衡。如果某个类别图片特别少,后面训练时必须专门处理,这个信息非常关键。

如果目录名是中文,DataFrame 处理没有问题,但保存模型和映射表的时候要小心编码。某些 Windows 环境下 Python 默认用 GBK 读文件,训练脚本报 UnicodeDecodeError 的话,在打开 CSV 或写 label_map 的位置显式加上 encoding="utf-8" 就能解决。这个细节在答辩现场很常见。

2.2 预处理三件套:resize、去噪、归一化

拿到图片后不能直接喂给模型,原因有三点:图片尺寸不统一,有的 300×300,有的 1200×800,而模型输入要求固定大小;手机拍摄的叶片照片多少带噪点;像素值范围是 0-255,而大部分机器学习算法在 0-1 或者 -1 到 1 的区间里表现更稳定。所以预处理基本就是三件事——统一尺寸、去噪、归一化。

import cv2 import numpy as np def load_and_preprocess(path, size=(128, 128)): img = cv2.imread(path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, size, interpolation=cv2.INTER_AREA) img = cv2.medianBlur(img, 3) img = img.astype(np.float32) / 255.0 return img

size 这里给的是 (128, 128),如果后面要上 CNN,我会改成 (224, 224),这是预训练网络最常用的输入尺寸,也是显存和精度之间的平衡点。medianBlur 是中值滤波,核大小取 3 就能去掉大部分椒盐噪声,又不会把叶片纹理抹掉太多。归一化直接除以 255,把 RGB 三通道压缩到 0-1。注意这一步返回的是 float32 数组,后面不管是提取特征还是喂给深度学习框架,都是通用格式。

2.3 数据集划分与数据增强

划分数据集有一条铁律:测试集从头到尾不参与训练和调参,验证集用来判断模型是否过拟合,训练集才是真正学参数的地方。常见做法是先分离测试集,再把剩余部分按 7:3 切成训练集和验证集,而且必须分层抽样,让每个类别的比例在三个集合里保持一致。

from sklearn.model_selection import train_test_split train_df, test_df = train_test_split(df, test_size=0.15, stratify=df["label"], random_state=42) train_df, val_df = train_test_split(train_df, test_size=0.15 / 0.85, stratify=train_df["label"], random_state=42) print(len(train_df), len(val_df), len(test_df))

test_size=0.15 表示测试集占全量的 15%;第二次划分的 test_size 写成 0.15/0.85,是因为要在剩余 85% 里再分出 15% 的验证集,这样最终三者的比例是 70:15:15。stratify 参数按 label 分层,避免某个类别在测试集里一个样本都没有。random_state 固定成 42,保证每次跑出来的划分结果完全一致,这对实验复现非常重要,没有固定种子的话,两次运行结果不同,调参就无从谈起。

如果走 CNN 路线,数据增强要在训练时做。常用的组合是随机水平翻转、旋转 10-15 度、亮度抖动和随机缩放:

from tensorflow.keras.preprocessing.image import ImageDataGenerator train_datagen = ImageDataGenerator( rescale=1.0/255, rotation_range=15, width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True )

增强只作用在训练集,验证集和测试集必须保持原图,否则评估结果会被"增强过"的图片污染。另外要注意,ImageDataGenerator 的 flow_from_directory 要求目录结构是 train/类别名/*.jpg,划分好之后需要把图片按 train、val、test 三个子目录重新归置一遍,复制而不是移动,防止误删原图。

3. 特征提取与模型选型:CNN 表现好,但 SVM 是一条更省算力的路线

3.1 手工特征:颜色直方图 + GLCM 纹理特征

走 SVM + 手工特征路线时,前面 2.2 的 load_and_preprocess 反而不适用了。特征提取要的是原始像素信息,而不是归一化后的浮点图,所以下面的 extract_features 函数直接接收 cv2.imread 出来的原图。

颜色特征我用 HSV 空间而不是 RGB。原因是 HSV 把色调、饱和度、明度分开,光照变化对色调通道的影响远小于对 RGB 三通道的直接影响。纹理特征用 GLCM 灰度共生矩阵,它统计的是像素对在某个方向和距离上同时出现的概率。实际使用中我不会把 GLCM 的全部属性都取出来,只取对比度、能量、同质性三个指标,相关性在叶片病害上的区分度太弱,取进来反而增加噪声。

import cv2 import numpy as np from skimage.feature import graycomatrix, graycoprops def extract_features(image): image = cv2.resize(image, (128, 128), interpolation=cv2.INTER_AREA) hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) hist = cv2.calcHist([hsv], [0, 1], None, [8, 8], [0, 180, 0, 256]) hist = cv2.normalize(hist, hist).flatten() gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) glcm = graycomatrix(gray, distances=[1], angles=[0, np.pi/4, np.pi/2, 3*np.pi/4], levels=256, symmetric=True, normed=True) contrast = graycoprops(glcm, 'contrast').mean() energy = graycoprops(glcm, 'energy').mean() homogeneity = graycoprops(glcm, 'homogeneity').mean() return np.hstack([hist, [contrast, energy, homogeneity]])

calcHist 的 bins 取 [8, 8],也就是色调和饱和度各分 8 个区间,拼出 64 维颜色特征。GLCM 的 distances=[1] 表示只统计距离 1 个像素的相邻关系,angles 取 0 度到 135 度四个方向,然后取均值,避免因叶片旋转造成特征突变。levels=256 对应灰度图的灰度级,normed=True 让矩阵值归一化成概率。最终特征向量是 64 + 3 = 67 维,这个维度对 SVM 来说非常友好,训练速度可以接受。

3.2 SVM 和 CNN 怎么选:先看数据和算力

做识别系统之前,先想清楚走哪条路线。两条路线在毕设场景里都能过,区别在论文的侧重点不同:SVM 路线重点写特征工程和参数分析,CNN 路线重点写网络设计和训练策略。

对比项SVM + 手工特征CNN
数据量需求几千张图片就能训练数据量越大越好,万级起步
硬件需求CPU 训练分钟级有 GPU 训练小时级
特征设计需要手工设计颜色、纹理特征网络自动学习特征
可解释性特征含义明确,论文好写黑匣子,但准确率上限更高
调参难度主要调 C 和 gamma学习率、结构、正则化多维度

我的判断标准很简单:如果手头只有几千张图,或者电脑没有独立显卡,先跑 SVM 基线。公开的作物病害数据集上,SVM + 手工特征通常能到 80%-90% 的准确率,作为毕设已经完全够用。如果后续发现瓶颈明显,再切 CNN 往上冲。项目源码里两种路线都有对应脚本的话,正好可以做对比实验,这也是答辩时很加分的点。

3.3 构造特征矩阵:先跑一个基线版本

选定了 SVM 路线,第一步是把所有训练图片过一遍特征提取,组装成特征矩阵。这里有个效率问题:用列表推导式逐张处理,一千张图可能要一两分钟,耐心等,不要中途把图片尺寸调得太大,128×128 足够。

from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline def read_and_extract(path): img = cv2.imread(path) return extract_features(img) X_train = np.array([read_and_extract(p) for p in train_df["image_path"]]) X_val = np.array([read_and_extract(p) for p in val_df["image_path"]]) y_train = train_df["label_id"].values y_val = val_df["label_id"].values baseline = make_pipeline(StandardScaler(), SVC(kernel="rbf", C=1.0, random_state=42)) baseline.fit(X_train, y_train) print("baseline val acc:", baseline.score(X_val, y_val))

为什么要把 StandardScaler 和 SVC 串成 pipeline?因为 SVM 依赖样本之间的距离,特征不做标准化的话,像素直方图这类取值范围大的特征会主导距离计算。用 make_pipeline 包起来,fit 的时候只对训练集计算均值和方差,验证集用同一套参数做变换,避免数据泄漏。C=1.0 只是基线值,不用纠结,下一章网格搜索会统一调。

4. 训练评估与调优:准确率之外还要盯住 F1 和混淆矩阵

4.1 网格搜索:C 和 gamma 到底怎么调

SVM 的 RBF 核有两个关键超参数:C 是误分类惩罚系数,C 越大模型越想把每个训练样本都分对,容易过拟合;gamma 控制 RBF 核的影响半径,gamma 越大决策边界越弯曲,同样容易过拟合。手动试 C 和 gamma 的组合非常累,直接用 GridSearchCV 跑网格搜索。

from sklearn.model_selection import GridSearchCV import joblib pipeline = make_pipeline(StandardScaler(), SVC(probability=True, random_state=42)) param_grid = { "svc__C": [0.1, 1, 10], "svc__gamma": ["scale", 0.01, 0.001] } grid = GridSearchCV(pipeline, param_grid, cv=3, scoring="f1_macro", n_jobs=-1) grid.fit(X_train, y_train) print("best params:", grid.best_params_) joblib.dump(grid.best_estimator_, "svm_model.joblib")

param_grid 的键名写成 svc__C 而不是 C,是因为 pipeline 内部步骤名为 svc,参数要通过"步骤名__参数名"的形式访问。这个双下划线经常有人漏写,报 KeyError 的十有八九是这个问题。scoring 用 f1_macro 而不是 accuracy,原因是类别不平衡时 accuracy 没有参考价值。cv=3 是 3 折交叉验证,数据集不大时够用。n_jobs=-1 让所有 CPU 核一起跑,网格搜索会快很多。

模型训练完成后,用 joblib 保存整个 pipeline,注意是保存 pipeline 而不是只保存 SVC,这样 StandardScaler 的均值和方差也一起存进去了,后面部署时少一个文件要管理。同时把标签映射表存成 JSON,方便 Web 接口读取:

import json label_map_json = {str(v): k for k, v in label_map.items()} with open("label_map.json", "w", encoding="utf-8") as f: json.dump(label_map_json, f, ensure_ascii=False, indent=2)

4.2 评估指标:不要只报一个 accuracy

训练完模型之后,打印一份完整的分类报告。很多同学只看 accuracy,但这个指标在类别不平衡的数据集上会骗人:假设健康叶片占 60%,模型把每张图都猜成健康,accuracy 就有 60%,看起来"及格"了,实际一个病害都没识别出来。

from sklearn.metrics import classification_report, confusion_matrix y_pred = grid.best_estimator_.predict(X_val) print(classification_report(y_val, y_pred, target_names=list(label_map.keys()))) print(confusion_matrix(y_val, y_pred))

classification_report 里每一行有 precision、recall、f1-score 三个值。我最先看每个类别的 recall,也就是"该类别的真实样本里,模型正确找回了多少"。recall 最低的那一类,就是模型最容易漏的类。比如某个病害类别的 recall 只有 61%,说明有近四成的样本被误判成了其他类,这通常是病斑早期特征不明显,或者和健康叶片的颜色分布太接近。

答辩时不要只给一个"准确率 95%"的结论,把分类报告里表现最差的类挑出来,解释一两个原因,再说明你用什么手段去弥补,这个深度和只会跑通代码完全是两个印象分。

4.3 如果数据量够:用 CNN 把准确率再往上推

SVM + 手工特征的天花板取决于你手工设计的特征够不够好。当数据量上升到上万张,或者你觉得手工特征已经榨不出提升空间,就切到 CNN。CNN 不需要设计特征,网络自己从像素里学,准确率通常能比 SVM 高出五到十个百分点。

from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping model = Sequential([ Conv2D(32, (3, 3), activation="relu", input_shape=(128, 128, 3)), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activation="relu"), MaxPooling2D((2, 2)), Flatten(), Dropout(0.5), Dense(64, activation="relu"), Dense(len(label_map), activation="softmax") ]) model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"]) early_stop = EarlyStopping(monitor="val_loss", patience=5, restore_best_weights=True) model.fit(train_images, train_labels, validation_data=(val_images, val_labels), batch_size=32, epochs=30, callbacks=[early_stop])

loss 用 sparse_categorical_crossentropy,是因为我们的标签是整数编号,不需要转 one-hot。batch_size 从 32 起步,显存不够就降到 16。early stopping 的 patience=5 表示验证集 loss 连续 5 轮不下降就停止训练,restore_best_weights=True 保证最后拿到的不是过拟合那一步的权重。如果训练过程中 loss 剧烈震荡,先把学习率从默认的 0.001 降到 0.0001,这是最常见的调参手段。

5. 避坑:图像分类毕设最容易翻车的五个环节

5.1 训练集验证集划分不当,结果虚高不再可信

现象:验证集准确率 97%,自己找几张图一测只有 65%,差距大到离谱。

原因:图片来自同一个植株或同一次拍摄批次,划分时没有做任何分组处理,同一个叶片的多张照片同时落进训练集和验证集。模型在训练时已经见过这个叶片的纹理,验证时自然"认识"。另一种可能是 random_state 没有固定,每次运行划分结果不同,你调参时依据的验证集一直在变。

解决:固定 random_state=42,划分前强制 shuffle。如果数据集里同一植株有多张图片,按文件名前缀或拍摄批次做分组划分,比如文件名前 8 位是植株编号,就把相同编号的图片全部划到同一集合里。毕设数据量不大,手动写个按前缀分组的函数几分钟就能完成。

5.2 预测时漏掉预处理链路,接口准确率暴跌

现象:训练时准确率不错,把模型包成接口后,传一张图进去返回的标签完全不靠谱。

原因:训练脚本里写了 resize、去噪、归一化,预测代码里直接 cv2.imread 就把原图丢给模型。训练和预测的输入分布不一致,模型看到的"特征空间"完全变了。

解决:把特征提取和预处理封装成独立模块,训练脚本和预测脚本 import 同一个函数。我在项目里习惯把这些函数放一个 feature_extractor.py 里,避免两边各写一份。

# app 和 train 都从同一个模块导入 from feature_extractor import extract_features # 预测时 features = extract_features(img).reshape(1, -1) proba = model.predict_proba(features)[0]

这是部署翻车率最高的一处,没有之一,任何图像分类项目都是同一个道理。

5.3 类别不平衡时模型永远猜"健康"

现象:模型对每张图都输出"健康叶片",整体准确率还挺高,但病害类别一个都识别不出来。

原因:健康样本在数据集中占 60% 以上,SVM 优化的是整体损失,多数类贡献的误差更大,决策边界自然偏向多数类。

解决:SVC 里加一个参数 class_weight="balanced",让少数类的误分类惩罚权重自动放大。

SVC(kernel="rbf", C=1.0, class_weight="balanced", random_state=42)

如果加了之后效果还不够,对少数类做简单过采样,复制少数类样本到多数类的数量级,或者用 imbalanced-learn 库里的 SMOTE 合成少数类样本。这一步做完再看 F1,比看准确率有意义得多。

5.4 训练准确率 99%,验证准确率 75%:过拟合来了

现象:训练 loss 持续下降,验证 loss 在某轮之后不降反升,训练集准确率和验证集准确率的差距越来越大。

原因:模型把训练集里的噪声和细节背下来了,没有学到可泛化的规律。叶子照片里的光照、背景土块、阴影都可能被当成特征。

解决:先加 EarlyStopping 兜底,连续几轮验证 loss 不降就停;然后给全连接层加 Dropout,比例从 0.5 开始;再对训练集做数据增强。如果还是过拟合,就减小网络容量,CNN 的卷积核数量减半,SVM 则把 C 调小、gamma 调小。

5.5 换一台电脑,模型加载失败

现象:在自己电脑上 joblib.load 一切正常,换到答辩电脑上报错 AttributeError 或者 ImportError,还有可能提示找不到模型文件。

原因:模型里保存了自定义类或使用了不兼容的版本。比如你为了做特征选择写了一个自定义 Transformer,pickle 在序列化时会把它所在的模块路径写死,换一台机器没有这个模块就加载失败。另一个常见问题是模型路径写的是绝对路径,换电脑就失效。

解决:模型统一用 joblib 保存,避免 pickle 序列化自定义类;scikit-learn 主版本号在训练机和部署机上保持一致,大版本不一致时 SVM 模型经常加载失败;路径用相对路径或基于file拼接。

from pathlib import Path model_path = Path(__file__).parent / "model" / "svm_model.joblib"

部署前把 requirements.txt 准备好,列清楚依赖库和版本范围,这是答辩前最容易补救的一项。

6. 部署与验证:用 Flask 把识别模型包成可用的 Web 接口

6.1 一个能直接复用的 Flask 预测接口

把模型落地成接口,最省事的方式就是用 Flask 包一层 HTTP 服务。核心逻辑只有三步:接收上传的图片,走和训练时完全相同的特征提取,然后返回预测结果和置信度。

import json import cv2 import numpy as np from flask import Flask, request, jsonify from joblib import load from feature_extractor import extract_features app = Flask(__name__) model = load("model/svm_model.joblib") with open("model/label_map.json", "r", encoding="utf-8") as f: label_map = json.load(f) @app.route("/predict", methods=["POST"]) def predict(): file = request.files.get("image") if file is None: return jsonify({"error": "no image uploaded"}), 400 data = np.frombuffer(file.read(), np.uint8) img = cv2.imdecode(data, cv2.IMREAD_COLOR) if img is None: return jsonify({"error": "invalid image format"}), 400 features = extract_features(img).reshape(1, -1) proba = model.predict_proba(features)[0] idx = int(np.argmax(proba)) label = label_map[str(idx)] confidence = float(proba[idx]) if confidence < 0.6: return jsonify({"label": "unknown", "confidence": confidence, "message": "置信度过低,请更换清晰的叶片照片"}) return jsonify({"label": label, "confidence": confidence}) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000)

这里直接复用第 3 章的 extract_features,保证预测链路和训练链路完全一致。注意 label_map 的键是字符串,因为 JSON 序列化时整数键会被转成字符串,所以取结果时用 str(idx) 才能匹配上。

6.2 用 requests 做一次端到端验证

接口写完之后,不要只打开浏览器看页面,用脚本做一次真正的请求测试,确认返回结果和本地 eval 一致。

import requests url = "http://127.0.0.1:5000/predict" with open("test_images/late_blight_sample.jpg", "rb") as f: resp = requests.post(url, files={"image": f}) print(resp.json())

测试图片一定要选训练集之外、并且和训练图片不是同一植株拍摄的样本。多传几次,SVM 是确定性模型,同样的图片反复请求应该返回完全一样的结果。如果两次结果不一致,检查接口里有没有混入随机操作。

6.3 置信度阈值:让系统知道"它认不出来"

很多识别系统翻车,不是模型不够好,而是系统在模型置信度很低的时候仍然硬给一个答案。一张空白背景、手部特写或者完全不相关的图片,模型的 softmax 概率分布依然会有一个最大值,只不过那个值只有 0.3 左右。加一个置信度阈值过滤,低于阈值的统一返回 unknown,让前端可以提示用户重新拍摄,而不是给一个错误诊断。

阈值 0.6 是我在类似项目里的常用起点。如果验证集上的整体表现因为阈值被卡掉过多样本,就调低到 0.5;如果某个类别频繁出现低置信度误判,就单独调高。把阈值当成一个和 C、gamma 一样的超参数来对待,而不是写死一个数字。

当初我部署完一个版本急着演示,跳过了这一步,现场传了一张没有叶片的背景图,系统信心满满地给出"番茄晚疫病",置信度只有 0.32。从那以后,我每做一个识别接口都会把阈值过滤加上,并且每次换数据集就用验证集把阈值重新调一遍。项目源码和数据集的完整内容,下载下来之后建议从第 2 章的目录扫描脚本开始跑,每一步的输出都先打印出来看一眼再往下走,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 17:24:08

基于Jupyter Notebook的Python用户画像构建源码实战与避坑指南

简介&#xff1a;这份资源是一套基于Jupyter Notebook的Python用户画像构建源码&#xff0c;面向数据分析初学者与从事用户研究的从业者&#xff0c;帮助其掌握从原始数据到画像输出的完整流程。包内共20个文件&#xff0c;以13个CSV数据文件为主要数据载体&#xff0c;配合4个…

作者头像 李华
网站建设 2026/10/1 17:23:34

AMD老显卡UEFI GOP缺失导致Win11安装黑屏的根源与修复

1. 为什么一块老AMD显卡突然“拒绝启动Windows”——UEFI GOP缺失的真实代价你有没有遇到过这样的场景&#xff1a;一台用了五年的AMD Radeon RX 580主机&#xff0c;某天重装Windows 11时卡在“无法安装Windows&#xff0c;因为这台电脑的磁盘布局不受UEFI支持”这行红字上&am…

作者头像 李华
网站建设 2026/10/1 17:23:29

4122张眼底图5类DR分级:VOC+YOLO双格式数据集实战指南

简介&#xff1a;本资源为糖尿病肾病视网膜病变检测数据集&#xff0c;面向医学图像处理、眼底病变识别方向的算法工程师与深度学习研究者&#xff0c;可用于目标检测模型的训练、验证与竞赛实践。数据集采用Pascal VOC与YOLO双格式标注&#xff0c;包含4122张jpg图片&#xff…

作者头像 李华
网站建设 2026/10/1 17:23:15

MediaPipe + KNN 健身动作计数:从骨骼提取到状态机实战

简介&#xff1a;这是一套基于MediaPipe与KNN分类算法的健身动作计数Python项目源码&#xff0c;面向具备一定Python基础、希望快速实现引体向上、深蹲、俯卧撑自动计数的开发者与健身应用爱好者。其核心思路是先提取人体关键点并归一化编码&#xff0c;再用k-NN完成姿态分类&a…

作者头像 李华