Skip to content

Project Completion by team G3 #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file added Deep_Learning_CNN_Presentation_G3.pdf
Binary file not shown.
Binary file added Deep_Learning_CNN_Presentation_G3.pptx
Binary file not shown.
Binary file not shown.
982 changes: 982 additions & 0 deletions My Project/Project_1_DeepLearning_ImageCassification_CNN.ipynb

Large diffs are not rendered by default.

Binary file added My Project/model_final.keras
Binary file not shown.
Binary file added My Project/model_initial.keras
Binary file not shown.
5 changes: 5 additions & 0 deletions gitignore.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
images/
myenv/
image-prediction/
processed/
/uploads
93 changes: 93 additions & 0 deletions image-classification-webapp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Image Classification Web App

## Overview
This web application allows users to classify images into predefined categories using a Convolutional Neural Network (CNN). Users can upload one or multiple images, view their preprocessed versions, and receive predictions with associated probabilities. The application integrates a custom-trained deep learning model hosted on Google Cloud's Vertex AI platform.

## Features
- **Image Upload**: Users can upload one or multiple images through a web interface.
- **Predictions**: The app displays the predicted class labels and associated probabilities.
- **Preprocessed Image Display**: Shows the preprocessed version of uploaded images to illustrate how the model interprets input data.

## Technology Stack
### Frontend
- **HTML**: Provides the structure for web pages.
- **HTMX**: Enables dynamic updates without full-page reloads.
- **Tailwind CSS**: Ensures a clean and professional design.

### Backend
- **Flask (Python)**: Handles image uploads, preprocessing, and API requests.

### Model API
- **Google AI Platform**: Hosts the trained CNN model and provides an endpoint for making predictions.

## Workflow
1. **User Interaction**:
- Users visit the web application and upload images.
2. **Image Preprocessing**:
- Images are resized to 32x32 pixels and normalized to match the model's input requirements.
3. **Model Prediction**:
- Preprocessed images are sent to the Google AI Platform endpoint for prediction.
- Predictions include class labels and their probabilities.
4. **Results Display**:
- The app displays predictions and preprocessed images.

## Installation and Setup

### Prerequisites
- Python 3.11 or later
- Google Cloud SDK (configured with service account credentials)
- Docker (optional, for local TensorFlow Serving)

### Steps
1. **Clone the Repository**:
```bash
git clone <repository-url>
cd image-classification-webapp
```

2. **Install Dependencies**:
```bash
pip install -r requirements.txt
```

3. **Set Environment Variables**:
Configure Google Cloud credentials:
```bash
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-key.json"
```

4. **Run the Application**:
```bash
flask run
```
The application will be accessible at `http://127.0.0.1:5000`.

## Usage
1. Navigate to the homepage.
2. Upload one or multiple images using the provided form.
3. View predictions and preprocessed images on the results page.

## Deployment
The application can be deployed to any server or cloud platform that supports Flask. For production use:
- Use a WSGI server like Gunicorn.
- Configure HTTPS.

## Folder Structure
```
image-classification-webapp/
├── app.py # Main application script
├── static/ # Static files (CSS, JS, images)
├── templates/ # HTML templates
├── requirements.txt # Python dependencies
└── README.md # Project documentation
```

## Acknowledgments
- **TensorFlow**: For providing tools to preprocess data and train models.
- **Google Cloud Vertex AI**: For hosting the model.
- **Flask**: For powering the web application backend.
- **Tailwind CSS**: For the frontend design.

## License
This project is licensed under the MIT License.

103 changes: 103 additions & 0 deletions image-classification-webapp/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import os
import json
import numpy as np
from flask import Flask, request, render_template, jsonify, send_from_directory
from werkzeug.utils import secure_filename
from tensorflow.keras.preprocessing import image
from google.auth import default
from google.auth.transport.requests import Request
import requests
from PIL import Image

# Flask app setup
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['PROCESSED_FOLDER'] = 'processed'

# Ensure the upload and processed directories exist
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['PROCESSED_FOLDER'], exist_ok=True)

