Upload project Klasifikasi Penyakit - AdindaCintya

This commit is contained in:
adindacintya 2026-08-12 10:08:52 +07:00
parent dfa11b62da
commit 9f7349294d
9 changed files with 762 additions and 115 deletions

View File

@ -40,7 +40,7 @@ public function prediksi(Request $request)
if (!$response->successful()) {
Log::error('Server prediksi gagal', ['body' => $response->body()]);
return back()->with('error', 'Tidak dapat memproses prediksi.');
return back()->with('error', 'Sistem tidak dapat melakukan klasifikasi, Pastikan gambar yang diunggah merupakan citra daun padi..');
}
$hasil = $response->json();

BIN
public/images/blast.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
public/images/blight.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
public/images/healthy.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1024 KiB

BIN
public/images/tungro.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -6,128 +6,576 @@ import numpy as np
from PIL import Image
from tensorflow.keras.models import load_model
# ==========================================
# FLASK CONFIG
# ==========================================
app = Flask(__name__)
CORS(app)
UPLOAD_FOLDER = os.path.join(os.path.dirname(__file__), 'uploads')
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
# 1. Sesuaikan nama file model!
MODEL_PATH = os.path.join(os.path.dirname(__file__), 'model_padi.h5')
# ==========================================
# FOLDER & MODEL
# ==========================================
UPLOAD_FOLDER = os.path.join(
os.path.dirname(__file__),
'uploads'
)
os.makedirs(
UPLOAD_FOLDER,
exist_ok=True
)
MODEL_PATH = os.path.join(
os.path.dirname(__file__),
'model_padi.h5'
)
# ==========================================
# CLASS SESUAI TRAINING MODEL
# ==========================================
# 2. Sesuaikan dengan urutan class di Colab kamu (4 class)
CLASS_NAMES = [
'Healthy',
'Blast',
'Blight',
'Non-Padi',
'Tungro'
]
# ==========================================
# DESKRIPSI HASIL
# ==========================================
DESCRIPTIONS = {
'Healthy': {
'deskripsi': 'Kondisi tanaman sehat.',
'solusi': 'Lanjutkan perawatan normal dan pemantauan rutin.'
'deskripsi':
'Daun padi dalam kondisi sehat.',
'solusi':
'Pertahankan pemupukan, pengairan, dan perawatan tanaman secara rutin.'
},
'Blast': {
'deskripsi': 'Blast adalah penyakit daun padi akibat jamur Pyricularia oryzae.',
'solusi': 'Lakukan pengendalian jamur dan semprot fungisida sesuai anjuran.'
'deskripsi':
'Blast merupakan penyakit daun padi yang disebabkan oleh jamur Pyricularia oryzae.',
'solusi':
'Gunakan fungisida dan varietas padi yang tahan terhadap penyakit blast.'
},
'Blight': {
'deskripsi': 'Blight merupakan hawar daun bakteri.',
'solusi': 'Gunakan varietas tahan dan sanitasi lahan yang baik.'
'deskripsi':
'Blight merupakan penyakit hawar daun yang disebabkan oleh bakteri.',
'solusi':
'Gunakan benih sehat dan lakukan sanitasi lahan.'
},
'Tungro': {
'deskripsi': 'Tungro adalah penyakit virus yang ditularkan oleh wereng.',
'solusi': 'Lakukan pengendalian hama wereng dan gunakan benih sehat.'
'deskripsi':
'Tungro merupakan penyakit virus yang ditularkan oleh wereng hijau.',
'solusi':
'Lakukan pengendalian wereng dan gunakan varietas tahan tungro.'
},
'Non-Padi': {
'deskripsi':
'Gambar yang diunggah bukan daun padi.',
'solusi':
'Silakan upload gambar daun padi yang jelas.'
}
}
# Load model
# ==========================================
# LOAD MODEL
# ==========================================
model = None
try:
model = load_model(MODEL_PATH)
print('Model berhasil dimuat dari', MODEL_PATH)
print("Model berhasil dimuat")
print(
"Input Shape :",
model.input_shape
)
print(
"Output Shape:",
model.output_shape
)
except Exception as e:
print("Gagal load model:", e)
print(
"Gagal load model:",
e
)
# ==========================================
# PREPROCESS IMAGE
# ==========================================
def preprocess_image(path):
image = Image.open(path).convert('RGB')
image = Image.open(path)
image = image.resize((224, 224))
image = image.convert('RGB')
image_array = np.array(image).astype('float32') / 255.0
image_array = np.expand_dims(image_array, axis=0)
image = image.resize(
(224,224)
)
image_array = np.array(image)
image_array = (
image_array.astype('float32')
/
255.0
)
image_array = np.expand_dims(
image_array,
axis=0
)
return image_array
@app.route('/prediksi', methods=['POST'])
# ==========================================
# TEST API
# ==========================================
@app.route('/')
def home():
return jsonify({
"status":
"API Klasifikasi Penyakit Daun Padi Aktif"
})
# ==========================================
# PREDIKSI
# ==========================================
@app.route(
'/prediksi',
methods=['POST']
)
def prediksi():
# 3. Menerima request dengan key 'gambar'
if 'gambar' not in request.files:
return jsonify({
'error': 'Tidak ada gambar'
}), 400
"error":
"Tidak ada gambar dikirim"
}),400
file = request.files['gambar']
if file.filename == '':
return jsonify({
'error': 'File kosong'
}), 400
# Simpan gambar
filename = datetime.now().strftime('%Y%m%d%H%M%S_') + file.filename
"error":
"File kosong"
}),400
filename = (
datetime.now()
.strftime('%Y%m%d%H%M%S_')
+
file.filename
)
filepath = os.path.join(
UPLOAD_FOLDER,
filename
)
filepath = os.path.join(UPLOAD_FOLDER, filename)
file.save(filepath)
print("Gambar diterima:", filename)
print("\n====================")
print(
"Gambar diterima:",
filename
)
if model is None:
return jsonify({'error': 'Model tidak tersedia. Pastikan file .h5 ada.'}), 500
return jsonify({
"error":
"Model tidak tersedia"
}),500
try:
# Preprocessing
image_array = preprocess_image(filepath)
predictions = model.predict(image_array, verbose=0)
predicted_index = int(np.argmax(predictions, axis=1)[0])
confidence = float(np.max(predictions, axis=1)[0]) * 100.0
label = CLASS_NAMES[predicted_index] if predicted_index < len(CLASS_NAMES) else f'Kelas {predicted_index + 1}'
confidence_display = f'{confidence:.2f}%'
all_predictions = {
CLASS_NAMES[i]: float(predictions[0][i] * 100.0)
for i in range(len(CLASS_NAMES))
}
description_data = DESCRIPTIONS.get(label, {
'deskripsi': 'Tidak ada deskripsi tersedia.',
'solusi': 'Tidak ada solusi tersedia.'
})
# preprocessing
image_array = preprocess_image(filepath)
# prediksi
predictions = model.predict(
image_array,
verbose=0
)
predicted_index = int(
np.argmax(predictions[0])
)
confidence = float(
np.max(predictions[0])
)
confidence_percent = confidence * 100
label = CLASS_NAMES[
predicted_index
]
print("\nHASIL PREDIKSI")
for i,nama in enumerate(CLASS_NAMES):
print(
nama,
":",
round(
predictions[0][i]*100,
2
),
"%"
)
print(
"HASIL AKHIR:",
label
)
print(
"CONFIDENCE:",
confidence_percent
)
# ==============================
# FILTER GAMBAR TIDAK JELAS
# ==============================
if confidence_percent < 60:
return jsonify({
"error":
"Gambar tidak dikenali. Pastikan gambar daun padi."
}),400
# ==============================
# TOLAK NON PADI
# ==============================
if label == "Non-Padi":
return jsonify({
"error":
"Gambar bukan daun padi. Silakan upload daun padi."
}),400
all_predictions = {}
for i in range(len(CLASS_NAMES)):
all_predictions[
CLASS_NAMES[i]
] = round(
float(
predictions[0][i]*100
),
2
)
detail = DESCRIPTIONS[label]
return jsonify({
'penyakit': label,
'confidence': confidence_display,
'all_predictions': all_predictions,
'deskripsi': description_data['deskripsi'],
'solusi': description_data['solusi']
"penyakit":
label,
"confidence":
f"{confidence_percent:.2f}%",
"all_predictions":
all_predictions,
"deskripsi":
detail['deskripsi'],
"solusi":
detail['solusi']
})
except Exception as e:
print("ERROR:", e)
print(
"ERROR:",
e
)
return jsonify({
'error': str(e)
}), 500
"error":
str(e)
}),500
# ==========================================
# RUN FLASK
# ==========================================
if __name__ == '__main__':
print('=' * 50)
print('Klasifikasi Penyakit Daun Padi')
print('=' * 50)
app.run(host='127.0.0.1', port=5000, debug=False)
print("="*50)
print(
"Klasifikasi Penyakit Daun Padi"
)
print("="*50)
app.run(
host='127.0.0.1',
port=5000,
debug=False
)

