Skip to content
This repository was archived by the owner on Aug 15, 2019. It is now read-only.

Commit 8dcc13b

Browse files
MaartenNikhil Thorat
authored andcommitted
Example: add a dataset to the model builder (Rune recognition) (#156)
* Create generate_train_examples_runes.html * Change styling Is it okay if we link the font from an external or should we include it in the repo? It's licensed under the [open font license](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) by the way. * Create generate_rune_data_for_model_builder.py * Add files via upload * Create rune_recognition.md * Update rune_recognition.md * Update rune_recognition.md * Update rune_recognition.md * Update rune_recognition.md * Create deleteme.md * Update rune_recognition.md * Update rune_recognition.md * Update rune_recognition.md * Minor text update (cherry picked from commit e2b4f01) * Runeless tutorial * Update text on where to find rune images * Merge branch 'master' into rune_recognition_tutorial_without_images * Use single quotes everywhere * Note that number of labels is different for runes than for MNIST * Merge branch 'master' into rune_recognition_tutorial_without_images * Update generate_train_examples_runes.html * Update rune_recognition.md * Merge branch 'master' into rune_recognition_tutorial_without_images
1 parent 0ae3c36 commit 8dcc13b

File tree

4 files changed

+449
-0
lines changed

4 files changed

+449
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from __future__ import absolute_import
2+
from __future__ import division
3+
from __future__ import print_function
4+
5+
import argparse
6+
import pickle
7+
import os
8+
import numpy as np
9+
import re
10+
from PIL import Image
11+
from os import listdir
12+
from os.path import isfile, join
13+
import json
14+
15+
path_to_images = 'runes/'
16+
output_file_image_collage = 'output.png'
17+
output_file_labels_packed = 'labels'
18+
output_file_label_names = 'labelNames.json'
19+
number_of_channels = 1
20+
21+
def pack(classes, labels):
22+
n_classes = len(classes)
23+
length = len(labels)
24+
result = [np.NaN] * length * n_classes
25+
26+
i = 0
27+
while i < length:
28+
label_candidate = labels[i]
29+
index = classes.index(label_candidate)
30+
offset = (i * n_classes)
31+
result[offset + index] = 1
32+
i += 1
33+
return result
34+
35+
def select_alpha_channel(image_array):
36+
return np.array([image_array[3]])
37+
38+
def determine_image_alpha_channel(image):
39+
# Image represented as tensor (width x height x channels)
40+
image_as_array = np.array(image)
41+
# Select just the alpha channel values from tensor
42+
return np.apply_along_axis(select_alpha_channel, axis=2, arr=image_as_array)
43+
44+
paths_to_images = [f for f in listdir(path_to_images) if
45+
isfile(join(path_to_images, f)) and re.match('.*\.png', f)]
46+
example_file_pattern = r'.*_([^(\s]+)(?: ?\([0-9]+\))?\.png$'
47+
48+
49+
# Determine label from filename
50+
labels = [re.search(example_file_pattern, s).group(1) for s in paths_to_images]
51+
if len(labels) != len(paths_to_images):
52+
raise ValueError('Expected number of labels to be equal to the number of example images!')
53+
54+
# Get array of distinct labels
55+
labels_clear_text = np.unique(np.asarray(labels))
56+
indexed_classes = labels_clear_text.tolist()
57+
labels5 = pack(indexed_classes, labels)
58+
print('...', len(labels_clear_text), 'classes found')
59+
60+
# Pack labels into the uint8 array that demo builder expects
61+
packed_labels = np.asarray(labels5).astype('uint8')
62+
packed_labels.tofile(output_file_labels_packed)
63+
print('...Saved packed labels to:', output_file_labels_packed)
64+
65+
# Also emit a list of the names that we associate with the labels
66+
with open(output_file_label_names, 'w') as outfile:
67+
json.dump(indexed_classes, outfile)
68+
print('...Saved label names to:', output_file_label_names)
69+
70+
# Load all image data into array. Note we load just one channel: transparency (the alpha channel)
71+
loaded_images = [determine_image_alpha_channel(
72+
Image.open(join(path_to_images, path_to_image))) for path_to_image in
73+
paths_to_images]
74+
images_array = np.array(loaded_images)
75+
76+
print('Read', images_array.shape[0], 'images')
77+
print('min/max pixel values: ', images_array.min(), '/', images_array.max())
78+
79+
# Make each image take a single row in the big batch image by flattening the
80+
# width (2nd) and height (3rd) dimension.
81+
# a has shape N x (Width*Height) x Channels.
82+
images_array = images_array.reshape([images_array.shape[0], -1, number_of_channels]).squeeze()
83+
im = Image.fromarray(images_array)
84+
im.save(output_file_image_collage)
85+
print('Saved image with width/height', im.size, 'at', output_file_image_collage)
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Generate training examples for Runic character</title>
6+
<link rel="stylesheet" media="screen" href="https://fontlibrary.org/face/runasans" type="text/css"/>
7+
<style>
8+
canvas {
9+
-moz-user-select: -moz-none;
10+
-khtml-user-select: none;
11+
-webkit-user-select: none;
12+
-ms-user-select: none;
13+
user-select: none;
14+
} </style>
15+
</head>
16+
<body>
17+
<p>Draw the following rune</p>
18+
<div>
19+
20+
<span id="showchar" style="border: 1px solid black;font-size: 124px; font-family: RunaSans">
21+
22+
</span>
23+
</div>
24+
<p>In this canvas</p>
25+
<div>
26+
<input type="button" value="Clear canvas" id="clr" size="23" onclick="clearCanvas()">
27+
</div>
28+
29+
<div>
30+
<canvas id="can" width="32" height="32"
31+
style="width:320px;height:320px;border:2px solid;"></canvas>
32+
<div>
33+
<input type="button" value="Save rune (or press spacebar)" id="btn" size="30" onclick="saveAndSetNewRune()"/>
34+
</div>
35+
</div>
36+
37+
<script type="text/javascript">
38+
var name = prompt("What's your name?") || new Date().toISOString();
39+
40+
var canvas, ctx, flag = false,
41+
prevX = 0,
42+
currX = 0,
43+
prevY = 0,
44+
currY = 0,
45+
dot_flag = false;
46+
47+
var x = "black",
48+
y = 2;
49+
50+
var char = "_";
51+
52+
var allChars = [
53+
"ᚠ", "ᚡ", "ᚢ", "ᚣ", "ᚤ", "ᚥ", "ᚦ", "ᚧ", "ᚨ", "ᚩ", "ᚪ", "ᚫ", "ᚬ", "ᚭ", "ᚮ", "ᚯ",
54+
"ᚰ", "ᚱ", "ᚲ", "ᚳ", "ᚴ", "ᚵ", "ᚶ", "ᚷ", "ᚸ", "ᚹ", "ᚺ", "ᚻ", "ᚼ", "ᚽ", "ᚾ", "ᚿ",
55+
"ᛀ", "ᛁ", "ᛂ", "ᛃ", "ᛄ", "ᛅ", "ᛆ", "ᛇ", "ᛈ", "ᛉ", "ᛊ", "ᛋ", "ᛌ", "ᛍ", "ᛎ", "ᛏ",
56+
"ᛐ", "ᛑ", "ᛒ", "ᛓ", "ᛔ", "ᛕ", "ᛖ", "ᛗ", "ᛘ", "ᛙ", "ᛚ", "ᛛ", "ᛜ", "ᛝ", "ᛞ", "ᛟ",
57+
"ᛠ", "ᛡ", "ᛢ", "ᛣ", "ᛤ", "ᛥ", "ᛦ", "ᛧ", "ᛨ", "ᛩ", "ᛪ", "᛫", "᛬", "᛭", "ᛮ", "ᛯ",
58+
"ᛰ", "ᛱ", "ᛲ", "ᛳ", "ᛴ", "ᛵ", "ᛶ", "ᛷ", "ᛸ"
59+
];
60+
61+
var charIndex = 0;
62+
63+
function init() {
64+
canvas = document.getElementById('can');
65+
ctx = canvas.getContext("2d");
66+
67+
canvas.addEventListener("mousemove", function (e) {
68+
findxy('move', e)
69+
}, false);
70+
canvas.addEventListener("mousedown", function (e) {
71+
findxy('down', e)
72+
}, false);
73+
canvas.addEventListener("mouseup", function (e) {
74+
findxy('up', e)
75+
}, false);
76+
canvas.addEventListener("mouseout", function (e) {
77+
findxy('out', e)
78+
}, false);
79+
}
80+
81+
function draw() {
82+
ctx.beginPath();
83+
ctx.moveTo(prevX, prevY);
84+
ctx.lineTo(currX, currY);
85+
ctx.strokeStyle = x;
86+
ctx.lineWidth = y;
87+
ctx.stroke();
88+
ctx.closePath();
89+
}
90+
91+
function clearCanvas() {
92+
ctx.clearRect(0, 0, 320, 320);
93+
}
94+
95+
function save() {
96+
var a = document.createElement('a');
97+
a.href = canvas.toDataURL();
98+
a.download = "drawn_by_" + name + "_" + char + ".png";
99+
a.click();
100+
}
101+
102+
function findxy(res, e) {
103+
if (res === 'down') {
104+
prevX = currX;
105+
prevY = currY;
106+
currX = (e.clientX - canvas.offsetLeft) / 10;
107+
currY = (e.clientY - canvas.offsetTop) / 10;
108+
109+
flag = true;
110+
dot_flag = true;
111+
if (dot_flag) {
112+
ctx.beginPath();
113+
ctx.fillStyle = x;
114+
ctx.fillRect(currX, currY, 1, 1);
115+
ctx.closePath();
116+
dot_flag = false;
117+
}
118+
}
119+
if (res === 'up' || res === "out") {
120+
flag = false;
121+
}
122+
if (res === 'move') {
123+
if (flag) {
124+
prevX = currX;
125+
prevY = currY;
126+
currX = (e.clientX - canvas.offsetLeft) / 10;
127+
currY = (e.clientY - canvas.offsetTop) / 10;
128+
129+
ctx.beginPath();
130+
ctx.moveTo(prevX, prevY);
131+
ctx.lineTo(currX, currY);
132+
ctx.strokeStyle = x;
133+
ctx.lineWidth = y;
134+
ctx.stroke();
135+
ctx.closePath();
136+
}
137+
}
138+
}
139+
140+
141+
function setNewRune(setValue) {
142+
clearCanvas();
143+
144+
if (typeof setValue === "number") {
145+
charIndex = setValue;
146+
} else {
147+
charIndex = (charIndex + 1) % allChars.length;
148+
}
149+
char = allChars[charIndex];
150+
document.getElementById("showchar").innerHTML = char;
151+
}
152+
153+
function saveAndSetNewRune() {
154+
save();
155+
setNewRune();
156+
}
157+
158+
window.onkeyup = function (e) {
159+
var key = e.keyCode ? e.keyCode : e.which;
160+
161+
if (key === 32) {
162+
saveAndSetNewRune();
163+
}
164+
};
165+
166+
init();
167+
setNewRune(0);
168+
</script>
169+
</body>
170+
</html>

0 commit comments

Comments
 (0)