# Function to preprocess the image
def preprocess_image(img_path, target_size=(32, 32)):
img = image.load_img(img_path, target_size=target_size)
img_array = image.img_to_array(img) # (32, 32, 3)
img_array = np.expand_dims(img_array, axis=0) # Add batch dimension -> (1, 32, 32, 3)
img_array /= 255.0 # Normalize to [0, 1] range

# Save the preprocessed image for visualization
processed_img_path = os.path.join(app.config['PROCESSED_FOLDER'], os.path.basename(img_path))
img_resized = Image.fromarray((img_array[0] * 255).astype(np.uint8))
img_resized.save(processed_img_path)

return img_array, processed_img_path

# Function to send prediction request
def predict_image(img_path):
# Preprocess the image
img_array, processed_img_path = preprocess_image(img_path)
input_data = img_array.tolist()

# Prepare the payload
data = json.dumps({"instances": input_data})
url = 'https://us-east1-aiplatform.googleapis.com/v1/projects/10609508497/locations/us-east1/endpoints/2868357556030406656:predict'

# Authenticate using service account credentials
credentials, project = default()
credentials.refresh(Request())

headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {credentials.token}"
}

# Send the request
response = requests.post(url, headers=headers, data=data)
response_data = response.json()

# Parse predictions
if 'predictions' in response_data:
predictions = response_data['predictions'][0]
return predictions, processed_img_path
else:
return {"error": response_data.get("error", "Unknown error")}, processed_img_path

# Class names
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

@app.route('/')
def index():
return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload():
images = request.files.getlist('images')
results = []

for img in images:
filename = secure_filename(img.filename)
img_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
img.save(img_path)
predictions, processed_img_path = predict_image(img_path)

if isinstance(predictions, dict) and "error" in predictions:
results.append({"filename": filename, "error": predictions["error"]})
else:
probabilities = {class_names[i]: float(prob) for i, prob in enumerate(predictions)}
predicted_class = class_names[np.argmax(predictions)]
results.append({
"filename": filename,
"processed_image": os.path.basename(processed_img_path), # Use basename here
"predicted_class": predicted_class,
"probabilities": probabilities
})

return render_template('results.html', results=results)


@app.route('/processed/<filename>')
def processed_image(filename):
return send_from_directory(app.config['PROCESSED_FOLDER'], filename)

if __name__ == '__main__':
app.run(debug=True)
2 changes: 2 additions & 0 deletions image-classification-webapp/gitignore.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
processed/
uploads/
18 changes: 18 additions & 0 deletions image-classification-webapp/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Classification</title>
<script src="https://cdn.jsdelivr.net/npm/htmx.org"></script>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>

<body class="bg-gray-100">
<div class="container mx-auto p-5">
{% block content %}{% endblock %}
</div>
</body>

</html>
11 changes: 11 additions & 0 deletions image-classification-webapp/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block content %}
<div class="bg-white p-6 rounded shadow-md">
<h1 class="text-2xl font-bold mb-4">Upload Images for Classification</h1>
<form action="/upload" method="post" enctype="multipart/form-data" hx-post="/upload" hx-target="#results">
<input type="file" name="images" multiple accept="image/*" class="mb-4">
<button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Upload</button>
</form>
<div id="results" class="mt-5"></div>
</div>
{% endblock %}
38 changes: 38 additions & 0 deletions image-classification-webapp/templates/results.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Results</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>

