CooCoo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/CooCoo.gemspec +47 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +88 -0
- data/README.md +123 -0
- data/Rakefile +81 -0
- data/bin/cuda-dev-info +25 -0
- data/bin/cuda-free +28 -0
- data/bin/cuda-free-trend +7 -0
- data/bin/ffi-gen +267 -0
- data/bin/spec_runner_html.sh +42 -0
- data/bin/trainer +198 -0
- data/bin/trend-cost +13 -0
- data/examples/char-rnn.rb +405 -0
- data/examples/cifar/cifar.rb +94 -0
- data/examples/img-similarity.rb +201 -0
- data/examples/math_ops.rb +57 -0
- data/examples/mnist.rb +365 -0
- data/examples/mnist_classifier.rb +293 -0
- data/examples/mnist_dream.rb +214 -0
- data/examples/seeds.rb +268 -0
- data/examples/seeds_dataset.txt +210 -0
- data/examples/t10k-images-idx3-ubyte +0 -0
- data/examples/t10k-labels-idx1-ubyte +0 -0
- data/examples/train-images-idx3-ubyte +0 -0
- data/examples/train-labels-idx1-ubyte +0 -0
- data/ext/buffer/Rakefile +50 -0
- data/ext/buffer/buffer.pre.cu +727 -0
- data/ext/buffer/matrix.pre.cu +49 -0
- data/lib/CooCoo.rb +1 -0
- data/lib/coo-coo.rb +18 -0
- data/lib/coo-coo/activation_functions.rb +344 -0
- data/lib/coo-coo/consts.rb +5 -0
- data/lib/coo-coo/convolution.rb +298 -0
- data/lib/coo-coo/core_ext.rb +75 -0
- data/lib/coo-coo/cost_functions.rb +91 -0
- data/lib/coo-coo/cuda.rb +116 -0
- data/lib/coo-coo/cuda/device_buffer.rb +240 -0
- data/lib/coo-coo/cuda/device_buffer/ffi.rb +109 -0
- data/lib/coo-coo/cuda/error.rb +51 -0
- data/lib/coo-coo/cuda/host_buffer.rb +117 -0
- data/lib/coo-coo/cuda/runtime.rb +157 -0
- data/lib/coo-coo/cuda/vector.rb +315 -0
- data/lib/coo-coo/data_sources.rb +2 -0
- data/lib/coo-coo/data_sources/xournal.rb +25 -0
- data/lib/coo-coo/data_sources/xournal/bitmap_stream.rb +197 -0
- data/lib/coo-coo/data_sources/xournal/document.rb +377 -0
- data/lib/coo-coo/data_sources/xournal/loader.rb +144 -0
- data/lib/coo-coo/data_sources/xournal/renderer.rb +101 -0
- data/lib/coo-coo/data_sources/xournal/saver.rb +99 -0
- data/lib/coo-coo/data_sources/xournal/training_document.rb +78 -0
- data/lib/coo-coo/data_sources/xournal/training_document/constants.rb +15 -0
- data/lib/coo-coo/data_sources/xournal/training_document/document_maker.rb +89 -0
- data/lib/coo-coo/data_sources/xournal/training_document/document_reader.rb +105 -0
- data/lib/coo-coo/data_sources/xournal/training_document/example.rb +37 -0
- data/lib/coo-coo/data_sources/xournal/training_document/sets.rb +76 -0
- data/lib/coo-coo/debug.rb +8 -0
- data/lib/coo-coo/dot.rb +129 -0
- data/lib/coo-coo/drawing.rb +4 -0
- data/lib/coo-coo/drawing/cairo_canvas.rb +100 -0
- data/lib/coo-coo/drawing/canvas.rb +68 -0
- data/lib/coo-coo/drawing/chunky_canvas.rb +101 -0
- data/lib/coo-coo/drawing/sixel.rb +214 -0
- data/lib/coo-coo/enum.rb +17 -0
- data/lib/coo-coo/from_name.rb +58 -0
- data/lib/coo-coo/fully_connected_layer.rb +205 -0
- data/lib/coo-coo/generation_script.rb +38 -0
- data/lib/coo-coo/grapher.rb +140 -0
- data/lib/coo-coo/image.rb +286 -0
- data/lib/coo-coo/layer.rb +67 -0
- data/lib/coo-coo/layer_factory.rb +26 -0
- data/lib/coo-coo/linear_layer.rb +59 -0
- data/lib/coo-coo/math.rb +607 -0
- data/lib/coo-coo/math/abstract_vector.rb +121 -0
- data/lib/coo-coo/math/functions.rb +39 -0
- data/lib/coo-coo/math/interpolation.rb +7 -0
- data/lib/coo-coo/network.rb +264 -0
- data/lib/coo-coo/neuron.rb +112 -0
- data/lib/coo-coo/neuron_layer.rb +168 -0
- data/lib/coo-coo/option_parser.rb +18 -0
- data/lib/coo-coo/platform.rb +17 -0
- data/lib/coo-coo/progress_bar.rb +11 -0
- data/lib/coo-coo/recurrence/backend.rb +99 -0
- data/lib/coo-coo/recurrence/frontend.rb +101 -0
- data/lib/coo-coo/sequence.rb +187 -0
- data/lib/coo-coo/shell.rb +2 -0
- data/lib/coo-coo/temporal_network.rb +291 -0
- data/lib/coo-coo/trainer.rb +21 -0
- data/lib/coo-coo/trainer/base.rb +67 -0
- data/lib/coo-coo/trainer/batch.rb +82 -0
- data/lib/coo-coo/trainer/batch_stats.rb +27 -0
- data/lib/coo-coo/trainer/momentum_stochastic.rb +59 -0
- data/lib/coo-coo/trainer/stochastic.rb +47 -0
- data/lib/coo-coo/transformer.rb +272 -0
- data/lib/coo-coo/vector_layer.rb +194 -0
- data/lib/coo-coo/version.rb +3 -0
- data/lib/coo-coo/weight_deltas.rb +23 -0
- data/prototypes/convolution.rb +116 -0
- data/prototypes/linear_drop.rb +51 -0
- data/prototypes/recurrent_layers.rb +79 -0
- data/www/images/screamer.png +0 -0
- data/www/images/screamer.xcf +0 -0
- data/www/index.html +82 -0
- metadata +373 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'zlib'
|
3
|
+
require 'chunky_png'
|
4
|
+
require 'base64'
|
5
|
+
require 'coo-coo/data_sources/xournal/document'
|
6
|
+
|
7
|
+
module CooCoo
|
8
|
+
module DataSources
|
9
|
+
module Xournal
|
10
|
+
# Loads a {Document}.
|
11
|
+
class Loader
|
12
|
+
# General catch all class for load errors.
|
13
|
+
class Error < RuntimeError
|
14
|
+
end
|
15
|
+
|
16
|
+
# Error while parsing the Xournal.
|
17
|
+
class ParseError < Error
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(doc)
|
21
|
+
@doc = doc || Document.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# Loads a Xournal document from the file at +path+.
|
25
|
+
# @return [Document]
|
26
|
+
def self.from_file(path)
|
27
|
+
doc = nil
|
28
|
+
|
29
|
+
Zlib::GzipReader.open(path) do |io|
|
30
|
+
doc = from_xml(io)
|
31
|
+
end
|
32
|
+
|
33
|
+
doc
|
34
|
+
rescue Zlib::GzipFile::Error
|
35
|
+
from_regular_file(path)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Loads a {Document} from XML in a String.
|
39
|
+
def self.from_xml(data)
|
40
|
+
xml = Nokogiri::XML(data)
|
41
|
+
root = xml.xpath('//xournal')[0]
|
42
|
+
raise ParseError.new("XML root is not 'xournal'") unless root
|
43
|
+
title_el = root.xpath("title")
|
44
|
+
title = title_el[0].text if title_el.size > 0
|
45
|
+
|
46
|
+
self.
|
47
|
+
new(Document.new(title, root['version'])).
|
48
|
+
from_xml(xml)
|
49
|
+
end
|
50
|
+
|
51
|
+
def from_xml(xml)
|
52
|
+
xml.xpath("//page").each do |page|
|
53
|
+
@doc.add_page(load_page(page))
|
54
|
+
end
|
55
|
+
|
56
|
+
@doc
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def self.from_regular_file(path)
|
62
|
+
File.open(path, 'rb') do |f|
|
63
|
+
from_xml(f)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def load_page(xml)
|
68
|
+
w = xml['width'].to_f
|
69
|
+
h = xml['height'].to_f
|
70
|
+
bg_xml = xml.xpath('background')
|
71
|
+
bg = load_background(bg_xml[0]) if bg_xml[0]
|
72
|
+
page = Page.new(w, h, bg)
|
73
|
+
|
74
|
+
xml.xpath('layer').each do |layer|
|
75
|
+
page.add_layer(load_layer(layer))
|
76
|
+
end
|
77
|
+
|
78
|
+
page
|
79
|
+
end
|
80
|
+
|
81
|
+
def load_background(xml)
|
82
|
+
case xml['type']
|
83
|
+
when 'pixmap' then PixmapBackground.new(xml['filename'], xml['domain'])
|
84
|
+
when 'pdf' then PDFBackground.new(xml['filename'], xml['pageno'], xml['domain'])
|
85
|
+
when 'solid' then Background.new(xml['color'], xml['style'])
|
86
|
+
else raise ParseError.new("Unknown background type #{xml['type']}: #{xml}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def load_layer(xml)
|
91
|
+
layer = Layer.new
|
92
|
+
|
93
|
+
xml.children.select(&:element?).each do |elem|
|
94
|
+
case elem.name
|
95
|
+
when 'stroke' then layer.add_stroke(load_stroke(elem))
|
96
|
+
when 'text' then layer.add_text(load_text(elem))
|
97
|
+
when 'image' then layer.add_image(load_image(elem))
|
98
|
+
else raise ParseError.new("Unknown element: #{elem}")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
layer
|
103
|
+
end
|
104
|
+
|
105
|
+
def load_image(xml)
|
106
|
+
Image.new(xml['left'],
|
107
|
+
xml['top'],
|
108
|
+
xml['right'],
|
109
|
+
xml['bottom'],
|
110
|
+
xml.text)
|
111
|
+
end
|
112
|
+
|
113
|
+
def load_text(xml)
|
114
|
+
Text.new(xml.text,
|
115
|
+
xml['x'].to_f,
|
116
|
+
xml['y'].to_f,
|
117
|
+
xml['size'].to_f,
|
118
|
+
xml['color'],
|
119
|
+
xml['font'])
|
120
|
+
end
|
121
|
+
|
122
|
+
def load_stroke(xml)
|
123
|
+
tool = xml['tool']
|
124
|
+
tool = Stroke::DefaultTool if tool == nil || tool.empty?
|
125
|
+
color = xml['color']
|
126
|
+
stroke = Stroke.new(tool, color)
|
127
|
+
widths = xml['width'].split.collect(&:to_f)
|
128
|
+
|
129
|
+
width = nil
|
130
|
+
xml.children.to_s.
|
131
|
+
split.
|
132
|
+
collect(&:to_f).
|
133
|
+
each_slice(2).
|
134
|
+
zip(widths) do |(x, y), w|
|
135
|
+
width ||= w if w
|
136
|
+
stroke.add_sample(x, y, width)
|
137
|
+
end
|
138
|
+
|
139
|
+
stroke
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'chunky_png'
|
2
|
+
require 'cairo'
|
3
|
+
require 'coo-coo/drawing'
|
4
|
+
|
5
|
+
module CooCoo
|
6
|
+
module DataSources
|
7
|
+
module Xournal
|
8
|
+
class Renderer
|
9
|
+
def initialize()
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(*args)
|
13
|
+
render_to_chunky(*args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def render_to_canvas(canvas, document, page_num, x = 0, y = 0, w = nil, h = nil, zx = 1.0, zy = 1.0)
|
17
|
+
page = document.pages[page_num]
|
18
|
+
w ||= (page.width - x).ceil.to_i
|
19
|
+
h ||= (page.height - y).ceil.to_i
|
20
|
+
render_page(canvas, page, x, y, x + w, y + h, zx, zy)
|
21
|
+
canvas
|
22
|
+
end
|
23
|
+
|
24
|
+
def render_to_chunky(document, page_num, x = 0, y = 0, w = nil, h = nil, zx = 1.0, zy = 1.0)
|
25
|
+
page = document.pages[page_num]
|
26
|
+
w ||= (page.width - x).ceil.to_i
|
27
|
+
h ||= (page.height - y).ceil.to_i
|
28
|
+
img = ChunkyPNG::Image.new((w * zx).to_i, (h * zy).to_i, chunky_color(page.background.color || :white))
|
29
|
+
canvas = Drawing::ChunkyCanvas.new(img)
|
30
|
+
render_to_canvas(canvas, document, page_num, x, y, w, h, zx, zy)
|
31
|
+
img
|
32
|
+
end
|
33
|
+
|
34
|
+
def render_to_cairo(document, page_num, x = 0, y = 0, w = nil, h = nil, zx = 1.0, zy = 1.0)
|
35
|
+
page = document.pages[page_num]
|
36
|
+
w ||= (page.width - x).ceil.to_i
|
37
|
+
h ||= (page.height - y).ceil.to_i
|
38
|
+
surface = Cairo::ImageSurface.new((w * zx).to_i, (h * zy).to_i)
|
39
|
+
canvas = Drawing::CairoCanvas.new(surface)
|
40
|
+
render_to_canvas(canvas, document, page_num, x, y, w, h, zx, zy)
|
41
|
+
surface
|
42
|
+
end
|
43
|
+
|
44
|
+
def chunky_color(color)
|
45
|
+
color && ChunkyPNG::Color.parse(color)
|
46
|
+
end
|
47
|
+
|
48
|
+
def render_page(canvas, page, min_x, min_y, max_x, max_y, zx, zy)
|
49
|
+
render_background(canvas, page.background, min_x, min_y, max_x, max_y, zx, zy)
|
50
|
+
page.each_layer do |layer|
|
51
|
+
render_layer(canvas, layer, min_x, min_y, max_x, max_y, zx, zy)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def render_background(canvas, bg, min_x, min_y, max_x, max_y, zx, zy)
|
56
|
+
color = chunky_color(bg.color || :white)
|
57
|
+
canvas.stroke_color = canvas.fill_color = color
|
58
|
+
canvas.rect(0, 0, ((max_x - min_x) * zx).to_i, ((max_y - min_y) * zy).to_i)
|
59
|
+
end
|
60
|
+
|
61
|
+
def render_layer(canvas, layer, min_x, min_y, max_x, max_y, zx, zy)
|
62
|
+
layer.each do |child|
|
63
|
+
#next unless child.within?(min_x, min_y, max_x, max_y)
|
64
|
+
case child
|
65
|
+
when Image then render_image(canvas, child, min_x, min_y, zx, zy)
|
66
|
+
when Stroke then render_stroke(canvas, child, min_x, min_y, max_x, max_y, zx, zy)
|
67
|
+
when Text then render_text(canvas, child, min_x, min_y, zx, zy)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def render_image(canvas, src, min_x, min_y, zx, zy)
|
73
|
+
canvas.blit(src.raw_data, ((src.left - min_x) * zx), ((src.top - min_y) * zy), src.width * zx, src.height * zy)
|
74
|
+
end
|
75
|
+
|
76
|
+
def render_stroke(canvas, stroke, min_x, min_y, max_x, max_y, zx, zy)
|
77
|
+
points = stroke.each_sample.inject([]) do |acc, sample|
|
78
|
+
#next unless sample.within?(min_x, min_y, max_x, max_y)
|
79
|
+
acc << [ (sample.x - min_x) * zx,
|
80
|
+
(sample.y - min_y) * zy,
|
81
|
+
sample.width * zx
|
82
|
+
]
|
83
|
+
end
|
84
|
+
|
85
|
+
canvas.stroke_color = chunky_color(stroke.color)
|
86
|
+
canvas.fill_color = chunky_color(stroke.color)
|
87
|
+
canvas.stroke(points)
|
88
|
+
end
|
89
|
+
|
90
|
+
def render_text(canvas, text, min_x, min_y, zx, zy)
|
91
|
+
canvas.fill_color = chunky_color(text.color)
|
92
|
+
canvas.text(text.text,
|
93
|
+
(text.x - min_x) * zx,
|
94
|
+
(text.y - min_y) * zy,
|
95
|
+
text.font,
|
96
|
+
text.size * zy)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module CooCoo
|
2
|
+
module DataSources
|
3
|
+
module Xournal
|
4
|
+
# Saves a {Document}
|
5
|
+
# @todo Keep linked images with the Xournal when it is saved.
|
6
|
+
class Saver
|
7
|
+
# Saves +doc+ to +io_or_path+ using Xournal's compressed XML format.
|
8
|
+
# @param io_or_path [String, IO] File name or an IO
|
9
|
+
def self.save(doc, io_or_path)
|
10
|
+
new.save(doc, io_or_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Saves +doc+ to an XML string.
|
14
|
+
def self.to_xml(doc)
|
15
|
+
new.to_xml(doc)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
end
|
20
|
+
|
21
|
+
def save(doc, io_or_path)
|
22
|
+
if io_or_path.respond_to?(:write)
|
23
|
+
save_to_io(doc, io_or_path)
|
24
|
+
elsif io_or_path.kind_of?(String)
|
25
|
+
save_to_file(doc, io_or_path)
|
26
|
+
else
|
27
|
+
raise ArgumentError.new("Only paths as String and IO are supported outputs. Not #{io_or_path.class}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_xml(doc)
|
32
|
+
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
33
|
+
xml.xournal(version: doc.version) do
|
34
|
+
xml.title(doc.title)
|
35
|
+
doc.pages.each do |p|
|
36
|
+
page_to_xml(p, xml)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end.to_xml
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def save_to_file(doc, path)
|
45
|
+
Zlib::GzipWriter.open(path) do |f|
|
46
|
+
save_to_io(doc, f)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def save_to_io(doc, io)
|
51
|
+
io.write(to_xml(doc))
|
52
|
+
end
|
53
|
+
|
54
|
+
def page_to_xml(p, xml)
|
55
|
+
xml.page(width: p.width, height: p.height) do
|
56
|
+
background_to_xml(p.background || Background::Default, xml)
|
57
|
+
p.layers.each do |l|
|
58
|
+
layer_to_xml(l, xml)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def background_to_xml(bg, xml)
|
64
|
+
case bg
|
65
|
+
when PixmapBackground then xml.background(type: 'pixmap', domain: bg.domain, filename: bg.filename)
|
66
|
+
when PDFBackground then xml.background(type: 'pdf', domain: bg.domain, filename: bg.filename, pageno: bg.page_number)
|
67
|
+
else xml.background(type: 'solid', color: bg.color, style: bg.style)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def layer_to_xml(layer, xml)
|
72
|
+
xml.layer do
|
73
|
+
layer.each do |child|
|
74
|
+
case child
|
75
|
+
when Image then image_to_xml(child, xml)
|
76
|
+
when Stroke then stroke_to_xml(child, xml)
|
77
|
+
when Text then text_to_xml(child, xml)
|
78
|
+
else raise ParseError.new("Unknown layer child: #{child.class} #{child.inspect}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def image_to_xml(img, xml)
|
85
|
+
xml.image(img.data_string, left: img.left, top: img.top, right: img.right, bottom: img.bottom)
|
86
|
+
end
|
87
|
+
|
88
|
+
def stroke_to_xml(stroke, xml)
|
89
|
+
xml.stroke(stroke.samples.collect { |s| [ s.x, s.y ] }.flatten.join(' '),
|
90
|
+
tool: stroke.tool, color: stroke.color, width: stroke.samples.collect(&:width).join(' '))
|
91
|
+
end
|
92
|
+
|
93
|
+
def text_to_xml(text, xml)
|
94
|
+
xml.text_(text.text, x: text.x, y: text.y, size: text.size, color: text.color, font: text.font)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'coo-coo/data_sources/xournal/training_document/constants'
|
2
|
+
require 'coo-coo/data_sources/xournal/training_document/example'
|
3
|
+
require 'coo-coo/data_sources/xournal/training_document/document_maker'
|
4
|
+
require 'coo-coo/data_sources/xournal/training_document/document_reader'
|
5
|
+
require 'coo-coo/data_sources/xournal/training_document/sets'
|
6
|
+
|
7
|
+
module CooCoo
|
8
|
+
module DataSources
|
9
|
+
module Xournal
|
10
|
+
# The {TrainingDocument} is the source of strokes for the trainer of
|
11
|
+
# the Xournal recognizer. Each {TrainingDocument} has a set of labels
|
12
|
+
# and associated strokes. Examples are loaded and stored to Xournal
|
13
|
+
# documents formatted into a grid with a label and strokes in each cell.
|
14
|
+
class TrainingDocument
|
15
|
+
attr_reader :examples
|
16
|
+
|
17
|
+
# @param examples [Array<Example>]
|
18
|
+
def initialize(examples = nil)
|
19
|
+
@examples = examples || Hash.new { |h, k| h[k] = Example.new(k) }
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Integer] Number of examples
|
23
|
+
def size
|
24
|
+
@examples.size
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Array<String>] of every example's label
|
28
|
+
def labels
|
29
|
+
@examples.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add an example to the set.
|
33
|
+
# @param label [String] The label of the example.
|
34
|
+
# @param strokes [Array<Stroke>] Strokes associated with this label.
|
35
|
+
# @return self
|
36
|
+
def add_example(label, strokes = nil)
|
37
|
+
ex = @examples[label]
|
38
|
+
ex.add_set(strokes) if strokes && !strokes.empty?
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
# Iterates each {Example}.
|
43
|
+
# @return [Enumerator]
|
44
|
+
def each_example(&block)
|
45
|
+
return to_enum(__method__) unless block_given?
|
46
|
+
|
47
|
+
@examples.each do |label, ex|
|
48
|
+
block.call(ex)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Convert the {Example} set into a {Document}.
|
53
|
+
# @param columns [Integer] Number of examples across the page.
|
54
|
+
# @param rows [Integer] Number of examples down the page.
|
55
|
+
# @param page_width [Float] Width of the page in points.
|
56
|
+
# @param page_height [Float] Height of the page in points.
|
57
|
+
# @return [Document]
|
58
|
+
def to_document(columns, rows, cells_per_example = 4, page_width = 612, page_height = 792)
|
59
|
+
DocumentMaker.new(self, columns, rows, cells_per_example, page_width, page_height).make_document
|
60
|
+
end
|
61
|
+
|
62
|
+
# Load {TrainingDocument} from a Xournal file.
|
63
|
+
# @param io_or_path [IO, String]
|
64
|
+
# @return [TrainingDocument]
|
65
|
+
def self.from_file(io_or_path)
|
66
|
+
DocumentReader.new.load(Xournal.from_file(io_or_path))
|
67
|
+
end
|
68
|
+
|
69
|
+
# Load a {TrainingDocument} from a {Document}.
|
70
|
+
# @param doc [Document]
|
71
|
+
# @return [TrainingDocument]
|
72
|
+
def self.from_document(doc)
|
73
|
+
DocumentReader.new.load(doc)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'chunky_png'
|
2
|
+
|
3
|
+
module CooCoo
|
4
|
+
module DataSources
|
5
|
+
module Xournal
|
6
|
+
class TrainingDocument
|
7
|
+
VERSION = '1'
|
8
|
+
GRID_COLOR = '#00E0FFFF' # FIXME Xournal places alpha up front
|
9
|
+
PARSED_GRID_COLOR = ChunkyPNG::Color.parse(GRID_COLOR)
|
10
|
+
META_LABEL = "Training Document"
|
11
|
+
META_LABEL_REGEX = /^#{META_LABEL}( +\d+)?: *(\d+)( +\d+)( +\d+)?/
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'coo-coo/data_sources/xournal/document'
|
2
|
+
|
3
|
+
module CooCoo
|
4
|
+
module DataSources
|
5
|
+
module Xournal
|
6
|
+
class TrainingDocument
|
7
|
+
class DocumentMaker
|
8
|
+
attr_reader :page_width
|
9
|
+
attr_reader :page_height
|
10
|
+
attr_reader :cells_per_example
|
11
|
+
attr_reader :columns
|
12
|
+
attr_reader :rows
|
13
|
+
|
14
|
+
def initialize(training_doc, columns, rows, cells_per_example, page_width, page_height)
|
15
|
+
@doc = training_doc
|
16
|
+
@columns = columns
|
17
|
+
@rows = rows
|
18
|
+
@cells_per_example = cells_per_example
|
19
|
+
@page_width = page_width
|
20
|
+
@page_height = page_height
|
21
|
+
end
|
22
|
+
|
23
|
+
def make_document
|
24
|
+
Document.new do |d|
|
25
|
+
@doc.each_example.each_slice(@columns * @rows / @cells_per_example).with_index do |labels, page_num|
|
26
|
+
d.add_page(make_page(labels, page_num))
|
27
|
+
end
|
28
|
+
|
29
|
+
d.pages.first.layers.last.add_text(Text.new("#{META_LABEL} #{VERSION}: #{@columns} #{@rows} #{@cells_per_example}", 0, @page_height - 14, 12, 'gray'))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def make_cell(layer, label, strokes, x, y, grid_w, grid_h)
|
34
|
+
layer.add_text(Text.new(label, x + 1, y + 1, 12, 'black', 'Serif'))
|
35
|
+
strokes.each do |stroke|
|
36
|
+
layer.add_stroke(stroke.scale(grid_w, grid_h, grid_w).translate(x, y))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def make_cells(examples, grid_w, grid_h)
|
41
|
+
layer = Layer.new
|
42
|
+
|
43
|
+
examples = examples.collect { |e|
|
44
|
+
e.each_set.collect { |s|
|
45
|
+
[ e.label, s ]
|
46
|
+
} + Array.new(@cells_per_example - e.size, [ e.label, [] ])
|
47
|
+
}.flatten(1)
|
48
|
+
|
49
|
+
examples.each_slice(@columns).with_index do |row, y|
|
50
|
+
row.each_with_index do |(label, strokes), x|
|
51
|
+
make_cell(layer, label, strokes, x * grid_w, y * grid_h, grid_w, grid_h)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
layer
|
56
|
+
end
|
57
|
+
|
58
|
+
def make_grid(grid_w, grid_h)
|
59
|
+
grid_layer = Layer.new
|
60
|
+
|
61
|
+
(1...@rows).each do |y|
|
62
|
+
(1...@columns).each do |x|
|
63
|
+
grid_layer.add_stroke(Stroke.new('pen', GRID_COLOR).
|
64
|
+
add_sample(x * grid_w, 0).
|
65
|
+
add_sample(x * grid_w, @page_height))
|
66
|
+
end
|
67
|
+
|
68
|
+
grid_layer.add_stroke(Stroke.new('pen', GRID_COLOR).
|
69
|
+
add_sample(0, y * grid_h).
|
70
|
+
add_sample(@page_width, y * grid_h))
|
71
|
+
end
|
72
|
+
|
73
|
+
grid_layer
|
74
|
+
end
|
75
|
+
|
76
|
+
def make_page(examples, page_num)
|
77
|
+
grid_w = @page_width / @columns
|
78
|
+
grid_h = @page_height / @rows
|
79
|
+
|
80
|
+
Page.new(@page_width, @page_height).
|
81
|
+
add_layer(make_grid(grid_w, grid_h)).
|
82
|
+
add_layer(make_cells(examples, grid_w, grid_h))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|