الكشف عن أمراض أوراق النبات وتحديدها باستخدام YOLOv4

الكشف المبكر عن أمراض النبات يمكن أن يمنع خسائر تصل إلى 40% من المحاصيل الزراعية سنوياً. في هذا الدليل العملي، نبني نظام كشف تلقائي باستخدام YOLOv4 يحقق دقة 99.99% على مجموعة بيانات PlantVillage — مع شرح كل خطوة من جمع البيانات إلى النشر. (Aldakheel et al., 2024)
لماذا الكشف الآلي عن أمراض النبات؟
تُسبّب أمراض النباتات خسائر اقتصادية تُقدّر بمليارات الدولارات سنوياً حول العالم. يعتمد المزارعون تقليدياً على الفحص البصري اليدوي، وهو أسلوب بطيء وعرضة للخطأ البشري — خاصة في المراحل المبكرة من المرض حيث تكون الأعراض غير واضحة.
التعلم العميق يُقدّم حلاً فعالاً: نظام يمكنه تحليل صور الأوراق في أجزاء من الثانية وتحديد نوع المرض بدقة تفوق الخبراء البشريين.
YOLOv4 (You Only Look Once v4) يتميز عن نماذج التصنيف التقليدية بقدرته على تحديد موقع المرض في الورقة وليس فقط تصنيفه — وهذا مفيد جداً لتقييم شدّة الإصابة.
ماذا ستتعلم في هذا الدليل
- إعداد بيئة التطوير مع Darknet و CUDA
- تحضير مجموعة بيانات PlantVillage وتشريحها
- تكوين وتدريب نموذج YOLOv4 مخصص
- تقييم الأداء باستخدام mAP و مصفوفة الارتباك
- استخدام النموذج للكشف في الوقت الفعلي
المتطلبات الأساسية
قبل البدء، تأكد من توفر التالي:
- Python 3.8+ مع
pip - CUDA 11.0+ و cuDNN 8.0+ (لتسريع GPU)
- OpenCV 4.5+
- ذاكرة GPU لا تقل عن 6 جيجابايت (يُنصح بـ NVIDIA RTX 2060 أو أعلى)
- معرفة أساسية بـ Python والتعلم العميق
الخطوة 1: إعداد بيئة التطوير
تثبيت Darknet
Darknet هو الإطار الأصلي لتشغيل YOLO. لنقم بتجميعه مع دعم GPU:
# استنساخ المستودع
git clone https://github.com/AlexeyAB/darknet.git
cd darknet
# تفعيل GPU و OpenCV في Makefile
sed -i 's/GPU=0/GPU=1/' Makefile
sed -i 's/CUDNN=0/CUDNN=1/' Makefile
sed -i 's/OPENCV=0/OPENCV=1/' Makefile
# التجميع
make -j$(nproc)إنشاء بيئة Python الافتراضية
python3 -m venv yolov4-env
source yolov4-env/bin/activate
pip install opencv-python numpy matplotlib pillow
pip install torch torchvision # اختياري — للتحليل والتقييمتأكد من توافق إصدار CUDA المثبت مع إصدار cuDNN وبطاقة الرسوميات. يمكنك التحقق بتشغيل nvcc --version و nvidia-smi.
الخطوة 2: مجموعة بيانات PlantVillage
نظرة عامة
مجموعة بيانات PlantVillage تحتوي على أكثر من 54,000 صورة لأوراق نباتات من 14 نوعاً مختلفاً، تغطي 38 فئة بين أوراق سليمة ومصابة.
| الإحصائية | القيمة |
|---|---|
| إجمالي الصور | 54,305 |
| أنواع النباتات | 14 |
| فئات الأمراض | 38 |
| الدقة | 256×256 بكسل |
| التنسيق | JPG/PNG |
تنزيل البيانات
# تنزيل من Kaggle
pip install kaggle
kaggle datasets download -d emmarex/plantdisease
unzip plantdisease.zip -d data/plantvillageبنية المجلدات
data/plantvillage/
├── Apple___Apple_scab/
│ ├── image_001.jpg
│ ├── image_002.jpg
│ └── ...
├── Apple___Black_rot/
├── Apple___Cedar_apple_rust/
├── Apple___healthy/
├── Tomato___Bacterial_spot/
├── Tomato___Early_blight/
├── Tomato___Late_blight/
├── Tomato___healthy/
└── ... (38 مجلداً)
الخطوة 3: تحضير البيانات لـ YOLOv4
يحتاج YOLOv4 إلى تنسيق خاص للتعليقات التوضيحية. كل صورة تحتاج ملف .txt مرافق يحتوي على إحداثيات المربعات المحيطة.
سكريبت التحويل
import os
import cv2
import random
from pathlib import Path
DATA_DIR = "data/plantvillage"
OUTPUT_DIR = "data/yolo_format"
CLASSES_FILE = "data/classes.txt"
# جمع الفئات
classes = sorted(os.listdir(DATA_DIR))
with open(CLASSES_FILE, "w") as f:
for cls in classes:
f.write(f"{cls}\n")
print(f"عدد الفئات: {len(classes)}")
# تحويل كل صورة إلى تنسيق YOLO
os.makedirs(f"{OUTPUT_DIR}/images/train", exist_ok=True)
os.makedirs(f"{OUTPUT_DIR}/images/val", exist_ok=True)
os.makedirs(f"{OUTPUT_DIR}/labels/train", exist_ok=True)
os.makedirs(f"{OUTPUT_DIR}/labels/val", exist_ok=True)
all_images = []
for class_idx, class_name in enumerate(classes):
class_dir = os.path.join(DATA_DIR, class_name)
for img_name in os.listdir(class_dir):
if img_name.lower().endswith(('.jpg', '.jpeg', '.png')):
all_images.append((
os.path.join(class_dir, img_name),
class_idx,
img_name
))
# تقسيم 80/20 للتدريب والتحقق
random.shuffle(all_images)
split = int(0.8 * len(all_images))
train_set = all_images[:split]
val_set = all_images[split:]
def process_image(img_path, class_idx, img_name, split_name):
"""تحويل صورة واحدة إلى تنسيق YOLO"""
img = cv2.imread(img_path)
h, w = img.shape[:2]
# نسخ الصورة
dest = f"{OUTPUT_DIR}/images/{split_name}/{img_name}"
cv2.imwrite(dest, img)
# إنشاء ملف التعليقات — الورقة تشغل معظم الصورة
# التنسيق: class_id center_x center_y width height (مُطبَّع)
label_name = Path(img_name).stem + ".txt"
label_path = f"{OUTPUT_DIR}/labels/{split_name}/{label_name}"
with open(label_path, "w") as f:
# المربع المحيط يغطي 90% من الصورة (الورقة هي الموضوع الرئيسي)
f.write(f"{class_idx} 0.5 0.5 0.9 0.9\n")
print(f"تدريب: {len(train_set)} | تحقق: {len(val_set)}")
for img_path, cls_idx, name in train_set:
process_image(img_path, cls_idx, name, "train")
for img_path, cls_idx, name in val_set:
process_image(img_path, cls_idx, name, "val")تطبيع الصور
import numpy as np
import cv2
def normalize_image(img_path):
"""تطبيع قيم البكسل من [0, 255] إلى [0, 1]"""
img = cv2.imread(img_path)
img = img.astype(np.float32) / 255.0
# تغيير الحجم إلى 416×416 (الحجم الافتراضي لـ YOLOv4)
img = cv2.resize(img, (416, 416))
return img
# تحقق من صورة عشوائية
sample = normalize_image("data/yolo_format/images/train/sample.jpg")
print(f"النطاق: [{sample.min():.2f}, {sample.max():.2f}]")
print(f"الشكل: {sample.shape}") # (416, 416, 3)الخطوة 4: شرح الصور وأدوات التعليق
لنتائج أفضل مع YOLOv4، يُفضّل استخدام مربعات محيطة دقيقة حول المناطق المصابة بدل المربع الكامل للورقة.
أدوات التعليق الموصى بها
| الأداة | المنصة | المميزات |
|---|---|---|
| LabelImg | سطح المكتب | مجاني، يدعم تنسيق YOLO مباشرة |
| CVAT | ويب | تعاوني، يدعم الفيديو |
| Roboflow | ويب | تكثيف البيانات تلقائياً، تصدير بتنسيقات متعددة |
| VoTT | سطح المكتب | من Microsoft، يدعم Active Learning |
استخدام LabelImg
pip install labelImg
labelImg data/yolo_format/images/train/ data/classes.txtللحصول على نتائج أدق، ارسم المربع المحيط حول المنطقة المصابة فقط وليس الورقة بأكملها. هذا يساعد النموذج على تعلم التمييز بين الأنسجة السليمة والمريضة.
الخطوة 5: فهم بنية YOLOv4
YOLOv4 يتكون من ثلاثة أجزاء رئيسية:
1. الشبكة الظهرية (Backbone) — CSPDarknet53
تستخرج الميزات الأساسية من الصورة باستخدام:
- اتصالات متبقية مُتقاطعة (CSP): تقلل من التكرار الحسابي
- دالة التنشيط Mish:
f(x) = x × tanh(softplus(x))— أكثر سلاسة من ReLU
2. العنق (Neck) — SPP + PANet
يجمع الميزات من مستويات مختلفة:
- التجميع الهرمي المكاني (SPP): يوسّع مجال الرؤية
- شبكة تجميع المسار (PANet): يدمج الميزات من الأعلى للأسفل والعكس
3. الرأس (Head) — YOLOv3 Head
يُنتج التنبؤات النهائية على ثلاثة مقاييس مختلفة (13×13، 26×26، 52×52).
المدخل (416×416×3)
│
▼
CSPDarknet53 (استخراج الميزات)
│
├──► مقياس صغير (13×13) — كائنات كبيرة
├──► مقياس متوسط (26×26) — كائنات متوسطة
└──► مقياس كبير (52×52) — كائنات صغيرة
│
▼
SPP + PANet (دمج الميزات)
│
▼
كشف على 3 مقاييس
الخطوة 6: تكوين ملفات التدريب
ملف البيانات (plant.data)
classes = 38
train = data/train.txt
valid = data/val.txt
names = data/classes.txt
backup = backup/إنشاء قوائم الصور
import glob
# قائمة صور التدريب
train_images = glob.glob("data/yolo_format/images/train/*.*")
with open("data/train.txt", "w") as f:
f.write("\n".join(train_images))
# قائمة صور التحقق
val_images = glob.glob("data/yolo_format/images/val/*.*")
with open("data/val.txt", "w") as f:
f.write("\n".join(val_images))
print(f"صور التدريب: {len(train_images)}")
print(f"صور التحقق: {len(val_images)}")ملف التكوين (yolov4-plant.cfg)
انسخ ملف التكوين الأصلي وعدّل المعلمات:
cp cfg/yolov4-custom.cfg cfg/yolov4-plant.cfgالمعلمات الأساسية التي يجب تعديلها:
[net]
batch = 64
subdivisions = 16
width = 416
height = 416
max_batches = 76000 # = عدد الفئات × 2000 = 38 × 2000
steps = 60800,68400 # = 80% و 90% من max_batches# لكل طبقة [yolo] (3 طبقات)
[yolo]
classes = 38
# لكل طبقة [convolutional] قبل [yolo]
[convolutional]
filters = 129 # = (classes + 5) × 3 = (38 + 5) × 3خطأ شائع: نسيان تعديل filters في طبقات [convolutional] الثلاث قبل طبقات [yolo]. يجب أن تكون القيمة دائماً (classes + 5) × 3.
الخطوة 7: تدريب النموذج
تنزيل الأوزان المدربة مسبقاً
wget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.conv.137بدء التدريب
./darknet detector train \
data/plant.data \
cfg/yolov4-plant.cfg \
yolov4.conv.137 \
-map \
-dont_showمراقبة التقدم
سيُنشئ Darknet رسماً بيانياً للخسارة تلقائياً في chart.png. يمكنك أيضاً متابعة التقدم عبر الطرفية:
CUDA-version: 11080 (11080)
Loading weights from yolov4.conv.137...
Learning Rate: 0.001, Momentum: 0.949, Decay: 0.0005
Iteration: 1000, loss: 2.5, avg loss: 3.1, rate: 0.001
Iteration: 2000, loss: 1.2, avg loss: 1.8, rate: 0.001
...
Iteration: 10000, loss: 0.08, avg loss: 0.15, rate: 0.0001
يُحفظ النموذج تلقائياً كل 1000 تكرار في مجلد backup/. إذا توقف التدريب، يمكنك استئنافه من آخر نقطة: ./darknet detector train ... backup/yolov4-plant_last.weights
الخطوة 8: تقييم النموذج
حساب mAP (متوسط الدقة المتوسط)
./darknet detector map \
data/plant.data \
cfg/yolov4-plant.cfg \
backup/yolov4-plant_best.weightsنتائج الأداء
| المقياس | القيمة |
|---|---|
| mAP@0.5 | 99.99% |
| الدقة (Precision) | 99.99% |
| الاستدعاء (Recall) | 99.98% |
| F1-Score | 99.99% |
| سرعة الاستدلال | ~30 FPS (RTX 2080) |
مصفوفة الارتباك بـ Python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
def evaluate_model(predictions, ground_truths, class_names):
"""إنشاء وعرض مصفوفة الارتباك"""
cm = confusion_matrix(ground_truths, predictions)
fig, ax = plt.subplots(figsize=(20, 20))
disp = ConfusionMatrixDisplay(cm, display_labels=class_names)
disp.plot(ax=ax, cmap='Greens', xticks_rotation=45)
plt.title("مصفوفة الارتباك — YOLOv4 Plant Disease Detection")
plt.tight_layout()
plt.savefig("confusion_matrix.png", dpi=150)
plt.show()
# حساب المقاييس لكل فئة
for i, name in enumerate(class_names):
tp = cm[i, i]
fp = cm[:, i].sum() - tp
fn = cm[i, :].sum() - tp
precision = tp / (tp + fp) if (tp + fp) > 0 else 0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0
print(f"{name}: Precision={precision:.4f}, Recall={recall:.4f}")الخطوة 9: الاستدلال والكشف في الوقت الفعلي
كشف على صورة واحدة
./darknet detector test \
data/plant.data \
cfg/yolov4-plant.cfg \
backup/yolov4-plant_best.weights \
data/test/tomato_late_blight.jpg \
-thresh 0.5كشف باستخدام Python و OpenCV
import cv2
import numpy as np
def detect_disease(image_path, config, weights, classes_file, conf_threshold=0.5):
"""كشف أمراض الأوراق في صورة"""
# تحميل الشبكة
net = cv2.dnn.readNetFromDarknet(config, weights)
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
# تحميل أسماء الفئات
with open(classes_file, "r") as f:
classes = f.read().strip().split("\n")
# تحضير الصورة
img = cv2.imread(image_path)
h, w = img.shape[:2]
blob = cv2.dnn.blobFromImage(img, 1/255.0, (416, 416), swapRB=True, crop=False)
net.setInput(blob)
# الاستدلال
layer_names = net.getLayerNames()
output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]
outputs = net.forward(output_layers)
# معالجة النتائج
boxes, confidences, class_ids = [], [], []
for output in outputs:
for detection in output:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > conf_threshold:
center_x = int(detection[0] * w)
center_y = int(detection[1] * h)
bw = int(detection[2] * w)
bh = int(detection[3] * h)
x = int(center_x - bw / 2)
y = int(center_y - bh / 2)
boxes.append([x, y, bw, bh])
confidences.append(float(confidence))
class_ids.append(class_id)
# إزالة التكرارات (NMS)
indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, 0.4)
# رسم النتائج
colors = np.random.uniform(0, 255, size=(len(classes), 3))
for i in indices.flatten():
x, y, bw, bh = boxes[i]
label = f"{classes[class_ids[i]]}: {confidences[i]:.2f}"
color = colors[class_ids[i]]
cv2.rectangle(img, (x, y), (x + bw, y + bh), color, 2)
cv2.putText(img, label, (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
return img, [(classes[class_ids[i]], confidences[i]) for i in indices.flatten()]
# الاستخدام
result_img, detections = detect_disease(
"test_leaf.jpg",
"cfg/yolov4-plant.cfg",
"backup/yolov4-plant_best.weights",
"data/classes.txt"
)
for disease, conf in detections:
print(f" {disease}: {conf:.1%}")
cv2.imwrite("result.jpg", result_img)كشف في الوقت الفعلي من الكاميرا
import cv2
cap = cv2.VideoCapture(0) # أو مسار ملف فيديو
while True:
ret, frame = cap.read()
if not ret:
break
# تحضير الإطار
blob = cv2.dnn.blobFromImage(frame, 1/255.0, (416, 416), swapRB=True)
net.setInput(blob)
outputs = net.forward(output_layers)
# ... معالجة النتائج (نفس الكود أعلاه)
cv2.imshow("Plant Disease Detection", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()التحليل المقارن
أظهرت الدراسة أن YOLOv4 يتفوق بوضوح على النماذج الأخرى:
| النموذج | الدقة | السرعة (FPS) | الحجم |
|---|---|---|---|
| YOLOv4 | 99.99% | 30 | 256 MB |
| DenseNet-121 | 99.75% | 12 | 33 MB |
| ResNet-50 | 99.68% | 15 | 98 MB |
| AlexNet | 97.82% | 45 | 233 MB |
| VGG-16 | 98.40% | 8 | 528 MB |
| SVM التقليدي | 89.50% | 2 | - |
YOLOv4 يجمع بين أعلى دقة وسرعة ممتازة في الوقت الفعلي — مما يجعله الخيار الأمثل للتطبيقات الميدانية على الأجهزة المتنقلة والطائرات المسيّرة.
الاتجاهات المستقبلية
- التوسع لأمراض أكثر: توسيع التغطية لتشمل أمراض الجذور والسيقان والثمار
- دمج البيانات متعددة الوسائط: الجمع بين الصور وبيانات المستشعرات (الرطوبة، الحرارة، التربة)
- النشر على الأجهزة الطرفية: تحويل النموذج إلى TensorRT أو ONNX للتشغيل على Jetson Nano أو Raspberry Pi
- التعلم المستمر: تطوير آليات لتحديث النموذج تلقائياً ببيانات جديدة من الحقل
- قابلية التفسير: استخدام تقنيات مثل Grad-CAM لتوضيح أسباب تنبؤات النموذج
الخلاصة
في هذا الدليل، بنينا نظام كشف عن أمراض أوراق النبات باستخدام YOLOv4 حقق دقة 99.99% على مجموعة بيانات PlantVillage. المنهجية تشمل:
- جمع وتحضير البيانات مع تعليقات توضيحية دقيقة
- تكوين وتدريب نموذج YOLOv4 مخصص
- تقييم شامل باستخدام مقاييس متعددة
- نشر عملي للكشف في الوقت الفعلي
هذا النهج يُمكّن المزارعين من اكتشاف الأمراض مبكراً واتخاذ إجراءات سريعة — مما يقلل الخسائر ويحسّن إنتاجية المحاصيل بشكل ملموس.
المراجع
- Aldakheel EA, Zakariah M, Alabdalall AH. Detection and identification of plant leaf diseases using YOLOv4. Front. Plant Sci. 15:1355941. doi: 10.3389/fpls.2024.1355941. المقال الكامل
- Bochkovskiy A, Wang CY, Liao HYM. YOLOv4: Optimal Speed and Accuracy of Object Detection. arXiv:2004.10934. الورقة البحثية
- Hughes DP, Salathé M. An open access repository of images on plant health to enable the development of mobile disease diagnostics. arXiv:1511.08060. مجموعة بيانات PlantVillage
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

بناء مفسر أكواد مخصص لوكلاء نماذج اللغة الكبيرة
تعلم كيفية إنشاء مفسر أكواد مخصص لوكلاء نماذج اللغة الكبيرة (LLM)، مما يتيح استدعاء الأدوات الديناميكي وتنفيذ الأكواد المعزول لتعزيز المرونة والأمان.

بناء أداة استخراج بيانات ذكية من الويب باستخدام Playwright و Claude API في TypeScript
تعلّم كيف تبني أداة استخراج بيانات ذكية تستخدم Playwright للتحكم بالمتصفح وClaude AI لفهم صفحات الويب واستخراج بيانات منظّمة — بدون محددات CSS هشّة.

البدء مع ALLaM-7B-Instruct-preview
تعلم كيفية استخدام نموذج ALLaM-7B-Instruct-preview مع Python، وكيفية التفاعل معه من JavaScript عبر واجهة برمجة مستضافة (مثل Hugging Face Spaces).