View File

@ -25,13 +25,6 @@
<h2 class="text-xl font-bold text-gray-800 mb-6">Statistik Penyakit</h2>
<div class="h-64"><canvas id="chartPenyakit"></canvas></div>
</div>
<div class="bg-white p-8 rounded-3xl shadow-sm border border-gray-100">
<h2 class="text-xl font-bold text-gray-800 mb-6">Confusion Matrix</h2>
<div class="bg-gray-50 p-4 rounded-2xl border border-dashed border-gray-200">
<img src="https://miro.medium.com/v2/resize:fit:1200/1*Z54JgbS4DUwWSknhDCvNTQ.png" class="rounded-xl w-full">
</div>
</div>
</div>
<div class="mt-10">
<div class="flex justify-between items-center mb-6">

View File

@ -21,7 +21,7 @@
<div class="group bg-white rounded-[2rem] shadow-lg hover:shadow-2xl border border-gray-100 overflow-hidden hover:-translate-y-2 transition-all duration-500 flex flex-col">
<div class="relative overflow-hidden h-64 shrink-0">
<img src="https://www.irri.org/sites/default/files/styles/large/public/blast-rice.jpg"
<img src="{{ asset('images/blast.jpg') }}"
class="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700">
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent"></div>
<div class="absolute bottom-6 left-6">
@ -73,7 +73,7 @@ class="w-full h-full object-cover group-hover:scale-110 transition-transform dur
<div class="group bg-white rounded-[2rem] shadow-lg hover:shadow-2xl border border-gray-100 overflow-hidden hover:-translate-y-2 transition-all duration-500 flex flex-col">
<div class="relative overflow-hidden h-64 shrink-0">
<img src="https://www.knowledgebank.irri.org/images/stories/xbacterial-blight.jpg.pagespeed.ic.y.jpg"
<img src="{{ asset('images/blight.jpg') }}"
class="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700">
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent"></div>
<div class="absolute bottom-6 left-6">
@ -125,7 +125,7 @@ class="w-full h-full object-cover group-hover:scale-110 transition-transform dur
<div class="group bg-white rounded-[2rem] shadow-lg hover:shadow-2xl border border-gray-100 overflow-hidden hover:-translate-y-2 transition-all duration-500 flex flex-col">
<div class="relative overflow-hidden h-64 shrink-0">
<img src="https://www.knowledgebank.irri.org/images/stories/tungro-rice.jpg"
<img src="{{ asset('images/tungro.jpg') }}"
class="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700">
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent"></div>
<div class="absolute bottom-6 left-6">
@ -182,7 +182,7 @@ class="w-full h-full object-cover group-hover:scale-110 transition-transform dur
</div>
<div class="relative overflow-hidden h-64 shrink-0">
<img src="https://www.knowledgebank.irri.org/images/stories/healthy-rice.jpg"
<img src="{{ asset('images/healthy.jpg') }}"
class="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700">
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent"></div>
<div class="absolute bottom-6 left-6">

