Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
/data/tomato/.ipynb_checkpoints/
*.pickle
/data/tomato/__pycache__
/images

/data/tomato/LeatherDefects
/data/tomato/new_dataset
/data/tomato/newleafdatasetbgrem10k

# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files

Expand Down Expand Up @@ -41,3 +46,4 @@ yarn-error.*
#vscode
/.vscode

/API/firebasekey.json
Binary file modified API/__pycache__/api.cpython-312.pyc
Binary file not shown.
Binary file added API/__pycache__/model_inference.cpython-312.pyc
Binary file not shown.
231 changes: 211 additions & 20 deletions API/api.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
from flask import Flask, request, jsonify
import base64
from io import BytesIO
from PIL import Image
import jwt
from flask import Flask, request, jsonify, send_file
from flask_cors import CORS
from firebase import firebase
from flask_bcrypt import Bcrypt
from datetime import datetime, timedelta, timezone
import os
from operator import itemgetter
from model_inference import ModelInference
import firebase_admin
from firebase_admin import credentials, db
from operator import itemgetter

cwd = os.getcwd()
# Initialize Firebase Admin SDK
key_path = os.path.join(cwd, 'API\\firebasekey.json')
cred = credentials.Certificate(key_path)
firebase_admin.initialize_app(cred, {
'databaseURL': 'https://anomaleaf-d6feb-default-rtdb.firebaseio.com/' # Replace with your database URL
})
root = db.reference()


firebase = firebase.FirebaseApplication('https://anomaleaf-d6feb-default-rtdb.firebaseio.com', None)
app = Flask(__name__)
CORS(app)
bcrypt = Bcrypt(app)
# Directory to save images
IMAGE_UPLOAD_FOLDER = "images"

print(os.getcwd())
model_inference = ModelInference('API\\rembg_training.h5')

@app.route("/")
def home():
result = firebase.get('/Users', None)
return str(result)
# Ensure the image upload folder exists
os.makedirs(IMAGE_UPLOAD_FOLDER, exist_ok=True)
# Secret key for JWT (keep it secure)
app.config['SECRET_KEY'] = '3fe988e252dbd290c6710248b58658d0ee9f2bb2b5803d411fdbda78cb8463fa'

@app.route("/signup", methods=["POST"])
def signup():
Expand All @@ -20,20 +47,38 @@ def signup():
user_email = request.json.get("email")
user_password = request.json.get("password")

users = firebase.get('/Users', None)
users_ref = db.reference('Users')
users = users_ref.get()

if users is None:
users = {} # If users is None, initialize as an empty dictionary

if not users:
users = [] # If users is None, initialize as an empty list
# Check if user already exists
for user_data in users.values():
if user_data.get("UserEmail") == user_email:
return jsonify({"error": "User already exists."}), 409

# Hash the password
hashed_password = bcrypt.generate_password_hash(user_password).decode('utf-8')

# If user doesn't exist, create new user
new_user = {
"UserEmail": user_email,
"UserPassword": user_password
"UserPassword": hashed_password
}

firebase.post('/Users', new_user)

return jsonify({"message": "User created successfully."}), 201
# Push the new user data to Firebase
new_user_ref = users_ref.push(new_user)
new_user_key = new_user_ref.key

# Generate JWT token for the new user
token = jwt.encode({
'user_id': user_email,
'exp': datetime.now(timezone.utc) + timedelta(days=60) # Token expiry in 60 days
}, app.config['SECRET_KEY'])

# Return the token in the response
return jsonify({"token": token}), 201
else:
return jsonify({"error": "Method not allowed."}), 405

Expand All @@ -46,21 +91,167 @@ def login():
user_email = request.json.get("email")
user_password = request.json.get("password")

# Retrieve users data from Firebase
users = firebase.get('/Users', None)
# Retrieve all users data from Firebase
users_ref = db.reference('Users')
users = users_ref.get()

if users is None:
return jsonify({"error": "No users found."}), 404

# Assuming users is a dictionary
# Check if the user with the provided email exists
user_data = next((user_data for user_data in users.values() if user_data.get("UserEmail") == user_email), None)

if user_data is None:
return jsonify({"error": "Wrong email or password"}), 401

