extract_curves 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/CVS/Entries +4 -0
  2. data/CVS/Repository +1 -0
  3. data/CVS/Root +1 -0
  4. data/bin/CVS/Entries +5 -0
  5. data/bin/CVS/Repository +1 -0
  6. data/bin/CVS/Root +1 -0
  7. data/bin/ec_rect2polar.rb +22 -0
  8. data/bin/ec_rev_lines.rb +5 -0
  9. data/bin/ec_sph_area.rb +30 -0
  10. data/bin/extract_curves.rb +2145 -0
  11. data/ruby_ext/CVS/Entries +1 -0
  12. data/ruby_ext/CVS/Repository +1 -0
  13. data/ruby_ext/CVS/Root +1 -0
  14. data/ruby_ext/pav/CVS/Entries +14 -0
  15. data/ruby_ext/pav/CVS/Repository +1 -0
  16. data/ruby_ext/pav/CVS/Root +1 -0
  17. data/ruby_ext/pav/cstr.c +82 -0
  18. data/ruby_ext/pav/cstr.h +17 -0
  19. data/ruby_ext/pav/extconf.rb +22 -0
  20. data/ruby_ext/pav/pav.c +162 -0
  21. data/ruby_ext/pav/pgtk.c +40 -0
  22. data/ruby_ext/pav/pgtk.h +14 -0
  23. data/ruby_ext/pav/pix.c +806 -0
  24. data/ruby_ext/pav/pix.h +236 -0
  25. data/ruby_ext/pav/t.rb +35 -0
  26. data/ruby_ext/pav/t1.rb +35 -0
  27. data/ruby_libs/CVS/Entries +1 -0
  28. data/ruby_libs/CVS/Repository +1 -0
  29. data/ruby_libs/CVS/Root +1 -0
  30. data/ruby_libs/pav/CVS/Entries +20 -0
  31. data/ruby_libs/pav/CVS/Repository +1 -0
  32. data/ruby_libs/pav/CVS/Root +1 -0
  33. data/ruby_libs/pav/attr_cache.rb +211 -0
  34. data/ruby_libs/pav/attr_cache.t1.rb +32 -0
  35. data/ruby_libs/pav/cache.rb +31 -0
  36. data/ruby_libs/pav/dbg_log.rb +458 -0
  37. data/ruby_libs/pav/floatsio.rb +53 -0
  38. data/ruby_libs/pav/generator_cache.rb +165 -0
  39. data/ruby_libs/pav/gtk/CVS/Entries +4 -0
  40. data/ruby_libs/pav/gtk/CVS/Repository +1 -0
  41. data/ruby_libs/pav/gtk/CVS/Root +1 -0
  42. data/ruby_libs/pav/gtk/button.rb +130 -0
  43. data/ruby_libs/pav/gtk/icons.rb +87 -0
  44. data/ruby_libs/pav/gtk/toolbar.rb +192 -0
  45. data/ruby_libs/pav/heap.rb +54 -0
  46. data/ruby_libs/pav/icons/CVS/Entries +17 -0
  47. data/ruby_libs/pav/icons/CVS/Repository +1 -0
  48. data/ruby_libs/pav/icons/CVS/Root +1 -0
  49. data/ruby_libs/pav/icons/alt_handle.xcf +0 -0
  50. data/ruby_libs/pav/icons/alt_handle.xpm +3832 -0
  51. data/ruby_libs/pav/icons/alt_handle_hover.xcf +0 -0
  52. data/ruby_libs/pav/icons/alt_handle_hover.xpm +3368 -0
  53. data/ruby_libs/pav/icons/alt_handle_pressed.xcf +0 -0
  54. data/ruby_libs/pav/icons/alt_handle_pressed.xpm +3828 -0
  55. data/ruby_libs/pav/icons/extract_curves/CVS/Entries +6 -0
  56. data/ruby_libs/pav/icons/extract_curves/CVS/Repository +1 -0
  57. data/ruby_libs/pav/icons/extract_curves/CVS/Root +1 -0
  58. data/ruby_libs/pav/icons/extract_curves/extract_curves-icon-rgb.ppm +14 -0
  59. data/ruby_libs/pav/icons/extract_curves/extract_curves-logo-rgb.gif +0 -0
  60. data/ruby_libs/pav/icons/extract_curves/extract_curves-logo-rgb.xcf +0 -0
  61. data/ruby_libs/pav/icons/extract_curves/trace_mark.xcf +0 -0
  62. data/ruby_libs/pav/icons/extract_curves/trace_mark.xpm +38 -0
  63. data/ruby_libs/pav/icons/handle.xcf +0 -0
  64. data/ruby_libs/pav/icons/handle.xpm +213 -0
  65. data/ruby_libs/pav/icons/next.xpm +29 -0
  66. data/ruby_libs/pav/icons/next_hover.xpm +315 -0
  67. data/ruby_libs/pav/icons/next_pressed.xpm +144 -0
  68. data/ruby_libs/pav/icons/prev.xpm +29 -0
  69. data/ruby_libs/pav/icons/prev_hover.xpm +315 -0
  70. data/ruby_libs/pav/icons/prev_pressed.xpm +144 -0
  71. data/ruby_libs/pav/icons/vnext.xpm +29 -0
  72. data/ruby_libs/pav/icons/vprev.xpm +29 -0
  73. data/ruby_libs/pav/numeric/CVS/Entries +2 -0
  74. data/ruby_libs/pav/numeric/CVS/Repository +1 -0
  75. data/ruby_libs/pav/numeric/CVS/Root +1 -0
  76. data/ruby_libs/pav/numeric/ext.rb +13 -0
  77. data/ruby_libs/pav/pav_find.rb +90 -0
  78. data/ruby_libs/pav/pix.rb +402 -0
  79. data/ruby_libs/pav/pix/CVS/Entries +11 -0
  80. data/ruby_libs/pav/pix/CVS/Repository +1 -0
  81. data/ruby_libs/pav/pix/CVS/Root +1 -0
  82. data/ruby_libs/pav/pix/aapix.rb +378 -0
  83. data/ruby_libs/pav/pix/blob.rb +543 -0
  84. data/ruby_libs/pav/pix/circle.rb +73 -0
  85. data/ruby_libs/pav/pix/contour.rb +644 -0
  86. data/ruby_libs/pav/pix/contour/CVS/Entries +5 -0
  87. data/ruby_libs/pav/pix/contour/CVS/Repository +1 -0
  88. data/ruby_libs/pav/pix/contour/CVS/Root +1 -0
  89. data/ruby_libs/pav/pix/contour/calc_situations.rb +9 -0
  90. data/ruby_libs/pav/pix/contour/carp_calc.rb +212 -0
  91. data/ruby_libs/pav/pix/contour/situations.dmp +0 -0
  92. data/ruby_libs/pav/pix/contour/situations.rb +21 -0
  93. data/ruby_libs/pav/pix/curve.rb +1508 -0
  94. data/ruby_libs/pav/pix/img_obj.rb +751 -0
  95. data/ruby_libs/pav/pix/node.rb +712 -0
  96. data/ruby_libs/pav/pix/node_grp.rb +853 -0
  97. data/ruby_libs/pav/pix/shaved_core.rb +534 -0
  98. data/ruby_libs/pav/pix/subpix.rb +212 -0
  99. data/ruby_libs/pav/rand_accessible.rb +16 -0
  100. data/ruby_libs/pav/rangeset.rb +63 -0
  101. data/ruby_libs/pav/search.rb +210 -0
  102. data/ruby_libs/pav/set.rb +20 -0
  103. data/ruby_libs/pav/string/CVS/Entries +6 -0
  104. data/ruby_libs/pav/string/CVS/Repository +1 -0
  105. data/ruby_libs/pav/string/CVS/Root +1 -0
  106. data/ruby_libs/pav/string/bits.rb +523 -0
  107. data/ruby_libs/pav/string/ext.rb +58 -0
  108. data/ruby_libs/pav/string/observable.rb +155 -0
  109. data/ruby_libs/pav/string/text.rb +79 -0
  110. data/ruby_libs/pav/string/words.rb +42 -0
  111. data/ruby_libs/pav/sub_arr.rb +55 -0
  112. data/ruby_libs/pav/traced_obj.rb +79 -0
  113. metadata +161 -0
