Inquisiter commited on
Commit
d7e9642
·
1 Parent(s): 63e7b2c

Delete Dermatrix

Browse files
Dermatrix/.gitattributes DELETED
@@ -1,35 +0,0 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz 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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dermatrix/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- __pycache__/
2
- *.pyc
3
- *.pyo
4
- *.pth
5
- env/
6
- .venv/
7
- *.env
8
- .DS_Store
9
- .cache/
10
-
 
 
 
 
 
 
 
 
 
 
 
Dermatrix/Dockerfile DELETED
@@ -1,19 +0,0 @@
1
- FROM python:3.11
2
-
3
- WORKDIR /app
4
-
5
- # Copy all code
6
- COPY . .
7
-
8
- # Install dependencies
9
- RUN pip install --upgrade pip && pip install -r requirements.txt
10
-
11
- # Expose port for Hugging Face
12
- EXPOSE 7860
13
-
14
- # Run Flask
15
- ENV FLASK_APP=app.py
16
- ENV FLASK_RUN_HOST=0.0.0.0
17
- ENV FLASK_RUN_PORT=7860
18
- CMD ["flask", "run"]
19
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dermatrix/README.md DELETED
@@ -1,11 +0,0 @@
1
- ---
2
- title: Dermatrix
3
- emoji: 💻
4
- colorFrom: gray
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- license: apache-2.0
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
Dermatrix/app.py DELETED
@@ -1,132 +0,0 @@
1
- from flask import Flask, request, jsonify, render_template, send_from_directory
2
- from transformers import AutoModelForImageClassification, AutoImageProcessor
3
- from huggingface_hub import InferenceClient
4
- from PIL import Image
5
- import torch
6
- import os
7
-
8
- app = Flask(__name__)
9
-
10
- # =======================================
11
- # 🔐 Hugging Face LLM Token + InferenceClient
12
- # =======================================
13
- HUGGINGFACE_TOKEN = os.environ.get("HUGGINGFACE_TOKEN")
14
-
15
- client = InferenceClient(
16
- model="mistralai/Mistral-7B-Instruct-v0.1",
17
- token=HUGGINGFACE_TOKEN
18
- )
19
-
20
- # =======================================
21
- # 🧠 Load Skin Disease Model
22
- # =======================================
23
- print("Loading skin condition classifier...")
24
- model_name = "Jayanth2002/dinov2-base-finetuned-SkinDisease"
25
- image_model = AutoModelForImageClassification.from_pretrained(model_name)
26
- processor = AutoImageProcessor.from_pretrained(model_name)
27
-
28
- # Class labels
29
- class_names = [
30
- 'Basal Cell Carcinoma', 'Darier_s Disease', 'Epidermolysis Bullosa Pruriginosa',
31
- 'Hailey-Hailey Disease', 'Herpes Simplex', 'Impetigo', 'Larva Migrans',
32
- 'Leprosy Borderline', 'Leprosy Lepromatous', 'Leprosy Tuberculoid', 'Lichen Planus',
33
- 'Lupus Erythematosus Chronicus Discoides', 'Melanoma', 'Molluscum Contagiosum',
34
- 'Mycosis Fungoides', 'Neurofibromatosis', 'Papilomatosis Confluentes And Reticulate',
35
- 'Pediculosis Capitis', 'Pityriasis Rosea', 'Porokeratosis Actinic', 'Psoriasis',
36
- 'Tinea Corporis', 'Tinea Nigra', 'Tungiasis', 'actinic keratosis', 'dermatofibroma',
37
- 'nevus', 'pigmented benign keratosis', 'seborrheic keratosis', 'squamous cell carcinoma',
38
- 'vascular lesion'
39
- ]
40
-
41
- # =======================================
42
- # 🌐 Frontend Routes
43
- # =======================================
44
- @app.route("/")
45
- def index():
46
- return render_template("index.html")
47
-
48
- @app.route("/upload")
49
- def upload():
50
- return render_template("upload.html")
51
-
52
- @app.route("/result")
53
- def result():
54
- return render_template("result.html") # Notice: matches the filename "results.html" instead of "result.html"
55
-
56
- # =======================================
57
- # 📸 /analyze Route
58
- # =======================================
59
- @app.route('/analyze', methods=['POST'])
60
- def analyze():
61
- if 'image' not in request.files:
62
- return jsonify({"error": "No image uploaded"}), 400
63
-
64
- image_file = request.files['image']
65
- image = Image.open(image_file.stream).convert("RGB")
66
- inputs = processor(images=image, return_tensors="pt")
67
-
68
- with torch.no_grad():
69
- logits = image_model(**inputs).logits
70
- probs = torch.softmax(logits, dim=-1)[0]
71
-
72
- top_idx = torch.argmax(probs).item()
73
- top_conf = probs[top_idx].item()
74
- prediction = class_names[top_idx]
75
-
76
- top_conditions = sorted(
77
- zip(class_names, probs.tolist()),
78
- key=lambda x: x[1],
79
- reverse=True
80
- )[:5]
81
-
82
- return jsonify({
83
- "prediction": prediction,
84
- "confidence": round(top_conf, 4),
85
- "topConditions": [(name, round(prob, 4)) for name, prob in top_conditions],
86
- "description": f"{prediction} is a skin condition. Please consult a medical professional.",
87
- "recommendations": [
88
- "Take a clearer image if unsure.",
89
- "Consider visiting a dermatologist.",
90
- "Avoid self-diagnosis or self-treatment."
91
- ]
92
- })
93
-
94
- # =======================================
95
- # 💬 /ask Route
96
- # =======================================
97
- @app.route('/ask', methods=['POST'])
98
- def ask():
99
- data = request.json
100
- question = data.get("question", "")
101
- condition = data.get("condition", "")
102
-
103
- if not question:
104
- return jsonify({"answer": "Please ask a valid question."}), 400
105
-
106
- messages = [
107
- {
108
- "role": "user",
109
- "content": f"A user may have {condition}. They asked: '{question}'. Respond like a helpful AI medical assistant."
110
- }
111
- ]
112
-
113
- try:
114
- response = client.chat_completion(
115
- messages=messages,
116
- max_tokens=200
117
- )
118
- answer = response.choices[0]["message"]["content"]
119
- return jsonify({"answer": answer.strip()})
120
- except Exception as e:
121
- return jsonify({"answer": f"Error communicating with Hugging Face: {e}"}), 500
122
-
123
- # Add route for placeholder images if needed
124
- @app.route('/api/placeholder/<width>/<height>')
125
- def placeholder(width, height):
126
- # This is a simple implementation - you might want to generate an actual placeholder image
127
- # For now, we'll just serve a static placeholder
128
- return send_from_directory('static/images', 'placeholder.jpg')
129
-
130
-
131
- if __name__ == '__main__':
132
- app.run(debug=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dermatrix/requirements.txt DELETED
@@ -1,8 +0,0 @@
1
- flask
2
- torch
3
- transformers
4
- torchvision
5
- huggingface_hub
6
- Pillow
7
- torchcam
8
-
 
 
 
 
 
 
 
 
 
Dermatrix/templates/index.html DELETED
@@ -1,289 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
- <title>SkinAI</title>
7
- <style>
8
- /* Reset */
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- body {
16
- font-family: Arial, sans-serif;
17
- background-color: #ffffff;
18
- color: #000;
19
- }
20
-
21
- /* Top Navigation with Enhanced Hover Effects */
22
- header {
23
- display: flex;
24
- align-items: center;
25
- justify-content: space-between;
26
- padding: 15px 5%;
27
- background-color: #fff;
28
- border-bottom: 1px solid #ccc;
29
- position: relative;
30
- z-index: 10;
31
- }
32
-
33
- .logo {
34
- font-size: 1.5rem;
35
- font-weight: bold;
36
- color: #333;
37
- }
38
-
39
- nav a {
40
- text-decoration: none;
41
- color: #333;
42
- margin-left: 20px;
43
- font-weight: 500;
44
- padding: 5px 0;
45
- position: relative;
46
- transition: color 0.3s ease;
47
- }
48
-
49
- /* Underline hover effect for nav links */
50
- nav a:after {
51
- content: '';
52
- position: absolute;
53
- width: 0;
54
- height: 2px;
55
- bottom: 0;
56
- left: 0;
57
- background-color: #007BFF;
58
- transition: width 0.3s ease;
59
- }
60
-
61
- nav a:hover {
62
- color: #007BFF;
63
- text-decoration: none;
64
- }
65
-
66
- nav a:hover:after {
67
- width: 100%;
68
- }
69
-
70
- .help-btn {
71
- background-color: #007BFF;
72
- color: #fff;
73
- border: none;
74
- padding: 8px 16px;
75
- border-radius: 4px;
76
- cursor: pointer;
77
- margin-left: 20px;
78
- transition: all 0.3s ease;
79
- }
80
-
81
- .help-btn:hover {
82
- background-color: #0056b3;
83
- transform: translateY(-2px);
84
- box-shadow: 0 4px 8px rgba(0,123,255,0.3);
85
- }
86
-
87
- /* Hero Section with Video Background */
88
- .hero {
89
- position: relative;
90
- height: 60vh;
91
- display: flex;
92
- align-items: center;
93
- justify-content: center;
94
- text-align: center;
95
- padding: 0 5%;
96
- overflow: hidden; /* Ensure video doesn't spill out */
97
- }
98
-
99
- /* Video Background Styling */
100
- .video-bg {
101
- position: absolute;
102
- top: 0;
103
- left: 0;
104
- width: 100%;
105
- height: 100%;
106
- object-fit: cover; /* Cover the entire container */
107
- z-index: 1;
108
- }
109
-
110
- .hero-overlay {
111
- position: absolute;
112
- top: 0; left: 0;
113
- width: 100%; height: 100%;
114
- background-color: rgba(0,0,0,0.4); /* Dark overlay for readability */
115
- z-index: 2;
116
- }
117
-
118
- .hero-content {
119
- position: relative; /* So it sits on top of the overlay */
120
- color: #fff;
121
- max-width: 600px;
122
- z-index: 3; /* Ensure content is above video and overlay */
123
- }
124
-
125
- .hero-content h1 {
126
- font-size: 2.5rem;
127
- margin-bottom: 15px;
128
- font-weight: 700;
129
- }
130
-
131
- .hero-content p {
132
- font-size: 1.1rem;
133
- margin-bottom: 20px;
134
- }
135
-
136
- .upload-btn {
137
- background-color: #007BFF;
138
- color: #fff;
139
- border: none;
140
- padding: 12px 24px;
141
- font-size: 1rem;
142
- border-radius: 4px;
143
- cursor: pointer;
144
- transition: all 0.3s ease;
145
- }
146
-
147
- .upload-btn:hover {
148
- background-color: #0056b3;
149
- transform: translateY(-2px);
150
- box-shadow: 0 4px 12px rgba(0,123,255,0.4);
151
- }
152
-
153
- /* Benefits Section with Enhanced Card Hover Effects */
154
- .benefits {
155
- padding: 60px 5%;
156
- text-align: center;
157
- }
158
-
159
- .benefits h2 {
160
- font-size: 2rem;
161
- margin-bottom: 40px;
162
- font-weight: 700;
163
- }
164
-
165
- .benefit-cards {
166
- display: flex;
167
- flex-wrap: wrap;
168
- justify-content: center;
169
- gap: 30px;
170
- }
171
-
172
- .card {
173
- flex: 1 1 250px;
174
- max-width: 300px;
175
- background-color: #f9f9f9;
176
- border-radius: 8px;
177
- padding: 25px;
178
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
179
- transition: all 0.3s ease;
180
- border-bottom: 3px solid transparent;
181
- }
182
-
183
- /* Card hover effects */
184
- .card:hover {
185
- transform: translateY(-10px);
186
- box-shadow: 0 10px 20px rgba(0,0,0,0.15);
187
- border-bottom: 3px solid #007BFF;
188
- background-color: #fff;
189
- }
190
-
191
- .card h3 {
192
- margin-bottom: 15px;
193
- font-size: 1.2rem;
194
- font-weight: 600;
195
- color: #333;
196
- transition: color 0.3s ease;
197
- }
198
-
199
- .card:hover h3 {
200
- color: #007BFF;
201
- }
202
-
203
- .card p {
204
- font-size: 0.95rem;
205
- line-height: 1.4;
206
- color: #555;
207
- }
208
-
209
- /* Responsive */
210
- @media (max-width: 768px) {
211
- .hero {
212
- height: 50vh;
213
- }
214
- .hero-content h1 {
215
- font-size: 2rem;
216
- }
217
- .benefits h2 {
218
- font-size: 1.7rem;
219
- }
220
- .card {
221
- margin: 0 auto;
222
- }
223
- }
224
- </style>
225
- </head>
226
- <body>
227
-
228
- <!-- Top Navigation -->
229
- <header>
230
- <div class="logo">SkinAI</div>
231
- <nav>
232
- <a href="/" style="color: #007BFF;">Home</a>
233
- <a href="/upload">Upload</a>
234
- <a href="/result">Results</a>
235
- <a href="#">Contact</a>
236
- <button class="help-btn">Help</button>
237
- </nav>
238
- </header>
239
-
240
- <!-- Hero Section with Video Background -->
241
- <section class="hero">
242
- <!-- Video Background -->
243
- <video class="video-bg" autoplay muted loop>
244
- <source src="{{ url_for('static', filename='videos/background-video.mp4') }}" type="video/mp4">
245
- <!-- Add fallback sources if needed -->
246
- <source src="{{ url_for('static', filename='videos/background-video.webm') }}" type="video/webm">
247
- <!-- Fallback for browsers that don't support video -->
248
- Your browser does not support the video tag.
249
- </video>
250
-
251
- <div class="hero-overlay"></div>
252
- <div class="hero-content">
253
- <h1>Identify Skin Issues Instantly</h1>
254
- <p>Upload an image to get AI-driven skin health insights.</p>
255
- <a href="/upload"><button class="upload-btn">Upload Image</button></a>
256
- </div>
257
- </section>
258
-
259
- <!-- Benefits Section -->
260
- <section class="benefits">
261
- <h2>Benefits of the Service</h2>
262
- <div class="benefit-cards">
263
- <!-- Card 1 -->
264
- <div class="card">
265
- <h3>Accurate Identification</h3>
266
- <p>Our tool analyzes skin images to provide precise detections, minimizing human error.</p>
267
- </div>
268
- <!-- Card 2 -->
269
- <div class="card">
270
- <h3>Diverse Skin Conditions</h3>
271
- <p>Identify various issues like psoriasis, eczema, and more with our comprehensive database.</p>
272
- </div>
273
- <!-- Card 3 -->
274
- <div class="card">
275
- <h3>Speed and Efficiency</h3>
276
- <p>Our service provides rapid results, enabling timely medical interventions.</p>
277
- </div>
278
- </div>
279
- </section>
280
-
281
- <script>
282
- // JavaScript can be added here for additional functionality
283
- document.querySelector('.upload-btn').addEventListener('click', function() {
284
- window.location.href = '/upload';
285
- });
286
- </script>
287
-
288
- </body>
289
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dermatrix/templates/result.html DELETED
@@ -1,655 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
- <title>SkinAI - Results</title>
7
- <style>
8
- /* Reset */
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- body {
16
- font-family: Arial, sans-serif;
17
- background-color: #f5f8fa;
18
- color: #000;
19
- }
20
-
21
- /* Top Navigation with Enhanced Hover Effects */
22
- header {
23
- display: flex;
24
- align-items: center;
25
- justify-content: space-between;
26
- padding: 15px 5%;
27
- background-color: #fff;
28
- border-bottom: 1px solid #ccc;
29
- position: relative;
30
- z-index: 10;
31
- }
32
-
33
- .logo {
34
- font-size: 1.5rem;
35
- font-weight: bold;
36
- color: #333;
37
- }
38
-
39
- nav a {
40
- text-decoration: none;
41
- color: #333;
42
- margin-left: 20px;
43
- font-weight: 500;
44
- padding: 5px 0;
45
- position: relative;
46
- transition: color 0.3s ease;
47
- }
48
-
49
- /* Underline hover effect for nav links */
50
- nav a:after {
51
- content: '';
52
- position: absolute;
53
- width: 0;
54
- height: 2px;
55
- bottom: 0;
56
- left: 0;
57
- background-color: #007BFF;
58
- transition: width 0.3s ease;
59
- }
60
-
61
- nav a:hover {
62
- color: #007BFF;
63
- text-decoration: none;
64
- }
65
-
66
- nav a:hover:after {
67
- width: 100%;
68
- }
69
-
70
- .help-btn {
71
- background-color: #007BFF;
72
- color: #fff;
73
- border: none;
74
- padding: 8px 16px;
75
- border-radius: 4px;
76
- cursor: pointer;
77
- margin-left: 20px;
78
- transition: all 0.3s ease;
79
- }
80
-
81
- .help-btn:hover {
82
- background-color: #0056b3;
83
- transform: translateY(-2px);
84
- box-shadow: 0 4px 8px rgba(0,123,255,0.3);
85
- }
86
-
87
- /* Main Content Area */
88
- .container {
89
- max-width: 1200px;
90
- margin: 40px auto;
91
- padding: 0 20px;
92
- }
93
-
94
- /* Results Section */
95
- .results-section {
96
- background-color: #fff;
97
- border-radius: 10px;
98
- box-shadow: 0 5px 15px rgba(0,0,0,0.08);
99
- padding: 40px;
100
- margin-bottom: 40px;
101
- }
102
-
103
- .results-header {
104
- display: flex;
105
- align-items: center;
106
- justify-content: space-between;
107
- margin-bottom: 30px;
108
- }
109
-
110
- .results-header h1 {
111
- font-size: 2rem;
112
- color: #333;
113
- }
114
-
115
- .results-header .new-analysis {
116
- background-color: #f8f9fa;
117
- color: #333;
118
- border: 1px solid #ddd;
119
- padding: 8px 16px;
120
- border-radius: 4px;
121
- text-decoration: none;
122
- transition: all 0.3s ease;
123
- }
124
-
125
- .results-header .new-analysis:hover {
126
- background-color: #e9ecef;
127
- transform: translateY(-2px);
128
- }
129
-
130
- /* Results Grid */
131
- .results-grid {
132
- display: grid;
133
- grid-template-columns: 1fr 2fr;
134
- gap: 30px;
135
- }
136
-
137
- /* Image Section */
138
- .image-section {
139
- background-color: #f8f9fa;
140
- border-radius: 8px;
141
- padding: 20px;
142
- text-align: center;
143
- }
144
-
145
- .image-section img {
146
- max-width: 100%;
147
- max-height: 300px;
148
- border-radius: 8px;
149
- margin-bottom: 15px;
150
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
151
- }
152
-
153
- .image-caption {
154
- font-size: 0.9rem;
155
- color: #666;
156
- }
157
-
158
- /* Analysis Section */
159
- .analysis-section {
160
- display: flex;
161
- flex-direction: column;
162
- gap: 20px;
163
- }
164
-
165
- .analysis-card {
166
- background-color: #f8f9fa;
167
- border-radius: 8px;
168
- padding: 20px;
169
- border-left: 4px solid #007BFF;
170
- }
171
-
172
- .analysis-card h2 {
173
- font-size: 1.4rem;
174
- color: #333;
175
- margin-bottom: 10px;
176
- display: flex;
177
- align-items: center;
178
- justify-content: space-between;
179
- }
180
-
181
- .confidence-badge {
182
- background-color: #e6f4ff;
183
- color: #0066cc;
184
- font-size: 0.9rem;
185
- padding: 4px 12px;
186
- border-radius: 20px;
187
- font-weight: normal;
188
- }
189
-
190
- .analysis-card p {
191
- font-size: 1rem;
192
- color: #555;
193
- line-height: 1.6;
194
- margin-bottom: 15px;
195
- }
196
-
197
- /* Recommendations List */
198
- .recommendations-list {
199
- margin-top: 10px;
200
- }
201
-
202
- .recommendations-list h3 {
203
- font-size: 1.1rem;
204
- color: #333;
205
- margin-bottom: 15px;
206
- }
207
-
208
- .recommendations-list ul {
209
- padding-left: 20px;
210
- }
211
-
212
- .recommendations-list li {
213
- margin-bottom: 10px;
214
- line-height: 1.5;
215
- font-size: 0.95rem;
216
- color: #444;
217
- }
218
-
219
- /* Other Possible Conditions */
220
- .other-conditions {
221
- margin-top: 20px;
222
- }
223
-
224
- .other-conditions h3 {
225
- font-size: 1.1rem;
226
- color: #333;
227
- margin-bottom: 15px;
228
- }
229
-
230
- .condition-bars {
231
- display: flex;
232
- flex-direction: column;
233
- gap: 10px;
234
- }
235
-
236
- .condition-bar {
237
- display: flex;
238
- align-items: center;
239
- gap: 10px;
240
- }
241
-
242
- .condition-name {
243
- min-width: 100px;
244
- font-size: 0.9rem;
245
- }
246
-
247
- .progress-bar {
248
- flex-grow: 1;
249
- height: 10px;
250
- background-color: #e9ecef;
251
- border-radius: 5px;
252
- overflow: hidden;
253
- }
254
-
255
- .progress-fill {
256
- height: 100%;
257
- background-color: #007BFF;
258
- }
259
-
260
- .condition-percentage {
261
- font-size: 0.9rem;
262
- color: #666;
263
- min-width: 50px;
264
- text-align: right;
265
- }
266
-
267
- /* Q&A Section */
268
- .qa-section {
269
- background-color: #fff;
270
- border-radius: 10px;
271
- box-shadow: 0 5px 15px rgba(0,0,0,0.08);
272
- padding: 40px;
273
- }
274
-
275
- .qa-section h2 {
276
- font-size: 1.6rem;
277
- color: #333;
278
- margin-bottom: 25px;
279
- }
280
-
281
- .qa-form {
282
- display: flex;
283
- gap: 10px;
284
- margin-bottom: 30px;
285
- }
286
-
287
- .qa-input {
288
- flex-grow: 1;
289
- padding: 12px 15px;
290
- border: 1px solid #ddd;
291
- border-radius: 4px;
292
- font-size: 1rem;
293
- }
294
-
295
- .qa-btn {
296
- background-color: #007BFF;
297
- color: #fff;
298
- border: none;
299
- padding: 0 20px;
300
- border-radius: 4px;
301
- cursor: pointer;
302
- transition: all 0.3s ease;
303
- }
304
-
305
- .qa-btn:hover {
306
- background-color: #0056b3;
307
- }
308
-
309
- .qa-response {
310
- background-color: #f8f9fa;
311
- border-radius: 8px;
312
- padding: 20px;
313
- border-left: 4px solid #28a745;
314
- margin-bottom: 20px;
315
- display: none; /* Initially hidden */
316
- }
317
-
318
- .qa-response h3 {
319
- font-size: 1.1rem;
320
- color: #333;
321
- margin-bottom: 10px;
322
- }
323
-
324
- .qa-response p {
325
- font-size: 0.95rem;
326
- color: #555;
327
- line-height: 1.6;
328
- }
329
-
330
- .disclaimer {
331
- margin-top: 20px;
332
- padding: 15px;
333
- background-color: #fff8e1;
334
- border-left: 4px solid #ffc107;
335
- font-size: 0.9rem;
336
- color: #6c4a00;
337
- line-height: 1.5;
338
- }
339
-
340
- /* Footer */
341
- footer {
342
- text-align: center;
343
- padding: 20px;
344
- margin-top: 40px;
345
- color: #777;
346
- font-size: 0.9rem;
347
- }
348
-
349
- /* Loader */
350
- .loader-container {
351
- display: flex;
352
- justify-content: center;
353
- align-items: center;
354
- margin: 20px 0;
355
- }
356
-
357
- .loader {
358
- border: 4px solid #f3f3f3;
359
- border-top: 4px solid #007BFF;
360
- border-radius: 50%;
361
- width: 30px;
362
- height: 30px;
363
- animation: spin 1s linear infinite;
364
- margin-right: 10px;
365
- }
366
-
367
- @keyframes spin {
368
- 0% { transform: rotate(0deg); }
369
- 100% { transform: rotate(360deg); }
370
- }
371
-
372
- /* Responsive */
373
- @media (max-width: 768px) {
374
- .container {
375
- padding: 0 15px;
376
- margin: 20px auto;
377
- }
378
-
379
- .results-section, .qa-section {
380
- padding: 30px 15px;
381
- }
382
-
383
- .results-grid {
384
- grid-template-columns: 1fr;
385
- }
386
-
387
- .results-header {
388
- flex-direction: column;
389
- align-items: flex-start;
390
- gap: 15px;
391
- }
392
- }
393
- </style>
394
- </head>
395
- <body>
396
-
397
- <!-- Top Navigation -->
398
- <header>
399
- <div class="logo">SkinAI</div>
400
- <nav>
401
- <a href="/">Home</a>
402
- <a href="/upload">Upload</a>
403
- <a href="/result" style="color: #007BFF;">Results</a>
404
- <a href="#">Contact</a>
405
- <button class="help-btn">Help</button>
406
- </nav>
407
- </header>
408
-
409
- <div class="container">
410
- <!-- Results Section -->
411
- <section class="results-section">
412
- <div class="results-header">
413
- <h1>Analysis Results</h1>
414
- <a href="upload" class="new-analysis">Upload New Image</a>
415
- </div>
416
-
417
- <div class="results-grid">
418
- <!-- Image Section -->
419
- <div class="image-section">
420
- <img id="analyzed-image" src="#" alt="Analyzed Skin Image">
421
- <div class="image-caption">Uploaded on <span id="upload-date">April 8, 2025</span></div>
422
- </div>
423
-
424
- <!-- Analysis Section -->
425
- <div class="analysis-section">
426
- <div class="analysis-card">
427
- <h2>
428
- <span id="condition-name">Loading result...</span>
429
- <span class="confidence-badge" id="confidence-level">--</span>
430
- </h2>
431
- <p id="condition-description">Analyzing your image...</p>
432
-
433
- <!-- Loader (initially shown) -->
434
- <div class="loader-container" id="analysis-loader">
435
- <div class="loader"></div>
436
- <span>Processing image with AI...</span>
437
- </div>
438
-
439
- <!-- Recommendations (initially hidden) -->
440
- <div class="recommendations-list" id="recommendations-section" style="display: none;">
441
- <h3>Recommendations</h3>
442
- <ul id="recommendations-list">
443
- <!-- Recommendations will be inserted here -->
444
- </ul>
445
- </div>
446
-
447
- <!-- Other Possible Conditions -->
448
- <div class="other-conditions" id="other-conditions-section" style="display: none;">
449
- <h3>Other Possible Conditions</h3>
450
- <div class="condition-bars" id="condition-bars">
451
- <!-- Condition bars will be inserted here -->
452
- </div>
453
- </div>
454
- </div>
455
-
456
- <div class="disclaimer">
457
- <strong>Disclaimer:</strong> This analysis is for informational purposes only and should not replace professional medical advice. Please consult a healthcare provider for proper diagnosis and treatment.
458
- </div>
459
- </div>
460
- </div>
461
- </section>
462
-
463
- <!-- Q&A Section -->
464
- <section class="qa-section">
465
- <h2>Ask About Your Condition</h2>
466
- <div class="qa-form">
467
- <input type="text" class="qa-input" id="question-input" placeholder="Ask a question about your skin condition...">
468
- <button class="qa-btn" id="ask-btn">Ask</button>
469
- </div>
470
-
471
- <!-- QA Response (initially hidden) -->
472
- <div class="qa-response" id="qa-response">
473
- <h3>Response</h3>
474
- <p id="qa-answer">The answer will appear here.</p>
475
- </div>
476
-
477
- <div class="disclaimer">
478
- <strong>Important:</strong> The AI assistant provides general information about skin conditions. Your specific case may vary, and medical professionals should be consulted for personalized advice and treatment.
479
- </div>
480
- </section>
481
- </div>
482
-
483
- <footer>
484
- <p>© 2025 SkinAI. All rights reserved. For educational purposes only. Not a substitute for professional medical advice.</p>
485
- </footer>
486
-
487
- <script>
488
- document.addEventListener('DOMContentLoaded', function() {
489
- // Retrieve data from sessionStorage (set during upload)
490
- const imageData = sessionStorage.getItem('analyzedImage');
491
- const analysisResult = JSON.parse(sessionStorage.getItem('analysisResult') || '{}');
492
- const condition = sessionStorage.getItem('detectedCondition') || '';
493
-
494
- // Set the image
495
- if (imageData) {
496
- document.getElementById('analyzed-image').src = imageData;
497
- } else {
498
- document.getElementById('analyzed-image').src = "/api/placeholder/400/320";
499
- document.getElementById('analyzed-image').alt = "No image available";
500
- }
501
-
502
- // Set the current date
503
- const now = new Date();
504
- document.getElementById('upload-date').textContent = now.toLocaleDateString('en-US', {
505
- year: 'numeric',
506
- month: 'long',
507
- day: 'numeric'
508
- });
509
-
510
- // Process and display results if available
511
- if (Object.keys(analysisResult).length > 0) {
512
- displayResults(analysisResult);
513
- } else {
514
- // If no results in session storage, check URL params
515
- // This allows direct navigation to this page with the results as parameters
516
- const urlParams = new URLSearchParams(window.location.search);
517
- const resultParam = urlParams.get('result');
518
-
519
- if (resultParam) {
520
- try {
521
- const decodedResult = JSON.parse(decodeURIComponent(resultParam));
522
- displayResults(decodedResult);
523
- } catch (e) {
524
- console.error("Failed to parse result from URL parameter", e);
525
- document.getElementById('condition-name').textContent = "No results available";
526
- document.getElementById('condition-description').textContent = "Please upload an image for analysis.";
527
- document.getElementById('analysis-loader').style.display = "none";
528
- }
529
- } else {
530
- // No results available - show message
531
- document.getElementById('condition-name').textContent = "No results available";
532
- document.getElementById('condition-description').textContent = "Please upload an image for analysis.";
533
- document.getElementById('analysis-loader').style.display = "none";
534
- }
535
- }
536
-
537
- // Handle Q&A form submission
538
- document.getElementById('ask-btn').addEventListener('click', function() {
539
- askQuestion();
540
- });
541
-
542
- document.getElementById('question-input').addEventListener('keypress', function(e) {
543
- if (e.key === 'Enter') {
544
- askQuestion();
545
- }
546
- });
547
-
548
- // Function to display analysis results
549
- function displayResults(result) {
550
- // Hide loader
551
- document.getElementById('analysis-loader').style.display = "none";
552
-
553
- // Display primary condition and confidence
554
- document.getElementById('condition-name').textContent = result.prediction || "Unknown Condition";
555
-
556
- const confidencePercent = result.confidence ? Math.round(result.confidence * 100) : 0;
557
- document.getElementById('confidence-level').textContent = `${confidencePercent}% Confidence`;
558
-
559
- // Display description
560
- document.getElementById('condition-description').textContent = result.description ||
561
- "No detailed information available for this condition.";
562
-
563
- // Display recommendations
564
- if (result.recommendations && result.recommendations.length > 0) {
565
- const recommendationsList = document.getElementById('recommendations-list');
566
- recommendationsList.innerHTML = '';
567
-
568
- result.recommendations.forEach(rec => {
569
- const li = document.createElement('li');
570
- li.textContent = rec;
571
- recommendationsList.appendChild(li);
572
- });
573
-
574
- document.getElementById('recommendations-section').style.display = "block";
575
- }
576
-
577
- // Display other possible conditions
578
- if (result.topConditions && result.topConditions.length > 0) {
579
- const conditionBars = document.getElementById('condition-bars');
580
- conditionBars.innerHTML = '';
581
-
582
- // Skip the first condition (already shown as primary) if it's the same as the top result
583
- const startIndex = (result.topConditions[0][0] === result.prediction) ? 1 : 0;
584
-
585
- // Display up to 4 additional conditions
586
- for (let i = startIndex; i < Math.min(startIndex + 4, result.topConditions.length); i++) {
587
- const condition = result.topConditions[i];
588
- const conditionName = condition[0];
589
- const probability = condition[1];
590
- const percentage = Math.round(probability * 100);
591
-
592
- const conditionBar = document.createElement('div');
593
- conditionBar.className = 'condition-bar';
594
- conditionBar.innerHTML = `
595
- <div class="condition-name">${conditionName}</div>
596
- <div class="progress-bar">
597
- <div class="progress-fill" style="width: ${percentage}%"></div>
598
- </div>
599
- <div class="condition-percentage">${percentage}%</div>
600
- `;
601
-
602
- conditionBars.appendChild(conditionBar);
603
- }
604
-
605
- document.getElementById('other-conditions-section').style.display = "block";
606
- }
607
-
608
- // Store detected condition for Q&A
609
- sessionStorage.setItem('detectedCondition', result.prediction || '');
610
- }
611
-
612
- // Function to handle Q&A
613
- function askQuestion() {
614
- const questionInput = document.getElementById('question-input');
615
- const question = questionInput.value.trim();
616
-
617
- if (!question) return;
618
-
619
- // Get the detected condition
620
- const detectedCondition = sessionStorage.getItem('detectedCondition') || '';
621
-
622
- // Show loading indication
623
- document.getElementById('qa-answer').textContent = "Getting answer...";
624
- document.getElementById('qa-response').style.display = "block";
625
-
626
- // Call the backend API
627
- fetch('/ask', {
628
- method: 'POST',
629
- headers: {
630
- 'Content-Type': 'application/json',
631
- },
632
- body: JSON.stringify({
633
- question: question,
634
- condition: detectedCondition
635
- })
636
- })
637
- .then(response => response.json())
638
- .then(data => {
639
- document.getElementById('qa-answer').textContent = data.answer ||
640
- "I couldn't generate an answer for that question.";
641
- })
642
- .catch(error => {
643
- console.error('Error:', error);
644
- document.getElementById('qa-answer').textContent =
645
- "Sorry, there was an error processing your question. Please try again later.";
646
- });
647
-
648
- // Clear the input
649
- questionInput.value = '';
650
- }
651
- });
652
- </script>
653
-
654
- </body>
655
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dermatrix/templates/upload.html DELETED
@@ -1,517 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
- <title>SkinAI - Upload</title>
7
- <style>
8
- /* Reset */
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- body {
16
- font-family: Arial, sans-serif;
17
- background-color: #f5f8fa;
18
- color: #000;
19
- }
20
-
21
- /* Top Navigation with Enhanced Hover Effects */
22
- header {
23
- display: flex;
24
- align-items: center;
25
- justify-content: space-between;
26
- padding: 15px 5%;
27
- background-color: #fff;
28
- border-bottom: 1px solid #ccc;
29
- position: relative;
30
- z-index: 10;
31
- }
32
-
33
- .logo {
34
- font-size: 1.5rem;
35
- font-weight: bold;
36
- color: #333;
37
- }
38
-
39
- nav a {
40
- text-decoration: none;
41
- color: #333;
42
- margin-left: 20px;
43
- font-weight: 500;
44
- padding: 5px 0;
45
- position: relative;
46
- transition: color 0.3s ease;
47
- }
48
-
49
- /* Underline hover effect for nav links */
50
- nav a:after {
51
- content: '';
52
- position: absolute;
53
- width: 0;
54
- height: 2px;
55
- bottom: 0;
56
- left: 0;
57
- background-color: #007BFF;
58
- transition: width 0.3s ease;
59
- }
60
-
61
- nav a:hover {
62
- color: #007BFF;
63
- text-decoration: none;
64
- }
65
-
66
- nav a:hover:after {
67
- width: 100%;
68
- }
69
-
70
- .help-btn {
71
- background-color: #007BFF;
72
- color: #fff;
73
- border: none;
74
- padding: 8px 16px;
75
- border-radius: 4px;
76
- cursor: pointer;
77
- margin-left: 20px;
78
- transition: all 0.3s ease;
79
- }
80
-
81
- .help-btn:hover {
82
- background-color: #0056b3;
83
- transform: translateY(-2px);
84
- box-shadow: 0 4px 8px rgba(0,123,255,0.3);
85
- }
86
-
87
- /* Main Content Area */
88
- .container {
89
- max-width: 1200px;
90
- margin: 40px auto;
91
- padding: 0 20px;
92
- }
93
-
94
- /* Upload Section */
95
- .upload-section {
96
- background-color: #fff;
97
- border-radius: 10px;
98
- box-shadow: 0 5px 15px rgba(0,0,0,0.08);
99
- padding: 40px;
100
- margin-bottom: 40px;
101
- text-align: center;
102
- }
103
-
104
- .upload-section h1 {
105
- font-size: 2rem;
106
- color: #333;
107
- margin-bottom: 20px;
108
- }
109
-
110
- .upload-section p {
111
- font-size: 1.1rem;
112
- color: #666;
113
- margin-bottom: 30px;
114
- max-width: 700px;
115
- margin-left: auto;
116
- margin-right: auto;
117
- }
118
-
119
- /* Upload Area */
120
- .upload-area {
121
- border: 2px dashed #aac;
122
- border-radius: 10px;
123
- padding: 50px 20px;
124
- background-color: #f8faff;
125
- cursor: pointer;
126
- transition: all 0.3s ease;
127
- margin-bottom: 30px;
128
- }
129
-
130
- .upload-area:hover {
131
- border-color: #007BFF;
132
- background-color: #f0f8ff;
133
- }
134
-
135
- .upload-area.active {
136
- border-color: #007BFF;
137
- background-color: #e6f4ff;
138
- }
139
-
140
- .upload-icon {
141
- font-size: 3rem;
142
- color: #007BFF;
143
- margin-bottom: 15px;
144
- }
145
-
146
- .upload-text {
147
- font-size: 1.2rem;
148
- color: #666;
149
- margin-bottom: 10px;
150
- }
151
-
152
- .upload-subtext {
153
- font-size: 0.9rem;
154
- color: #888;
155
- }
156
-
157
- /* File Input */
158
- #file-input {
159
- display: none;
160
- }
161
-
162
- /* Preview Area */
163
- .preview-area {
164
- display: none;
165
- margin: 30px auto;
166
- max-width: 500px;
167
- }
168
-
169
- .preview-area img {
170
- max-width: 100%;
171
- max-height: 400px;
172
- border-radius: 8px;
173
- box-shadow: 0 4px 12px rgba(0,0,0,0.15);
174
- }
175
-
176
- .preview-info {
177
- margin-top: 15px;
178
- font-size: 0.9rem;
179
- color: #666;
180
- }
181
-
182
- /* Button Styles */
183
- .btn {
184
- background-color: #007BFF;
185
- color: #fff;
186
- border: none;
187
- padding: 12px 24px;
188
- font-size: 1rem;
189
- border-radius: 4px;
190
- cursor: pointer;
191
- transition: all 0.3s ease;
192
- display: inline-block;
193
- }
194
-
195
- .btn:hover {
196
- background-color: #0056b3;
197
- transform: translateY(-2px);
198
- box-shadow: 0 4px 12px rgba(0,123,255,0.4);
199
- }
200
-
201
- .btn-secondary {
202
- background-color: #6c757d;
203
- margin-right: 10px;
204
- }
205
-
206
- .btn-secondary:hover {
207
- background-color: #5a6268;
208
- }
209
-
210
- /* Instructions Section */
211
- .instructions {
212
- background-color: #fff;
213
- border-radius: 10px;
214
- box-shadow: 0 5px 15px rgba(0,0,0,0.08);
215
- padding: 30px;
216
- }
217
-
218
- .instructions h2 {
219
- font-size: 1.5rem;
220
- color: #333;
221
- margin-bottom: 20px;
222
- }
223
-
224
- .instruction-cards {
225
- display: flex;
226
- flex-wrap: wrap;
227
- gap: 20px;
228
- }
229
-
230
- .instruction-card {
231
- flex: 1 1 300px;
232
- background-color: #f9f9f9;
233
- border-radius: 8px;
234
- padding: 20px;
235
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
236
- transition: all 0.3s ease;
237
- border-left: 3px solid transparent;
238
- }
239
-
240
- .instruction-card:hover {
241
- transform: translateY(-5px);
242
- box-shadow: 0 8px 15px rgba(0,0,0,0.1);
243
- border-left: 3px solid #007BFF;
244
- }
245
-
246
- .instruction-card h3 {
247
- font-size: 1.1rem;
248
- color: #333;
249
- margin-bottom: 10px;
250
- }
251
-
252
- .instruction-card p {
253
- font-size: 0.95rem;
254
- color: #666;
255
- line-height: 1.5;
256
- }
257
-
258
- /* Footer */
259
- footer {
260
- text-align: center;
261
- padding: 20px;
262
- margin-top: 40px;
263
- color: #777;
264
- font-size: 0.9rem;
265
- }
266
-
267
- /* Responsive */
268
- @media (max-width: 768px) {
269
- .container {
270
- padding: 0 15px;
271
- margin: 20px auto;
272
- }
273
-
274
- .upload-section {
275
- padding: 30px 15px;
276
- }
277
-
278
- .upload-section h1 {
279
- font-size: 1.7rem;
280
- }
281
-
282
- .upload-area {
283
- padding: 30px 15px;
284
- }
285
-
286
- .instructions {
287
- padding: 20px 15px;
288
- }
289
- }
290
- </style>
291
- </head>
292
- <body>
293
-
294
- <!-- Top Navigation -->
295
- <header>
296
- <div class="logo">SkinAI</div>
297
- <nav>
298
- <a href="/">Home</a>
299
- <a href="/upload" style="color: #007BFF;">Upload</a>
300
- <a href="/result">Results</a>
301
- <a href="#">Contact</a>
302
- <button class="help-btn">Help</button>
303
- </nav>
304
- </header>
305
-
306
- <div class="container">
307
- <!-- Upload Section -->
308
- <section class="upload-section">
309
- <h1>Upload Your Skin Image</h1>
310
- <p>Our AI will analyze your image and provide insights about potential skin conditions. Please upload a clear, well-lit photo of the affected area.</p>
311
-
312
- <!-- Upload Area -->
313
- <div class="upload-area" id="drop-area">
314
- <div class="upload-icon">📤</div>
315
- <div class="upload-text">Drag & Drop your image here</div>
316
- <div class="upload-subtext">or click to browse files</div>
317
- <input type="file" id="file-input" accept="image/*">
318
- </div>
319
-
320
- <!-- Preview Area (initially hidden) -->
321
- <div class="preview-area" id="preview-area">
322
- <img id="preview-image" src="#" alt="Preview">
323
- <div class="preview-info" id="file-info">File information will appear here</div>
324
- <div style="margin-top: 20px;">
325
- <button class="btn btn-secondary" id="cancel-btn">Cancel</button>
326
- <button class="btn" id="analyze-btn">Analyze Image</button>
327
- </div>
328
- </div>
329
- </section>
330
-
331
- <!-- Instructions Section -->
332
- <section class="instructions">
333
- <h2>Best Practices for Accurate Results</h2>
334
- <div class="instruction-cards">
335
- <!-- Instruction 1 -->
336
- <div class="instruction-card">
337
- <h3>Good Lighting</h3>
338
- <p>Ensure your photo is taken in bright, natural light. Avoid shadows or harsh lighting that might distort colors or details.</p>
339
- </div>
340
- <!-- Instruction 2 -->
341
- <div class="instruction-card">
342
- <h3>Clear Focus</h3>
343
- <p>Take a clear, in-focus image of the affected area. Blurry images may lead to inaccurate results.</p>
344
- </div>
345
- <!-- Instruction 3 -->
346
- <div class="instruction-card">
347
- <h3>Proper Distance</h3>
348
- <p>Capture the image from about 6-12 inches away to show sufficient detail while maintaining context of the affected area.</p>
349
- </div>
350
- </div>
351
- </section>
352
- </div>
353
-
354
- <footer>
355
- <p>© 2025 SkinAI. All rights reserved. For educational purposes only. Not a substitute for professional medical advice.</p>
356
- </footer>
357
-
358
- <script>
359
- // JavaScript for handling file uploads and preview
360
- // JavaScript for handling file uploads, preview, and analysis
361
- document.addEventListener('DOMContentLoaded', function() {
362
- const dropArea = document.getElementById('drop-area');
363
- const fileInput = document.getElementById('file-input');
364
- const previewArea = document.getElementById('preview-area');
365
- const previewImage = document.getElementById('preview-image');
366
- const fileInfo = document.getElementById('file-info');
367
- const cancelBtn = document.getElementById('cancel-btn');
368
- const analyzeBtn = document.getElementById('analyze-btn');
369
-
370
- // Open file browser when clicking the upload area
371
- dropArea.addEventListener('click', () => {
372
- fileInput.click();
373
- });
374
-
375
- // Handle file selection
376
- fileInput.addEventListener('change', handleFiles);
377
-
378
- // Prevent default drag behaviors
379
- ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
380
- dropArea.addEventListener(eventName, preventDefaults, false);
381
- document.body.addEventListener(eventName, preventDefaults, false);
382
- });
383
-
384
- // Highlight drop area when dragging over it
385
- ['dragenter', 'dragover'].forEach(eventName => {
386
- dropArea.addEventListener(eventName, highlight, false);
387
- });
388
-
389
- ['dragleave', 'drop'].forEach(eventName => {
390
- dropArea.addEventListener(eventName, unhighlight, false);
391
- });
392
-
393
- // Handle dropped files
394
- dropArea.addEventListener('drop', handleDrop, false);
395
-
396
- // Cancel button functionality
397
- cancelBtn.addEventListener('click', () => {
398
- resetUpload();
399
- });
400
-
401
- // Analyze button functionality - send to backend
402
- analyzeBtn.addEventListener('click', () => {
403
- sendImageForAnalysis();
404
- });
405
-
406
- // Functions
407
- function preventDefaults(e) {
408
- e.preventDefault();
409
- e.stopPropagation();
410
- }
411
-
412
- function highlight() {
413
- dropArea.classList.add('active');
414
- }
415
-
416
- function unhighlight() {
417
- dropArea.classList.remove('active');
418
- }
419
-
420
- function handleDrop(e) {
421
- const dt = e.dataTransfer;
422
- const files = dt.files;
423
- handleFiles(files);
424
- }
425
-
426
- function handleFiles(e) {
427
- const files = this.files || e;
428
- if (files && files[0]) {
429
- const file = files[0];
430
-
431
- // Check if it's an image
432
- if (!file.type.match('image.*')) {
433
- alert('Please upload an image file');
434
- return;
435
- }
436
-
437
- // Display file info
438
- const size = (file.size / 1024).toFixed(2);
439
- fileInfo.textContent = `${file.name} (${size} KB)`;
440
-
441
- // Show preview
442
- const reader = new FileReader();
443
- reader.onload = function(e) {
444
- previewImage.src = e.target.result;
445
- dropArea.style.display = 'none';
446
- previewArea.style.display = 'block';
447
-
448
- // Store image data for results page
449
- sessionStorage.setItem('analyzedImage', e.target.result);
450
- }
451
- reader.readAsDataURL(file);
452
- }
453
- }
454
-
455
- function resetUpload() {
456
- fileInput.value = '';
457
- previewImage.src = '#';
458
- previewArea.style.display = 'none';
459
- dropArea.style.display = 'block';
460
-
461
- // Clear session storage data
462
- sessionStorage.removeItem('analyzedImage');
463
- sessionStorage.removeItem('analysisResult');
464
- sessionStorage.removeItem('detectedCondition');
465
- }
466
-
467
- function sendImageForAnalysis() {
468
- // Show loading state
469
- analyzeBtn.disabled = true;
470
- analyzeBtn.textContent = 'Analyzing...';
471
-
472
- // Get the file
473
- const file = fileInput.files[0];
474
- if (!file) {
475
- alert('Please select an image first');
476
- analyzeBtn.disabled = false;
477
- analyzeBtn.textContent = 'Analyze Image';
478
- return;
479
- }
480
-
481
- // Create FormData object
482
- const formData = new FormData();
483
- formData.append('image', file);
484
-
485
- // Send to backend
486
- fetch('/analyze', {
487
- method: 'POST',
488
- body: formData
489
- })
490
- .then(response => {
491
- if (!response.ok) {
492
- throw new Error('Network response was not ok');
493
- }
494
- return response.json();
495
- })
496
- .then(result => {
497
- // Store result in session storage
498
- sessionStorage.setItem('analysisResult', JSON.stringify(result));
499
- sessionStorage.setItem('detectedCondition', result.prediction || '');
500
-
501
- // Redirect to results page
502
- window.location.href = 'result';
503
- })
504
- .catch(error => {
505
- console.error('Error:', error);
506
- alert('There was an error analyzing your image. Please try again.');
507
-
508
- // Reset button state
509
- analyzeBtn.disabled = false;
510
- analyzeBtn.textContent = 'Analyze Image';
511
- });
512
- }
513
- });
514
- </script>
515
-
516
- </body>
517
- </html>