# Verify the password
if bcrypt.check_password_hash(user_data.get("UserPassword"), user_password):
# Generate JWT token
token = jwt.encode({
'user_id': user_data.get("UserEmail"),
'exp': datetime.now(timezone.utc) + timedelta(days=60) # Token expiry in 60 days
}, app.config['SECRET_KEY'])
return jsonify({"token": token, "id": user_data.get("UserEmail")}), 200
else:
return jsonify({"error": "Wrong email or password"}), 401
else:
return jsonify({"error": "Method not allowed."}), 405

@app.route("/resetPassword", methods=["POST"])
def reset_password():
if request.method == "POST":
if not request.json:
return jsonify({"error": "No data provided."}), 400
else:
old_password = request.json.get("oldPassword")
new_password = request.json.get("newPassword")
email = request.json.get("email")

# Fetch user data from the database
user_ref = db.reference('Users')
users = user_ref.get()

if users is None:
return jsonify({"error": "No users found."}), 404

# Check if user exists and old password matches
for user_id, user_data in users.items():
if user_data.get("UserEmail") == user_email and user_data.get("UserPassword") == user_password:
return jsonify({"message": "User found.", "UserID": user_id}), 200
if user_data.get("UserEmail") == email:
stored_password = user_data.get("UserPassword")
if bcrypt.check_password_hash(stored_password, old_password):
# Hash the new password
hashed_password = bcrypt.generate_password_hash(new_password).decode('utf-8')

# Update the password in the database
user_ref.child(user_id).update({"UserPassword": hashed_password})
return jsonify({"message": "Password updated successfully."}), 200
else:
return jsonify({"error": "Old password is incorrect."}), 401

# If no matching user is found
return jsonify({"error": "Incorrect Username or Password."}), 401
return jsonify({"error": "User not found."}), 404
else:
return jsonify({"error": "Method not allowed."}), 405

@app.route('/get_Submissions', methods=['POST'])
def get_Submissions():
userID = request.json.get("userID")
imageFilter = request.json.get("filterType")

# Retrieve images from Firebase
images_ref = root.child('Images')
images = images_ref.order_by_child('userID').equal_to(userID).get()

# Filter images based on filterType
if imageFilter == '': # Sort by date most recent to oldest
sorted_images = sorted(images.values(), key=lambda x: x['timestamp'], reverse=True)
elif imageFilter == '1': # Sort by date most recent to oldest
sorted_images = sorted(images.values(), key=lambda x: x['timestamp'], reverse=True)
elif imageFilter == '2': # Sort by oldest to newest
sorted_images = sorted(images.values(), key=lambda x: x['timestamp'])
elif imageFilter == '3': # Sort by isAnomaly by date most recent
sorted_images = [img for img in images.values() if img.get('isAnomaly')]
sorted_images.sort(key=lambda x: x['timestamp'], reverse=True)
elif imageFilter == '4': # Filter isAnomaly = false by date most recent
sorted_images = [img for img in images.values() if not img['isAnomaly']]
sorted_images.sort(key=lambda x: x['timestamp'], reverse=True)
else:
return jsonify({"error": "Invalid filter type"}), 400

# Retrieve image data for sorted images
image_info = []
for img in sorted_images:
image_path = img.get('imageDirectory') # Assuming 'imageDirectory' holds the image file path
if image_path:
# Normalize the image path to remove redundant components like the dot
normalized_path = os.path.normpath(image_path)
# Construct the URL for the image
image_url = f"http://{request.host}/{normalized_path.replace(os.sep, '/')}"
image_info.append({
"url": image_url,
"isAnomaly": img.get('isAnomaly'),
"captureDate": img.get('timestamp')
})

return jsonify({"images": image_info}), 200



@app.route('/images/<path:image_filename>')
def get_image(image_filename):
# Construct the path to the image file
image_path = os.path.join(cwd, IMAGE_UPLOAD_FOLDER, image_filename)

# Check if the image file exists
if os.path.isfile(image_path):
# Send the image file as a response
return send_file(image_path, mimetype='image/jpeg') # Adjust mimetype based on image type
else:
return jsonify({"error": "Image not found"}), 404

@app.route('/upload_image', methods=['POST'])
def upload_image():
# This assumes you've received the base64 encoded data in `imageBase64` field.
if 'imageBase64' not in request.form:
return jsonify({"error": "No image data"}), 400

