Dyno1307 commited on
Commit
ff88581
·
verified ·
1 Parent(s): c6d7f16

Upload 18 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,11 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ src/models/best_model.keras filter=lfs diff=lfs merge=lfs -text
37
+ src/models/classification_model_backup.keras filter=lfs diff=lfs merge=lfs -text
38
+ src/models/flagship_model.keras filter=lfs diff=lfs merge=lfs -text
39
+ src/static/output.jpg filter=lfs diff=lfs merge=lfs -text
40
+ src/templates/wheat[[:space:]]image.jpg filter=lfs diff=lfs merge=lfs -text
41
+ src/templates/wheat[[:space:]]logo.png filter=lfs diff=lfs merge=lfs -text
42
+ src/templates/wheat-image-new.jpg filter=lfs diff=lfs merge=lfs -text
43
+ src/templates/wheat-image.jpg filter=lfs diff=lfs merge=lfs -text
src/api/__pycache__/main.cpython-313.pyc ADDED
Binary file (4.66 kB). View file
 
src/api/main.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ import uvicorn
4
+ import io
5
+ import numpy as np
6
+ import cv2
7
+ from PIL import Image
8
+ import tensorflow as tf
9
+ from tensorflow.keras.models import load_model
10
+ import os
11
+
12
+ app = FastAPI(
13
+ title="Flagship Wheat Disease Detection API",
14
+ description="High-performance inference API for wheat disease classification.",
15
+ version="1.0.0"
16
+ )
17
+
18
+ # CORS Configuration
19
+ app.add_middleware(
20
+ CORSMiddleware,
21
+ allow_origins=["*"], # Allow all for dev; restrict in prod
22
+ allow_credentials=True,
23
+ allow_methods=["*"],
24
+ allow_headers=["*"],
25
+ )
26
+
27
+ # Global Model Variable
28
+ model = None
29
+ CLASSES = ["Crown and Root Rot", "Healthy Wheat", "Leaf Rust", "Wheat Loose Smut"]
30
+ MODEL_PATH = os.path.join(os.path.dirname(__file__), "../models/flagship_model.keras")
31
+
32
+ @app.on_event("startup")
33
+ async def load_ml_model():
34
+ global model
35
+ print(f"Loading model from {MODEL_PATH}...")
36
+ try:
37
+ # Check if model exists
38
+ if os.path.exists(MODEL_PATH):
39
+ model = load_model(MODEL_PATH)
40
+ print("Model loaded successfully!")
41
+ else:
42
+ print(f"WARNING: Model file not found at {MODEL_PATH}. API will return mock predictions.")
43
+ except Exception as e:
44
+ print(f"ERROR: Failed to load model: {e}")
45
+
46
+ @app.get("/")
47
+ def read_root():
48
+ return {"message": "Wheat Analysis Flagship API is running."}
49
+
50
+ @app.post("/predict")
51
+ async def predict(file: UploadFile = File(...)):
52
+ if not file.content_type.startswith("image/"):
53
+ raise HTTPException(status_code=400, detail="File must be an image")
54
+
55
+ # Read image
56
+ contents = await file.read()
57
+ image = Image.open(io.BytesIO(contents)).convert("RGB")
58
+
59
+ # Preprocess
60
+ img_np = np.array(image)
61
+ # Resize to 260x260 (EfficientNetV2B2 native)
62
+ img_resized = cv2.resize(img_np, (260, 260))
63
+ img_batch = np.expand_dims(img_resized, axis=0)
64
+
65
+ # Inference
66
+ if model:
67
+ predictions = model.predict(img_batch)
68
+ confidence_scores = predictions[0]
69
+ predicted_class_index = np.argmax(confidence_scores)
70
+ raw_confidence = float(confidence_scores[predicted_class_index])
71
+
72
+ # PROBABILITY THRESHOLD: Filter out non-wheat images
73
+ # If the model isn't at least 70% sure, we flag it.
74
+ THRESHOLD = 0.70
75
+
76
+ scores_dict = {cls: float(score) for cls, score in zip(CLASSES, confidence_scores)}
77
+
78
+ if raw_confidence < THRESHOLD:
79
+ result = {
80
+ "prediction": "Unknown / Not Wheat",
81
+ "confidence": raw_confidence,
82
+ "scores": scores_dict,
83
+ "alert": "Low confidence. Please ensure the image is a clear leaf photo."
84
+ }
85
+ else:
86
+ result = {
87
+ "prediction": CLASSES[predicted_class_index],
88
+ "confidence": raw_confidence,
89
+ "scores": scores_dict
90
+ }
91
+ else:
92
+ # Mock Response for Dev/Testing if model isn't trained yet
93
+ import random
94
+ mock_class = random.choice(CLASSES)
95
+ result = {
96
+ "prediction": mock_class,
97
+ "confidence": 0.95,
98
+ "scores": {c: (0.95 if c == mock_class else 0.01) for c in CLASSES},
99
+ "warning": "Mock prediction (Model not loaded)"
100
+ }
101
+
102
+ return result
103
+
104
+ if __name__ == "__main__":
105
+ uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
src/app.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, flash, redirect, url_for
2
+ from tensorflow.keras.models import load_model
3
+ import numpy as np
4
+ from PIL import Image
5
+ import io
6
+ import cv2
7
+ import os
8
+ import tensorflow as tf
9
+ import json
10
+ import time
11
+
12
+ app = Flask(__name__)
13
+ app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' # Secret key for flash messages
14
+
15
+ # Define allowed extensions for image uploads
16
+ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
17
+
18
+ # Load the classification labels from a JSON file
19
+ with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'labels.json'), 'r') as f:
20
+ CLASSIFICATION_LABELS = json.load(f)
21
+
22
+ # Get the absolute path to the classification model
23
+ model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'models/classification_model.keras')
24
+ # Load the pre-trained classification model
25
+ classification_model = load_model(model_path)
26
+
27
+ def allowed_file(filename):
28
+ """
29
+ Checks if a given filename has an allowed image extension.
30
+
31
+ Args:
32
+ filename (str): The name of the file.
33
+
34
+ Returns:
35
+ bool: True if the file extension is allowed, False otherwise.
36
+ """
37
+ return '.' in filename and \
38
+ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
39
+
40
+ @app.route('/')
41
+ def index():
42
+ """
43
+ Renders the main index page of the web application.
44
+ """
45
+ return render_template('index.html')
46
+
47
+ @app.route('/predict', methods=['POST'])
48
+ def predict():
49
+ """
50
+ Handles image uploads, preprocesses the image, makes a prediction using the
51
+ classification model, and displays the result.
52
+ """
53
+ # Check if a file was part of the request
54
+ if 'file' not in request.files:
55
+ flash('No file part')
56
+ return redirect(request.url)
57
+
58
+ file = request.files['file']
59
+
60
+ # Check if a file was selected
61
+ if file.filename == '':
62
+ flash('No selected file')
63
+ return redirect(request.url)
64
+
65
+ # Process the file if it exists and is allowed
66
+ if file and allowed_file(file.filename):
67
+ # Read the image file into a BytesIO object
68
+ img = Image.open(io.BytesIO(file.read()))
69
+ img_np = np.array(img)
70
+
71
+ # Convert RGB image to BGR for OpenCV compatibility (if needed for other operations)
72
+ img_bgr = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)
73
+
74
+ # Preprocess the image for the classification model (Expected input: 300x300 for EfficientNetV2B3)
75
+ img_resized_classification = cv2.resize(img_np, (300, 300)) # Resize to model's expected input
76
+ img_reshaped_classification = np.reshape(img_resized_classification, (1, 300, 300, 3)) # Reshape for model input
77
+
78
+ # EfficientNetV2B3 handles normalization internally (expects 0-255 inputs)
79
+ # So we just pass the resized image directly
80
+ img_preprocessed = img_reshaped_classification
81
+
82
+ # Run the classification model to get predictions
83
+ prediction = classification_model.predict(img_preprocessed)
84
+ label_index = np.argmax(prediction) # Get the index of the highest probability class
85
+ label = CLASSIFICATION_LABELS[label_index] # Get the corresponding label string
86
+
87
+ # Generate a unique filename for the output image using a timestamp
88
+ timestamp = str(int(time.time()))
89
+ output_image_filename = f'output_{timestamp}.jpg'
90
+ # Define the path to save the output image in the static folder
91
+ output_image_path = os.path.join('static', output_image_filename)
92
+ # Save the processed image (original BGR version) to the static folder
93
+ cv2.imwrite(output_image_path, img_bgr)
94
+
95
+ # Cleanup old images (older than 1 hour)
96
+ cleanup_old_images()
97
+
98
+ # Render the result page with the predicted label and image path
99
+ return render_template('result.html', image_path=output_image_filename, label=label, timestamp=timestamp)
100
+ else:
101
+ # Flash an error message for invalid file types and redirect to the index page
102
+ flash('Invalid file type. Please upload an image (png, jpg, jpeg).')
103
+ return redirect(url_for('index'))
104
+
105
+ def cleanup_old_images(folder='static', age_seconds=3600):
106
+ """
107
+ Removes files in the specified folder that are older than age_seconds.
108
+ """
109
+ try:
110
+ current_time = time.time()
111
+ folder_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), folder)
112
+ for filename in os.listdir(folder_path):
113
+ if filename.startswith('output_') and filename.endswith('.jpg'):
114
+ file_path = os.path.join(folder_path, filename)
115
+ file_creation_time = os.path.getmtime(file_path)
116
+ if current_time - file_creation_time > age_seconds:
117
+ os.remove(file_path)
118
+ print(f"Deleted old image: {filename}")
119
+ except Exception as e:
120
+ print(f"Error cleaning up images: {e}")
121
+
122
+ if __name__ == '__main__':
123
+ # Get the port from environment variable or use 5000 as default
124
+ port = int(os.environ.get('PORT', 5000))
125
+ # Run the Flask application
126
+ app.run(host='0.0.0.0', port=port, debug=True)
src/labels.json ADDED
@@ -0,0 +1 @@
 
 
1
+ ["Crown and Root Rot", "Healthy Wheat", "Leaf Rust", "Wheat Loose Smut"]
src/models/best_model.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1f102b8281196a966f8b075acefe05a6523c04d7274e104c3b272cf442fb96a1
3
+ size 159529661
src/models/classification_model.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7dc8f32fcc51a455b25255dc5d540a69fa5b5279fa2355cdae93f47d334a14ed
3
+ size 81005272
src/models/classification_model_backup.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8bb9ccc358eff58035d072c7a0c62e93dab9ea5be3e5737d28bd79792c106b84
3
+ size 159515976
src/models/flagship_model.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:868154920618fbabbef924056df3af16e5e2484ad3e844219f77ff735ce1f4ed
3
+ size 40667128
src/static/css/style.css ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --primary-color: #2e7d32;
3
+ --secondary-color: #1b5e20;
4
+ --accent-color: #ffd54f;
5
+ --text-color: #333;
6
+ --glass-bg: rgba(255, 255, 255, 0.1);
7
+ --glass-border: rgba(255, 255, 255, 0.2);
8
+ --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
9
+ }
10
+
11
+ body {
12
+ font-family: 'Poppins', sans-serif;
13
+ margin: 0;
14
+ padding: 0;
15
+ overflow-x: hidden;
16
+ background-color: #1a1a1a; /* Fallback */
17
+ }
18
+
19
+ /* Background with overlay */
20
+ .background-overlay {
21
+ position: fixed;
22
+ top: 0;
23
+ left: 0;
24
+ width: 100%;
25
+ height: 100%;
26
+ background: url('../wheat-image-new.jpg') no-repeat center center/cover;
27
+ z-index: -1;
28
+ }
29
+
30
+ .background-overlay::before {
31
+ content: '';
32
+ position: absolute;
33
+ top: 0;
34
+ left: 0;
35
+ width: 100%;
36
+ height: 100%;
37
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.7) 0%, rgba(20, 50, 20, 0.6) 100%);
38
+ backdrop-filter: blur(5px);
39
+ }
40
+
41
+ /* Glassmorphism Card */
42
+ .glass-card {
43
+ background: var(--glass-bg);
44
+ backdrop-filter: blur(12px);
45
+ -webkit-backdrop-filter: blur(12px);
46
+ border: 1px solid var(--glass-border);
47
+ border-radius: 20px;
48
+ box-shadow: var(--glass-shadow);
49
+ width: 100%;
50
+ max-width: 500px;
51
+ transition: transform 0.3s ease;
52
+ }
53
+
54
+ .glass-card:hover {
55
+ transform: translateY(-5px);
56
+ }
57
+
58
+ /* Logo */
59
+ .logo-img {
60
+ width: 80px;
61
+ height: auto;
62
+ filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.3));
63
+ }
64
+
65
+ /* Upload Zone */
66
+ .upload-zone {
67
+ border: 2px dashed rgba(255, 255, 255, 0.5);
68
+ border-radius: 15px;
69
+ padding: 30px;
70
+ text-align: center;
71
+ transition: all 0.3s ease;
72
+ background: rgba(255, 255, 255, 0.05);
73
+ cursor: pointer;
74
+ }
75
+
76
+ .upload-zone:hover, .upload-zone.drag-over {
77
+ background: rgba(255, 255, 255, 0.15);
78
+ border-color: #fff;
79
+ }
80
+
81
+ /* Buttons */
82
+ .btn-gradient {
83
+ background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
84
+ border: none;
85
+ color: white;
86
+ font-weight: 600;
87
+ padding: 12px 20px;
88
+ border-radius: 10px;
89
+ transition: all 0.3s ease;
90
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
91
+ }
92
+
93
+ .btn-gradient:hover {
94
+ background: linear-gradient(45deg, var(--secondary-color), var(--primary-color));
95
+ transform: scale(1.02);
96
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
97
+ color: white;
98
+ }
99
+
100
+ .btn-gradient:disabled {
101
+ background: #555;
102
+ cursor: not-allowed;
103
+ transform: none;
104
+ }
105
+
106
+ /* Result Page */
107
+ .result-image-container {
108
+ padding: 10px;
109
+ background: rgba(255, 255, 255, 0.1);
110
+ border-radius: 15px;
111
+ }
112
+
113
+ .result-img {
114
+ max-height: 300px;
115
+ object-fit: contain;
116
+ width: 100%;
117
+ }
118
+
119
+ .result-badge {
120
+ background: rgba(0, 0, 0, 0.4);
121
+ padding: 15px;
122
+ border-radius: 10px;
123
+ border: 1px solid rgba(255, 255, 255, 0.1);
124
+ }
125
+
126
+ /* Utilities */
127
+ .tracking-wider {
128
+ letter-spacing: 0.1em;
129
+ }
src/static/output.jpg ADDED

