extract_curves 0.0.1

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.
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