Spaces:
Sleeping
Sleeping
ksvmuralidhar
commited on
Commit
•
530f5ca
1
Parent(s):
9c21c9e
Upload 15 files
Browse files- Dockerfile +7 -0
- app.py +104 -0
- models/vegetable_classification_model_mnet.tflite +3 -0
- requirements.txt +7 -0
- static/output/display.jpg +0 -0
- static/styles.css +136 -0
- static/uploads/1283.jpg +0 -0
- static/white.png +0 -0
- templates/index.html +44 -0
- utils/__init__.py +0 -0
- utils/__pycache__/__init__.cpython-310.pyc +0 -0
- utils/__pycache__/delete_file.cpython-310.pyc +0 -0
- utils/__pycache__/make_upload_dir.cpython-310.pyc +0 -0
- utils/delete_file.py +7 -0
- utils/make_upload_dir.py +8 -0
Dockerfile
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.11-slim
|
2 |
+
WORKDIR /webapp
|
3 |
+
COPY . .
|
4 |
+
RUN pip install --upgrade pip
|
5 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
6 |
+
CMD ["gunicorn", "-b", "0.0.0.0:7860", "--timeout", "120", "app:app"]
|
7 |
+
EXPOSE 7860
|
app.py
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from flask import Flask, render_template, request
|
3 |
+
from flask_cors import cross_origin, CORS
|
4 |
+
import tensorflow.lite as lite
|
5 |
+
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
|
6 |
+
from utils.make_upload_dir import make_upload_dir
|
7 |
+
from tensorflow.keras.utils import load_img, save_img, img_to_array
|
8 |
+
from numpy import array, round
|
9 |
+
from utils.delete_file import delete_file
|
10 |
+
from requests import get
|
11 |
+
|
12 |
+
|
13 |
+
app = Flask(__name__, template_folder='templates', static_folder='static')
|
14 |
+
CORS(app)
|
15 |
+
|
16 |
+
CLASS_INDICES = {'Bean': 0,
|
17 |
+
'Bitter_Gourd': 1,
|
18 |
+
'Bottle_Gourd': 2,
|
19 |
+
'Brinjal': 3,
|
20 |
+
'Broccoli': 4,
|
21 |
+
'Cabbage': 5,
|
22 |
+
'Capsicum': 6,
|
23 |
+
'Carrot': 7,
|
24 |
+
'Cauliflower': 8,
|
25 |
+
'Cucumber': 9,
|
26 |
+
'Papaya': 10,
|
27 |
+
'Potato': 11,
|
28 |
+
'Pumpkin': 12,
|
29 |
+
'Radish': 13,
|
30 |
+
'Tomato': 14}
|
31 |
+
|
32 |
+
interpreter = lite.Interpreter(model_path=os.path.join("models", "vegetable_classification_model_mnet.tflite"))
|
33 |
+
|
34 |
+
|
35 |
+
def predict(test_image):
|
36 |
+
interpreter.allocate_tensors()
|
37 |
+
|
38 |
+
input_details = interpreter.get_input_details()[0]
|
39 |
+
output_details = interpreter.get_output_details()[0]
|
40 |
+
|
41 |
+
test_image = test_image.astype(input_details["dtype"])
|
42 |
+
interpreter.set_tensor(input_details["index"], test_image)
|
43 |
+
interpreter.invoke()
|
44 |
+
output = interpreter.get_tensor(output_details["index"])[0]
|
45 |
+
|
46 |
+
prediction = [*CLASS_INDICES.keys()][output.argmax()]
|
47 |
+
probability = round(output.max() * 100, 2)
|
48 |
+
result = f'{prediction} ({probability}%)'
|
49 |
+
return result
|
50 |
+
|
51 |
+
|
52 |
+
@app.route("/")
|
53 |
+
@cross_origin()
|
54 |
+
def index():
|
55 |
+
"""
|
56 |
+
displays the index.html page
|
57 |
+
"""
|
58 |
+
display_image = os.path.join("static", "white.png")
|
59 |
+
delete_file(os.path.join('.', 'static', 'uploads'))
|
60 |
+
delete_file(os.path.join('.', 'static', 'output', 'display.jpg'))
|
61 |
+
return render_template("index.html", display_image=display_image)
|
62 |
+
|
63 |
+
|
64 |
+
@app.route("/", methods=["POST"])
|
65 |
+
@cross_origin()
|
66 |
+
def file_prediction():
|
67 |
+
upload_file_path = ""
|
68 |
+
result = ''
|
69 |
+
error = ""
|
70 |
+
try:
|
71 |
+
upload_file_path = os.path.join('.', 'static', 'uploads')
|
72 |
+
make_upload_dir(upload_file_path)
|
73 |
+
upload_filename = "input.jpg"
|
74 |
+
upload_file_path = os.path.join(upload_file_path, upload_filename)
|
75 |
+
|
76 |
+
file_url = request.form['fileinput']
|
77 |
+
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36'}
|
78 |
+
r = get(file_url, headers=headers)
|
79 |
+
with open(upload_file_path, "wb") as file:
|
80 |
+
file.write(r.content)
|
81 |
+
|
82 |
+
test_image = load_img(upload_file_path, color_mode="rgb", target_size=(224, 224))
|
83 |
+
test_image = img_to_array(test_image)
|
84 |
+
test_image = preprocess_input(test_image)
|
85 |
+
test_image = array([test_image])
|
86 |
+
|
87 |
+
display_image = os.path.join('.', 'static', 'output', 'display.jpg')
|
88 |
+
save_img(path=display_image, x=test_image[0])
|
89 |
+
|
90 |
+
result = predict(test_image)
|
91 |
+
delete_file(upload_file_path)
|
92 |
+
|
93 |
+
except Exception as e:
|
94 |
+
# raise
|
95 |
+
result = ''
|
96 |
+
error = e
|
97 |
+
display_image = os.path.join("static", "white.png")
|
98 |
+
delete_file(upload_file_path)
|
99 |
+
|
100 |
+
return render_template("index.html", result=result, error=error, display_image=display_image)
|
101 |
+
|
102 |
+
|
103 |
+
if __name__ == "__main__":
|
104 |
+
app.run(host="0.0.0.0", port=5002)
|
models/vegetable_classification_model_mnet.tflite
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:646ea12cef64a1b4bca597811ec7c21b6f7da0eb6ff4d567207e165184e2dffb
|
3 |
+
size 21413256
|
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
tensorflow-cpu==2.12.0
|
2 |
+
numpy==1.22.4
|
3 |
+
flask==2.2.2
|
4 |
+
flask_cors==3.0.10
|
5 |
+
gunicorn==20.1.0
|
6 |
+
Pillow==9.5.0
|
7 |
+
requests==2.30.0
|
static/output/display.jpg
ADDED
static/styles.css
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
html, body {
|
2 |
+
min-height: 100%;
|
3 |
+
}
|
4 |
+
body, div, form, input, select, p {
|
5 |
+
padding: 0;
|
6 |
+
margin: 0;
|
7 |
+
outline: none;
|
8 |
+
font-family: Roboto, Arial, sans-serif;
|
9 |
+
font-size: 14px;
|
10 |
+
color: #666;
|
11 |
+
line-height: 22px;
|
12 |
+
}
|
13 |
+
h1 {
|
14 |
+
position: absolute;
|
15 |
+
margin: 0;
|
16 |
+
font-size: 28px;
|
17 |
+
color: #fff;
|
18 |
+
z-index: 2;
|
19 |
+
}
|
20 |
+
h2 {
|
21 |
+
font-weight: 400;
|
22 |
+
}
|
23 |
+
.testbox {
|
24 |
+
display: flex;
|
25 |
+
justify-content: center;
|
26 |
+
align-items: center;
|
27 |
+
height: inherit;
|
28 |
+
padding: 20px;
|
29 |
+
}
|
30 |
+
.testbox1 {
|
31 |
+
display: flex;
|
32 |
+
justify-content: center;
|
33 |
+
align-items: center;
|
34 |
+
height: inherit;
|
35 |
+
padding: 5px;
|
36 |
+
}
|
37 |
+
form {
|
38 |
+
width: 30%;
|
39 |
+
padding: 20px;
|
40 |
+
border-radius: 6px;
|
41 |
+
background: #fff;
|
42 |
+
box-shadow: 0 0 20px 0 #095484;
|
43 |
+
}
|
44 |
+
.banner {
|
45 |
+
position: relative;
|
46 |
+
height: 80px;
|
47 |
+
background-size: cover;
|
48 |
+
display: flex;
|
49 |
+
justify-content: center;
|
50 |
+
align-items: center;
|
51 |
+
text-align: center;
|
52 |
+
}
|
53 |
+
|
54 |
+
.banner::after {
|
55 |
+
content: "";
|
56 |
+
background-color: rgba(0, 0, 0, 0.4);
|
57 |
+
position: absolute;
|
58 |
+
width: 100%;
|
59 |
+
height: 100%;
|
60 |
+
}
|
61 |
+
input, select {
|
62 |
+
margin-bottom: 2px;
|
63 |
+
border: 1px solid #ccc;
|
64 |
+
border-radius: 3px;
|
65 |
+
}
|
66 |
+
input {
|
67 |
+
width: calc(100% - 10px);
|
68 |
+
padding: 5px;
|
69 |
+
}
|
70 |
+
select {
|
71 |
+
width: 100%;
|
72 |
+
padding: 7px 0;
|
73 |
+
background: transparent;
|
74 |
+
}
|
75 |
+
.item:hover p, .item:hover i, .question:hover p, .question label:hover, input:hover::placeholder, a {
|
76 |
+
color: #095484;
|
77 |
+
}
|
78 |
+
.item input:hover, .item select:hover {
|
79 |
+
border: 1px solid transparent;
|
80 |
+
box-shadow: 0 0 6px 0 #095484;
|
81 |
+
color: #095484;
|
82 |
+
}
|
83 |
+
.item {
|
84 |
+
position: relative;
|
85 |
+
margin: 10px 0;
|
86 |
+
}
|
87 |
+
|
88 |
+
|
89 |
+
.btn-block {
|
90 |
+
margin-top: 10px;
|
91 |
+
text-align: center;
|
92 |
+
}
|
93 |
+
button {
|
94 |
+
width: 150px;
|
95 |
+
padding: 10px;
|
96 |
+
border: none;
|
97 |
+
border-radius: 5px;
|
98 |
+
background: #095484;
|
99 |
+
font-size: 16px;
|
100 |
+
color: #fff;
|
101 |
+
cursor: pointer;
|
102 |
+
}
|
103 |
+
button:hover {
|
104 |
+
background: #0666a3;
|
105 |
+
}
|
106 |
+
ul {
|
107 |
+
list-style-type: none;
|
108 |
+
margin: 0;
|
109 |
+
padding: 0;
|
110 |
+
overflow: hidden;
|
111 |
+
background-color: #333;
|
112 |
+
}
|
113 |
+
|
114 |
+
li {
|
115 |
+
float: left;
|
116 |
+
}
|
117 |
+
|
118 |
+
li a {
|
119 |
+
display: block;
|
120 |
+
color: white;
|
121 |
+
text-align: center;
|
122 |
+
padding: 14px 16px;
|
123 |
+
text-decoration: none;
|
124 |
+
}
|
125 |
+
|
126 |
+
/* Change the link color to #111 (black) on hover */
|
127 |
+
li a:hover {
|
128 |
+
background-color: #111;
|
129 |
+
|
130 |
+
li {
|
131 |
+
border-right: 1px solid #bbb;
|
132 |
+
}
|
133 |
+
|
134 |
+
li:last-child {
|
135 |
+
border-right: none;
|
136 |
+
}
|
static/uploads/1283.jpg
ADDED
static/white.png
ADDED
templates/index.html
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!--sourced and modified from https://www.w3docs.com/learn-html/html-form-templates.html -->
|
2 |
+
<!DOCTYPE html>
|
3 |
+
<html>
|
4 |
+
<head>
|
5 |
+
<title>Vegetable Classifier</title>
|
6 |
+
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet">
|
7 |
+
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
|
8 |
+
<link rel="stylesheet" href="static/styles.css">
|
9 |
+
</head>
|
10 |
+
<body>
|
11 |
+
<div class="testbox">
|
12 |
+
<form action="/" method="POST" enctype="multipart/form-data">
|
13 |
+
<div class="banner">
|
14 |
+
<h1>Vegetable Classifier</h1>
|
15 |
+
</div>
|
16 |
+
<h5>Detectable Vegetables: Broad Bean, Bitter Gourd, Bottle Gourd, Green Round Brinjal, Broccoli, Cabbage, Capsicum, Carrot,
|
17 |
+
Cauliflower, Cucumber, Raw Papaya, Potato, Green Pumpkin, Radish, Tomato</h5>
|
18 |
+
<h5>The model is trained on <a href="https://www.kaggle.com/datasets/misrakahmed/vegetable-image-dataset" target="_blank">images</a> clicked using a mobile phone camera.</h5>
|
19 |
+
<p>Please paste the image URL of a vegetable<sup style="color: red;">*</sup></p>
|
20 |
+
<div class="item">
|
21 |
+
|
22 |
+
<input type="text" name="fileinput" id="fileinput" autocomplete="off" required/>
|
23 |
+
</div>
|
24 |
+
|
25 |
+
|
26 |
+
<div class="btn-block">
|
27 |
+
<button type="submit" href="/file_input">Submit</button>
|
28 |
+
</div>
|
29 |
+
<div class="item">
|
30 |
+
<p style="color: red;">{{error}}</p>
|
31 |
+
</div>
|
32 |
+
</form>
|
33 |
+
|
34 |
+
</div>
|
35 |
+
<div class="testbox1">
|
36 |
+
{{result}}
|
37 |
+
</div>
|
38 |
+
<div class="testbox1">
|
39 |
+
<img src={{display_image}} />
|
40 |
+
</div>
|
41 |
+
|
42 |
+
|
43 |
+
</body>
|
44 |
+
</html>
|
utils/__init__.py
ADDED
File without changes
|
utils/__pycache__/__init__.cpython-310.pyc
ADDED
Binary file (160 Bytes). View file
|
|
utils/__pycache__/delete_file.cpython-310.pyc
ADDED
Binary file (425 Bytes). View file
|
|
utils/__pycache__/make_upload_dir.cpython-310.pyc
ADDED
Binary file (461 Bytes). View file
|
|
utils/delete_file.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from os import path
|
2 |
+
from shutil import rmtree
|
3 |
+
|
4 |
+
|
5 |
+
def delete_file(path_: str):
|
6 |
+
if path.exists(path_):
|
7 |
+
rmtree(path=path_, ignore_errors=True)
|
utils/make_upload_dir.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from os import path, makedirs
|
2 |
+
from shutil import rmtree
|
3 |
+
|
4 |
+
|
5 |
+
def make_upload_dir(path_: str):
|
6 |
+
if path.exists(path_):
|
7 |
+
rmtree(path=path_, ignore_errors=True)
|
8 |
+
makedirs(path_)
|