@@ -0,0 +1,11 @@
1
+ /aapix.rb/1.1.1.1/Tue Nov 15 17:32:14 2005//
2
+ /blob.rb/1.1.1.1/Tue Nov 15 17:32:14 2005//
3
+ /circle.rb/1.1.1.1/Tue Nov 15 17:32:14 2005//
4
+ /contour.rb/1.1.1.1/Tue Nov 15 17:32:16 2005//
5
+ /curve.rb/1.1.1.1/Tue Nov 15 17:32:15 2005//
6
+ /img_obj.rb/1.1.1.1/Tue Nov 15 17:32:14 2005//
7
+ /node.rb/1.1.1.1/Tue Nov 15 17:32:16 2005//
8
+ /node_grp.rb/1.1.1.1/Tue Nov 15 17:32:13 2005//
9
+ /shaved_core.rb/1.1.1.1/Tue Nov 15 17:32:13 2005//
10
+ /subpix.rb/1.1.1.1/Tue Nov 15 17:32:16 2005//
11
+ D/contour////
@@ -0,0 +1 @@
1
+ extract-curves/ruby_libs/pav/pix
@@ -0,0 +1 @@
1
+ :ext:pavpen@rubyforge.org:/var/cvs/extract-curves
@@ -0,0 +1,378 @@
1
+ require 'pav/pix'
2
+ require 'pav/numeric/ext'
3
+
4
+ module PPix
5
+
6
+ class Xy
7
+ def a_eql?(pix, eps=2e-10)
8
+ (self.ay - pix.ay).abs < eps && (self.ax - pix.ax).abs < eps
9
+ end
10
+
11
+ alias_method :ax, :x
12
+ alias_method :ay, :y
13
+ alias_method :eql?, :a_eql?
14
+
15
+ def a_neighb8?(pix, eps=2e-10)
16
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
17
+ dx <= 1.0+eps && dy <= 1.0+eps
18
+ end
19
+
20
+ def a_neighb4?(pix, eps=2e-10)
21
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
22
+ (dx <= 1.0+eps && dy < eps) || (dy <= 1.0+eps && dx < eps)
23
+ end
24
+
25
+ def a_close_neighb?(pix, eps=2e-10)
26
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
27
+ (dx <= 0.5+eps && dy <= 0.5+eps)
28
+ end
29
+
30
+ def semi_neighb8?(pix, eps=2e-10)
31
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
32
+ dx <= 1.5+eps && dy <= 1.5+eps
33
+ end
34
+
35
+ def semi_neighb4?(pix, eps=2e-10)
36
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
37
+ (dx <= 1.5+eps && dy < eps) || (dy <= 1.5+eps && dx < eps)
38
+ end
39
+
40
+ def semi_close_neighb?(pix, eps=2e-10)
41
+ dx = (self.ax - pix.ax).abs; dy = (self.ay - pix.ay).abs
42
+ (dx <= 0.5-eps && dy <= 0.5-eps)
43
+ end
44
+
45
+ def a_yx(eps=1e-10)
46
+ [self.ay.scaled_round(eps), self.ax.scaled_round(eps)]
47
+ end
48
+
49
+ def a_leg(pix)
50
+ leg = (self.ay <=> pix.ay)
51
+ return leg if leg != 0
52
+ self.ax <=> pix.ax
53
+ end
54
+ end
55
+
56
+ class AaXy < Xy
57
+ attr_reader :ax, :ay
58
+
59
+ def self.a_yx(x, y, eps=1e-10)
60
+ [y.scaled_round(eps), x.scaled_round(eps)]
61
+ end
62
+
63
+ def initialize(ax, ay)
64
+ @ax = ax; @ay = ay
65
+ super(@ax.round, @ay.round)
66
+ end
67
+
68
+ def a_eql?(pix, eps=1e-10)
69
+ (@ay - pix.ay).abs < eps && (@ax - pix.ax).abs < eps
70
+ end
71
+
72
+ def hash
73
+ self.a_yx.hash
74
+ end
75
+
76
+ alias_method :eql?, :a_eql?
77
+
78
+ def to_s
79
+ "(#{@ax}, #{@ay})"
80
+ end
81
+
82
+ alias_method :coords_to_s, :to_s
83
+ end
84
+
85
+ class AaPix < AaXy
86
+ include MPixbufPix
87
+
88
+ def initialize(pixbuf, ax, ay)
89
+ @pixbuf = pixbuf
90
+ super(ax, ay)
91
+ end
92
+
93
+ def dup_aapix
94
+ AaPix.new(@pixbuf, self.ax, self.ay)
95
+ end
96
+ end
97
+
98
+ class AaXyCancelI < AaXy
99
+ attr_accessor :cancel, :semi_adj8_i, :semi_adj8_idxs
100
+
101
+ def initialize(ax, ay, cancel, semi_adj8_i, semi_adj8_idxs)
102
+ @cancel = cancel
103
+ @semi_adj8_i = semi_adj8_i
104
+ @semi_adj8_idxs = semi_adj8_idxs
105
+ super(ax, ay)
106
+ end
107
+ end
108
+
109
+ class AaXy
110
+ ADJ8_SEMI_ADJ8_SPEC = [
111
+ AaXyCancelI.new( 0,-0.5, [ 1,17,18], 3, [15,0, 1, 2, 3, 4, 5, 6, 7]),
112
+ AaXyCancelI.new( 0, -1, [], 3, [1, 2, 3, 4, 5]),
113
+ AaXyCancelI.new(-0.5, 0, [ 3,19,20], 7, [3, 4, 5, 6, 7, 8, 9,10,11]),
114
+ AaXyCancelI.new( -1, 0, [], 7, [5, 6, 7, 8, 9]),
115
+ AaXyCancelI.new( 0, 0.5, [ 5,21,22],11, [7, 8, 9,10,11,12,13,14,15]),
116
+ AaXyCancelI.new( 0, 1, [],11, [9,10,11,12,13]),
117
+ AaXyCancelI.new( 0.5, 0, [ 7,23,16],15, [11,12,13,14,15,0, 1, 2, 3]),
118
+ AaXyCancelI.new( 1, 0, [],15, [13,14,15,0, 1]),
119
+ AaXyCancelI.new( 0.5,-0.5, [ 9,16,17], 1, [15,0, 1, 2, 3]),
120
+ AaXyCancelI.new( 1, -1, [], 1, [0, 1, 2]),
121
+ AaXyCancelI.new(-0.5,-0.5, [11,18,19], 5, [3, 4, 5, 6, 7]),
122
+ AaXyCancelI.new( -1, -1, [], 5, [4, 5, 6]),
123
+ AaXyCancelI.new(-0.5, 0.5, [13,20,21], 9, [7, 8, 9,10,11]),
124
+ AaXyCancelI.new( -1, 1, [], 9, [8, 9,10]),
125
+ AaXyCancelI.new( 0.5, 0.5, [15,22,23],13, [11,12,13,14,15]),
126
+ AaXyCancelI.new( 1, 1, [],13, [12,13,14]),
127
+ AaXyCancelI.new( 1,-0.5, [], 0, [15,0, 1, 2]),
128
+ AaXyCancelI.new( 0.5, -1, [], 2, [0, 1, 2, 3]),
129
+ AaXyCancelI.new(-0.5, -1, [], 4, [3, 4, 5, 6]),
130
+ AaXyCancelI.new( -1,-0.5, [], 6, [4, 5, 6, 7]),
131
+ AaXyCancelI.new( -1, 0.5, [], 8, [7, 8, 9,10]),
132
+ AaXyCancelI.new(-0.5, 1, [],10, [8, 9,10,11]),
133
+ AaXyCancelI.new( 0.5, 1, [],12, [11,12,13,14]),
134
+ AaXyCancelI.new( 1, 0.5, [],14, [12,13,14,15]),
135
+ ]
136
+
137
+ tmp1 = []; 16.times { tmp1 << [] }
138
+ ADJ8_SEMI_ADJ8_SPEC.each { |a| tmp1[a.semi_adj8_i] << a }
139
+ tmp2 = []
140
+ tmp1.each { |m| tmp2 << m.first }; tmp1.each { |m| tmp2 << m.last }
141
+ SEMI_ADJ8 = tmp2
142
+
143
+ def self.semi_neighbs8_contig_arr_get_segs(arr)
144
+ #$PDbgLog.sig_call(self)
145
+ #$PDbgLog.print_msg "ngbs: #{(0...arr.length).find_all { |i|
146
+ # arr[i]}.join(" ")}: "
147
+ end_i = -1
148
+ while !arr[end_i]
149
+ end_i -= 1
150
+ if end_i < -15
151
+ #$PDbgLog.sig_return('Empty.')
152
+ return []
153
+ end
154
+ end
155
+ end_i += 17
156
+ #$PDbgLog.print_msg "end_i=#{end_i} "
157
+ start_p = 0
158
+ start_p += 1 while !arr[start_p]
159
+ #$PDbgLog.print_msg "start_p=#{start_p} "
160
+ if start_p >= end_i
161
+ #$PDbgLog.sig_return("[[#{start_p}, #{start_p+1}]]")
162
+ return [[start_p, start_p+1]]
163
+ end
164
+ res = []
165
+ i = start_p + 1
166
+ loop do
167
+ i += 1 while arr[i]
168
+ res << [start_p, i]
169
+ break if i >= end_i
170
+ i += 1 while !arr[i]
171
+ start_p = i
172
+ end
173
+ if res.length > 1 && res[0][0] % 16 == res.last[1] % 16
174
+ len = res.last[1] - res.last[0]
175
+ res[0][0] -= len
176
+ res.pop
177
+ end
178
+ #$PDbgLog.sig_return(res.inspect)
179
+ res
180
+ end
181
+
182
+ def self.semi_neighbs8_arr_get_segs(ngbs)
183
+ #$PDbgLog.sig_call(self)
184
+ #$PDbgLog.print_msg "ngbs: #{(0...ngbs.length).find_all { |i|
185
+ # ngbs[i]}.join(" ")}: "
186
+ end_p = start_p = -1
187
+ if ngbs[start_p]
188
+ while ngbs[end_p += 1] || end_p % 4 < 3
189
+ if end_p + 1 >= 16
190
+ #$PDbgLog.sig_return('whole circle.')
191
+ return [[0, 0]]
192
+ end
193
+ end
194
+ while ngbs[start_p-=1] || start_p % 4 < 3
195
+ end
196
+ end_i = 16 + start_p
197
+ start_p += 1
198
+ start_p += 1 while !ngbs[start_p]
199
+ end_p -= 1 while !ngbs[end_p-1]
200
+ res = [[start_p, end_p]]
201
+ else
202
+ res = []
203
+ end_i = 15
204
+ end
205
+ loop do
206
+ start_p = end_p + 1
207
+ while !ngbs[start_p]
208
+ start_p += 1
209
+ if start_p >= end_i
210
+ #$PDbgLog.sig_return(res.inspect)
211
+ return res
212
+ end
213
+ end
214
+ end_p = start_p + 1
215
+ end_p += 1 while (ngbs[end_p] || end_p % 4 < 3) &&
216
+ end_p < end_i
217
+ end_p -= 1 while !ngbs[end_p-1]
218
+ res << [start_p, end_p]
219
+ end
220
+ end
221
+
222
+ def self.semi_neighbs8_with_arr_get_segs(arr, ngbs)
223
+ $PDbgLog.sig_call(self)
224
+ $PDbgLog.print_msg "ngbs: #{(0...arr.length).find_all { |i|
225
+ arr[i]}.collect{|i|ngbs[arr[i]].coords_to_s+"[#{i}]"}.join(" "
226
+ )}: " #"
227
+ end_i = -1
228
+ while !arr[end_i]
229
+ end_i -= 1
230
+ if end_i < -15
231
+ $PDbgLog.sig_return('Empty.')
232
+ return []
233
+ end
234
+ end
235
+ end_i += 16
236
+ $PDbgLog.print_msg "end_i=#{end_i} "
237
+ start_p = 0
238
+ start_p += 1 while !arr[start_p]
239
+ $PDbgLog.print_msg "start_p=#{start_p} "
240
+ if start_p >= end_i
241
+ $PDbgLog.sig_return("[[#{start_p}, #{start_p+1}]]")
242
+ return [[start_p, start_p+1]]
243
+ end
244
+ last_pix = ngbs[arr[start_p]]
245
+ res = []
246
+ end_p = i = start_p
247
+ loop do
248
+ i += 1
249
+ i += 1 while !arr[i]
250
+ $PDbgLog.print_msg "i=#{i} "
251
+ if last_pix.a_neighb8?(ngbs[arr[i]])
252
+ if (end_p = i) >= end_i
253
+ if res.empty?
254
+ # if arr.compact.length > 2 && ngbs[arr[
255
+ # start_p]].a_neighb8?(ngbs[arr[i]])
256
+ # $PDbgLog.print_msg("Full circle ")
257
+ # res << [0, 0]
258
+ # else
259
+ res << [start_p, end_p+1]
260
+ # end
261
+ elsif ngbs[arr[res[0][0]]].a_neighb8?(
262
+ ngbs[arr[i]])
263
+ res[0][0] = start_p - 16
264
+ else
265
+ res << [start_p, end_p+1]
266
+ end
267
+ $PDbgLog.sig_return(res.inspect)
268
+ return res
269
+ end
270
+ else
271
+ $PDbgLog.puts_msg "seg[#{start_p},#{end_p+1})"
272
+ res << [start_p, end_p+1]
273
+ if i >= end_i
274
+ if ngbs[arr[res[0][0]]].neighb8?(ngbs[arr[i]])
275
+ res[0][0] = i - 16
276
+ else
277
+ res << [i, i+1]
278
+ end
279
+ $PDbgLog.sig_return(res.inspect)
280
+ return res
281
+ end
282
+ start_p = i
283
+ last_pix = ngbs[arr[start_p]]
284
+ end
285
+ last_pix = ngbs[arr[i]]
286
+ end
287
+ end
288
+ end
289
+
290
+ class Xy
291
+ def semi_neighbs8_arr(neighbs)
292
+ #$PDbgLog.sig_call(self)
293
+ #$PDbgLog.print_msg self.coords_to_s + ", neighbs: #{PixbufPix.
294
+ # path_to_s(neighbs)}: "
295
+ warn("#{self.class.name}.semi_neighbs8_arr: " +
296
+ "Non-neighbors given to #{self.coords_to_s}: #{PixbufPix.
297
+ path_to_s(neighbs)}") if neighbs.detect { |a|
298
+ !a.a_neighb8?(self) }
299
+ res = Array.new(16)
300
+ ranks = Array.new(16, 100)
301
+ neighbs.each_with_index { |ngb, ngb_i|
302
+ ax = (ngb.ax - self.ax).scaled_round(0.5)
303
+ ay = (ngb.ay - self.ay).scaled_round(0.5)
304
+ AaXy::ADJ8_SEMI_ADJ8_SPEC.each_with_index{|spec,rk|
305
+ if ay == spec.ay && ax == spec.ax
306
+ if ranks[spec.semi_adj8_i] > rk
307
+ res[spec.semi_adj8_i] = ngb_i
308
+ ranks[spec.semi_adj8_i] = rk
309
+ end
310
+ break
311
+ end
312
+ }
313
+ }
314
+ #$PDbgLog.sig_return(res.inspect)
315
+ res
316
+ end
317
+
318
+ def semi_neighbs8_contig_arr(neighbs)
319
+ #$PDbgLog.sig_call(self)
320
+ #$PDbgLog.print_msg self.coords_to_s + ", neighbs: #{Xy.
321
+ # path_to_s(neighbs)}: "
322
+ warn("#{self.class.name}.semi_neighbs8_contig_arr: " +
323
+ "Non-neighbors given to #{self.coords_to_s}: #{PixbufPix.
324
+ path_to_s(neighbs)}") if neighbs.detect { |a|
325
+ !a.semi_neighb8?(self) }
326
+ res = Array.new(16)
327
+ ranks = Array.new(16, 100)
328
+ neighbs.each_with_index { |ngb, ngb_i|
329
+ ax = (ngb.ax - self.ax).scaled_round(0.5)
330
+ ay = (ngb.ay - self.ay).scaled_round(0.5)
331
+ AaXy::ADJ8_SEMI_ADJ8_SPEC.each_with_index{|spec,rk|
332
+ if ay == spec.ay && ax == spec.ax
333
+ spec.semi_adj8_idxs.each { |semi_adj8_i|
334
+ if ranks[semi_adj8_i] > rk
335
+ res[semi_adj8_i] = ngb_i
336
+ ranks[semi_adj8_i] = rk
337
+ end
338
+ }
339
+ break
340
+ end
341
+ }
342
+ }
343
+ #$PDbgLog.sig_return(res.inspect)
344
+ res
345
+ end
346
+ end
347
+
348
+ module MPixbufPix
349
+ def adj8_semi_adj8_each
350
+ #$PDbgLog.sig_call(self)
351
+ pixbuf = self.pixbuf; ax = self.ax; ay = self.ay
352
+ #$PDbgLog.print_msg self.to_s + ",ax=#{ax},ay=#{ay}: "
353
+ cancelled = []
354
+ AaXy::ADJ8_SEMI_ADJ8_SPEC.each_with_index { |a, ai|
355
+ next if cancelled[ai]
356
+ if yield(AaPix.new(pixbuf, ax+a.ax, ay+a.ay))
357
+ a.cancel.each { |cnc| cancelled[cnc] = true }
358
+ end
359
+ }
360
+ #$PDbgLog.sig_return('done.')
361
+ end
362
+
363
+ def semi_adj8_incl_set_each(incl_set)
364
+ i = -1
365
+ self.adj8_semi_adj8_each { |ngb|
366
+ i += 1
367
+ if incl_set.include?(ngb)
368
+ yield(ngb, AaXy::ADJ8_SEMI_ADJ8_SPEC.at(i).
369
+ semi_adj8_i)
370
+ true
371
+ else
372
+ false
373
+ end
374
+ }
375
+ end
376
+ end
377
+
378
+ end
@@ -0,0 +1,543 @@
1
+ require 'set'
2
+ require 'pav/pix'
3
+ require 'pav/cache'
4
+ require 'pav/search'
5
+ require 'pav/attr_cache'
6
+ require 'pav/pix/circle'
7
+ require 'pav/pix/contour'
8
+ require 'pav/pix/img_obj'
9
+ require 'pav/string/bits'
10
+
11
+ module PPix
12
+
13
+ class Pixbuf
14
+ def blob_id_arr
15
+ if !@blob_id_arr
16
+ #@blob_id_arr = IntBitPlaneArr.new(
17
+ # "", self.width * self.height)
18
+ @blob_id_arr = Array.new(self.picture_width *
19
+ self.picture_height, 0)
20
+ end
21
+ @blob_id_arr
22
+ end
23
+
24
+ def blob_id_max
25
+ @blob_id_max = 0 if !@blob_id_max
26
+ @blob_id_max
27
+ end
28
+
29
+ def blob_free_ids
30
+ @blob_free_ids = [] if !@blob_free_ids
31
+ @blob_free_ids
32
+ end
33
+
34
+ def blob_id2ref_arr
35
+ @blob_id2ref_arr = [] if !@blob_id2ref_arr
36
+ @blob_id2ref_arr
37
+ end
38
+
39
+ def set_blob_id(x, y, id)
40
+ @blob_id_max = id if id > self.blob_id_max
41
+ self.blob_id_arr[y * self.picture_width + x] = id
42
+ end
43
+
44
+ def add_blob(blob)
45
+ #$PDbgLog.sig_call(self)
46
+ if self.blob_free_ids.empty?
47
+ #$PDbgLog.print_msg "Using blob_id_max: "
48
+ @blob_id_max = id = self.blob_id_max + 1
49
+ self.blob_id2ref_arr << blob
50
+ else
51
+ #$PDbgLog.print_msg "Using free id: "
52
+ #$PDbgLog.print_msg "#{self.blob_id2ref_arr.collect{|bl|
53
+ # bl.blob_id}.inspect}: "
54
+ id = self.blob_free_ids.shift
55
+ self.blob_id2ref_arr[id-1,0] = [blob]
56
+ #$PDbgLog.print_msg "#{self.blob_id2ref_arr.collect{|bl|
57
+ # bl.blob_id}.inspect}: "
58
+ end
59
+ #$PDbgLog.sig_return(id)
60
+ return id
61
+ end
62
+
63
+ def unregister_blob_id(id)
64
+ return if id == 0
65
+ if id == self.blob_id_max
66
+ #@bpc = self.blob_id_arr.bit_planes_cnt
67
+ #if self.blob_id_max & (self.blob_id_max-1) == 0
68
+ # bpc -= 1
69
+ #end
70
+ self.blob_id2ref_arr.pop
71
+ @blob_id_max = self.blob_id_max-1
72
+ while self.blob_free_ids.last == self.blob_id_max
73
+ #if self.blob_id_max & (self.blob_id_max-1) == 0
74
+ # bpc -= 1
75
+ #end
76
+ @blob_id_max -= self.blob_id_max-1
77
+ self.blob_free_ids.pop
78
+ self.blob_id2ref_arr.pop
79
+ end
80
+ #self.blob_id_arr.bit_planes_cnt = bpc
81
+ else
82
+ if (pos = PSearch.bsearch(self.blob_free_ids, id)) >= 0
83
+ return # blob ID not currently registered
84
+ end
85
+ self.blob_free_ids[-pos-1,0] = id
86
+ self.blob_id2ref_arr[id+pos,1] = nil
87
+ end
88
+ end
89
+
90
+ def blob_id_at(x, y)
91
+ self.blob_id_arr.at(y * self.picture_width + x)
92
+ end
93
+
94
+ def blob_id_at?(x, y, id)
95
+ #self.blob_id_arr.at?(y * self.width + x, id)
96
+ self.blob_id_arr.at(y * self.picture_width + x) == id
97
+ end
98
+
99
+ def blob_at(x, y)
100
+ return nil if (id=self.blob_id_arr[y*self.picture_width+x]) == 0
101
+ self.blob_id2ref_arr[id+PSearch.bsearch(self.blob_free_ids,id)]
102
+ end
103
+
104
+ def blob_by_id(id)
105
+ return nil if id < 1
106
+ return nil if (j = PSearch.bsearch(self.blob_free_ids,id)) >= 0
107
+ self.blob_id2ref_arr[id + j]
108
+ end
109
+
110
+ def delete_blob(spec)
111
+ if spec.kind_of?(Integer)
112
+ blob = self.get_blob_by_id(spec)
113
+ else
114
+ blob = spec
115
+ end
116
+ return unless blob
117
+ blob.each { |pix| self.set_blob_id(pix.x, pix.y, 0) }
118
+ self.unregister_blob_id(blob.blob_id)
119
+ end
120
+ end
121
+
122
+ class PixbufPix
123
+ def blob(contour_geometry=8, external_contours=true,
124
+ incorp_subblob_max_size=0, min_outer_lp_length=0, &get_neighbs)
125
+ b = self.pixbuf.blob_at(self.x, self.y)
126
+ return b if b
127
+ $PDbgLog.sig_call(self)
128
+ #$PDbgLog.print_msg "Creating blob for #{self.coords_to_s}: "
129
+ id = self.pixbuf.add_blob(b = Blob.new(self.pixbuf,
130
+ contour_geometry, external_contours,
131
+ min_outer_lp_length, &get_neighbs))
132
+ b.blob_id = id
133
+ b.rebuild(self, incorp_subblob_max_size)
134
+ #$PDbgLog.puts_msg "done."
135
+ $PDbgLog.sig_return
136
+ b
137
+ end
138
+ end
139
+
140
+ class BlobYxSet
141
+ attr_reader :blob, :blob_id, :pixbuf
142
+
143
+ def initialize(blob)
144
+ @blob = blob
145
+ @blob_id = @blob.blob_id
146
+ @pixbuf = @blob.pixbuf
147
+ end
148
+
149
+ def include?(yx)
150
+ @pixbuf.blob_id_at?(yx.at(1), yx.at(0), @blob_id)
151
+ end
152
+
153
+ alias_method :member?, :include?
154
+
155
+ def length
156
+ @blob.length
157
+ end
158
+ end
159
+
160
+ class Blob < ImageObject
161
+ def Blob.local_top_left(start_pix, diag_conn=true)
162
+ moved, clr = true, start_pix.color_id
163
+ pixbuf, x, y = start_pix.pixbuf, start_pix.x, start_pix.y
164
+ while moved
165
+ moved = false
166
+ while x > 0 && pixbuf[y][x-1].color_id == clr
167
+ x -= 1
168
+ moved = true
169
+ end
170
+ while y > 0 && pixbuf[y-1][x].color_id == clr
171
+ y -= 1
172
+ moved = true
173
+ end
174
+ while diag_conn && x > 0 && y > 0 &&
175
+ pixbuf[y-1][x-1].color_id == clr
176
+ x -= 1
177
+ y -= 1
178
+ moved = true
179
+ end
180
+ end
181
+ return pixbuf[y][x]
182
+ end
183
+
184
+ def self.mk_rgb_get_neighbs_proc(cmp_pix_to, toler_r, toler_g, toler_b)
185
+ case cmp_pix_to.downcase
186
+ when "blob 8 neighbors' colors"
187
+ #get_neighbs_proc =
188
+ proc { |pix|
189
+ pix.adj_fuzzy_rgb_cocolor(PPix::PixbufPix::ADJACENT8,
190
+ toler_r, toler_g, toler_b).
191
+ find_all { |p| p.pixbuf.blob_id_at?(p.x, p.y, 0) }
192
+ }
193
+ when "blob 4 neighbors' colors"
194
+ #get_neighbs_proc =
195
+ proc { |pix|
196
+ pix.adj_fuzzy_rgb_cocolor(PPix::PixbufPix::ADJACENT4,
197
+ toler_r, toler_g, toler_b).
198
+ find_all { |p| p.pixbuf.blob_id_at?(p.x, p.y, 0) }
199
+ }
200
+ when "blob mean color"
201
+ tot_r = tot_g = tot_b = blen = 0
202
+ #get_neighbs_proc =
203
+ proc { |pix|
204
+ pix.adj8.find_all { |p| r,g,b = *p.color_rgb
205
+ if p.pixbuf.blob_id_at?(p.x, p.y, 0) &&
206
+ (tot_r - r*blen).abs <= toler_r*blen &&
207
+ (tot_g - g*blen).abs <= toler_g*blen &&
208
+ (tot_b - b*blen).abs <= toler_b*blen
209
+ tot_r += r; tot_g += g; tot_b += b; blen += 1
210
+ true
211
+ else
212
+ false
213
+ end
214
+ }
215
+ }
216
+ else
217
+ if cmp_pix_to.kind_of?(Array) && cmp_pix_to.length == 2 &&
218
+ cmp_pix_to[0] == "mean blob color in a circle" &&
219
+ (rad = cmp_pix_to[1]).kind_of?(Numeric)
220
+ #get_neighbs_proc =
221
+ proc { |pix|
222
+ pix.adj8.find_all { |p| mr = mg = mb = mlen = 0
223
+ p.pixbuf.blob_id_at?(p.x, p.y, 0) &&
224
+ (PPix::AFilledCircle(p.x, p.y, rad) { |x,y|
225
+ pp = p.pixbuf.get_pix(x,y)
226
+ unless pp.virtual?
227
+ r, g, b = *pp.color_rgb
228
+ mr += r; mg += g; mb += b; mlen += 1
229
+ end
230
+ }
231
+ r,g,b = *p.color_rgb
232
+ (mr - r*mlen).abs <= toler_r*mlen &&
233
+ (mg - g*mlen).abs <= toler_g*mlen &&
234
+ (mb - b*mlen).abs <= toler_b*mlen)
235
+ }
236
+ }
237
+ else
238
+ raise ArgumentError, "Invalid cmp_pix_to argument: #{
239
+ cmp_pix_to.inspect}!"
240
+ end
241
+ end
242
+ end
243
+
244
+ def self.mk_hsv_get_neighbs_proc(cmp_pix_to, toler_h, toler_s, toler_v)
245
+ case cmp_pix_to.downcase
246
+ when "blob 8 neighbors' colors"
247
+ #get_neighbs_proc =
248
+ proc { |pix|
249
+ pix.adj_fuzzy_hsv_cocolor(PPix::PixbufPix::ADJACENT8,
250
+ toler_h, toler_s, toler_v).
251
+ find_all { |p| p.pixbuf.blob_id_at?(p.x, p.y, 0) }
252
+ }
253
+ when "blob 4 neighbors' colors"
254
+ #get_neighbs_proc =
255
+ proc { |pix|
256
+ pix.adj_fuzzy_hsv_cocolor(PPix::PixbufPix::ADJACENT4,
257
+ toler_h, toler_s, toler_v).
258
+ find_all { |p| p.pixbuf.blob_id_at?(p.x, p.y, 0) }
259
+ }
260
+ when "blob mean color"
261
+ tot_h = tot_s = tot_v = blen = 0
262
+ #get_neighbs_proc =
263
+ proc { |pix|
264
+ pix.adj8.find_all { |p| h,s,v = *p.color_hsv
265
+ if p.pixbuf.blob_id_at?(p.x, p.y, 0) &&
266
+ (tot_h - h*blen).abs <= toler_h*blen &&
267
+ (tot_s - s*blen).abs <= toler_s*blen &&
268
+ (tot_v - v*blen).abs <= toler_v*blen
269
+ tot_h += h; tot_s += s; tot_v += v; blen += 1
270
+ true
271
+ else
272
+ false
273
+ end
274
+ }
275
+ }
276
+ else
277
+ if cmp_pix_to.kind_of?(Array) && cmp_pix_to.length == 2 &&
278
+ cmp_pix_to[0] == "mean blob color in a circle" &&
279
+ (rad = cmp_pix_to[1]).kind_of?(Numeric)
280
+ #get_neighbs_proc =
281
+ proc { |pix|
282
+ pix.adj8.find_all { |p| mh = ms = mv = mlen = 0
283
+ p.pixbuf.blob_id_at?(p.x, p.y, 0) &&
284
+ (PPix::AFilledCircle(p.x, p.y, rad) { |x,y|
285
+ pp = p.pixbuf.get_pix(x,y)
286
+ unless pp.virtual?
287
+ h, s, v = *pp.color_hsv
288
+ mh += h; ms += s; mv += v; mlen += 1
289
+ end
290
+ }
291
+ h,s,v = *p.color_hsv
292
+ (mh - h*mlen).abs <= toler_h*mlen &&
293
+ (ms - s*mlen).abs <= toler_s*mlen &&
294
+ (mv - v*mlen).abs <= toler_v*mlen)
295
+ }
296
+ }
297
+ else
298
+ raise ArgumentError, "Invalid cmp_pix_to argument: #{
299
+ cmp_pix_to.inspect}!"
300
+ end
301
+ end
302
+ end
303
+
304
+ def self.mk_get_neighbs_proc(clr_scheme, cmp_pix_to, toler_1, toler_2,
305
+ toler_3)
306
+ case clr_scheme.downcase
307
+ when 'rgb'
308
+ self.mk_rgb_get_neighbs_proc(cmp_pix_to, toler_1,
309
+ toler_2, toler_3)
310
+ when 'hsv'
311
+ self.mk_hsv_get_neighbs_proc(cmp_pix_to, toler_1,
312
+ toler_2, toler_3)
313
+ else
314
+ raise ArgumentError, "Invalid clr_scheme, must be " +
315
+ "'rgb' or 'hsv': #{clr_scheme}!"
316
+ end
317
+ end
318
+
319
+ attr_accessor :blob_id
320
+ attr_reader :pixbuf, :top_left_pix, :get_neighbs,
321
+ :external_contours, :min_outer_lp_length, :contour_geometry
322
+
323
+ self.img_obj_add_sub_obj :contour_carpets
324
+
325
+ def initialize(pixbuf, contour_geometry=8, external_contours=true,
326
+ min_outer_lp_length=0, &get_neighbs)
327
+ @blob_id = nil
328
+ @pixbuf = pixbuf
329
+ @contour_geometry = contour_geometry
330
+ @external_contours = external_contours
331
+ @min_outer_lp_length = min_outer_lp_length
332
+ @pix_neighbs_cache = PCache.new
333
+ if block_given?
334
+ @get_neighbs = get_neighbs
335
+ else
336
+ @get_neighbs = proc { |pix| pix.adj_cocolor8.find_all {
337
+ |px| px.pixbuf.blob_id_at?(px.x, px.y, 0) }}
338
+ end
339
+ @contour_carpets = nil
340
+ super()
341
+ end
342
+
343
+ def add(bpix)
344
+ self << bpix
345
+ if @top_left_pix
346
+ @top_left_pix = bpix if bpix.y < @top_left_pix.y ||
347
+ (bpix.y == @top_left_pix.y &&
348
+ bpix.x < @top_left_pix.x)
349
+ else
350
+ @top_left_pix = bpix
351
+ end
352
+ @pixbuf.set_blob_id(bpix.x, bpix.y, @blob_id)
353
+ end
354
+
355
+ def rebuild(start_pix, incorp_subblob_max_size)
356
+ $PDbgLog.sig_call(self)
357
+ @top_left_pix = start_pix
358
+ $PDbgLog.print_msg "id=#{@blob_id}, start pix#{@top_left_pix.
359
+ coords_to_s}: "
360
+ self.clear
361
+ self << @top_left_pix
362
+ @pixbuf.set_blob_id(@top_left_pix.x, @top_left_pix.y, @blob_id)
363
+ tail = []
364
+ self.pix_known_neighbs(@top_left_pix).each { |p|
365
+ next unless @pixbuf.blob_id_at(p.x, p.y) == 0
366
+ @pixbuf.set_blob_id(p.x, p.y, @blob_id)
367
+ @top_left_pix = p if p.y < @top_left_pix.y ||
368
+ (p.y == @top_left_pix.y &&
369
+ p.x < @top_left_pix.x)
370
+ tail.push(p)
371
+ self << p
372
+ }
373
+ if tail.empty?
374
+ $PDbgLog.sig_return
375
+ return
376
+ end
377
+ $PDbgLog.new_progr
378
+ $PDbgLog.progr.show_as = "/ pixels"
379
+ while !tail.empty?
380
+ 500.times { # Don't update the progress bar for every pixel
381
+ break if tail.empty?
382
+ pix = tail.shift
383
+ self.pix_known_neighbs(pix).each { |p|
384
+ next unless @pixbuf.blob_id_at(p.x, p.y) == 0
385
+ @pixbuf.set_blob_id(p.x, p.y, @blob_id)
386
+ @top_left_pix = p if p.y < @top_left_pix.y ||
387
+ (p.y == @top_left_pix.y &&
388
+ p.x < @top_left_pix.x)
389
+ tail.push(p)
390
+ self << p
391
+ }
392
+ }
393
+ $PDbgLog.progr.progr_units = self.length
394
+ end
395
+ if incorp_subblob_max_size > 0
396
+ #$PDbgLog.print_msg "Incorporating subblobs: "
397
+ # Skip the outer contour carpet which is first:
398
+ i = 0
399
+ carps = self.contour_carpets
400
+ while (i += 1) < carps.length
401
+ ctr = carps[i]
402
+ #$PDbgLog.print_msg "Carpet #{ctr.length} pix "
403
+ next if ctr.length > incorp_subblob_max_size ||
404
+ ctr.loop_carpet.internal_blob.length >
405
+ incorp_subblob_max_size
406
+ ctr.loop_carpet.internal_blob.each { |p|
407
+ next unless @pixbuf.blob_id_at(p.x, p.y) == 0
408
+ @pixbuf.set_blob_id(p.x, p.y, @blob_id)
409
+ self << p
410
+ }
411
+ $PDbgLog.progr.progr_units = self.length
412
+ carps.delete_at(i)
413
+ i -= 1
414
+ end
415
+ end
416
+ $PDbgLog.sig_return(".")
417
+ end
418
+
419
+ def pix_known_neighbs(pix)
420
+ self.get_neighbs.call(pix)
421
+ end
422
+
423
+ def pix_neighbs(pix)
424
+ if (tmp = @pix_neighbs_cache[pix.yx])
425
+ return tmp
426
+ end
427
+ @pix_neighbs_cache[pix.yx] = pix.adj8.find_all { |ngb|
428
+ pix.pixbuf.blob_id_at?(ngb.x, ngb.y, self.blob_id) }
429
+ end
430
+
431
+ def contour_carpets
432
+ return @contour_carpets if @contour_carpets
433
+ $PDbgLog.sig_call(self)
434
+ @contour_carpets = []
435
+ excl_set = {}
436
+ # Ensure the first contour carpet is the outer one.
437
+ if @external_contours
438
+ if @contour_geometry == 8
439
+ cneighbs_proc = proc { |cngb_pix|
440
+ nres = []
441
+ Xy::ADJACENT8.each { |cngb_adj|
442
+ x = cngb_pix.x + cngb_adj.x
443
+ y = cngb_pix.y + cngb_adj.y
444
+ next unless (x < 0 || x >= @pixbuf.picture_width ||
445
+ y < 0 || y >= @pixbuf.picture_height ||
446
+ !@pixbuf.blob_id_at?(x, y, @blob_id)) &&
447
+ Xy::ADJACENT4.detect { |cngb_ad|
448
+ (x1=x+cngb_ad.x) >= 0 &&
449
+ x1 < @pixbuf.picture_width &&
450
+ (y1=y+cngb_ad.y) >= 0 &&
451
+ y1 < @pixbuf.picture_height &&
452
+ @pixbuf.blob_id_at?(x1, y1, @blob_id)
453
+ }
454
+ nres << @pixbuf.get_pix(x,y)
455
+ }
456
+ nres
457
+ }
458
+ else
459
+ cneighbs_proc = proc { |px|
460
+ nres = []
461
+ Xy::ADJACENT4.each { |adj|
462
+ x = px.x + adj.x; y = px.y + adj.y
463
+ next unless (x < 0 || x >= @pixbuf.picture_width ||
464
+ y < 0 || y >= @pixbuf.picture_height ||
465
+ !@pixbuf.blob_id_at?(x, y, @blob_id)) &&
466
+ PixbufPix::ADJACENT8.detect { |ad|
467
+ (x1=x+ad.x) >= 0 && x1 < @pixbuf.picture_width &&
468
+ (y1=y+ad.y) >= 0 && y1 < @pixbuf.picture_height &&
469
+ @pixbuf.blob_id_at?(x1, y1, @blob_id)
470
+ }
471
+ nres << @pixbuf.get_pix(x,y)
472
+ }
473
+ nres
474
+ }
475
+ end
476
+ @contour_carpets << ContourCarpet.new(
477
+ @pixbuf.get_pix(@top_left_pix.x,@top_left_pix.y-1),self,
478
+ @contour_geometry, @external_contours,
479
+ @min_outer_lp_length, &cneighbs_proc)
480
+ @contour_carpets.last.each{|p| excl_set[p.yx] = true}
481
+ self.each { |pix|
482
+ Xy::ADJACENT4.each { |adj|
483
+ x = pix.x + adj.x; y = pix.y + adj.y
484
+ if (x < 0 || x >= @pixbuf.picture_width ||
485
+ y < 0 || y >= @pixbuf.picture_height ||
486
+ !@pixbuf.blob_id_at?(x, y, @blob_id)) &&
487
+ !excl_set.include?([y, x])
488
+ @contour_carpets << ContourCarpet.new(
489
+ @pixbuf.get_pix(x, y), self,
490
+ @contour_geometry, @external_contours,
491
+ &cneighbs_proc)
492
+ @contour_carpets.last.each { |p|
493
+ excl_set[p.yx] = true }
494
+ end
495
+ }
496
+ }
497
+ else
498
+ @contour_carpets << ContourCarpet.new(@top_left_pix, self,
499
+ @contour_geometry, @external_contours,
500
+ @min_outer_lp_length)
501
+ @contour_carpets.last.each{|p| excl_set[p.yx] = true}
502
+ self.each { |pix|
503
+ #$PDbgLog.print_msg "pix#{pix.coords_to_s}.border4?=#{
504
+ # pix.border4?} "
505
+ if !excl_set.include?(pix.yx) && (
506
+ (adj4 = pix.adj4).length < 4 || adj4.detect { |a|
507
+ !a.pixbuf.blob_id_at?(a.x, a.y, self.blob_id) })
508
+ #$PDbgLog.puts_msg "ContourCarpet"
509
+ @contour_carpets << ContourCarpet.new(pix, self,
510
+ @contour_geometry, @external_contours)
511
+ @contour_carpets.last.each { |p|
512
+ excl_set[p.yx] = true }
513
+ end
514
+ }
515
+ end
516
+ $PDbgLog.sig_return
517
+ @contour_carpets
518
+ end
519
+
520
+ def delete_contour_carpets
521
+ @contour_carpets = nil
522
+ end
523
+
524
+ def to_s
525
+ "<Blob:%x: " % self.object_id + "pixbuf:#{@pixbuf}, @blob_id=#{
526
+ @blob_id}, @top_left_pix#{@top_left_pix.coords_to_s
527
+ }, @get_neighbs:#{@get_neighbs}>"
528
+ end
529
+
530
+ alias_method :inspect, :to_s
531
+
532
+ def img_obj_yx_set
533
+ BlobYxSet.new(self)
534
+ end
535
+
536
+ def clear_cache
537
+ @pix_neighbs_cache.clear
538
+ end
539
+
540
+ cache_attr! :img_obj_yx_set
541
+ end
542
+
543
+ end