Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
| 1 |
import os
|
| 2 |
import numpy as np
|
| 3 |
import pandas as pd
|
|
|
|
|
|
|
|
|
|
| 4 |
from flask import Flask, request, jsonify, render_template_string
|
| 5 |
-
from
|
| 6 |
-
from
|
| 7 |
from math import radians, cos, sin, sqrt, atan2
|
| 8 |
import joblib
|
| 9 |
import requests
|
|
@@ -19,30 +22,46 @@ app = Flask(__name__)
|
|
| 19 |
UPLOAD_FOLDER = '/tmp/uploads'
|
| 20 |
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
| 21 |
|
| 22 |
-
#
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
crop_model = joblib.load("models/model_random_forest.joblib")
|
| 25 |
crop_model_label = joblib.load("models/label_encoder.joblib")
|
|
|
|
|
|
|
| 26 |
soil_df = pd.read_csv("data/soil_data_with_class.csv")
|
| 27 |
agri_df = pd.read_csv("data/tips_menanam_dan_manfaat_tanaman.csv")
|
| 28 |
-
class_labels = ['Black Soil', 'Cinder Soil', 'Laterite Soil', 'Peat Soil', 'Yellow Soil']
|
| 29 |
|
| 30 |
-
# Fungsi preprocessing gambar
|
| 31 |
def preprocess_image(img_path):
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
# Fungsi prediksi jenis tanah
|
| 37 |
def predict_soil_type(img_path):
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
| 41 |
predicted_class = class_labels[predicted_index]
|
| 42 |
-
accuracy = float(
|
| 43 |
return predicted_class, accuracy
|
| 44 |
|
| 45 |
-
#
|
| 46 |
def find_nearest_soil_data_weighted(soil_type, lat, lon, n_points=1):
|
| 47 |
filtered = soil_df[soil_df['Class_Name'] == soil_type].copy()
|
| 48 |
if filtered.empty:
|
|
@@ -54,7 +73,7 @@ def find_nearest_soil_data_weighted(soil_type, lat, lon, n_points=1):
|
|
| 54 |
lat2, lon2 = radians(row['Location_Latitude']), radians(row['Location_Longitude'])
|
| 55 |
dlat = lat2 - user_lat
|
| 56 |
dlon = lon2 - user_lon
|
| 57 |
-
a = sin(dlat / 2)
|
| 58 |
c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
| 59 |
return 6371 * c
|
| 60 |
|
|
@@ -72,7 +91,7 @@ def find_nearest_soil_data_weighted(soil_type, lat, lon, n_points=1):
|
|
| 72 |
"distance_km": float(row['Distance_km'])
|
| 73 |
}
|
| 74 |
|
| 75 |
-
# Ambil data cuaca
|
| 76 |
def get_weather_data(lat, lon):
|
| 77 |
url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={OPENWEATHER_API_KEY}&units=metric"
|
| 78 |
res = requests.get(url)
|
|
@@ -84,7 +103,7 @@ def get_weather_data(lat, lon):
|
|
| 84 |
"humidity": float(data['main']['humidity']),
|
| 85 |
}
|
| 86 |
|
| 87 |
-
#
|
| 88 |
def get_farming_tips(df, crop_name):
|
| 89 |
crop_name_str = str(crop_name)
|
| 90 |
match = df[df['Nama Tanaman'].str.lower() == crop_name_str.lower()]
|
|
@@ -98,7 +117,7 @@ def get_farming_tips(df, crop_name):
|
|
| 98 |
}
|
| 99 |
return None
|
| 100 |
|
| 101 |
-
#
|
| 102 |
def get_location_name(lat, lon):
|
| 103 |
try:
|
| 104 |
url = f"https://nominatim.openstreetmap.org/reverse?lat={lat}&lon={lon}&format=json"
|
|
@@ -136,7 +155,7 @@ def index():
|
|
| 136 |
"""
|
| 137 |
return render_template_string(html_content)
|
| 138 |
|
| 139 |
-
# Endpoint analisis
|
| 140 |
@app.route('/analyze', methods=['POST'])
|
| 141 |
def analyze():
|
| 142 |
if 'image' not in request.files:
|
|
@@ -222,5 +241,6 @@ def analyze():
|
|
| 222 |
|
| 223 |
return jsonify(result)
|
| 224 |
|
|
|
|
| 225 |
if __name__ == '__main__':
|
| 226 |
app.run(debug=True)
|
|
|
|
| 1 |
import os
|
| 2 |
import numpy as np
|
| 3 |
import pandas as pd
|
| 4 |
+
import torch
|
| 5 |
+
import timm
|
| 6 |
+
import torch.nn as nn
|
| 7 |
from flask import Flask, request, jsonify, render_template_string
|
| 8 |
+
from torchvision import transforms
|
| 9 |
+
from PIL import Image
|
| 10 |
from math import radians, cos, sin, sqrt, atan2
|
| 11 |
import joblib
|
| 12 |
import requests
|
|
|
|
| 22 |
UPLOAD_FOLDER = '/tmp/uploads'
|
| 23 |
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
| 24 |
|
| 25 |
+
# Label klasifikasi tanah
|
| 26 |
+
class_labels = ['Black Soil', 'Cinder Soil', 'Laterite Soil', 'Peat Soil', 'Yellow Soil']
|
| 27 |
+
num_classes = len(class_labels)
|
| 28 |
+
|
| 29 |
+
# Load model ViT (PyTorch)
|
| 30 |
+
soil_model = timm.create_model("vit_base_patch16_224", pretrained=False, num_classes=num_classes)
|
| 31 |
+
soil_model.head = nn.Linear(soil_model.head.in_features, num_classes)
|
| 32 |
+
soil_model.load_state_dict(torch.load("models/best_vit_model.pth", map_location=torch.device("cpu")))
|
| 33 |
+
soil_model.eval()
|
| 34 |
+
|
| 35 |
+
# Load model crop recommendation
|
| 36 |
crop_model = joblib.load("models/model_random_forest.joblib")
|
| 37 |
crop_model_label = joblib.load("models/label_encoder.joblib")
|
| 38 |
+
|
| 39 |
+
# Load data referensi
|
| 40 |
soil_df = pd.read_csv("data/soil_data_with_class.csv")
|
| 41 |
agri_df = pd.read_csv("data/tips_menanam_dan_manfaat_tanaman.csv")
|
|
|
|
| 42 |
|
| 43 |
+
# Fungsi preprocessing gambar (PyTorch style)
|
| 44 |
def preprocess_image(img_path):
|
| 45 |
+
transform = transforms.Compose([
|
| 46 |
+
transforms.Resize((224, 224)),
|
| 47 |
+
transforms.ToTensor(),
|
| 48 |
+
transforms.Normalize(mean=[0.5]*3, std=[0.5]*3)
|
| 49 |
+
])
|
| 50 |
+
img = Image.open(img_path).convert("RGB")
|
| 51 |
+
return transform(img).unsqueeze(0)
|
| 52 |
|
| 53 |
# Fungsi prediksi jenis tanah
|
| 54 |
def predict_soil_type(img_path):
|
| 55 |
+
img_tensor = preprocess_image(img_path)
|
| 56 |
+
with torch.no_grad():
|
| 57 |
+
outputs = soil_model(img_tensor)
|
| 58 |
+
probabilities = torch.softmax(outputs, dim=1).numpy()[0]
|
| 59 |
+
predicted_index = np.argmax(probabilities)
|
| 60 |
predicted_class = class_labels[predicted_index]
|
| 61 |
+
accuracy = float(probabilities[predicted_index])
|
| 62 |
return predicted_class, accuracy
|
| 63 |
|
| 64 |
+
# Hitung jarak dengan haversine
|
| 65 |
def find_nearest_soil_data_weighted(soil_type, lat, lon, n_points=1):
|
| 66 |
filtered = soil_df[soil_df['Class_Name'] == soil_type].copy()
|
| 67 |
if filtered.empty:
|
|
|
|
| 73 |
lat2, lon2 = radians(row['Location_Latitude']), radians(row['Location_Longitude'])
|
| 74 |
dlat = lat2 - user_lat
|
| 75 |
dlon = lon2 - user_lon
|
| 76 |
+
a = sin(dlat / 2)**2 + cos(user_lat) * cos(lat2) * sin(dlon / 2)**2
|
| 77 |
c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
| 78 |
return 6371 * c
|
| 79 |
|
|
|
|
| 91 |
"distance_km": float(row['Distance_km'])
|
| 92 |
}
|
| 93 |
|
| 94 |
+
# Ambil data cuaca dari OpenWeather
|
| 95 |
def get_weather_data(lat, lon):
|
| 96 |
url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={OPENWEATHER_API_KEY}&units=metric"
|
| 97 |
res = requests.get(url)
|
|
|
|
| 103 |
"humidity": float(data['main']['humidity']),
|
| 104 |
}
|
| 105 |
|
| 106 |
+
# Ambil tips bertani dan manfaat tanaman
|
| 107 |
def get_farming_tips(df, crop_name):
|
| 108 |
crop_name_str = str(crop_name)
|
| 109 |
match = df[df['Nama Tanaman'].str.lower() == crop_name_str.lower()]
|
|
|
|
| 117 |
}
|
| 118 |
return None
|
| 119 |
|
| 120 |
+
# Reverse geocoding
|
| 121 |
def get_location_name(lat, lon):
|
| 122 |
try:
|
| 123 |
url = f"https://nominatim.openstreetmap.org/reverse?lat={lat}&lon={lon}&format=json"
|
|
|
|
| 155 |
"""
|
| 156 |
return render_template_string(html_content)
|
| 157 |
|
| 158 |
+
# Endpoint analisis
|
| 159 |
@app.route('/analyze', methods=['POST'])
|
| 160 |
def analyze():
|
| 161 |
if 'image' not in request.files:
|
|
|
|
| 241 |
|
| 242 |
return jsonify(result)
|
| 243 |
|
| 244 |
+
# Jalankan server
|
| 245 |
if __name__ == '__main__':
|
| 246 |
app.run(debug=True)
|