<body class="bg-gray-100">
<div class="container mx-auto p-4">
<h1 class="text-2xl font-bold text-center mb-4">Image Classification Results</h1>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{% for result in results %}
<div class="bg-white rounded-lg shadow p-4">
<h2 class="text-lg font-bold">{{ result.filename }}</h2>
{% if result.error %}
<p class="text-red-500">{{ result.error }}</p>
{% else %}
<img class="w-full h-32 object-cover rounded my-2" src="/processed/{{ result.processed_image }}"
alt="Preprocessed Image">
<p class="text-green-500 font-semibold">Predicted Class: {{ result.predicted_class }}</p>
<h3 class="font-bold mt-2">Probabilities:</h3>
<ul>
{% for class_name, prob in result.probabilities.items() %}
<li>{{ class_name }}: {{ prob|round(3) }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
</div>
<a href="/" class="block text-center mt-4 text-blue-500 hover:underline">Upload more images</a>
</div>
</body>

</html>
682 changes: 682 additions & 0 deletions main-depricated.ipynb

Large diffs are not rendered by default.

832 changes: 832 additions & 0 deletions main.ipynb

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions request-google.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import requests
import json
import numpy as np
from tensorflow.keras.preprocessing import image
from google.auth import default
from google.auth.transport.requests import Request

# Load and preprocess the image
def preprocess_image(img_path, target_size=(32, 32)):
img = image.load_img(img_path, target_size=target_size)
img_array = image.img_to_array(img) # (32, 32, 3)
img_array = np.expand_dims(img_array, axis=0) # Add batch dimension -> (1, 32, 32, 3)
img_array /= 255.0 # Normalize to [0, 1] range
return img_array

# Load the image
img_path = 'image-prediction/images/360_F_177742846_umwpEr5OqwEQd4a9VyS7BGJX3tINNDe7.jpg'
img_array = preprocess_image(img_path)

# Debug input shape
print("Input shape:", img_array.shape) # Should print (1, 32, 32, 3)

# Convert to JSON serializable format
input_data = img_array.tolist()

# Prepare the payload
data = json.dumps({"instances": input_data})

# Define your endpoint URL
url = 'https://us-east1-aiplatform.googleapis.com/v1/projects/10609508497/locations/us-east1/endpoints/2868357556030406656:predict'

# Authenticate using service account credentials
credentials, project = default()
credentials.refresh(Request())

# Add the authorization header
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {credentials.token}"
}

# Send the request
response = requests.post(url, headers=headers, data=data)

# Print the entire response for debugging
response_data = response.json()
print("Response:", response_data)

# Check for predictions
if 'predictions' in response_data:
predictions = response_data['predictions']
prediction_values = predictions[0] # Assuming there's one result
predicted_class_index = np.argmax(prediction_values)
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
print(f"Predicted class: {class_names[predicted_class_index]}")
else:
print("Error: 'predictions' not found in the response.")
44 changes: 44 additions & 0 deletions request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from tensorflow.keras.preprocessing import image
import numpy as np
import requests
import json

# Load and preprocess the image
def preprocess_image(img_path, target_size=(32, 32)):
img = image.load_img(img_path, target_size=target_size)
img_array = image.img_to_array(img)
img_array = np.expand_dims(img_array, axis=0) # Add batch dimension
img_array /= 255.0 # Normalize to [0, 1] range
return img_array

# Path to the image you want to predict
img_path = './image-prediction/images/360_F_177742846_umwpEr5OqwEQd4a9VyS7BGJX3tINNDe7.jpg'

# Preprocess the image
img_array = preprocess_image(img_path)

# Prepare the request
input_data = np.expand_dims(img_array, axis=0).tolist()

# Send the request to TensorFlow Serving
data = json.dumps({"instances": input_data})
headers = {"content-type": "application/json"}
response = requests.post('http://localhost:8501/v1/models/my_model:predict', data=data, headers=headers)

# Print the predicted class
predictions = response.json()

# Class names
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

prediction_values = predictions['predictions'][0]

# Get the index of the class with the highest probability
predicted_class_index = np.argmax(prediction_values)

# Get the class name corresponding to that index
predicted_class_name = class_names[predicted_class_index]

# Print the predicted class name
print(f"Predicted Class: {predicted_class_name}")

1 change: 1 addition & 0 deletions saved_model/my_model/1/fingerprint.pb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
�����ۤ����뵼�l�����ӥ�� ���Ϯ��6(���������2
Binary file added saved_model/my_model/1/saved_model.pb
Binary file not shown.
Binary file not shown.
Binary file added saved_model/my_model/1/variables/variables.index
Binary file not shown.