Git LFS Details

  • SHA256: 000acb106f0eef5f7d2a33d37d1e6337b2d2e1740a5da4993d2a32f8100e8d89
  • Pointer size: 132 Bytes
  • Size of remote file: 3.58 MB
src/templates/index.html ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Wheat Disease Detection</title>
8
+ <!-- Google Fonts -->
9
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
10
+ <!-- Bootstrap CSS -->
11
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
12
+ <!-- Custom CSS -->
13
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
14
+ <!-- Font Awesome -->
15
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
16
+ </head>
17
+ <body>
18
+
19
+ <div class="background-overlay"></div>
20
+
21
+ <div class="container d-flex align-items-center justify-content-center min-vh-100">
22
+ <div class="glass-card p-5 text-center">
23
+ <div class="mb-4">
24
+ <img src="{{ url_for('static', filename='wheat-logo-new.png') }}" alt="Logo" class="logo-img mb-3" onerror="this.style.display='none'">
25
+ <h1 class="display-4 fw-bold text-white">Wheat Guardian</h1>
26
+ <p class="lead text-white-50">Advanced AI Disease Detection</p>
27
+ </div>
28
+
29
+ <form action="/predict" method="post" enctype="multipart/form-data" id="uploadForm">
30
+ <div class="upload-zone mb-4" id="dropZone">
31
+ <i class="fas fa-cloud-upload-alt fa-3x text-white mb-3"></i>
32
+ <p class="text-white mb-2">Drag & Drop your image here</p>
33
+ <p class="text-white-50 small">or</p>
34
+ <label for="fileInput" class="btn btn-outline-light btn-sm mt-2">Browse Files</label>
35
+ <input type="file" id="fileInput" name="file" accept="image/*" hidden required>
36
+ <p id="fileName" class="text-white mt-2 small"></p>
37
+ </div>
38
+
39
+ <button type="submit" class="btn btn-gradient btn-lg w-100" id="submitBtn">
40
+ <span id="btnText">Analyze Crop</span>
41
+ <span id="loadingSpinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
42
+ </button>
43
+ </form>
44
+ </div>
45
+ </div>
46
+
47
+ <script>
48
+ const dropZone = document.getElementById('dropZone');
49
+ const fileInput = document.getElementById('fileInput');
50
+ const fileName = document.getElementById('fileName');
51
+ const uploadForm = document.getElementById('uploadForm');
52
+ const submitBtn = document.getElementById('submitBtn');
53
+ const btnText = document.getElementById('btnText');
54
+ const loadingSpinner = document.getElementById('loadingSpinner');
55
+
56
+ // Drag & Drop functionality
57
+ dropZone.addEventListener('dragover', (e) => {
58
+ e.preventDefault();
59
+ dropZone.classList.add('drag-over');
60
+ });
61
+
62
+ dropZone.addEventListener('dragleave', () => {
63
+ dropZone.classList.remove('drag-over');
64
+ });
65
+
66
+ dropZone.addEventListener('drop', (e) => {
67
+ e.preventDefault();
68
+ dropZone.classList.remove('drag-over');
69
+ const files = e.dataTransfer.files;
70
+ if (files.length > 0) {
71
+ fileInput.files = files;
72
+ updateFileName();
73
+ }
74
+ });
75
+
76
+ fileInput.addEventListener('change', updateFileName);
77
+
78
+ function updateFileName() {
79
+ if (fileInput.files.length > 0) {
80
+ fileName.textContent = fileInput.files[0].name;
81
+ } else {
82
+ fileName.textContent = '';
83
+ }
84
+ }
85
+
86
+ // Form Submit Animation
87
+ uploadForm.addEventListener('submit', () => {
88
+ submitBtn.disabled = true;
89
+ btnText.textContent = 'Analyzing...';
90
+ loadingSpinner.classList.remove('d-none');
91
+ });
92
+ </script>
93
+
94
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
95
+ </body>
96
+ </html>
src/templates/result.html ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Analysis Result - Wheat Guardian</title>
8
+ <!-- Google Fonts -->
9
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
10
+ <!-- Bootstrap CSS -->
11
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
12
+ <!-- Custom CSS -->
13
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
14
+ <!-- Font Awesome -->
15
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
16
+ </head>
17
+ <body>
18
+
19
+ <div class="background-overlay"></div>
20
+
21
+ <div class="container d-flex align-items-center justify-content-center min-vh-100">
22
+ <div class="glass-card p-4 text-center" style="max-width: 600px; width: 100%;">
23
+ <h2 class="text-white mb-4 fw-bold">Analysis Report</h2>
24
+
25
+ <div class="result-image-container mb-4">
26
+ <img src="{{ url_for('static', filename=image_path) }}?v={{ timestamp }}" class="img-fluid rounded shadow-lg result-img" alt="Analyzed Image">
27
+ </div>
28
+
29
+ <div class="result-badge mb-4">
30
+ <span class="text-white-50 small text-uppercase tracking-wider">Detected Condition</span>
31
+ <h3 class="text-white fw-bold mt-1">{{ label }}</h3>
32
+ </div>
33
+
34
+ <div class="d-grid gap-2">
35
+ <a href="/" class="btn btn-outline-light btn-lg">
36
+ <i class="fas fa-redo me-2"></i>Analyze Another
37
+ </a>
38
+ </div>
39
+ </div>
40
+ </div>
41
+
42
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
43
+ </body>
44
+ </html>
src/templates/style.css ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ html{
2
+ height: 100%
3
+ }
4
+ body {
5
+ background-color: brown;
6
+ background-image: url(wheat-image-new.jpg);
7
+ background-size: cover;
8
+ background-repeat: no-repeat;
9
+ background-position: center;
10
+
11
+ }
12
+
13
+
14
+
15
+ .container{
16
+ margin-left: auto;
17
+ margin-right: auto;
18
+ width: 1024px;
19
+ }
20
+
21
+ header {
22
+ height: 100px;
23
+ width: 1024px;
24
+ padding-top: 30px;
25
+ text-align: center;
26
+ }
27
+
28
+ nav {
29
+ background-color: rgba(0, 0, 0, 0.158);
30
+ height: 50px;
31
+ width: 1024px;
32
+ text-align: center;
33
+ padding-top: 25px;
34
+ }
35
+
36
+ main {
37
+ height: 500px;
38
+ width: 1024px;
39
+ }
40
+
41
+ nav a{
42
+ text-decoration: none;
43
+ padding-left: 50px;
44
+ padding-right: 50px;
45
+ color: white;
46
+ font-size: 18px;
47
+
48
+ }
49
+
50
+ footer {
51
+ background-color: rgba(0, 0, 0, 0.158);
52
+ height: 50px;
53
+ width: 1024px;
54
+ }
55
+
56
+ h1 {
57
+ text-align: center;
58
+ padding-top: 50px;
59
+ color: rgba(82, 55, 55, 0.774);
60
+ }
61
+
62
+ main p {
63
+ text-align: center;
64
+ padding-left: 150px;
65
+ padding-right: 150px;
66
+ color: white;
67
+ font-family:Arial, Helvetica, sans-serif;
68
+ font-size: 18px;
69
+ }
70
+
71
+ footer p{
72
+ font-family: Arial, Helvetica, sans-serif;
73
+ color: white;
74
+ padding-left: 150px;
75
+ padding-right: 150px;
76
+ font-size: 15px;
77
+ padding-top: 5px;
78
+ text-align: center;
79
+
80
+ }
src/templates/wheat image.jpg ADDED

Git LFS Details

  • SHA256: 4b5e27a76e5fb875b5aca6d13a289e5b0439baddf6e61b458c6e6a1b7c20a36a
  • Pointer size: 132 Bytes
  • Size of remote file: 2.5 MB
src/templates/wheat logo.png ADDED

Git LFS Details

  • SHA256: 9f5f02ad56aedacbcd9d09e740b52d23f3c2b873c1dc36f4dd9b9d46db38b243
  • Pointer size: 131 Bytes
  • Size of remote file: 122 kB
src/templates/wheat-image-new.jpg ADDED

Git LFS Details

  • SHA256: 6734acf870056194789622d6d3ffd5483e442ddb92451baf9887febfde328766
  • Pointer size: 132 Bytes
  • Size of remote file: 2.39 MB
src/templates/wheat-image.jpg ADDED

Git LFS Details

  • SHA256: 4b5e27a76e5fb875b5aca6d13a289e5b0439baddf6e61b458c6e6a1b7c20a36a
  • Pointer size: 132 Bytes
  • Size of remote file: 2.5 MB
src/templates/wheat-logo-new.png ADDED