pujan paudel commited on
Commit
1a11305
1 Parent(s): d88cc48

first commit

Browse files
__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">&times;</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>