image_data = request.form['imageBase64']
image_data = image_data[image_data.find(",")+1:] # Strip the base64 prefix if present
image_bytes = base64.b64decode(image_data)
image = Image.open(BytesIO(image_bytes))

userID = request.form['userID']
timestamp = datetime.now().timestamp() # Get the current timestamp
image_id = f"{userID}_{int(timestamp)}"
image_path = os.path.join(IMAGE_UPLOAD_FOLDER, f"{image_id}.jpg")

image.save(image_path)

normalized_path = os.path.normpath(image_path)
# Construct the URL for the image
image_url = f"http://{request.host}/{normalized_path.replace(os.sep, '/')}"

# Proceed to save image metadata in Firebase as before
image_metadata = {
"imageDirectory": image_path,
"isAnomaly": False,
"userID": request.form['userID'],
"timestamp": timestamp # Include the timestamp in metadata
}

#TODO: run inference on the image and figure out if it's an anomaly or not
is_anomaly = model_inference.predict(image_path)
image_metadata["isAnomaly"] = bool(is_anomaly)

# Save the image metadata in Firebase
images_ref = db.reference('Images')
images_ref.push(image_metadata)

return jsonify({"message": "Image uploaded successfully",
"imageID": image_id,
"isAnomaly": image_metadata["isAnomaly"],
"timestamp": image_metadata["timestamp"],
"imageURL": image_url}), 201


if __name__ == "__main__":
app.run(debug=True, threaded=True, host="0.0.0.0", port=5000)
Binary file added API/cnn.h5
Binary file not shown.
Binary file added API/merged_model.h5
Binary file not shown.
59 changes: 59 additions & 0 deletions API/model_inference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# model_inference.py

import tensorflow as tf
from tensorflow.keras.models import load_model # type: ignore
from tensorflow.keras.preprocessing import image # type: ignore
import numpy as np
import os
from rembg import remove
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
class ModelInference:
def __init__(self, model_path):
self.model = load_model(model_path)

def preprocess_image(self, image_path, target_size=(224, 224)):
"""
Loads and preprocesses the image for model prediction, ensuring it's in RGB format
and normalized to the [0, 1] range.
"""
# Load the original image in RGB format
img = image.load_img(image_path, target_size=target_size, color_mode='rgb')

# Remove background, 'remove' function returns an image with alpha channel
img = remove(img, alpha_matting=True)

# Convert the PIL image to a NumPy array
img_array = image.img_to_array(img)

# Ensure only 3 channels (RGB) are present, discarding the alpha channel if exists
if img_array.shape[2] == 4: # Check if there's an alpha channel
img_array = img_array[:, :, :3] # Keep only the first three channels (RGB)

# Normalize the image array to [0, 1]
img_array /= 255.0

# Add a new axis to fit model's input dimensions (batch size of 1)
img_array_expanded_dims = np.expand_dims(img_array, axis=0)
return img_array_expanded_dims



def predict(self, image_path):
"""
Runs the model prediction on the given image and determines if it's an anomaly.
"""
preprocessed_image = self.preprocess_image(image_path)
prediction = self.model.predict(preprocessed_image)

# Assuming the anomaly class index is known. Here, we're just checking if the highest predicted class
# index is the anomaly. Adjust the condition based on your specific use case.
print(np.argmax(prediction))
isHealthy = np.argmax(prediction) == 9 # 9 happens to be the class index of a healthy leaf. Adjust this based on your model.
is_anomaly = not isHealthy
print ('is_anomaly:', bool(is_anomaly))
return is_anomaly

# Example usage:
# model_inference = ModelInference('path/to/your/merged_model.h5')
# is_anomaly = model_inference.predict('path/to/the/image.jpg')
# print(f"Is anomaly: {is_anomaly}")
Binary file added API/rembg_training.h5
Binary file not shown.
15 changes: 11 additions & 4 deletions App.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import React from 'react';
import {AppLoading} from 'expo';
import Navigator from './routes/homeStack'
import { AuthProvider } from './routes/AuthContext';
import Navigator from './routes/homeStack';

export default function App(){
return (<Navigator/>);


export default function App() {
return (
<AuthProvider>
<Navigator />

</AuthProvider>
);
}
Loading