_umannn commited on
Commit
244c30f
·
1 Parent(s): 4342c4c

Deploy Flask Batik Classifier

Browse files
Files changed (5) hide show
  1. Dockerfile +20 -0
  2. app.py +28 -0
  3. mobilenetv3_batik.tflite +3 -0
  4. requirements.txt +5 -0
  5. utils.py +47 -0
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Gunakan Python versi stabil
2
+ FROM python:3.10
3
+
4
+ # Set folder kerja di dalam container
5
+ WORKDIR /app
6
+
7
+ # Copy requirements.txt ke container
8
+ COPY requirements.txt .
9
+
10
+ # Install semua dependencies
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+
13
+ # Copy semua file project (app.py, utils.py, model, dll)
14
+ COPY . .
15
+
16
+ # Expose port HuggingFace
17
+ EXPOSE 7860
18
+
19
+ # Jalankan Flask API
20
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify
2
+ from flask_cors import CORS
3
+ import os
4
+ from werkzeug.utils import secure_filename
5
+ from utils import predict_image
6
+
7
+ app = Flask(__name__)
8
+ CORS(app) # Allow cross-origin (Next.js ke Flask)
9
+
10
+ UPLOAD_FOLDER = 'uploads'
11
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
12
+
13
+ @app.route('/predict', methods=['POST'])
14
+ def predict():
15
+ if 'file' not in request.files:
16
+ return jsonify({'error': 'No file uploaded'}), 400
17
+
18
+ file = request.files['file']
19
+ filename = secure_filename(file.filename)
20
+ filepath = os.path.join(UPLOAD_FOLDER, filename)
21
+ file.save(filepath)
22
+
23
+ result = predict_image(filepath)
24
+
25
+ return jsonify(result)
26
+
27
+ if __name__ == '__main__':
28
+ app.run(debug=True)
mobilenetv3_batik.tflite ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4ae89f2869d74e5335d53c7329d42ba51164cd733e5e7977d0648844e500e95f
3
+ size 4032392
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ flask
2
+ flask-cors
3
+ tensorflow==2.13.0
4
+ pillow
5
+ gunicorn
utils.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tensorflow as tf
2
+ from tensorflow.keras.utils import img_to_array, load_img
3
+ import numpy as np
4
+ import os
5
+
6
+ # Path model TFLite
7
+ model_path = r"D:\KULIAH UNS\KLASIFIKASI BATIK\training\mobilenetv3_batik.tflite"
8
+
9
+ # Load interpreter TFLite
10
+ interpreter = tf.lite.Interpreter(model_path=model_path)
11
+ interpreter.allocate_tensors()
12
+
13
+ # Ambil detail input & output
14
+ input_details = interpreter.get_input_details()
15
+ output_details = interpreter.get_output_details()
16
+
17
+ # Label kelas
18
+ class_names = [
19
+ 'batik-bali', 'batik-betawi', 'batik-celup', 'batik-cendrawasih',
20
+ 'batik-ceplok', 'batik-ciamis', 'batik-garutan', 'batik-gentongan',
21
+ 'batik-kawung', 'batik-keraton', 'batik-lasem', 'batik-megamendung',
22
+ 'batik-parang', 'batik-pekalongan', 'batik-priangan', 'batik-sekar',
23
+ 'batik-sidoluhur', 'batik-sidomukti', 'batik-sogan', 'batik-tambal'
24
+ ]
25
+
26
+ def predict_image(image_path):
27
+ # Load & preprocess image
28
+ image = load_img(image_path, target_size=(224, 224))
29
+ image = img_to_array(image) / 255.0
30
+ image = np.expand_dims(image, axis=0).astype(np.float32)
31
+
32
+ # Set tensor input
33
+ interpreter.set_tensor(input_details[0]['index'], image)
34
+
35
+ # Jalankan inference
36
+ interpreter.invoke()
37
+
38
+ # Ambil tensor output
39
+ predictions = interpreter.get_tensor(output_details[0]['index'])[0]
40
+ max_index = np.argmax(predictions)
41
+ predicted_class = class_names[max_index]
42
+ confidence = float(predictions[max_index]) * 100
43
+
44
+ return {
45
+ "class": predicted_class,
46
+ "confidence": round(confidence, 2)
47
+ }