JosephCatrambone commited on
Commit
52d714a
·
1 Parent(s): 7193fb8

First import of classifier.

Browse files
app.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ import gradio as gr
4
+ import numpy
5
+ import onnxruntime as ort
6
+ from PIL import Image
7
+
8
+ ort_sess = ort.InferenceSession('tiny_letter_classifier_v2_q8quant.onnx')
9
+
10
+ # force reload now!
11
+
12
+ def get_bounds(img):
13
+ # Assumes a BLACK BACKGROUND!
14
+ # White letters on a black background!
15
+ left = img.shape[1]
16
+ right = 0
17
+ top = img.shape[0]
18
+ bottom = 0
19
+ min_color = numpy.min(img)
20
+ max_color = numpy.max(img)
21
+ mean_color = 0.5*(min_color+max_color)
22
+ # Do this the dumb way.
23
+ for y in range(0, img.shape[0]):
24
+ for x in range(0, img.shape[1]):
25
+ if img[y,x] > mean_color:
26
+ left = min(left, x)
27
+ right = max(right, x)
28
+ top = min(top, y)
29
+ bottom = max(bottom, y)
30
+ return (top, bottom, left, right)
31
+
32
+ def resize_maxpool(img, out_width: int, out_height: int):
33
+ out = numpy.zeros((out_height, out_width), dtype=img.dtype)
34
+ scale_factor_y = img.shape[0] // out_height
35
+ scale_factor_x = img.shape[1] // out_width
36
+ for y in range(0, out.shape[0]):
37
+ for x in range(0, out.shape[1]):
38
+ out[y,x] = numpy.max(img[y*scale_factor_y:(y+1)*scale_factor_y, x*scale_factor_x:(x+1)*scale_factor_x])
39
+ return out
40
+
41
+ def process_input(input_msg):
42
+ img = input_msg["composite"]
43
+ # Image is inverted. 255 is white, 0 is what's drawn.
44
+ img_mean = 0.5 * (numpy.max(img) + numpy.min(img))
45
+ img = 1.0 * (img < img_mean) # Invert the image and convert to a float.
46
+ #crop_area = get_bounds(img)
47
+ #img = img[crop_area[0]:crop_area[1]+2, crop_area[2]:crop_area[3]+2]
48
+ img = resize_maxpool(img, 32, 32)
49
+ img = numpy.expand_dims(img, axis=0) # Unsqueeze
50
+ return img
51
+
52
+ def softmax(arr):
53
+ arr = arr - numpy.max(arr)
54
+ return numpy.exp(arr) / numpy.sum(numpy.exp(arr), axis=-1)
55
+
56
+ def normalize(arr):
57
+ arr = numpy.atleast_2d(arr)
58
+ if arr.shape[0] == 1:
59
+ magnitude = arr @ arr.T
60
+ elif arr.shape[1] == 1:
61
+ magnitude = arr.T @ arr
62
+ return arr / magnitude
63
+
64
+ def predict(input_img):
65
+ img = process_input(input_img)
66
+ class_preds = ort_sess.run(None, {'input': img.astype(numpy.float32)})[0]
67
+ class_preds = softmax(class_preds)[0]
68
+ class_idx_to_name = list("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
69
+ max_class_idx = numpy.argmax(class_preds)
70
+
71
+ text_out = json.dumps({class_idx_to_name[i]: "#"*int(10*j) for i,j in enumerate(class_preds)}, indent=2)
72
+ return Image.fromarray(numpy.clip((img[0] * 254), 0, 255).astype(numpy.uint8)), f"Pred: {class_idx_to_name[max_class_idx]}: {class_preds[max_class_idx]}", text_out
73
+ #return sim[0][0], text_out
74
+
75
+
76
+ demo = gr.Interface(
77
+ fn=predict,
78
+ inputs=[
79
+ #gr.Sketchpad(image_mode='L', type='numpy'),
80
+ #gr.ImageEditor(
81
+ gr.Sketchpad(
82
+ width=320, height=320,
83
+ canvas_size=(320, 320),
84
+ sources = ["upload", "clipboard"], # Webcam
85
+ layers=False,
86
+ image_mode='L',
87
+ type='numpy',
88
+ ),
89
+ ],
90
+ outputs=["image", "text", "text"],
91
+ )
92
+
93
+ demo.launch(share=True)
94
+
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ onnxruntime==1.21.1
2
+ numpy==1.26.4
3
+ gradio==5.29.0
tiny_letter_classifier_v2_q8quant.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1c3c80f832dd0d13970592e1d008a9b5eb26b22381a349ea41df48225606b190
3
+ size 2845664