AtthalaricNero
Remove brightness normalization function and update preprocessing pipeline to directly use normalized image data
1590197 | import numpy as np | |
| import cv2 | |
| import joblib | |
| import base64 | |
| import io | |
| from flask import Flask, request, render_template | |
| from PIL import Image | |
| from skimage.feature import local_binary_pattern | |
| app = Flask(__name__) | |
| try: | |
| model = joblib.load("svm_fruit_model.pkl") | |
| pca = joblib.load("pca_transformer.pkl") | |
| except Exception as e: | |
| print(f"Error loading model: {e}") | |
| CLASS_NAMES = [ | |
| "Avocado", | |
| "Avocado ripe", | |
| "Banana Lady", | |
| "Banana Red", | |
| "Banana Yellow", | |
| "Carambula", | |
| "Cherimoya", | |
| "Dates", | |
| "Fig", | |
| "Guava", | |
| "Kaki", | |
| "Kiwi", | |
| "Lychee", | |
| "Mango", | |
| "Mango Red", | |
| "Mangostan", | |
| "Papaya", | |
| "Pineapple", | |
| "Pineapple Mini", | |
| "Pomegranate", | |
| "Quince", | |
| "Rambutan", | |
| "Salak", | |
| ] | |
| def extract_color_histogram(img, bins=(8, 8, 8)): | |
| hist = cv2.calcHist([img], [0, 1, 2], None, bins, [0, 256, 0, 256, 0, 256]) | |
| hist = cv2.normalize(hist, hist).flatten() | |
| return hist | |
| def extract_lbp_features(gray_img, P=8, R=1, method="uniform"): | |
| lbp = local_binary_pattern(gray_img, P, R, method) | |
| hist, _ = np.histogram(lbp.ravel(), bins=np.arange(0, P + 3), range=(0, P + 2)) | |
| hist = hist.astype("float") | |
| hist /= hist.sum() + 1e-6 | |
| return hist | |
| def preprocessing_pipeline(pil_img): | |
| img = np.array(pil_img.convert('RGB')) | |
| img = cv2.resize(img, (100, 100), interpolation=cv2.INTER_AREA) | |
| img_float = img.astype(np.float32) / 255.0 | |
| img_uint8 = (img_float * 255).astype(np.uint8) | |
| feat_color = extract_color_histogram(img_uint8) | |
| img_gray = cv2.cvtColor(img_uint8, cv2.COLOR_RGB2GRAY) | |
| feat_lbp = extract_lbp_features(img_gray) | |
| combined = np.hstack([feat_color, feat_lbp]) | |
| combined = combined.reshape(1, -1) | |
| final_features = pca.transform(combined) | |
| return final_features | |
| def index(): | |
| prediction_text = None | |
| img_data = None | |
| confidence = None | |
| top_3_predictions = None | |
| if request.method == "POST": | |
| if "file" not in request.files: | |
| return render_template("index.html", msg="Tidak ada file") | |
| file = request.files["file"] | |
| if file.filename == "": | |
| return render_template("index.html", msg="Nama file kosong") | |
| if file: | |
| try: | |
| image = Image.open(file.stream) | |
| features = preprocessing_pipeline(image) | |
| preprocessed_img = cv2.resize(np.array(image.convert('RGB')), (100, 100), interpolation=cv2.INTER_AREA) | |
| preprocessed_pil = Image.fromarray(preprocessed_img) | |
| img_io = io.BytesIO() | |
| preprocessed_pil.save(img_io, "PNG") | |
| encoded_img = base64.b64encode(img_io.getvalue()).decode("ascii") | |
| img_data = f"data:image/png;base64, {encoded_img}" | |
| pred_index = model.predict(features)[0] | |
| prediction_text = CLASS_NAMES[int(pred_index)] | |
| if hasattr(model, 'predict_proba'): | |
| probabilities = model.predict_proba(features)[0] | |
| confidence = float(probabilities[int(pred_index)]) * 100 | |
| top_3_indices = probabilities.argsort()[-3:][::-1] | |
| top_3_predictions = [ | |
| { | |
| 'name': CLASS_NAMES[idx], | |
| 'probability': float(probabilities[idx]) * 100 | |
| } | |
| for idx in top_3_indices | |
| ] | |
| else: | |
| confidence = None | |
| top_3_predictions = None | |
| except Exception as e: | |
| prediction_text = f"Error: {str(e)}" | |
| confidence = None | |
| top_3_predictions = None | |
| return render_template("index.html", prediction=prediction_text, img_data=img_data, | |
| confidence=confidence, top_predictions=top_3_predictions) | |
| if __name__ == "__main__": | |
| app.run(debug=True, port=7860) | |