1/* Deep disease detection â a MobileNet classifier fine-tuned on the 2 * PlantVillage dataset (38 classes across 14 crops), converted to 3 * TensorFlow.js and vendored locally so it runs fully on-device and 4 * offline. Model: Rishit-dagli/Greenathon-Plant-AI (Apache-2.0). 5 * 6 * Loaded lazily â only when the user requests a deep scan â because the 7 * weights are ~13 MB. */ 8window.PlantDisease = (function () { 9 const MODEL_URL = 'models/plant-disease/model.json'; 10 let modelPromise = null; 11 12 // Class order matches the model's class_indices.json exactly. 13 const CLASSES = [ 14 'Apple___Apple_scab', 'Apple___Black_rot', 'Apple___Cedar_apple_rust', 'Apple___healthy', 15 'Blueberry___healthy', 'Cherry___Powdery_mildew', 'Cherry___healthy', 16 'Corn___Cercospora_leaf_spot (Gray leaf spot)', 'Corn___Common_rust', 'Corn___Northern_Leaf_Blight', 'Corn___healthy', 17 'Grape___Black_rot', 'Grape___Esca (Black Measles)', 'Grape___Leaf_blight (Isariopsis Leaf Spot)', 'Grape___healthy', 18 'Orange___Huanglongbing (Citrus greening)', 19 'Peach___Bacterial_spot', 'Peach___healthy', 20 'Pepper (bell)___Bacterial_spot', 'Pepper (bell)___healthy', 21 'Potato___Early_blight', 'Potato___Late_blight', 'Potato___healthy', 22 'Raspberry___healthy', 'Soybean___healthy', 'Squash___Powdery_mildew', 23 'Strawberry___Leaf_scorch', 'Strawberry___healthy', 24 'Tomato___Bacterial_spot', 'Tomato___Early_blight', 'Tomato___Late_blight', 'Tomato___Leaf_Mold', 25 'Tomato___Septoria_leaf_spot', 'Tomato___Spider_mites (Two-spotted spider mite)', 'Tomato___Target_Spot', 26 'Tomato___Tomato_Yellow_Leaf_Curl_Virus', 'Tomato___Tomato_mosaic_virus', 'Tomato___healthy' 27 ]; 28 29 // Disease-specific treatment guidance, keyed by the disease portion of 30 // the class name. Compiled from horticultural extension references. 31 const TREATMENTS = { 32 'Apple_scab': 'Rake and destroy fallen leaves to break the cycle. Apply a sulfur or captan fungicide from bud break, repeating per label through spring. Prune for airflow and choose scab-resistant cultivars when replanting.', 33 'Black_rot': 'Prune out cankers and mummified fruit and destroy them. Apply a captan or copper fungicide at bud break and through the season. Keep the ground clear of dropped debris.', 34 'Cedar_apple_rust': 'Remove nearby juniper/cedar galls (the alternate host) within a few hundred metres if possible. Apply a myclobutanil fungicide from pink bud through early summer. Plant resistant varieties.', 35 'Powdery_mildew': 'Improve airflow and avoid wetting foliage. Spray weekly with either potassium-bicarbonate/baking-soda solution (1 tsp per litre + a drop of soap) or sulfur fungicide. Remove the worst-affected leaves.', 36 'Cercospora_leaf_spot (Gray leaf spot)': 'Rotate crops and remove infected debris. Improve spacing for airflow. Apply a strobilurin or triazole fungicide if it spreads; avoid overhead watering.', 37 'Common_rust': 'Usually cosmetic on healthy plants. For severe cases apply a triazole or mancozeb fungicide early. Plant resistant hybrids and avoid overhead watering.', 38 'Northern_Leaf_Blight': 'Rotate away from corn for a season and bury residue. Apply a fungicide at first sign if the crop is valuable. Use resistant hybrids.', 39 'Esca (Black Measles)': 'No cure â prune out and destroy affected wood in dry weather, sealing large cuts. Avoid pruning in wet conditions and keep vines unstressed with steady watering.', 40 'Leaf_blight (Isariopsis Leaf Spot)': 'Remove infected leaves and improve canopy airflow. Apply a copper or mancozeb fungicide. Avoid overhead irrigation.', 41 'Huanglongbing (Citrus greening)': 'A serious incurable bacterial disease spread by the citrus psyllid. Isolate the plant and consult your local agricultural authority â infected trees are usually removed to protect others. Control psyllids with appropriate insecticide.', 42 'Bacterial_spot': 'Remove infected leaves and avoid handling plants when wet. Apply a copper-based bactericide preventively. Use disease-free seed, rotate crops, and never work among wet plants.', 43 'Early_blight': 'Remove affected lower leaves and mulch to stop soil splash. Apply a chlorothalonil or copper fungicide every 7â10 days. Rotate crops and water at the base, not overhead.', 44 'Late_blight': 'Acts fast and is destructive â remove and bag affected plants immediately (do not compost). Apply a chlorothalonil or copper fungicide to protect the rest. Avoid overhead watering and improve airflow.', 45 'Leaf_Mold': 'Lower humidity and increase ventilation, especially in greenhouses. Remove affected leaves and apply a copper or chlorothalonil fungicide. Space plants and water at the base.', 46 'Septoria_leaf_spot': 'Remove infected lower leaves and mulch to block soil splash. Apply a chlorothalonil or copper fungicide every 7â14 days. Rotate crops and avoid wetting foliage.', 47 'Spider_mites (Two-spotted spider mite)': 'Rinse foliage with water to knock mites down, then spray insecticidal soap or neem oil (1 tsp neem + 1½ tsp soap per litre) on leaf undersides every 3â5 days. Raise humidity â mites thrive in hot, dry air.', 48 'Target_Spot': 'Remove affected leaves and improve airflow. Apply a chlorothalonil or mancozeb fungicide. Avoid overhead irrigation and rotate crops.', 49 'Tomato_Yellow_Leaf_Curl_Virus': 'An incurable virus spread by whiteflies. Remove and destroy infected plants to protect the rest. Control whiteflies with insecticidal soap or yellow sticky traps, and use resistant varieties next season.', 50 'Tomato_mosaic_virus': 'No cure â remove and destroy infected plants. The virus survives on tools and hands, so wash thoroughly and avoid tobacco products near plants. Use resistant seed and disinfect tools.', 51 'Leaf_scorch': 'Remove scorched leaves and improve airflow. Apply a copper fungicide if it spreads. Water at the base in the morning and mulch to keep roots evenly moist.' 52 }; 53 54 function pretty(cls) { 55 const [cropRaw, diseaseRaw] = cls.split('___'); 56 const crop = cropRaw.replace(/_/g, ' ');
57 const healthy = /healthy/i.test(diseaseRaw); 58 const disease = diseaseRaw.replace(/_/g, ' '); 59 return { crop, disease, healthy, treatment: healthy ? null : TREATMENTS[diseaseRaw] || null }; 60 } 61 62 // TensorFlow.js (~1.4 MB) is loaded on demand, only when a deep scan is 63 // requested, so it never weighs on first paint. The service worker 64 // caches it after first use, so later scans work offline. 65 let tfPromise = null; 66 function loadTf() { 67 if (typeof tf !== 'undefined') return Promise.resolve(); 68 if (!tfPromise) { 69 tfPromise = new Promise((resolve, reject) => { 70 const s = document.createElement('script'); 71 s.src = 'js/tf.min.js'; 72 s.onload = resolve; 73 s.onerror = () => { tfPromise = null; reject(new Error('failed to load TensorFlow.js')); }; 74 document.head.appendChild(s); 75 }); 76 } 77 return tfPromise; 78 } 79 80 async function load() { 81 await loadTf(); 82 if (typeof tf === 'undefined') throw new Error('TensorFlow.js not loaded'); 83 return tf.loadLayersModel(MODEL_URL); 84 } 85 86 /* Classify a canvas. Returns { crop, disease, healthy, confidence, 87 * treatment, alternatives:[{label,prob}] } or throws. */ 88 async function classify(canvas) { 89 if (!modelPromise) modelPromise = load(); 90 const model = await modelPromise; 91 92 const logits = tf.tidy(() => { 93 const raw = tf.browser.fromPixels(canvas); 94 // Center-crop to a square so a non-square photo isn't distorted, 95 // then bilinear-resize to 224Ã224 â this matches how the model was 96 // trained (antialiased resize of centered single-leaf images) and 97 // yields noticeably more accurate predictions than a nearest- 98 // neighbour squash of the whole frame. 99 const h0 = raw.shape[0], w0 = raw.shape[1]; 100 const side = Math.min(h0, w0); 101 const top = Math.floor((h0 - side) / 2); 102 const left = Math.floor((w0 - side) / 2); 103 const img = raw 104 .slice([top, left, 0], [side, side, 3]) 105 .resizeBilinear([224, 224]) 106 .toFloat() 107 .div(255) 108 .expandDims(); 109 return model.predict(img); 110 }); 111 const probs = await logits.data(); 112 logits.dispose(); 113 114 const ranked = Array.from(probs) 115 .map((p, i) => ({ i, p })) 116 .sort((a, b) => b.p - a.p); 117 118 const top = ranked[0]; 119 const info = pretty(CLASSES[top.i]); 120 return { 121 crop: info.crop, 122 disease: info.disease, 123 healthy: info.healthy, 124 treatment: info.treatment, 125 confidence: Math.round(top.p * 100), 126 alternatives: ranked.slice(1, 3).map(r => { 127 const alt = pretty(CLASSES[r.i]); 128 return { label: alt.crop + ' â ' + alt.disease, prob: Math.round(r.p * 100) }; 129 }) 130 }; 131 } 132 133 return { classify }; 134})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.