View File

@ -127,7 +127,7 @@ class="relative rounded-3xl shadow-2xl border-4 border-white transform hover:sca
<div class="absolute top-0 left-0 w-2 h-full bg-green-500"></div>
<h2 class="text-3xl font-extrabold text-gray-800 mb-8 text-center">
Hasil Analisis AI
Hasil Analisis
</h2>
<img src="{{ asset('uploads/' . session('gambar')) }}" class="w-72 rounded-2xl shadow-xl mb-10 mx-auto border-4 border-white transform hover:scale-105 transition-transform duration-500">
@ -198,58 +198,264 @@ class="relative rounded-3xl shadow-2xl border-4 border-white transform hover:sca
</div>
</section>
<section class="max-w-7xl mx-auto px-6 pb-24">
<div class="text-center mb-16">
<h2 class="text-4xl font-extrabold text-gray-800 mb-4">Galeri Penyakit Padi</h2>
<p class="text-gray-500 max-w-2xl mx-auto">Kenali berbagai jenis penyakit yang sering menyerang daun padi beserta gejalanya secara visual.</p>
<!-- ================= GALERI PENYAKIT ================= -->
<section class="max-w-7xl mx-auto px-6 pb-24">
<div class="text-center mb-16">
<h2 class="text-4xl font-extrabold text-gray-800 mb-4">
Galeri Penyakit Padi
</h2>
<p class="text-gray-500 max-w-2xl mx-auto">
Kenali berbagai jenis penyakit yang sering menyerang daun padi beserta gejalanya secara visual.
</p>
</div>
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
<!-- BLAST -->
<div class="group bg-white rounded-3xl shadow-lg hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 overflow-hidden border">
<div class="relative overflow-hidden">
<img
src="{{ asset('images/blast.jpg') }}"
class="h-56 w-full object-cover group-hover:scale-110 transition-transform duration-700"
>
<div class="absolute top-4 right-4 bg-white/90 px-3 py-1 rounded-full text-xs font-bold text-red-600">
Bahaya
</div>
</div>
<div class="p-6">
<h3 class="text-2xl font-bold text-gray-800 mb-3">
Blast
</h3>
<p class="text-gray-500 text-sm leading-relaxed">
Penyakit daun padi yang disebabkan oleh jamur
<b>Pyricularia oryzae</b> dengan gejala bercak berbentuk belah ketupat.
</p>
</div>
</div>
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
<div class="group bg-white rounded-3xl shadow-lg hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 overflow-hidden border border-gray-100">
<div class="relative overflow-hidden">
<img src="https://images.unsplash.com/photo-1592982537447-6f2a6a0f4d4d?q=80&w=1200&auto=format&fit=crop" class="h-56 w-full object-cover group-hover:scale-110 transition-transform duration-700">
<div class="absolute top-4 right-4 bg-white/90 backdrop-blur-sm px-3 py-1 rounded-full text-xs font-bold text-red-600 shadow-sm">Bahaya</div>
</div>
<div class="p-6">
<h3 class="text-2xl font-bold text-gray-800 mb-3 group-hover:text-red-600 transition-colors">Blast</h3>
<p class="text-gray-500 text-sm leading-relaxed">Penyakit akibat jamur yang menyerang daun padi, ditandai dengan bercak belah ketupat.</p>
<!-- BLIGHT -->
<div class="group bg-white rounded-3xl shadow-lg hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 overflow-hidden border">
<div class="relative overflow-hidden">
<img
src="{{ asset('images/blight.jpg') }}"
class="h-56 w-full object-cover group-hover:scale-110 transition-transform duration-700"
>
<div class="absolute top-4 right-4 bg-white/90 px-3 py-1 rounded-full text-xs font-bold text-yellow-600">
Waspada
</div>
</div>
<div class="group bg-white rounded-3xl shadow-lg hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 overflow-hidden border border-gray-100">
<div class="relative overflow-hidden">
<img src="https://images.unsplash.com/photo-1500382017468-9049fed747ef?q=80&w=1200&auto=format&fit=crop" class="h-56 w-full object-cover group-hover:scale-110 transition-transform duration-700">
<div class="absolute top-4 right-4 bg-white/90 backdrop-blur-sm px-3 py-1 rounded-full text-xs font-bold text-yellow-600 shadow-sm">Waspada</div>
</div>
<div class="p-6">
<h3 class="text-2xl font-bold text-gray-800 mb-3 group-hover:text-yellow-600 transition-colors">Blight</h3>
<p class="text-gray-500 text-sm leading-relaxed">Hawar daun bakteri yang menyebabkan daun menguning, mengering, dan mati dari ujungnya.</p>
</div>
<div class="p-6">
<h3 class="text-2xl font-bold text-gray-800 mb-3">
Blight
</h3>
<p class="text-gray-500 text-sm leading-relaxed">
Penyakit hawar daun bakteri yang menyebabkan daun padi
menguning, layu, dan mengalami kerusakan.
</p>
</div>
<div class="group bg-white rounded-3xl shadow-lg hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 overflow-hidden border border-gray-100">
<div class="relative overflow-hidden">
<img src="https://images.unsplash.com/photo-1464226184884-fa280b87c399?q=80&w=1200&auto=format&fit=crop" class="h-56 w-full object-cover group-hover:scale-110 transition-transform duration-700">
<div class="absolute top-4 right-4 bg-white/90 backdrop-blur-sm px-3 py-1 rounded-full text-xs font-bold text-orange-500 shadow-sm">Virus</div>
</div>
<div class="p-6">
<h3 class="text-2xl font-bold text-gray-800 mb-3 group-hover:text-orange-500 transition-colors">Tungro</h3>
<p class="text-gray-500 text-sm leading-relaxed">Penyakit virus yang ditularkan wereng hijau, menyebabkan tanaman kerdil dan daun kuning-oranye.</p>
</div>
</div>
<div class="group bg-white rounded-3xl shadow-lg hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 overflow-hidden border border-gray-100">
<div class="relative overflow-hidden">
<img src="https://images.unsplash.com/photo-1472396961693-142e6e269027?q=80&w=1200&auto=format&fit=crop" class="h-56 w-full object-cover group-hover:scale-110 transition-transform duration-700">
<div class="absolute top-4 right-4 bg-white/90 backdrop-blur-sm px-3 py-1 rounded-full text-xs font-bold text-green-600 shadow-sm">Aman</div>
</div>
<div class="p-6">
<h3 class="text-2xl font-bold text-gray-800 mb-3 group-hover:text-green-600 transition-colors">Healthy</h3>
<p class="text-gray-500 text-sm leading-relaxed">Daun padi sehat tanpa gejala penyakit. Memiliki warna hijau merata dan tekstur normal.</p>
</div>
</div>
</div>
</section>
<!-- TUNGRO -->
<div class="group bg-white rounded-3xl shadow-lg hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 overflow-hidden border">
<div class="relative overflow-hidden">
<img
src="{{ asset('images/tungro.jpg') }}"
class="h-56 w-full object-cover group-hover:scale-110 transition-transform duration-700"
>
<div class="absolute top-4 right-4 bg-white/90 px-3 py-1 rounded-full text-xs font-bold text-orange-500">
Virus
</div>
</div>
<div class="p-6">
<h3 class="text-2xl font-bold text-gray-800 mb-3">
Tungro
</h3>
<p class="text-gray-500 text-sm leading-relaxed">
Penyakit yang disebabkan oleh virus dan ditularkan oleh wereng hijau.
Gejalanya berupa daun menguning dan pertumbuhan tanaman terhambat.
</p>
</div>
</div>
<!-- HEALTHY -->
<div class="group bg-white rounded-3xl shadow-lg hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 overflow-hidden border">
<div class="relative overflow-hidden">
<img
src="{{ asset('images/healthy.jpg') }}"
class="h-56 w-full object-cover group-hover:scale-110 transition-transform duration-700"
>
<div class="absolute top-4 right-4 bg-white/90 px-3 py-1 rounded-full text-xs font-bold text-green-600">
Aman
</div>
</div>
<div class="p-6">
<h3 class="text-2xl font-bold text-gray-800 mb-3">
Healthy
</h3>
<p class="text-gray-500 text-sm leading-relaxed">
Kondisi daun padi sehat dengan warna hijau normal,
tidak ditemukan tanda penyakit.
</p>
</div>
</div>
</div>
</section>
</div>
@ -261,7 +467,7 @@ class="relative rounded-3xl shadow-2xl border-4 border-white transform hover:sca
<div class="absolute inset-4 rounded-full border-b-4 border-yellow-400 animate-spin"></div>
</div>
<h2 class="text-2xl font-extrabold text-gray-800 mb-2">Menganalisis...</h2>
<p class="text-gray-500 font-medium">AI sedang memproses gambar Anda.</p>
<p class="text-gray-500 font-medium">sedang memproses gambar Anda.</p>
</div>
</div>
@endsection