tesseract-ocr 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +18 -7
- data/Rakefile +5 -1
- data/examples/nerdz-captcha-breaker.rb +56 -0
- data/lib/tesseract/api.rb +36 -65
- data/lib/tesseract/api/image.rb +84 -0
- data/lib/tesseract/api/iterator.rb +122 -0
- data/lib/tesseract/c.rb +7 -274
- data/lib/tesseract/c/baseapi.rb +237 -0
- data/lib/tesseract/c/iterator.rb +282 -0
- data/lib/tesseract/c/leptonica.rb +88 -0
- data/lib/tesseract/engine.rb +71 -81
- data/lib/tesseract/engine/baseline.rb +43 -0
- data/lib/tesseract/engine/bounding_box.rb +54 -0
- data/lib/tesseract/engine/font_attributes.rb +50 -0
- data/lib/tesseract/engine/iterator.rb +161 -0
- data/lib/tesseract/engine/orientation.rb +45 -0
- data/lib/tesseract/extensions.rb +2 -12
- data/lib/tesseract/iterator.rb +38 -0
- data/lib/tesseract/version.rb +1 -1
- data/tesseract-ocr.gemspec +1 -0
- data/test/tesseract_bench.rb +31 -0
- data/test/tesseract_spec.rb +16 -60
- metadata +35 -10
@@ -0,0 +1,282 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2011 meh. All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification, are
|
5
|
+
# permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
8
|
+
# conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# THIS SOFTWARE IS PROVIDED BY meh ''AS IS'' AND ANY EXPRESS OR IMPLIED
|
11
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL meh OR
|
13
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
14
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
15
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
16
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
17
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
18
|
+
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
19
|
+
#
|
20
|
+
# The views and conclusions contained in the software and documentation are those of the
|
21
|
+
# authors and should not be interpreted as representing official policies, either expressed
|
22
|
+
# or implied, of meh.
|
23
|
+
#++
|
24
|
+
|
25
|
+
module Tesseract; module C
|
26
|
+
|
27
|
+
module Iterator
|
28
|
+
extend FFI::Inliner
|
29
|
+
|
30
|
+
inline 'C++' do |cpp|
|
31
|
+
cpp.include 'tesseract/resultiterator.h'
|
32
|
+
cpp.libraries 'tesseract'
|
33
|
+
|
34
|
+
cpp.raw 'using namespace tesseract;'
|
35
|
+
|
36
|
+
cpp.eval {
|
37
|
+
enum :PolyBlockType, [
|
38
|
+
:UNKNOWN,
|
39
|
+
:FLOWING_TEXT, :HEADING_TEXT, :PULLOUT_TEXT, :TABLE, :VERTICAL_TEXT, :CAPTION_TEXT,
|
40
|
+
:FLOWING_IMAGE, :HEADING_IMAGE, :PULLOUT_IMAGE,
|
41
|
+
:HORZ_LINE, :VERT_LINE, :NOISE, :COUNT
|
42
|
+
]
|
43
|
+
|
44
|
+
enum :PageIteratorLevel, [
|
45
|
+
:BLOCK, :PARAGRAPH, :LINE, :WORD, :SYMBOL
|
46
|
+
]
|
47
|
+
|
48
|
+
orientation = enum :UP, :RIGHT, :DOWN, :LEFT
|
49
|
+
direction = enum :LEFT_TO_RIGHT, :RIGHT_TO_LEFT, :TOP_TO_BOTTOM
|
50
|
+
|
51
|
+
BoundingBox = Class.new(FFI::Struct) {
|
52
|
+
layout \
|
53
|
+
:left, :int,
|
54
|
+
:top, :int,
|
55
|
+
:right, :int,
|
56
|
+
:bottom, :int
|
57
|
+
}
|
58
|
+
|
59
|
+
Image = Class.new(FFI::Struct) {
|
60
|
+
layout \
|
61
|
+
:pix, :pointer,
|
62
|
+
:x, :int,
|
63
|
+
:y, :int
|
64
|
+
}
|
65
|
+
|
66
|
+
Baseline = Class.new(FFI::Struct) {
|
67
|
+
layout \
|
68
|
+
:x1, :int,
|
69
|
+
:y1, :int,
|
70
|
+
:x2, :int,
|
71
|
+
:y2, :int
|
72
|
+
}
|
73
|
+
|
74
|
+
Orientation = Class.new(FFI::Struct) {
|
75
|
+
layout \
|
76
|
+
:orientation, orientation,
|
77
|
+
:writing_direction, direction,
|
78
|
+
:textline_order, direction,
|
79
|
+
:deskew_angle, :float
|
80
|
+
}
|
81
|
+
|
82
|
+
FontAttributes = Class.new(FFI::Struct) {
|
83
|
+
layout \
|
84
|
+
:id, :int,
|
85
|
+
:name, :string,
|
86
|
+
:pointsize, :int,
|
87
|
+
|
88
|
+
:is_bold, :bool,
|
89
|
+
:is_italic, :bool,
|
90
|
+
:is_underlined, :bool,
|
91
|
+
:is_monospace, :bool,
|
92
|
+
:is_serif, :bool,
|
93
|
+
:is_smallcaps, :bool
|
94
|
+
}
|
95
|
+
|
96
|
+
typedef BoundingBox.by_value, :BoundingBox
|
97
|
+
typedef Image.by_value, :Image
|
98
|
+
typedef Baseline.by_value, :Baseline
|
99
|
+
typedef Orientation.by_value, :OrientationResult
|
100
|
+
typedef FontAttributes.by_value, :FontAttributes
|
101
|
+
}
|
102
|
+
|
103
|
+
cpp.raw %{
|
104
|
+
typedef struct BoundingBox {
|
105
|
+
int left;
|
106
|
+
int top;
|
107
|
+
int right;
|
108
|
+
int bottom;
|
109
|
+
} BoundingBox;
|
110
|
+
|
111
|
+
typedef struct Image {
|
112
|
+
Pix* pix;
|
113
|
+
int x;
|
114
|
+
int y;
|
115
|
+
} Image;
|
116
|
+
|
117
|
+
typedef struct Baseline {
|
118
|
+
int x1;
|
119
|
+
int y1;
|
120
|
+
int x2;
|
121
|
+
int y2;
|
122
|
+
} Baseline;
|
123
|
+
|
124
|
+
typedef struct OrientationResult {
|
125
|
+
Orientation orientation;
|
126
|
+
WritingDirection writing_direction;
|
127
|
+
TextlineOrder textline_order;
|
128
|
+
float deskew_angle;
|
129
|
+
} OrientationResult;
|
130
|
+
|
131
|
+
typedef struct FontAttributes {
|
132
|
+
int id;
|
133
|
+
const char* name;
|
134
|
+
int pointsize;
|
135
|
+
|
136
|
+
bool is_bold;
|
137
|
+
bool is_italic;
|
138
|
+
bool is_underlined;
|
139
|
+
bool is_monospace;
|
140
|
+
bool is_serif;
|
141
|
+
bool is_smallcaps;
|
142
|
+
} FontAttributes;
|
143
|
+
}
|
144
|
+
|
145
|
+
cpp.function %{
|
146
|
+
void destroy (PageIterator* it) {
|
147
|
+
delete it;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
cpp.function %{
|
152
|
+
void begin (PageIterator* it) {
|
153
|
+
it->Begin();
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
cpp.function %{
|
158
|
+
bool next (PageIterator* it, PageIteratorLevel level) {
|
159
|
+
return it->Next(level);
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
cpp.function %{
|
164
|
+
bool is_at_beginning_of (PageIterator* it, PageIteratorLevel level) {
|
165
|
+
return it->IsAtBeginningOf(level);
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
cpp.function %{
|
170
|
+
bool is_at_final_element (PageIterator* it, PageIteratorLevel level, PageIteratorLevel element) {
|
171
|
+
return it->IsAtFinalElement(level, element);
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
cpp.function %{
|
176
|
+
BoundingBox bounding_box (PageIterator* it, PageIteratorLevel level) {
|
177
|
+
BoundingBox result;
|
178
|
+
|
179
|
+
it->BoundingBox(level, &result.left, &result.top, &result.right, &result.bottom);
|
180
|
+
|
181
|
+
return result;
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
cpp.function %{
|
186
|
+
PolyBlockType block_type (PageIterator* it) {
|
187
|
+
return it->BlockType();
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
cpp.function %{
|
192
|
+
Pix* get_binary_image (PageIterator* it, PageIteratorLevel level) {
|
193
|
+
return it->GetBinaryImage(level);
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
cpp.function %{
|
198
|
+
Image get_image (PageIterator* it, PageIteratorLevel level, int padding) {
|
199
|
+
Image result;
|
200
|
+
|
201
|
+
result.pix = it->GetImage(level, padding, &result.x, &result.y);
|
202
|
+
|
203
|
+
return result;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
cpp.function %{
|
208
|
+
Baseline baseline (PageIterator* it, PageIteratorLevel level) {
|
209
|
+
Baseline result;
|
210
|
+
|
211
|
+
it->Baseline(level, &result.x1, &result.y1, &result.x2, &result.y2);
|
212
|
+
|
213
|
+
return result;
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
cpp.function %{
|
218
|
+
OrientationResult orientation (PageIterator* it) {
|
219
|
+
OrientationResult result;
|
220
|
+
|
221
|
+
it->Orientation(&result.orientation, &result.writing_direction, &result.textline_order, &result.deskew_angle);
|
222
|
+
|
223
|
+
return result;
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
cpp.function %{
|
228
|
+
char* get_utf8_text (ResultIterator* it, PageIteratorLevel level) {
|
229
|
+
return it->GetUTF8Text(level);
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
cpp.function %{
|
234
|
+
float confidence (ResultIterator* it, PageIteratorLevel level) {
|
235
|
+
return it->Confidence(level);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
cpp.function %{
|
240
|
+
FontAttributes word_font_attributes (ResultIterator* it) {
|
241
|
+
FontAttributes result;
|
242
|
+
|
243
|
+
result.name = it->WordFontAttributes(&result.is_bold, &result.is_italic, &result.is_underlined,
|
244
|
+
&result.is_monospace, &result.is_serif, &result.is_smallcaps, &result.pointsize, &result.id);
|
245
|
+
|
246
|
+
return result;
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
cpp.function %{
|
251
|
+
bool word_is_from_dictionary (ResultIterator* it) {
|
252
|
+
return it->WordIsFromDictionary();
|
253
|
+
}
|
254
|
+
}
|
255
|
+
|
256
|
+
cpp.function %{
|
257
|
+
bool word_is_numeric (ResultIterator* it) {
|
258
|
+
return it->WordIsNumeric();
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
cpp.function %{
|
263
|
+
bool symbol_is_superscript (ResultIterator* it) {
|
264
|
+
return it->SymbolIsSuperscript();
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
cpp.function %{
|
269
|
+
bool symbol_is_subscript (ResultIterator* it) {
|
270
|
+
return it->SymbolIsSubscript();
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
cpp.function %{
|
275
|
+
bool symbol_is_dropcap (ResultIterator* it) {
|
276
|
+
return it->SymbolIsDropcap();
|
277
|
+
}
|
278
|
+
}
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
end; end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2011 meh. All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification, are
|
5
|
+
# permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
8
|
+
# conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# THIS SOFTWARE IS PROVIDED BY meh ''AS IS'' AND ANY EXPRESS OR IMPLIED
|
11
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL meh OR
|
13
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
14
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
15
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
16
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
17
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
18
|
+
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
19
|
+
#
|
20
|
+
# The views and conclusions contained in the software and documentation are those of the
|
21
|
+
# authors and should not be interpreted as representing official policies, either expressed
|
22
|
+
# or implied, of meh.
|
23
|
+
#++
|
24
|
+
|
25
|
+
module Tesseract; module C
|
26
|
+
|
27
|
+
module Leptonica
|
28
|
+
extend FFI::Inliner
|
29
|
+
|
30
|
+
inline 'C++' do |cpp|
|
31
|
+
cpp.include 'leptonica/allheaders.h'
|
32
|
+
cpp.libraries 'lept'
|
33
|
+
|
34
|
+
cpp.eval {
|
35
|
+
enum :Format, [
|
36
|
+
:UNKNOWN, :BMP, :JFIF_JPEG, :PNG,
|
37
|
+
:TIFF, :TIFF_PACKBITS, :TIFF_RLE, :TIFF_G3, :TIFF_G4, :TIFF_LZW, :TIFF_ZIP,
|
38
|
+
:PNM, :PS, :GIF, :JP2, :WEBP, :LPDF, :DEFAULT, :SPIX
|
39
|
+
]
|
40
|
+
}
|
41
|
+
|
42
|
+
cpp.typedef 'int32_t', 'Format'
|
43
|
+
|
44
|
+
cpp.function %{
|
45
|
+
Pix* pix_read (const char* path) {
|
46
|
+
return pixRead(path);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
cpp.function %{
|
51
|
+
Pix* pix_read_fd (int fd) {
|
52
|
+
return pixReadStream(fdopen(fd, "rb"), 0);
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
cpp.function %{
|
57
|
+
Pix* pix_read_mem (const l_uint8* data, size_t size) {
|
58
|
+
return pixReadMem(data, size);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
cpp.function %{
|
63
|
+
bool pix_write_mem (Pix* pix, uint8_t** data, size_t* size, Format format) {
|
64
|
+
return pixWriteMem(data, size, pix, format);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
cpp.function %{
|
69
|
+
void pix_destroy (Pix* pix) {
|
70
|
+
pixDestroy(&pix);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
cpp.function %{
|
75
|
+
int32_t pix_get_width (Pix* pix) {
|
76
|
+
return pixGetWidth(pix);
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
cpp.function %{
|
81
|
+
int32_t pix_get_height (Pix* pix) {
|
82
|
+
return pixGetHeight(pix);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end; end
|
data/lib/tesseract/engine.rb
CHANGED
@@ -24,6 +24,8 @@
|
|
24
24
|
|
25
25
|
require 'tesseract/api'
|
26
26
|
|
27
|
+
require 'tesseract/engine/iterator'
|
28
|
+
|
27
29
|
module Tesseract
|
28
30
|
|
29
31
|
class Engine
|
@@ -32,16 +34,18 @@ class Engine
|
|
32
34
|
namedic :path, :language, :mode, :variables,
|
33
35
|
:optional => { :path => '.', :language => :eng, :mode => :DEFAULT, :variables => {}, :config => [] },
|
34
36
|
:alias => { :data => :path, :lang => :language }
|
35
|
-
def initialize (path = '.', language = :eng, mode = :DEFAULT, variables = {}, config = []) # :yields: self
|
37
|
+
def initialize (path = '.', language = :eng, mode = :DEFAULT, variables = {}, config = [], &block) # :yields: self
|
36
38
|
@api = API.new
|
37
39
|
|
38
40
|
@initializing = true
|
39
41
|
|
42
|
+
@init = block
|
40
43
|
@path = path
|
41
44
|
@language = language
|
42
45
|
@mode = mode
|
43
46
|
@variables = variables
|
44
47
|
@config = config
|
48
|
+
@rectangle = []
|
45
49
|
|
46
50
|
yield self if block_given?
|
47
51
|
|
@@ -65,7 +69,10 @@ class Engine
|
|
65
69
|
end
|
66
70
|
|
67
71
|
def with (&block) # :yields: self
|
68
|
-
self.class.new(@path, @language, @mode, @variables.clone,
|
72
|
+
self.class.new(@path, @language, @mode, @variables.clone, @config.clone) {|e|
|
73
|
+
@init.call(e) if @init
|
74
|
+
block.call(e) if block
|
75
|
+
}
|
69
76
|
end
|
70
77
|
|
71
78
|
def set (name, value)
|
@@ -111,35 +118,25 @@ class Engine
|
|
111
118
|
end
|
112
119
|
|
113
120
|
def page_segmentation_mode= (value)
|
114
|
-
@api.
|
115
|
-
value.to_i
|
116
|
-
else
|
117
|
-
value.to_s.upcase.to_sym
|
118
|
-
end
|
121
|
+
@api.set_page_seg_mode C.for_enum(value)
|
119
122
|
end
|
120
123
|
|
121
124
|
def image= (image)
|
122
125
|
@image = image
|
123
126
|
end
|
124
127
|
|
128
|
+
namedic :x, :y, :width, :height,
|
129
|
+
:optional => 0 .. -1,
|
130
|
+
:alias => { :w => :width, :h => :height }
|
131
|
+
def select (x = nil, y = nil, width = nil, height = nil)
|
132
|
+
@rectangle = [x, y, width, height]
|
133
|
+
end
|
134
|
+
|
125
135
|
namedic :image, :x, :y, :width, :height,
|
126
136
|
:optional => 0 .. -1,
|
127
137
|
:alias => { :w => :width, :h => :height }
|
128
138
|
def text_for (image = nil, x = nil, y = nil, width = nil, height = nil)
|
129
|
-
image
|
130
|
-
image = API.image_for(image)
|
131
|
-
|
132
|
-
x ||= 0
|
133
|
-
y ||= 0
|
134
|
-
width ||= image.width
|
135
|
-
height ||= image.height
|
136
|
-
|
137
|
-
if (x + width) > image.width || (y + height) > image.height
|
138
|
-
raise IndexError, 'image access out of boundaries'
|
139
|
-
end
|
140
|
-
|
141
|
-
@api.set_image(image)
|
142
|
-
@api.set_rectangle(x, y, width, height)
|
139
|
+
_setup(image, x, y, width, height)
|
143
140
|
|
144
141
|
@api.get_text.tap {|text|
|
145
142
|
text.instance_exec(@api) {|api|
|
@@ -160,61 +157,40 @@ class Engine
|
|
160
157
|
text_for(nil, x, y, width, height)
|
161
158
|
end
|
162
159
|
|
163
|
-
def
|
164
|
-
|
160
|
+
def text
|
161
|
+
text_at
|
165
162
|
end
|
166
163
|
|
167
|
-
|
168
|
-
|
169
|
-
|
164
|
+
%w(block paragraph line word symbol).each {|level|
|
165
|
+
define_method "each_#{level}" do |&block|
|
166
|
+
raise ArgumentError, 'you have to pass a block' unless block
|
170
167
|
|
171
|
-
|
172
|
-
:optional => 0 .. -1,
|
173
|
-
:alias => { :w => :width, :h => :height }
|
174
|
-
def chars_for (image = nil, page = nil, x = nil, y = nil, width = nil, height = nil)
|
175
|
-
image ||= @image or raise ArgumentError, 'you have to set an image first'
|
176
|
-
image = API.image_for(image)
|
177
|
-
|
178
|
-
page ||= 0
|
179
|
-
x ||= 0
|
180
|
-
y ||= 0
|
181
|
-
width ||= image.width
|
182
|
-
height ||= image.height
|
183
|
-
|
184
|
-
if (x + width) > image.width || (y + height) > image.height
|
185
|
-
raise IndexError, 'image access out of boundaries'
|
168
|
+
_iterator.__send__ "each_#{level}", &block
|
186
169
|
end
|
187
170
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
@api.get_box(page).lines.map {|line|
|
192
|
-
char, x, y, width, height, page = line.chomp.split ' '
|
193
|
-
|
194
|
-
char.instance_eval {
|
195
|
-
@x = x.to_i
|
196
|
-
@y = y.to_i
|
197
|
-
@width = width.to_i
|
198
|
-
@height = height.to_i
|
199
|
-
@page = page.to_i
|
171
|
+
define_method "#{level}s" do
|
172
|
+
_iterator.__send__ "#{level}s"
|
173
|
+
end
|
200
174
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
175
|
+
namedic :image, :x, :y, :width, :height,
|
176
|
+
:optional => 0 .. -1,
|
177
|
+
:alias => { :w => :width, :h => :height }
|
178
|
+
define_method "#{level}s_for" do |image = nil, x = nil, y = nil, width = nil, height = nil|
|
179
|
+
self.image = image if image
|
180
|
+
select x, y, width, height
|
205
181
|
|
206
|
-
|
207
|
-
|
208
|
-
end
|
182
|
+
__send__("#{level}s")
|
183
|
+
end
|
209
184
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
185
|
+
namedic :x, :y, :width, :height,
|
186
|
+
:optional => 0 .. -1,
|
187
|
+
:alias => { :w => :width, :h => :height }
|
188
|
+
define_method "#{level}s_at" do |x = nil, y = nil, width = nil, height = nil|
|
189
|
+
__send__("#{level}s_for", nil, x, y, width, height)
|
190
|
+
end
|
191
|
+
}
|
216
192
|
|
217
|
-
|
193
|
+
protected
|
218
194
|
def _init
|
219
195
|
@api.end
|
220
196
|
|
@@ -229,27 +205,41 @@ private
|
|
229
205
|
}
|
230
206
|
end
|
231
207
|
|
232
|
-
def
|
233
|
-
|
234
|
-
|
208
|
+
def _setup (image = nil, x = nil, y = nil, width = nil, height = nil)
|
209
|
+
image ||= @image or raise ArgumentError, 'you have to set an image first'
|
210
|
+
image = API.image_for(image)
|
235
211
|
|
236
|
-
|
237
|
-
|
212
|
+
if !width && x
|
213
|
+
width = image.width - x
|
214
|
+
end
|
238
215
|
|
239
|
-
|
240
|
-
|
216
|
+
if !height && y
|
217
|
+
height = image.height - y
|
218
|
+
end
|
241
219
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
220
|
+
x ||= @rectangle[0] || 0
|
221
|
+
y ||= @rectangle[1] || 0
|
222
|
+
width ||= @rectangle[2] || image.width
|
223
|
+
height ||= @rectangle[3] || image.height
|
246
224
|
|
247
|
-
|
225
|
+
if (x + width) > image.width || (y + height) > image.height
|
226
|
+
raise IndexError, 'image access out of boundaries'
|
248
227
|
end
|
249
228
|
|
250
|
-
|
229
|
+
@api.set_image(image)
|
230
|
+
@api.set_rectangle(x, y, width, height)
|
231
|
+
end
|
232
|
+
|
233
|
+
def _recognize
|
234
|
+
_setup
|
235
|
+
|
236
|
+
@api.get_text
|
237
|
+
end
|
238
|
+
|
239
|
+
def _iterator
|
240
|
+
_recognize
|
251
241
|
|
252
|
-
|
242
|
+
Iterator.new(@api.get_iterator)
|
253
243
|
end
|
254
244
|
end
|
255
245
|
|