pujan paudel
commited on
Commit
•
1a11305
1
Parent(s):
d88cc48
first commit
Browse files- __init__.py +0 -0
- __pycache__/__init__.cpython-311.pyc +0 -0
- __pycache__/app.cpython-311.pyc +0 -0
- __pycache__/model.cpython-311.pyc +0 -0
- app.py +96 -0
- canvas.jpeg +0 -0
- dockerfile +11 -0
- model.py +169 -0
- requirements.txt +8 -0
- static/banner.png +0 -0
- static/clear.png +0 -0
- static/logo.png +0 -0
- static/script/dialog.js +87 -0
- static/script/faq.js +10 -0
- static/script/metrices.js +0 -0
- static/script/nav.js +14 -0
- static/script/plotly.js +0 -0
- static/script/popup.js +33 -0
- static/script/script.js +260 -0
- static/style/about.css +93 -0
- static/style/dialog.css +121 -0
- static/style/help.css +53 -0
- static/style/home.css +90 -0
- static/style/info-panel.css +57 -0
- static/style/navbar.css +71 -0
- static/style/popup.css +50 -0
- static/style/style.css +64 -0
- templates/about.html +102 -0
- templates/components/navbar.html +13 -0
- templates/help.html +63 -0
- templates/index.html +124 -0
- templates/layout.html +13 -0
__init__.py
ADDED
File without changes
|
__pycache__/__init__.cpython-311.pyc
ADDED
Binary file (157 Bytes). View file
|
|
__pycache__/app.cpython-311.pyc
ADDED
Binary file (4.37 kB). View file
|
|
__pycache__/model.cpython-311.pyc
ADDED
Binary file (11 kB). View file
|
|
app.py
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import io
|
2 |
+
import time
|
3 |
+
import cv2
|
4 |
+
from flask import Flask, render_template, request, redirect, url_for, flash,jsonify
|
5 |
+
from PIL import Image # For image processing (optional)
|
6 |
+
import os
|
7 |
+
import base64
|
8 |
+
import json
|
9 |
+
from .model import predict
|
10 |
+
|
11 |
+
ALLOWED_EXTENSIONS = {'jpg', 'jpeg'}
|
12 |
+
|
13 |
+
app = Flask(__name__)
|
14 |
+
app.secret_key='your_secret_key'
|
15 |
+
|
16 |
+
def allowed_file(filename):
|
17 |
+
return '.' in filename and \
|
18 |
+
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
19 |
+
|
20 |
+
@app.route('/')
|
21 |
+
def home():
|
22 |
+
return render_template('index.html')
|
23 |
+
|
24 |
+
|
25 |
+
@app.route('/help')
|
26 |
+
def help():
|
27 |
+
return render_template('help.html')
|
28 |
+
|
29 |
+
|
30 |
+
@app.route('/about')
|
31 |
+
def aboutus():
|
32 |
+
return render_template('about.html')
|
33 |
+
|
34 |
+
|
35 |
+
@app.route('/upload', methods=['POST'])
|
36 |
+
def upload_image():
|
37 |
+
|
38 |
+
|
39 |
+
if 'file-input' in request.files:
|
40 |
+
uploaded_file = request.files['file-input']
|
41 |
+
|
42 |
+
|
43 |
+
if uploaded_file and allowed_file(uploaded_file.filename):
|
44 |
+
|
45 |
+
filename= uploaded_file.filename
|
46 |
+
|
47 |
+
try:
|
48 |
+
# Read and validate the image (modify as needed)
|
49 |
+
image_buffer=io.BytesIO(uploaded_file.read())
|
50 |
+
|
51 |
+
# print(image_buffer.getvalue())
|
52 |
+
|
53 |
+
pred= predict(image_buffer=image_buffer)
|
54 |
+
|
55 |
+
|
56 |
+
return jsonify(pred)
|
57 |
+
|
58 |
+
except (IOError, OSError, ValueError) as e:
|
59 |
+
return jsonify(json.dumps({'error': "Server Error "+ str(e)})), 500
|
60 |
+
|
61 |
+
|
62 |
+
else:
|
63 |
+
return jsonify(json.dumps({'error': "Invalid file format. Please upload a JPEG, or JPG image."})), 500
|
64 |
+
|
65 |
+
|
66 |
+
if 'file-input-64' in request.form:
|
67 |
+
try:
|
68 |
+
# Decode the base64 data (replace with your processing logic)
|
69 |
+
base64_data = request.form['file-input-64']
|
70 |
+
|
71 |
+
image_buffer= io.BytesIO(base64.b64decode(base64_data))
|
72 |
+
|
73 |
+
|
74 |
+
try:
|
75 |
+
image_buffer.seek(0)
|
76 |
+
|
77 |
+
# Use Pillow to open the image data from BytesIO
|
78 |
+
image = Image.open(image_buffer)
|
79 |
+
|
80 |
+
# Save the image as a JPEG with appropriate quality (adjust quality as needed)
|
81 |
+
# image.save("canvas.jpeg", quality=90, format="JPEG")
|
82 |
+
image_buffer.seek(0)
|
83 |
+
|
84 |
+
pred= predict(image_buffer=image_buffer)
|
85 |
+
return jsonify(pred)
|
86 |
+
|
87 |
+
except (IOError, OSError, ValueError) as e:
|
88 |
+
return jsonify(json.dumps({'error': "Khali Na Patha"})), 500
|
89 |
+
|
90 |
+
|
91 |
+
|
92 |
+
except Exception as e:
|
93 |
+
return jsonify(json.dumps({'error': str(e)})), 500
|
94 |
+
|
95 |
+
if __name__ == '__main__':
|
96 |
+
app.run(debug=True,reload=True)
|
canvas.jpeg
ADDED
dockerfile
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.9
|
2 |
+
|
3 |
+
WORKDIR /code
|
4 |
+
|
5 |
+
COPY ./requirements.txt /code/requirements.txt
|
6 |
+
|
7 |
+
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
8 |
+
|
9 |
+
COPY . .
|
10 |
+
|
11 |
+
CMD ["gunicorn", "-b", "0.0.0.0:7860","main:app"]
|
model.py
ADDED
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
from io import BytesIO
|
3 |
+
import cv2
|
4 |
+
from captum.attr import IntegratedGradients
|
5 |
+
from captum.attr import visualization as viz
|
6 |
+
import io
|
7 |
+
import torch
|
8 |
+
import torchvision
|
9 |
+
import torch.nn as nn
|
10 |
+
from torchvision import transforms,models
|
11 |
+
from torchvision.transforms import v2
|
12 |
+
import torch.nn.functional as F
|
13 |
+
torchvision.disable_beta_transforms_warning()
|
14 |
+
import json
|
15 |
+
import base64
|
16 |
+
|
17 |
+
|
18 |
+
index_to_target = {
|
19 |
+
0: 'अ', 1: 'अं', 2: 'अः', 3: 'आ', 4: 'इ', 5: 'ई', 6: 'उ', 7: 'ऊ', 8: 'ए', 9: 'ऐ', 10: 'ओ', 11: 'औ', 12: 'क', 13: 'क्ष', 14: 'ख', 15: 'ग', 16: 'घ', 17: 'ङ', 18: 'च', 19: 'छ', 20: 'ज', 21: 'ज्ञ', 22: 'झ', 23: 'ञ', 24: 'ट', 25: 'ठ', 26: 'ड', 27: 'ढ', 28: 'ण', 29: 'त', 30: 'त्र', 31: 'थ', 32: 'द', 33: 'ध', 34: 'न', 35: 'प', 36: 'फ', 37: 'ब', 38: 'भ', 39: 'म', 40: 'य', 41: 'र', 42: 'ल', 43: 'व', 44: 'श', 45: 'ष', 46: 'स', 47: 'ह', 48: '०', 49: '१', 50: '२', 51: '३', 52: '४', 53: '५', 54: '६', 55: '७', 56: '८', 57: '९'}
|
20 |
+
target_to_index = {value:key for key,value in index_to_target.items()}
|
21 |
+
|
22 |
+
|
23 |
+
device = 'cuda:0' if torch.cuda.is_available() else "cpu"
|
24 |
+
|
25 |
+
img_transforms = v2.Compose([
|
26 |
+
transforms.ToTensor(),
|
27 |
+
v2.ToDtype(torch.float32),
|
28 |
+
v2.Normalize((0.5,),(0.5,))
|
29 |
+
])
|
30 |
+
|
31 |
+
#
|
32 |
+
def crop_characters(img) -> np.array:
|
33 |
+
|
34 |
+
|
35 |
+
blur_img =cv2.GaussianBlur(img,(5,5),3)
|
36 |
+
gray = cv2.cvtColor(blur_img,cv2.COLOR_BGR2GRAY)
|
37 |
+
|
38 |
+
# bin_img= cv2.threshold(gray,0,255,cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
|
39 |
+
|
40 |
+
thres_value,thresh_img= cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
|
41 |
+
|
42 |
+
contours, _ = cv2.findContours(thresh_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
43 |
+
|
44 |
+
# cv2.drawContours(img,contours,-1,(0,255,0),2)
|
45 |
+
|
46 |
+
|
47 |
+
bounding_boxes = []
|
48 |
+
for contour in contours:
|
49 |
+
area = cv2.contourArea(contour)
|
50 |
+
|
51 |
+
#neglecting very small contours which are actually noise
|
52 |
+
if area<200:
|
53 |
+
continue
|
54 |
+
|
55 |
+
# Get the bounding box coordinates
|
56 |
+
x, y, w, h = cv2.boundingRect(contour)
|
57 |
+
|
58 |
+
# Draw rectangle around contour
|
59 |
+
# cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0),1)
|
60 |
+
|
61 |
+
# Store bounding box coordinates
|
62 |
+
bounding_boxes.append((x, y, x + w, y + h))
|
63 |
+
|
64 |
+
# Calculate the minimum bounding rectangle that encloses all the smaller bounding rectangles
|
65 |
+
x_min = min(box[0] for box in bounding_boxes)
|
66 |
+
y_min = min(box[1] for box in bounding_boxes)
|
67 |
+
x_max = max(box[2] for box in bounding_boxes)
|
68 |
+
y_max = max(box[3] for box in bounding_boxes)
|
69 |
+
|
70 |
+
padding_left=3
|
71 |
+
padding_right =3
|
72 |
+
padding_bottom =3
|
73 |
+
padding_top =3
|
74 |
+
x_min -= padding_left
|
75 |
+
y_min -= padding_bottom
|
76 |
+
x_max += padding_top
|
77 |
+
y_max += padding_right
|
78 |
+
|
79 |
+
|
80 |
+
cropped_img = img[y_min:y_max, x_min:x_max]
|
81 |
+
return cropped_img
|
82 |
+
|
83 |
+
|
84 |
+
|
85 |
+
|
86 |
+
def predict(image_buffer:BytesIO)->str:
|
87 |
+
device = 'cuda:0' if torch.cuda.is_available() else "cpu"
|
88 |
+
|
89 |
+
model_path ="res97_state.pth"
|
90 |
+
|
91 |
+
model = models.resnet101(weights=None).to(device)
|
92 |
+
num_classes = 58
|
93 |
+
model.fc = nn.Linear(model.fc.in_features, num_classes).to(device)
|
94 |
+
model.load_state_dict(torch.load(model_path,map_location=device))
|
95 |
+
|
96 |
+
image_buffer.seek(0)
|
97 |
+
|
98 |
+
img= cv2.imdecode(np.frombuffer(image_buffer.read(),np.uint8),-1)
|
99 |
+
|
100 |
+
if img is None:
|
101 |
+
raise RuntimeError("Failed to decode image")
|
102 |
+
|
103 |
+
img = crop_characters(img)
|
104 |
+
|
105 |
+
img = cv2.resize(img, (28, 28), interpolation=cv2.INTER_AREA)
|
106 |
+
|
107 |
+
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Convert to grayscale
|
108 |
+
|
109 |
+
|
110 |
+
img_bin = cv2.adaptiveThreshold(img_gray,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,11,6)
|
111 |
+
thres_rgb= cv2.cvtColor(img_bin,cv2.COLOR_GRAY2BGR)
|
112 |
+
|
113 |
+
model.eval()
|
114 |
+
|
115 |
+
transformed_img = img_transforms(thres_rgb).unsqueeze(dim=0)
|
116 |
+
|
117 |
+
|
118 |
+
|
119 |
+
with torch.inference_mode():
|
120 |
+
transformed_img = transformed_img.to(device)
|
121 |
+
outputs = model(transformed_img)
|
122 |
+
_,predicted_index = torch.max(outputs.data,1) #returns max value and index
|
123 |
+
probabilities = F.softmax(outputs,1)
|
124 |
+
|
125 |
+
|
126 |
+
|
127 |
+
def attribute_image_features(algorithm,image , **kwargs):
|
128 |
+
model.zero_grad()
|
129 |
+
tensor_attributions = algorithm.attribute(image,
|
130 |
+
target=predicted_index,
|
131 |
+
**kwargs
|
132 |
+
)
|
133 |
+
|
134 |
+
return tensor_attributions
|
135 |
+
|
136 |
+
ig = IntegratedGradients(model)
|
137 |
+
attr_ig, delta = attribute_image_features(ig, transformed_img, baselines=transformed_img * 0, return_convergence_delta=True)
|
138 |
+
attr_ig = np.transpose(attr_ig.squeeze().cpu().detach().numpy(), (1, 2, 0))
|
139 |
+
|
140 |
+
|
141 |
+
original_image = img
|
142 |
+
|
143 |
+
# org_img,axes1 = viz.visualize_image_attr(None, original_image,
|
144 |
+
# method="original_image", title="Original Image",use_pyplot=False)
|
145 |
+
|
146 |
+
img_attr,axes2= viz.visualize_image_attr(attr_ig, original_image, method="blended_heat_map",sign="all",
|
147 |
+
title="Overlayed Integrated Gradients",use_pyplot=False,show_colorbar=True)
|
148 |
+
|
149 |
+
img_attr_bytes = io.BytesIO()
|
150 |
+
img_attr.savefig(img_attr_bytes,format="jpeg")
|
151 |
+
|
152 |
+
|
153 |
+
|
154 |
+
top3,top3index= torch.topk(probabilities,3)
|
155 |
+
# print(top3,top3index)
|
156 |
+
top3Value= top3.tolist()
|
157 |
+
top3Index= top3index.tolist()
|
158 |
+
# print(top3Value,top3Index)
|
159 |
+
|
160 |
+
|
161 |
+
json_data_dict={
|
162 |
+
"prob":top3Value[0],
|
163 |
+
"item":[ index_to_target[int(item)] for item in top3Index[0]],
|
164 |
+
"ig":base64.b64encode(img_attr_bytes.getvalue()).decode('utf-8')
|
165 |
+
|
166 |
+
}
|
167 |
+
|
168 |
+
return json.dumps( json_data_dict)
|
169 |
+
|
requirements.txt
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
flask
|
2 |
+
opencv-python
|
3 |
+
scikit-learn
|
4 |
+
numpy
|
5 |
+
pandas
|
6 |
+
matplotlib
|
7 |
+
torch
|
8 |
+
torchvision
|
static/banner.png
ADDED
static/clear.png
ADDED
static/logo.png
ADDED
static/script/dialog.js
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
const canvas = document.getElementById('drawing-board');
|
3 |
+
|
4 |
+
|
5 |
+
// Get the 2D rendering context of the canvas
|
6 |
+
var ctx = canvas.getContext('2d');
|
7 |
+
|
8 |
+
// Initialize variables to track pointer movements
|
9 |
+
var isDrawing = false;
|
10 |
+
var lastX = 0;
|
11 |
+
var lastY = 0;
|
12 |
+
|
13 |
+
// Event listener to track pointer movements and draw lines
|
14 |
+
canvas.addEventListener('pointerdown', function (e) {
|
15 |
+
isDrawing = true;
|
16 |
+
[lastX, lastY] = [e.offsetX, e.offsetY];
|
17 |
+
});
|
18 |
+
|
19 |
+
canvas.addEventListener('pointermove', function (e) {
|
20 |
+
if (isDrawing) {
|
21 |
+
var x = e.offsetX;
|
22 |
+
var y = e.offsetY;
|
23 |
+
drawLine(lastX, lastY, x, y);
|
24 |
+
lastX = x;
|
25 |
+
lastY = y;
|
26 |
+
}
|
27 |
+
});
|
28 |
+
|
29 |
+
canvas.addEventListener('pointerup', function () {
|
30 |
+
isDrawing = false;
|
31 |
+
});
|
32 |
+
|
33 |
+
canvas.addEventListener('pointerout', function () {
|
34 |
+
isDrawing = false;
|
35 |
+
});
|
36 |
+
|
37 |
+
// Function to draw lines on the canvas
|
38 |
+
function drawLine(startX, startY, endX, endY) {
|
39 |
+
ctx.beginPath();
|
40 |
+
ctx.moveTo(startX, startY);
|
41 |
+
ctx.lineTo(endX, endY);
|
42 |
+
ctx.strokeStyle = '#000'; // Color of the line
|
43 |
+
ctx.lineWidth = 2; // Thickness of the line
|
44 |
+
ctx.stroke();
|
45 |
+
ctx.closePath();
|
46 |
+
}
|
47 |
+
|
48 |
+
|
49 |
+
|
50 |
+
function showDialog() {
|
51 |
+
|
52 |
+
const modal = document.getElementById("dialog-area")
|
53 |
+
const overlay = document.getElementById("overlay");
|
54 |
+
|
55 |
+
|
56 |
+
const canvas = document.getElementById("drawing-board");
|
57 |
+
const ctx = canvas.getContext('2d');
|
58 |
+
|
59 |
+
|
60 |
+
|
61 |
+
ctx.fillStyle = "white";
|
62 |
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
63 |
+
|
64 |
+
modal.style.display = 'flex';
|
65 |
+
overlay.style.display = 'block';
|
66 |
+
|
67 |
+
}
|
68 |
+
|
69 |
+
|
70 |
+
|
71 |
+
function closeDialog() {
|
72 |
+
const modal = document.getElementById("dialog-area");
|
73 |
+
const overlay = document.getElementById("overlay");
|
74 |
+
|
75 |
+
const canvas = document.getElementById("drawing-board");
|
76 |
+
const ctx = canvas.getContext('2d');
|
77 |
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
78 |
+
modal.style.display = 'none';
|
79 |
+
overlay.style.display = 'none';
|
80 |
+
|
81 |
+
}
|
82 |
+
|
83 |
+
|
84 |
+
function clearCanvas() {
|
85 |
+
ctx.fillStyle = "white";
|
86 |
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
87 |
+
}
|
static/script/faq.js
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const faqQuestions = document.querySelectorAll('.faq-question');
|
2 |
+
|
3 |
+
faqQuestions.forEach(question => {
|
4 |
+
question.addEventListener('click', function () {
|
5 |
+
const answer = this.nextElementSibling;
|
6 |
+
answer.classList.toggle('show');
|
7 |
+
// const arrow = this.querySelector('.faq-arrow');
|
8 |
+
// arrow.classList.toggle('rotate'); // Simplified class toggle
|
9 |
+
});
|
10 |
+
});
|
static/script/metrices.js
ADDED
The diff for this file is too large to render.
See raw diff
|
|
static/script/nav.js
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
|
3 |
+
|
4 |
+
function setIdActive(id) {
|
5 |
+
const menu = document.getElementById(id);
|
6 |
+
|
7 |
+
// const navMenu = document.querySelectorAll("#nav-bar a");
|
8 |
+
|
9 |
+
// navMenu.forEach(item => {
|
10 |
+
// item.classList.remove("active");
|
11 |
+
// });
|
12 |
+
|
13 |
+
menu.classList.add("active");
|
14 |
+
}
|
static/script/plotly.js
ADDED
The diff for this file is too large to render.
See raw diff
|
|
static/script/popup.js
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const images = [...document.querySelectorAll('.image')];
|
2 |
+
|
3 |
+
// popup
|
4 |
+
|
5 |
+
const popup = document.querySelector('.popup');
|
6 |
+
const closeBtn = document.querySelector('.close-btn');
|
7 |
+
const largeImage = document.querySelector('.large-image');
|
8 |
+
|
9 |
+
|
10 |
+
popup.addEventListener('click', (e) => {
|
11 |
+
|
12 |
+
const bound = largeImage.getBoundingClientRect();
|
13 |
+
const x = e.clientX;
|
14 |
+
const y = e.clientY;
|
15 |
+
|
16 |
+
if (!(x >= bound.left && x <= bound.right &&
|
17 |
+
y >= bound.top && y <= bound.bottom)) {
|
18 |
+
popup.classList.toggle('active');u
|
19 |
+
}
|
20 |
+
});
|
21 |
+
|
22 |
+
images.forEach((item, i) => {
|
23 |
+
item.addEventListener('click', () => {
|
24 |
+
|
25 |
+
largeImage.src = item.src;
|
26 |
+
popup.classList.toggle('active');
|
27 |
+
})
|
28 |
+
})
|
29 |
+
|
30 |
+
|
31 |
+
closeBtn.addEventListener('click', () => {
|
32 |
+
popup.classList.toggle('active');
|
33 |
+
})
|
static/script/script.js
ADDED
@@ -0,0 +1,260 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
const fileInput = document.getElementById('file-input');
|
3 |
+
const button = document.getElementById('button');
|
4 |
+
const errorMessage = document.getElementById('error-message');
|
5 |
+
const container = document.getElementById('container');
|
6 |
+
|
7 |
+
const imageContainer = document.getElementById('image-container');
|
8 |
+
const previewImage = document.getElementById('file-image');
|
9 |
+
const resultImage = document.getElementById('result-image');
|
10 |
+
|
11 |
+
|
12 |
+
const fileTitle = document.querySelector(".title");
|
13 |
+
const predInfo = document.querySelector(".pred-info");
|
14 |
+
|
15 |
+
const predictedValue = document.querySelector(".pred-info h3 span");
|
16 |
+
|
17 |
+
|
18 |
+
const topPred = document.querySelector(".pred-info ul");
|
19 |
+
|
20 |
+
|
21 |
+
|
22 |
+
const sendByCanvas = document.getElementById("canvas-send");
|
23 |
+
|
24 |
+
|
25 |
+
function createBlobFromImageData(imageData, mimeType = "image/jpeg") {
|
26 |
+
const buffer = new ArrayBuffer(imageData.length);
|
27 |
+
const view = new Uint8Array(buffer);
|
28 |
+
for (let i = 0; i < imageData.length; i++) {
|
29 |
+
view[i] = imageData.charCodeAt(i);
|
30 |
+
}
|
31 |
+
|
32 |
+
return new Blob([buffer], { type: mimeType });
|
33 |
+
}
|
34 |
+
|
35 |
+
|
36 |
+
function initialState() {
|
37 |
+
button.textContent = 'Choose file';
|
38 |
+
predInfo.style.display = 'none';
|
39 |
+
fileTitle.textContent = "Upload your file here";
|
40 |
+
imageContainer.style.display = 'none';
|
41 |
+
}
|
42 |
+
|
43 |
+
function displayError(message, timeout = 3000) { // Set default timeout if needed
|
44 |
+
errorMessage.textContent = message;
|
45 |
+
errorMessage.style.display = 'block';
|
46 |
+
initialState();
|
47 |
+
setTimeout(() => {
|
48 |
+
errorMessage.style.display = 'none';
|
49 |
+
}, timeout);
|
50 |
+
}
|
51 |
+
|
52 |
+
|
53 |
+
async function sendImageToServer(image, filename) {
|
54 |
+
|
55 |
+
|
56 |
+
|
57 |
+
// Send the file to the server using Fetch API
|
58 |
+
try {
|
59 |
+
const formData = new FormData();
|
60 |
+
|
61 |
+
console.log(image)
|
62 |
+
|
63 |
+
const base64Data = image.split(',')[1];
|
64 |
+
|
65 |
+
formData.append('file-input-64', base64Data);
|
66 |
+
|
67 |
+
|
68 |
+
|
69 |
+
|
70 |
+
// formData.append('file-name', filename);
|
71 |
+
|
72 |
+
// console.log(`form data ${formData}`);
|
73 |
+
|
74 |
+
|
75 |
+
predInfo.style.display = 'none';
|
76 |
+
button.textContent = 'Processing....';
|
77 |
+
fileTitle.textContent = filename;
|
78 |
+
|
79 |
+
|
80 |
+
|
81 |
+
console.log("Submit button is clicked");
|
82 |
+
|
83 |
+
const response = await fetch('/upload', {
|
84 |
+
method: 'POST',
|
85 |
+
body: formData
|
86 |
+
});
|
87 |
+
|
88 |
+
topPred.innerHTML = "";
|
89 |
+
console.log('I am here')
|
90 |
+
|
91 |
+
const jsonData = await response.json();
|
92 |
+
|
93 |
+
console.log(jsonData)
|
94 |
+
console.log(typeof (jsonData))
|
95 |
+
const jsonObject = JSON.parse(jsonData);
|
96 |
+
|
97 |
+
if (!response.ok) {
|
98 |
+
throw new Error(`Error uploading file: ${jsonData.error}`);
|
99 |
+
}
|
100 |
+
|
101 |
+
resultImage.src = `data:image/jpeg;base64,` + jsonObject["ig"];
|
102 |
+
predInfo.style.display = 'flex';
|
103 |
+
|
104 |
+
predictedValue.textContent = `${jsonObject.item[0]} ( ${(jsonObject.prob[0] * 100).toFixed(3)}%)`;
|
105 |
+
|
106 |
+
for (let index = 0; index < jsonObject.item.length; index++) {
|
107 |
+
const newItem = document.createElement("li");
|
108 |
+
newItem.textContent = `${jsonObject.item[index]} ( ${(jsonObject.prob[index] * 100).toFixed(3)}%)`
|
109 |
+
topPred.appendChild(newItem);
|
110 |
+
}
|
111 |
+
|
112 |
+
|
113 |
+
// Handle successful upload response (e.g., display message)
|
114 |
+
console.log('File uploaded successfully!');
|
115 |
+
|
116 |
+
} catch (error) {
|
117 |
+
// errorMessage.textContent = `Error uploading file: ${error.message}`;
|
118 |
+
displayError(error.message);
|
119 |
+
} finally {
|
120 |
+
// Hide progress message (optional)
|
121 |
+
button.textContent = 'Choose file';
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
async function sentImageDataToSeverViaImage(event) {
|
126 |
+
|
127 |
+
const file = event.target.files[0];
|
128 |
+
const allowedExtensions = ['jpeg', 'jpg'];
|
129 |
+
|
130 |
+
const extension = file.name.split('.').pop().toLowerCase();
|
131 |
+
|
132 |
+
|
133 |
+
// console.log(extension);
|
134 |
+
|
135 |
+
if (!allowedExtensions.includes(extension)) {
|
136 |
+
event.target.value = ''; // Clear file selection
|
137 |
+
displayError('Invalid file format. Please upload a JPG or JPEG image.')
|
138 |
+
} else {
|
139 |
+
|
140 |
+
|
141 |
+
const reader = new FileReader();
|
142 |
+
|
143 |
+
|
144 |
+
reader.onload = function (event) {
|
145 |
+
previewImage.src = event.target.result;
|
146 |
+
resultImage.src = '';
|
147 |
+
|
148 |
+
imageContainer.style.display = 'flex';
|
149 |
+
|
150 |
+
};
|
151 |
+
|
152 |
+
reader.readAsDataURL(file); // Read the file and convert it to a data URL
|
153 |
+
|
154 |
+
|
155 |
+
// Send the file to the server using Fetch API
|
156 |
+
try {
|
157 |
+
const formData = new FormData();
|
158 |
+
formData.append('file-input', file);
|
159 |
+
|
160 |
+
|
161 |
+
|
162 |
+
button.textContent = 'Processing....';
|
163 |
+
fileTitle.textContent = file.name;
|
164 |
+
predInfo.style.display = 'none';
|
165 |
+
|
166 |
+
|
167 |
+
|
168 |
+
|
169 |
+
const response = await fetch('/upload', {
|
170 |
+
method: 'POST',
|
171 |
+
body: formData
|
172 |
+
});
|
173 |
+
|
174 |
+
if (!response.ok) {
|
175 |
+
throw new Error(`Error uploading file: ${response.statusText}`);
|
176 |
+
}
|
177 |
+
|
178 |
+
topPred.innerHTML = "";
|
179 |
+
|
180 |
+
const jsonData = await response.json();
|
181 |
+
|
182 |
+
console.log(jsonData)
|
183 |
+
console.log(typeof (jsonData))
|
184 |
+
const jsonObject = JSON.parse(jsonData);
|
185 |
+
|
186 |
+
|
187 |
+
resultImage.src = `data:image/jpeg;base64,` + jsonObject["ig"];
|
188 |
+
predInfo.style.display = 'flex';
|
189 |
+
|
190 |
+
predictedValue.textContent = `${jsonObject.item[0]} ( ${(jsonObject.prob[0] * 100).toFixed(3)}%)`;
|
191 |
+
|
192 |
+
for (let index = 0; index < jsonObject.item.length; index++) {
|
193 |
+
const newItem = document.createElement("li");
|
194 |
+
newItem.textContent = `${jsonObject.item[index]} ( ${(jsonObject.prob[index] * 100).toFixed(3)}%)`
|
195 |
+
topPred.appendChild(newItem);
|
196 |
+
}
|
197 |
+
|
198 |
+
|
199 |
+
// Handle successful upload response (e.g., display message)
|
200 |
+
console.log('File uploaded successfully!');
|
201 |
+
|
202 |
+
} catch (error) {
|
203 |
+
errorMessage.textContent = `Error uploading file: ${error.message}`;
|
204 |
+
} finally {
|
205 |
+
// Hide progress message (optional)
|
206 |
+
button.textContent = 'Choose file';
|
207 |
+
}
|
208 |
+
errorMessage.style.display = 'none'; // Hide the error message
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
|
213 |
+
async function sentImageDataToSeverViaCanvas() {
|
214 |
+
const canvas = document.getElementById('drawing-board'); // Replace with your canvas ID
|
215 |
+
var dataURL = canvas.toDataURL("image/jpeg");
|
216 |
+
|
217 |
+
previewImage.src = dataURL;
|
218 |
+
resultImage.src = '';
|
219 |
+
|
220 |
+
imageContainer.style.display = 'flex';
|
221 |
+
|
222 |
+
// // Create a dummy link text
|
223 |
+
// var a = document.createElement('a');
|
224 |
+
// // Set the link to the image so that when clicked, the image begins downloading
|
225 |
+
// a.href = dataURL
|
226 |
+
// // Specify the image filename
|
227 |
+
// a.download = 'canvas-download.jpeg';
|
228 |
+
// // Click on the link to set off download
|
229 |
+
// a.click();
|
230 |
+
|
231 |
+
closeDialog();
|
232 |
+
|
233 |
+
await sendImageToServer(dataURL, 'canvas_image.jpeg');
|
234 |
+
|
235 |
+
|
236 |
+
|
237 |
+
|
238 |
+
}
|
239 |
+
|
240 |
+
|
241 |
+
|
242 |
+
button.addEventListener('click', () => {
|
243 |
+
errorMessage.style.display = 'none';
|
244 |
+
fileInput.click();
|
245 |
+
});
|
246 |
+
|
247 |
+
|
248 |
+
|
249 |
+
|
250 |
+
sendByCanvas.addEventListener('click', async () => {
|
251 |
+
|
252 |
+
|
253 |
+
|
254 |
+
sentImageDataToSeverViaCanvas();
|
255 |
+
});
|
256 |
+
|
257 |
+
|
258 |
+
fileInput.addEventListener('change', async (event) => {
|
259 |
+
sentImageDataToSeverViaImage(event);
|
260 |
+
});
|
static/style/about.css
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#my-table {
|
2 |
+
width: 100%;
|
3 |
+
border-collapse: collapse;
|
4 |
+
/* border-radius: 8px; */
|
5 |
+
border-radius: 8px;
|
6 |
+
}
|
7 |
+
|
8 |
+
.container {
|
9 |
+
margin: 2%;
|
10 |
+
}
|
11 |
+
|
12 |
+
#table-container {
|
13 |
+
border-radius: 12px;
|
14 |
+
|
15 |
+
}
|
16 |
+
|
17 |
+
#my-table th,
|
18 |
+
#my-table td {
|
19 |
+
border: 1px solid #ddd;
|
20 |
+
padding: 8px;
|
21 |
+
text-align: left;
|
22 |
+
}
|
23 |
+
|
24 |
+
tr:nth-child(odd) {
|
25 |
+
background: #e6e1e1;
|
26 |
+
}
|
27 |
+
|
28 |
+
#my-table th {
|
29 |
+
background-color: #a7cefa;
|
30 |
+
color: white;
|
31 |
+
}
|
32 |
+
|
33 |
+
#table-container button {
|
34 |
+
display: inline-block;
|
35 |
+
padding: 6px 12px;
|
36 |
+
margin: 10px 0;
|
37 |
+
cursor: pointer;
|
38 |
+
}
|
39 |
+
|
40 |
+
#table-container button:hover {
|
41 |
+
/* background-color: #45a049; */
|
42 |
+
border-color: #0F79EF;
|
43 |
+
}
|
44 |
+
|
45 |
+
|
46 |
+
#my-table {
|
47 |
+
width: 100%;
|
48 |
+
border-collapse: collapse;
|
49 |
+
border-radius: 8px;
|
50 |
+
/* Add border radius for roundish feel */
|
51 |
+
}
|
52 |
+
|
53 |
+
#my-table th,
|
54 |
+
#my-table td {
|
55 |
+
border: 1px solid #ddd;
|
56 |
+
padding: 8px;
|
57 |
+
text-align: left;
|
58 |
+
color: #333;
|
59 |
+
/* Set text color */
|
60 |
+
}
|
61 |
+
|
62 |
+
tr:nth-child(odd) {
|
63 |
+
background: #f9f9f9;
|
64 |
+
/* Lighter background color for odd rows */
|
65 |
+
}
|
66 |
+
|
67 |
+
#my-table th {
|
68 |
+
background-color: #4a90e2;
|
69 |
+
/* Blue background for table header */
|
70 |
+
color: white;
|
71 |
+
}
|
72 |
+
|
73 |
+
#table-container button {
|
74 |
+
display: inline-block;
|
75 |
+
padding: 6px 12px;
|
76 |
+
margin: 10px 0;
|
77 |
+
cursor: pointer;
|
78 |
+
background-color: #4a90e2;
|
79 |
+
/* Blue button background */
|
80 |
+
color: white;
|
81 |
+
/* Button text color */
|
82 |
+
border: 1px solid #4a90e2;
|
83 |
+
/* Button border color */
|
84 |
+
border-radius: 4px;
|
85 |
+
/* Button border radius */
|
86 |
+
}
|
87 |
+
|
88 |
+
#table-container button:hover {
|
89 |
+
background-color: #0F79EF;
|
90 |
+
/* Darker blue on hover */
|
91 |
+
border-color: #0F79EF;
|
92 |
+
/* Darker blue for border on hover */
|
93 |
+
}
|
static/style/dialog.css
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#dialog-area {
|
2 |
+
/* min-width: 50vw;
|
3 |
+
max-width: 60vw;
|
4 |
+
min-height: fit-content;
|
5 |
+
|
6 |
+
max-height: 60vh; */
|
7 |
+
|
8 |
+
|
9 |
+
|
10 |
+
/* height: 300px; */
|
11 |
+
width: 300px;
|
12 |
+
height: 300px;
|
13 |
+
position: fixed;
|
14 |
+
top: 50%;
|
15 |
+
left: 50%;
|
16 |
+
|
17 |
+
|
18 |
+
/* padding-bottom: 24px; */
|
19 |
+
padding-right: 12px;
|
20 |
+
padding-left: 12px;
|
21 |
+
|
22 |
+
/* border-width: 2px;
|
23 |
+
border: 2px solid black; */
|
24 |
+
|
25 |
+
transform: translate(-50%, -50%);
|
26 |
+
display: flex;
|
27 |
+
flex-direction: column;
|
28 |
+
|
29 |
+
background-color: #f2f2f2;
|
30 |
+
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
|
31 |
+
|
32 |
+
z-index: 999;
|
33 |
+
|
34 |
+
}
|
35 |
+
|
36 |
+
.close {
|
37 |
+
font-size: 45px;
|
38 |
+
font-weight: 600;
|
39 |
+
}
|
40 |
+
|
41 |
+
|
42 |
+
#canvas-send {
|
43 |
+
margin: 0 auto;
|
44 |
+
flex: 1;
|
45 |
+
}
|
46 |
+
|
47 |
+
#clear-btn {
|
48 |
+
height: 30px;
|
49 |
+
width: 30px;
|
50 |
+
margin-right: 5px;
|
51 |
+
|
52 |
+
cursor: pointer;
|
53 |
+
}
|
54 |
+
|
55 |
+
|
56 |
+
|
57 |
+
.drawing-board {
|
58 |
+
margin-top: 20px;
|
59 |
+
/* padding: 12px;
|
60 |
+
padding-left: 30px; */
|
61 |
+
margin-bottom: 8px;
|
62 |
+
}
|
63 |
+
|
64 |
+
#drawing-board {
|
65 |
+
|
66 |
+
|
67 |
+
margin: 20px;
|
68 |
+
width: 100%;
|
69 |
+
/* height: 100%; */
|
70 |
+
|
71 |
+
flex: 1;
|
72 |
+
margin: 0 auto;
|
73 |
+
background-color: #f2f2f2;
|
74 |
+
|
75 |
+
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
|
76 |
+
|
77 |
+
}
|
78 |
+
|
79 |
+
.action-section {
|
80 |
+
|
81 |
+
margin-top: 10px;
|
82 |
+
display: flex;
|
83 |
+
/* height: 50px; */
|
84 |
+
flex-direction: row;
|
85 |
+
/* justify-content: flex-end; */
|
86 |
+
height: 35px;
|
87 |
+
}
|
88 |
+
|
89 |
+
|
90 |
+
|
91 |
+
canvas {
|
92 |
+
width: 100%;
|
93 |
+
height: 100%;
|
94 |
+
}
|
95 |
+
|
96 |
+
|
97 |
+
|
98 |
+
|
99 |
+
#close-cntr {
|
100 |
+
display: flex;
|
101 |
+
justify-content: start;
|
102 |
+
}
|
103 |
+
|
104 |
+
|
105 |
+
#close-cntr span:hover {
|
106 |
+
cursor: pointer;
|
107 |
+
}
|
108 |
+
|
109 |
+
|
110 |
+
#overlay {
|
111 |
+
|
112 |
+
position: fixed;
|
113 |
+
top: 0;
|
114 |
+
left: 0;
|
115 |
+
width: 100%;
|
116 |
+
height: 100%;
|
117 |
+
background-color: rgba(0, 0, 0, 0.5);
|
118 |
+
z-index: 998;
|
119 |
+
|
120 |
+
display: none;
|
121 |
+
}
|
static/style/help.css
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.container {
|
2 |
+
margin: 2%;
|
3 |
+
}
|
4 |
+
|
5 |
+
|
6 |
+
.faq {
|
7 |
+
margin-bottom: 20px;
|
8 |
+
position: relative;
|
9 |
+
/* Required for arrow positioning */
|
10 |
+
}
|
11 |
+
|
12 |
+
.faq-question {
|
13 |
+
cursor: pointer;
|
14 |
+
font-weight: bold;
|
15 |
+
padding: 10px 15px;
|
16 |
+
border-bottom: 1px solid #ddd;
|
17 |
+
position: relative;
|
18 |
+
/* Required for arrow positioning */
|
19 |
+
}
|
20 |
+
|
21 |
+
.faq-answer {
|
22 |
+
padding: 10px 15px;
|
23 |
+
display: none;
|
24 |
+
}
|
25 |
+
|
26 |
+
.faq-answer.show {
|
27 |
+
display: block;
|
28 |
+
}
|
29 |
+
|
30 |
+
.faq-arrow {
|
31 |
+
width: 0;
|
32 |
+
height: 0;
|
33 |
+
border-left: 5px solid transparent;
|
34 |
+
border-right: 5px solid transparent;
|
35 |
+
border-bottom: 8px solid #ddd;
|
36 |
+
position: absolute;
|
37 |
+
/* Position the arrow */
|
38 |
+
right: 10px;
|
39 |
+
/* Place it on the right edge */
|
40 |
+
top: 50%;
|
41 |
+
transform: translateY(-50%);
|
42 |
+
/* Vertical centering */
|
43 |
+
transition: transform 0.3s ease-in-out;
|
44 |
+
}
|
45 |
+
|
46 |
+
.faq-answer.show .faq-arrow {
|
47 |
+
transform: translateY(-50%) rotate(180deg);
|
48 |
+
/* Rotate on expand */
|
49 |
+
}
|
50 |
+
|
51 |
+
|
52 |
+
|
53 |
+
|
static/style/home.css
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
text-align: center;
|
3 |
+
|
4 |
+
}
|
5 |
+
|
6 |
+
#banner {
|
7 |
+
/* Set image to full width and height */
|
8 |
+
width: 100%;
|
9 |
+
height: 25vh;
|
10 |
+
|
11 |
+
/* Maintain image aspect ratio and avoid distortion */
|
12 |
+
object-fit: contain;
|
13 |
+
|
14 |
+
|
15 |
+
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
|
16 |
+
|
17 |
+
/* Optional: Center the image vertically */
|
18 |
+
/* position: absolute; */
|
19 |
+
/* top: 50%; */
|
20 |
+
/* transform: translateY(-50%); */
|
21 |
+
}
|
22 |
+
|
23 |
+
#blank-area {
|
24 |
+
height: 25vh;
|
25 |
+
width: 100%;
|
26 |
+
background-color: #252525;
|
27 |
+
/* margin-top: 20vh; */
|
28 |
+
|
29 |
+
/* background-color: rgb(102, 91, 91); */
|
30 |
+
/* background-image: url("./banner.png"); */
|
31 |
+
/* background-position: -20px; */
|
32 |
+
margin-bottom: 50px;
|
33 |
+
/* background-repeat: no-repeat;
|
34 |
+
background-size: contain; */
|
35 |
+
|
36 |
+
}
|
37 |
+
|
38 |
+
#container {
|
39 |
+
/* width: 400px; */
|
40 |
+
height: 20vh;
|
41 |
+
margin: 0 auto;
|
42 |
+
|
43 |
+
padding: 20px;
|
44 |
+
border-radius: 5px;
|
45 |
+
}
|
46 |
+
|
47 |
+
.title {
|
48 |
+
font-size: 24px;
|
49 |
+
margin-bottom: 10px;
|
50 |
+
}
|
51 |
+
|
52 |
+
.button {
|
53 |
+
background-color: red;
|
54 |
+
color: white;
|
55 |
+
padding: 10px 20px;
|
56 |
+
border: none;
|
57 |
+
border-radius: 5px;
|
58 |
+
cursor: pointer;
|
59 |
+
}
|
60 |
+
|
61 |
+
.file-input {
|
62 |
+
display: none;
|
63 |
+
}
|
64 |
+
|
65 |
+
#image-container {
|
66 |
+
display: none;
|
67 |
+
text-align: center;
|
68 |
+
justify-content: center;
|
69 |
+
|
70 |
+
flex-wrap: wrap;
|
71 |
+
|
72 |
+
|
73 |
+
}
|
74 |
+
|
75 |
+
#image-container div {
|
76 |
+
width: 200px;
|
77 |
+
}
|
78 |
+
|
79 |
+
#file-image {
|
80 |
+
max-width: 200px;
|
81 |
+
}
|
82 |
+
|
83 |
+
|
84 |
+
|
85 |
+
#result-image {
|
86 |
+
max-width: 200px;
|
87 |
+
|
88 |
+
|
89 |
+
transition: transform 0.5s ease-in-out;
|
90 |
+
}
|
static/style/info-panel.css
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.info-panel {
|
2 |
+
width: 70%;
|
3 |
+
padding: 20px;
|
4 |
+
background-color: white;
|
5 |
+
border-radius: 5px;
|
6 |
+
|
7 |
+
margin: 0 auto;
|
8 |
+
margin-bottom: 20px;
|
9 |
+
|
10 |
+
display: flex;
|
11 |
+
flex-wrap: wrap;
|
12 |
+
|
13 |
+
|
14 |
+
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
|
15 |
+
}
|
16 |
+
|
17 |
+
.info-panel .pred-info {
|
18 |
+
display: flex;
|
19 |
+
flex-direction: column;
|
20 |
+
text-align: left;
|
21 |
+
flex: 1;
|
22 |
+
|
23 |
+
display: none;
|
24 |
+
|
25 |
+
transition: display 1s ease-in-out;
|
26 |
+
}
|
27 |
+
|
28 |
+
|
29 |
+
.info-panel .img-info {
|
30 |
+
flex: 2;
|
31 |
+
padding-left: 12px;
|
32 |
+
|
33 |
+
text-align: left;
|
34 |
+
|
35 |
+
transition: width 1s ease-out;
|
36 |
+
}
|
37 |
+
|
38 |
+
.info-panel ul {
|
39 |
+
list-style: none;
|
40 |
+
padding: 0;
|
41 |
+
margin: 0;
|
42 |
+
}
|
43 |
+
|
44 |
+
.bar {
|
45 |
+
height: 70px;
|
46 |
+
width: 5px;
|
47 |
+
|
48 |
+
|
49 |
+
background-color: red;
|
50 |
+
}
|
51 |
+
|
52 |
+
|
53 |
+
.img-info label {
|
54 |
+
display: flex;
|
55 |
+
justify-content: flex-end;
|
56 |
+
/* Align label content to end */
|
57 |
+
}
|
static/style/navbar.css
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#nav-bar {
|
2 |
+
/* padding-top: 8px; */
|
3 |
+
padding-left: 8px;
|
4 |
+
/* padding-bottom: 50px; */
|
5 |
+
padding-right: 8px;
|
6 |
+
|
7 |
+
|
8 |
+
background-color: white;
|
9 |
+
|
10 |
+
|
11 |
+
display: flex;
|
12 |
+
align-items: center;
|
13 |
+
/* Ensure vertical alignment */
|
14 |
+
justify-content: space-between;
|
15 |
+
|
16 |
+
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
|
17 |
+
|
18 |
+
/* Distribute evenly */
|
19 |
+
}
|
20 |
+
|
21 |
+
|
22 |
+
#nav-bar img {
|
23 |
+
max-height: 75px;
|
24 |
+
/* Adjust as needed */
|
25 |
+
}
|
26 |
+
|
27 |
+
#nav-bar a {
|
28 |
+
text-decoration: none;
|
29 |
+
color: inherit;
|
30 |
+
}
|
31 |
+
|
32 |
+
.nav-area {
|
33 |
+
display: flex;
|
34 |
+
}
|
35 |
+
|
36 |
+
.logo-area {
|
37 |
+
display: flex;
|
38 |
+
flex: 1;
|
39 |
+
|
40 |
+
|
41 |
+
|
42 |
+
display: flex;
|
43 |
+
align-items: center;
|
44 |
+
/* Optional, depending on desired alignment */
|
45 |
+
|
46 |
+
}
|
47 |
+
|
48 |
+
.logo-area span {
|
49 |
+
padding-bottom: 0;
|
50 |
+
/* padding-top: 12px; */
|
51 |
+
}
|
52 |
+
|
53 |
+
#nav-bar span {
|
54 |
+
padding: 12px;
|
55 |
+
font-weight: bold;
|
56 |
+
}
|
57 |
+
|
58 |
+
#nav-bar span:hover {
|
59 |
+
/* background-color: rgb(180, 173, 173); */
|
60 |
+
justify-content: center;
|
61 |
+
cursor: pointer;
|
62 |
+
color: #0F79EF;
|
63 |
+
|
64 |
+
}
|
65 |
+
|
66 |
+
#nav-bar a.active {
|
67 |
+
color: #0F79EF;
|
68 |
+
/* text-decoration: underline;
|
69 |
+
text-decoration-color: red;
|
70 |
+
text-decoration-thickness: 4px; */
|
71 |
+
}
|
static/style/popup.css
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.popup {
|
2 |
+
display: none;
|
3 |
+
/* Initially hide the modal */
|
4 |
+
position: fixed;
|
5 |
+
left: 0;
|
6 |
+
top: 0;
|
7 |
+
width: 100%;
|
8 |
+
height: 100%;
|
9 |
+
background-color: rgba(0, 0, 0, 0.7);
|
10 |
+
/* Transparent background with slight opacity */
|
11 |
+
text-align: center;
|
12 |
+
z-index: 10;
|
13 |
+
/* Ensure modal stays above other content */
|
14 |
+
}
|
15 |
+
|
16 |
+
.popup.active {
|
17 |
+
display: block;
|
18 |
+
/* Show the modal when the 'active' class is added */
|
19 |
+
}
|
20 |
+
|
21 |
+
.top-bar {
|
22 |
+
position: absolute;
|
23 |
+
top: 10px;
|
24 |
+
right: 10px;
|
25 |
+
}
|
26 |
+
|
27 |
+
.close-btn {
|
28 |
+
color: red;
|
29 |
+
font-size: 36px;
|
30 |
+
font-weight: bold;
|
31 |
+
cursor: pointer;
|
32 |
+
transition: 0.3s ease;
|
33 |
+
}
|
34 |
+
|
35 |
+
.close-btn:hover {
|
36 |
+
color: #f1f1f1;
|
37 |
+
}
|
38 |
+
|
39 |
+
.large-image {
|
40 |
+
max-width: 100%;
|
41 |
+
max-height: 100%;
|
42 |
+
margin: auto;
|
43 |
+
margin-top: 12px;
|
44 |
+
margin-bottom: 12px;
|
45 |
+
display: block;
|
46 |
+
}
|
47 |
+
|
48 |
+
.image {
|
49 |
+
cursor: pointer;
|
50 |
+
}
|
static/style/style.css
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
font-family: sans-serif;
|
3 |
+
margin: 0;
|
4 |
+
padding: 0;
|
5 |
+
/* padding: 20px; */
|
6 |
+
background-color: #f2f2f2;
|
7 |
+
}
|
8 |
+
|
9 |
+
|
10 |
+
#error-message {
|
11 |
+
color: white;
|
12 |
+
background-color: red;
|
13 |
+
padding: 30px;
|
14 |
+
border-radius: 5px;
|
15 |
+
font-size: 14px;
|
16 |
+
display: none;
|
17 |
+
|
18 |
+
|
19 |
+
font: size 32px;
|
20 |
+
font-weight: bold;
|
21 |
+
|
22 |
+
position: fixed;
|
23 |
+
|
24 |
+
top: 90%;
|
25 |
+
left: 81%;
|
26 |
+
transform: translate(-50%, -50%);
|
27 |
+
|
28 |
+
/* Center the message horizontally and vertically */
|
29 |
+
width: 400px;
|
30 |
+
/* Specify desired width */
|
31 |
+
text-align: center;
|
32 |
+
z-index: 999;
|
33 |
+
/* Ensure the message is on top of other elements */
|
34 |
+
animation: fade-in 0.5s ease-in-out;
|
35 |
+
/* Add a smooth fade-in animation */
|
36 |
+
}
|
37 |
+
|
38 |
+
@keyframes fade-in {
|
39 |
+
from {
|
40 |
+
opacity: 0;
|
41 |
+
}
|
42 |
+
|
43 |
+
to {
|
44 |
+
opacity: 1;
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
#error-message.fade-out {
|
49 |
+
animation: fade-out 0.5s ease-in-out backwards;
|
50 |
+
/* Add a smooth fade-out animation */
|
51 |
+
}
|
52 |
+
|
53 |
+
@keyframes fade-out {
|
54 |
+
from {
|
55 |
+
opacity: 1;
|
56 |
+
}
|
57 |
+
|
58 |
+
to {
|
59 |
+
opacity: 0;
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
|
64 |
+
|
templates/about.html
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title>About</title>
|
8 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/about.css') }}">
|
9 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/navbar.css') }}">
|
10 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
11 |
+
|
12 |
+
</head>
|
13 |
+
|
14 |
+
<body>
|
15 |
+
|
16 |
+
{% extends 'layout.html' %}
|
17 |
+
|
18 |
+
{% block content %}
|
19 |
+
|
20 |
+
|
21 |
+
<div class="container">
|
22 |
+
|
23 |
+
<h1>Developing an Interpretable Nepali Handwritten Character Recognizer</h1>
|
24 |
+
|
25 |
+
This project was driven by the desire to create a robust and informative character recognition system for the
|
26 |
+
Nepali
|
27 |
+
language. We believe that understanding how models make decisions is crucial for building trust and
|
28 |
+
transparency,
|
29 |
+
especially in culturally significant domains like handwriting recognition.
|
30 |
+
|
31 |
+
|
32 |
+
<h3>Technology Stack</h3>
|
33 |
+
<ul>
|
34 |
+
<li>
|
35 |
+
Programming language: Python
|
36 |
+
</li>
|
37 |
+
<li>
|
38 |
+
Deep learning libraries: PyTorch
|
39 |
+
</li>
|
40 |
+
<li>
|
41 |
+
Visualization tools:Matplotlib
|
42 |
+
</li>
|
43 |
+
<li>
|
44 |
+
Web Stack: FlaskI
|
45 |
+
</li>
|
46 |
+
</ul>
|
47 |
+
|
48 |
+
<!-- <h3>Future Plans</h3>
|
49 |
+
|
50 |
+
- Expanding character set recognition
|
51 |
+
- Enhancing interpretability methods
|
52 |
+
- Integrating with educational or assistive technologies
|
53 |
+
|
54 |
+
We are committed to advancing this project and its capabilities. We welcome your feedback and collaboration! -->
|
55 |
+
|
56 |
+
<h2>Metrices</h2>
|
57 |
+
|
58 |
+
<div id="confusion_matrix_cnt" style="width: 90%;"></div>
|
59 |
+
|
60 |
+
|
61 |
+
<h3>Classification Report</h3>
|
62 |
+
|
63 |
+
<div id="table-container">
|
64 |
+
<table id="my-table">
|
65 |
+
<thead>
|
66 |
+
<tr>
|
67 |
+
<th>Class</th>
|
68 |
+
<th>Precision</th>
|
69 |
+
<th>Recall</th>
|
70 |
+
<th>F1 score</th>
|
71 |
+
|
72 |
+
</tr>
|
73 |
+
</thead>
|
74 |
+
<tbody>
|
75 |
+
<!-- Table rows will be added here -->
|
76 |
+
</tbody>
|
77 |
+
</table>
|
78 |
+
|
79 |
+
<!-- Pagination buttons -->
|
80 |
+
<button id="prev-btn">Previous</button>
|
81 |
+
<span id="page-info"></span>
|
82 |
+
<button id="next-btn">Next</button>
|
83 |
+
</div>
|
84 |
+
|
85 |
+
|
86 |
+
|
87 |
+
|
88 |
+
<script src="{{ url_for('static', filename='script/plotly.js') }}"></script>
|
89 |
+
<script src="{{ url_for('static', filename='script/metrices.js') }}"></script>
|
90 |
+
|
91 |
+
|
92 |
+
|
93 |
+
</div>
|
94 |
+
|
95 |
+
<script>
|
96 |
+
setIdActive("about-men");
|
97 |
+
</script>
|
98 |
+
{% endblock %}
|
99 |
+
|
100 |
+
</body>
|
101 |
+
|
102 |
+
</html>
|
templates/components/navbar.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div id="nav-bar">
|
2 |
+
<div class="logo-area">
|
3 |
+
<a href="/"><img src="{{ url_for('static', filename='logo.png') }}"></a>
|
4 |
+
<a href="/help" id="help-men"><span>Help</span></a>
|
5 |
+
|
6 |
+
</div>
|
7 |
+
<div class="nav-area">
|
8 |
+
<a href="/" id="home-men"><span>Home</span></a>
|
9 |
+
<a href="/about" id="about-men"> <span>About</span></a>
|
10 |
+
</div>
|
11 |
+
|
12 |
+
<script src="{{ url_for('static', filename='script/nav.js') }}"></script>
|
13 |
+
</div>
|
templates/help.html
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title>Help</title>
|
8 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
9 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/navbar.css') }}">
|
10 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/help.css') }}">
|
11 |
+
</head>
|
12 |
+
|
13 |
+
<body>
|
14 |
+
{% extends 'layout.html' %}
|
15 |
+
|
16 |
+
{% block content %}
|
17 |
+
|
18 |
+
<div class="container">
|
19 |
+
|
20 |
+
|
21 |
+
<h1>Getting Started</h1>
|
22 |
+
|
23 |
+
<li> Upload a clear image of a handwritten Nepali character in supported formats (e.g., JPG, JPEG).</li>
|
24 |
+
<li> The recognized character will be displayed, along with a visualization of the pixels that influenced the
|
25 |
+
prediction.</li>
|
26 |
+
|
27 |
+
|
28 |
+
|
29 |
+
|
30 |
+
<h3>FAQ</h3>
|
31 |
+
|
32 |
+
<div class="faq">
|
33 |
+
<h3 class="faq-question">What character sets are supported?</h3>
|
34 |
+
<div class="faq-answer">
|
35 |
+
Currently, the model recognizes all basic Nepali characters (क्ष, त्र,ज्ञ,etc.).
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
<div class="faq">
|
39 |
+
<h3 class="faq-question">How accurate is the recognition?</h3>
|
40 |
+
<div class="faq-answer">
|
41 |
+
The accuracy rate is 97.5% on a standard test dataset.
|
42 |
+
</div>
|
43 |
+
</div>
|
44 |
+
<div class="faq">
|
45 |
+
<h3 class="faq-question">Will you add support for more features?</h3>
|
46 |
+
<div class="faq-answer">
|
47 |
+
We are actively working on improving the model and adding new
|
48 |
+
features. Stay tuned for updates!
|
49 |
+
</div>
|
50 |
+
</div>
|
51 |
+
|
52 |
+
|
53 |
+
</div>
|
54 |
+
<script src="{{ url_for('static', filename='script/faq.js') }}"></script>
|
55 |
+
<script>
|
56 |
+
setIdActive("help-men");
|
57 |
+
</script>
|
58 |
+
{% endblock %}
|
59 |
+
|
60 |
+
|
61 |
+
</body>
|
62 |
+
|
63 |
+
</html>
|
templates/index.html
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title>अक्षर</title>
|
8 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
9 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/navbar.css') }}">
|
10 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/home.css') }}">
|
11 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/info-panel.css') }}">
|
12 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/dialog.css') }}">
|
13 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/popup.css') }}">
|
14 |
+
<link rel="icon" href="{{ url_for('static', filename='logo.png') }}" type="image/x-icon">
|
15 |
+
</head>
|
16 |
+
|
17 |
+
<body>
|
18 |
+
|
19 |
+
{% extends 'layout.html' %}
|
20 |
+
|
21 |
+
{% block content %}
|
22 |
+
|
23 |
+
|
24 |
+
<div id="blank-area">
|
25 |
+
<img id="banner" src="{{ url_for('static', filename='banner.png') }}">
|
26 |
+
</div>
|
27 |
+
|
28 |
+
|
29 |
+
|
30 |
+
<!-- <div id="container">
|
31 |
+
|
32 |
+
|
33 |
+
</div> -->
|
34 |
+
|
35 |
+
|
36 |
+
|
37 |
+
<div class="popup">
|
38 |
+
<div class="top-bar">
|
39 |
+
<span class="close-btn">×</span>
|
40 |
+
</div>
|
41 |
+
<img src="" class="large-image" alt="">
|
42 |
+
</div>
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
|
47 |
+
<div class="info-panel">
|
48 |
+
<div class="pred-info">
|
49 |
+
<h3>Predicted Label: <span></span></h3>
|
50 |
+
<h5>Top 3 Predictions:</h5>
|
51 |
+
<ul>
|
52 |
+
</ul>
|
53 |
+
</div>
|
54 |
+
|
55 |
+
<div class="bar"></div>
|
56 |
+
|
57 |
+
<div class="img-info">
|
58 |
+
<h3>Photos</h3>
|
59 |
+
<h6 class="title">Upload your file here</h6>
|
60 |
+
<div id="image-container">
|
61 |
+
<img id='file-image'>
|
62 |
+
<img id='result-image' class="image">
|
63 |
+
</div>
|
64 |
+
|
65 |
+
|
66 |
+
<div style="display: flex;flex-direction: row;justify-content: flex-end;">
|
67 |
+
<label style="margin-right: 12px;">
|
68 |
+
<button class="button" id="try-btn" style="background-color: #1363EF;" onclick="showDialog()">Try
|
69 |
+
Yourself</button>
|
70 |
+
</label>
|
71 |
+
|
72 |
+
<label for="file-input">
|
73 |
+
<input type="file" id="file-input" class="file-input" accept="image/jpeg,image/jpg">
|
74 |
+
<button class="button" id="button">Choose file </button>
|
75 |
+
</label>
|
76 |
+
</div>
|
77 |
+
</div>
|
78 |
+
|
79 |
+
</div>
|
80 |
+
|
81 |
+
|
82 |
+
<div id="overlay"></div>
|
83 |
+
<div id="dialog-area" style="display: none;">
|
84 |
+
|
85 |
+
<div id="close-cntr"> <span
|
86 |
+
style="padding-left: 90px; padding-top: 15px; font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; font-size: 18px;font-weight: bold;">
|
87 |
+
PLAYGROUND</span>
|
88 |
+
<div style="flex: 1;"></div><span class="close" onclick="closeDialog()">×</span>
|
89 |
+
</div>
|
90 |
+
|
91 |
+
<div class="drawing-board">
|
92 |
+
<canvas id="drawing-board"></canvas>
|
93 |
+
</div>
|
94 |
+
<span
|
95 |
+
style="font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; font-size: 12px;font-weight: 600; color: grey;">NOTE:
|
96 |
+
Make sure to cover the drawing area</span>
|
97 |
+
<div class="action-section">
|
98 |
+
<img src="{{ url_for('static', filename='clear.png') }}" id="clear-btn" onclick="clearCanvas()">
|
99 |
+
<button class="button" style="background-color: #1363EF;" id="canvas-send">Submit</button>
|
100 |
+
</div>
|
101 |
+
|
102 |
+
</div>
|
103 |
+
|
104 |
+
|
105 |
+
|
106 |
+
|
107 |
+
<div id="error-message"></div>
|
108 |
+
<script src="{{ url_for('static', filename='script/script.js') }}"></script>
|
109 |
+
<script src="{{ url_for('static', filename='script/dialog.js') }}"></script>
|
110 |
+
<script src="{{ url_for('static', filename='script/popup.js') }}"></script>
|
111 |
+
|
112 |
+
|
113 |
+
<script>
|
114 |
+
setIdActive("home-men");
|
115 |
+
</script>
|
116 |
+
|
117 |
+
{% endblock %}
|
118 |
+
|
119 |
+
|
120 |
+
|
121 |
+
|
122 |
+
</body>
|
123 |
+
|
124 |
+
</html>
|
templates/layout.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>My Website</title>
|
5 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
6 |
+
</head>
|
7 |
+
<body>
|
8 |
+
{% include 'components/navbar.html' %}
|
9 |
+
<main>
|
10 |
+
{% block content %}{% endblock %}
|
11 |
+
</main>
|
12 |
+
</body>
|
13 |
+
</html>
|