1 // This is mul/vil3d/tests/test_algo_find_blobs.cxx
2 
3 #include <iostream>
4 #include <map>
5 #ifdef _MSC_VER
6 #  include "vcl_msvc_warnings.h"
7 #endif
8 #include "testlib/testlib_test.h"
9 #include <vil3d/algo/vil3d_find_blobs.h>
10 #include <vil3d/vil3d_crop.h>
11 #include <vil3d/vil3d_print.h>
12 #include "vgl/vgl_point_3d.h"
13 
14 
15 //========================================================================
16 // Find the first voxel (in raster scan order) of each blob in a blob image
17 //========================================================================
first_voxel_of_blobs(const vil3d_image_view<unsigned> & img,std::map<unsigned,vgl_point_3d<unsigned>> & blob_vox)18 static void first_voxel_of_blobs(const vil3d_image_view<unsigned>& img,
19                                  std::map<unsigned, vgl_point_3d<unsigned> >& blob_vox)
20 {
21   blob_vox.clear();
22 
23   for (unsigned k=0; k<img.nk(); ++k)
24   {
25     for (unsigned j=0; j<img.nj(); ++j)
26     {
27       for (unsigned i=0; i<img.ni(); ++i)
28       {
29         unsigned label = img(i,j,k);
30         if (blob_vox.find(label)==blob_vox.end())
31         {
32           blob_vox[label] = vgl_point_3d<unsigned>(i,j,k);
33         }
34       }
35     }
36   }
37 }
38 
39 
40 //========================================================================
41 // Find blobs and check against true answer
42 //========================================================================
test_blob_image(const vil3d_image_view<bool> & src,const vil3d_find_blob_connectivity conn,const vil3d_image_view<unsigned> & tru,const std::string & testname)43 static void test_blob_image(const vil3d_image_view<bool>& src,
44                             const vil3d_find_blob_connectivity conn,
45                             const vil3d_image_view<unsigned>& tru,
46                             const std::string& testname)
47 {
48   vil3d_image_view<unsigned> dst;
49   vil3d_find_blobs(src, conn, dst);
50 
51   // Check first voxels
52   std::map<unsigned, vgl_point_3d<unsigned> > blob_vox;
53   first_voxel_of_blobs(src, blob_vox);
54 
55 
56   // NB FIX THIS: labels need not match but must have 1:1 relationship...
57   TEST(testname.c_str(), vil3d_image_view_deep_equality(dst,tru), true);
58 
59 #if 0
60 //#ifndef NDEBUG
61   std::cout << "--------------------------------------------------\n"
62            << testname << "\n\n";
63 
64   std::cout << "src image:\n";
65   vil3d_print_all(std::cout, src);
66   std::cout << '\n';
67 
68   std::cout << "dst image:\n";
69   vil3d_print_all(std::cout, dst);
70   std::cout << '\n';
71 
72   std::cout << "tru image:\n";
73   vil3d_print_all(std::cout, tru);
74   std::cout << '\n';
75 
76   std::cout << "first voxels:\n";
77   for (std::map<unsigned, vgl_point_3d<unsigned> >::const_iterator it=blob_vox.begin();
78        it!=blob_vox.end(); ++it)
79   {
80     std::cout << it->first << ": "
81              << it->second.x() << ' '
82              << it->second.y() << ' '
83              << it->second.z() << '\n';
84   }
85   std::cout << '\n';
86 
87   std::cout << "--------------------------------------------------\n";
88 //#endif // NDEBUG
89 #endif
90 }
91 
92 
93 //========================================================================
94 // All-background image
95 //========================================================================
test_all_background()96 static void test_all_background()
97 {
98   const unsigned ni=9, nj=10, nk=11, np=1;
99 
100   vil3d_image_view<bool> src(ni, nj, nk, np);
101   src.fill(false);
102 
103   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
104   tru.fill(0);
105 
106   test_blob_image(src, vil3d_find_blob_connectivity_6_conn, tru,
107                   "Test all-background image");
108   test_blob_image(src, vil3d_find_blob_connectivity_26_conn, tru,
109                   "Test all-background image");
110 }
111 
112 
113 //========================================================================
114 // All-foreground image
115 //========================================================================
test_all_foreground()116 static void test_all_foreground()
117 {
118   const unsigned ni=3, nj=4, nk=5, np=1;
119 
120   vil3d_image_view<bool> src(ni, nj, nk, np);
121   src.fill(true);
122 
123   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
124   tru.fill(1);
125 
126   test_blob_image(src, vil3d_find_blob_connectivity_6_conn, tru,
127                   "Test all-foreground image");
128   test_blob_image(src, vil3d_find_blob_connectivity_26_conn, tru,
129                   "Test all-foreground image");
130 }
131 
132 
133 //========================================================================
134 // Single central blob
135 //========================================================================
test_single_central()136 static void test_single_central()
137 {
138   const unsigned ni=9, nj=10, nk=11, np=1;
139 
140   vil3d_image_view<bool> src(ni, nj, nk, np);
141   src.fill(false);
142   vil3d_crop(src, 3,3, 3,4, 3,5).fill(true);
143   src(6,5,4)=true; src(4,7,5)=true; src(5,6,8)=true;
144 
145   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
146   tru.fill(0);
147   vil3d_crop(tru, 3,3, 3,4, 3,5).fill(1);
148   tru(6,5,4)=1; tru(4,7,5)=1; tru(5,6,8)=1;
149 
150   test_blob_image(src, vil3d_find_blob_connectivity_6_conn,  tru, "Test single central blob");
151   test_blob_image(src, vil3d_find_blob_connectivity_26_conn, tru, "Test single central blob");
152 }
153 
154 
155 //========================================================================
156 // Single blob at image edge
157 //========================================================================
test_single_edge()158 static void test_single_edge()
159 {
160   const unsigned ni=9, nj=10, nk=11, np=1;
161 
162   vil3d_image_view<bool> src(ni, nj, nk, np);
163   src.fill(false);
164   vil3d_crop(src, 0,3, 3,4, 3,5).fill(true);
165 
166   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
167   tru.fill(0);
168   vil3d_crop(tru, 0,3, 3,4, 3,5).fill(1);
169 
170   test_blob_image(src, vil3d_find_blob_connectivity_6_conn,  tru, "Test single edge blob");
171   test_blob_image(src, vil3d_find_blob_connectivity_26_conn, tru, "Test single edge blob");
172 }
173 
174 
175 //========================================================================
176 // Multiple separate blobs
177 // Design note: these blobs are all separate (even with 26-conn) in 3D;
178 // however, they appear to overlap in 2 of the 3 axes.
179 //========================================================================
test_multiple_separate()180 static void test_multiple_separate()
181 {
182   const unsigned ni=9, nj=10, nk=11, np=1;
183 
184   vil3d_image_view<bool> src(ni, nj, nk, np);
185   src.fill(false);
186   vil3d_crop(src, 1,3, 1,4, 1,5).fill(true);
187   vil3d_crop(src, 6,3, 2,4, 2,4).fill(true);
188   vil3d_crop(src, 2,4, 7,3, 3,3).fill(true);
189   vil3d_crop(src, 2,5, 2,5, 7,3).fill(true);
190 
191   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
192   tru.fill(0);
193   vil3d_crop(tru, 1,3, 1,4, 1,5).fill(1);
194   vil3d_crop(tru, 6,3, 2,4, 2,4).fill(2);
195   vil3d_crop(tru, 2,4, 7,3, 3,3).fill(3);
196   vil3d_crop(tru, 2,5, 2,5, 7,3).fill(4);
197 
198   test_blob_image(src, vil3d_find_blob_connectivity_6_conn,  tru, "Test multiple separate blobs");
199   test_blob_image(src, vil3d_find_blob_connectivity_26_conn, tru, "Test multiple separate blobs");
200 }
201 
202 //========================================================================
203 // two blobs, with one that will invoke the connected merging code.
204 //========================================================================
test_merging_blocks()205 static void test_merging_blocks()
206 {
207   const unsigned ni=9, nj=10, nk=11, np=1;
208 
209   vil3d_image_view<bool> src(ni, nj, nk, np);
210   src.fill(false);
211   vil3d_crop(src, 1,7, 1,7, 1,7).fill(true);
212   vil3d_crop(src, 1,6, 1,6, 1,6).fill(false);
213   vil3d_crop(src, 1,3, 1,3, 1,3).fill(true);
214 
215   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
216   tru.fill(0);
217   vil3d_crop(tru, 1,7, 1,7, 1,7).fill(2);
218   vil3d_crop(tru, 1,6, 1,6, 1,6).fill(0);
219   vil3d_crop(tru, 1,3, 1,3, 1,3).fill(1);
220 
221   test_blob_image(src, vil3d_find_blob_connectivity_6_conn,  tru, "Test simple merging case");
222   test_blob_image(src, vil3d_find_blob_connectivity_26_conn, tru, "Test simple merging case");
223 }
224 
225 
226 //========================================================================
227 // One diagonal line that exercises the merging code.
228 //========================================================================
test_merging_diagonal_line()229 static void test_merging_diagonal_line()
230 {
231   const unsigned ni=9, nj=10, nk=11, np=1;
232 
233   vil3d_image_view<bool> src(ni, nj, nk, np);
234   src.fill(false);
235   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
236   tru.fill(0);
237   for (unsigned i=0; i<ni; ++i)
238   {
239     src(ni-i-1, i, i) = true;
240     tru(ni-i-1, i, i) = 1;
241   }
242 
243   test_blob_image(src, vil3d_find_blob_connectivity_26_conn, tru,
244     "Test merging diagonal line");
245 }
246 
247 //=============================================================================
248 // lots of limbs in a single blob that exercises the merging code.
249 // Check that renumber also makes label set compact.
250 //=============================================================================
test_multilimb_merging()251 static void test_multilimb_merging()
252 {
253   const unsigned ni=9, nj=9, nk=9, np=1;
254 
255   vil3d_image_view<bool> src(ni, nj, nk, np);
256   src.fill(false);
257   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
258   tru.fill(0);
259   for (unsigned i=0; i<ni-1; i+=2)
260   {
261     for (unsigned k=0; k<nk-1; k+=2)
262     {
263       src(i,  1, k  )=true;
264       src(i,  2, k  )=true;
265       src(i+1,2, k  )=true;
266       src(i,  2, k+1)=true;
267       src(i+1,2, k+1)=true;
268       tru(i,  1, k  )=1;
269       tru(i,  2, k  )=1;
270       tru(i+1,2, k  )=1;
271       tru(i,  2, k+1)=1;
272       tru(i+1,2, k+1)=1;
273     }
274   }
275 
276   src(5, 5, 5)=true;
277   tru(5, 5, 5)=2;
278 
279   test_blob_image(src, vil3d_find_blob_connectivity_6_conn, tru,
280                   "Test multilimb blob merging");
281 }
282 
283 //=============================================================================
284 // lots of long awkward limbs in a single blob that exercises the merging code.
285 // Check that renumber also makes label set compact.
286 //=============================================================================
test_multilimb_merging2()287 static void test_multilimb_merging2()
288 {
289   const unsigned ni=9, nj=9, nk=9, np=1;
290 
291   vil3d_image_view<bool> src(ni, nj, nk, np);
292   src.fill(false);
293   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
294   tru.fill(0);
295   for (unsigned i=1; i<ni-2; i+=2)
296   {
297     vil3d_crop(src, 1,ni-i-1, 2,1, i,1).fill(true);
298     vil3d_crop(tru, 1,ni-i-1, 2,1, i,1).fill(1);
299     src(ni-i-1, 2, i+1)=true;
300     src(ni-i-1, 2, i+2)=true;
301     src(ni-i-2, 2, i+2)=true;
302     tru(ni-i-1, 2, i+1)=1;
303     tru(ni-i-1, 2, i+2)=1;
304     tru(ni-i-2, 2, i+2)=1;
305   }
306 
307   src(5, 4, 5)=true;
308   tru(5, 4, 5)=2;
309 
310   test_blob_image(src, vil3d_find_blob_connectivity_6_conn, tru,
311                   "Test multilimb blob merging2");
312   test_blob_image(src, vil3d_find_blob_connectivity_26_conn, tru,
313                   "Test multilimb blob merging2");
314 }
315 
316 
317 //=============================================================================
318 // lots of long awkward limbs in a single blob that exercises the merging code.
319 // Check that renumber also makes label set compact.
320 //=============================================================================
test_multilimb_merging3()321 static void test_multilimb_merging3()
322 {
323   const unsigned ni=9, nj=9, nk=9, np=1;
324 
325   vil3d_image_view<bool> src(ni, nj, nk, np);
326   src.fill(false);
327   vil3d_image_view<unsigned> tru(ni, nj, nk, np);
328   tru.fill(0);
329   for (unsigned i=1; i<ni-2; i+=2)
330   {
331     vil3d_crop(src, 1,ni-i-1, 2,1, i,1).fill(true);
332     vil3d_crop(tru, 1,ni-i-1, 2,1, i,1).fill(1);
333     src(ni-i,   2, i+1)=true;
334     src(ni-i-1, 2, i+2)=true;
335     src(ni-i-2, 2, i+2)=true;
336     tru(ni-i,   2, i+1)=1;
337     tru(ni-i-1, 2, i+2)=1;
338     tru(ni-i-2, 2, i+2)=1;
339   }
340 
341   src(5, 4, 5)=true;
342   tru(5, 4, 5)=2;
343 
344   test_blob_image(src, vil3d_find_blob_connectivity_26_conn, tru,
345                   "Test multilimb blob merging3");
346 }
347 
348 //========================================================================
349 // Main function
350 //========================================================================
test_algo_find_blobs()351 static void test_algo_find_blobs()
352 {
353   std::cout << "**************************\n"
354            << " Testing vil3d_find_blobs\n"
355            << "**************************\n";
356 
357   test_all_background();
358   test_all_foreground();
359   test_single_central();
360   test_single_edge();
361   test_multiple_separate();
362   test_merging_blocks();
363   test_merging_diagonal_line();
364   test_multilimb_merging();
365   test_multilimb_merging2();
366   test_multilimb_merging3();
367 
368 
369   // Multiple blobs with diagonal connectivity
370 
371   // Multiple nested blobs
372 
373   // Test multi-plane image - no need for this, blob-finder only claims to handle first plane
374 }
375 
376 
377 //========================================================================
378 // test macro
379 //========================================================================
380 TESTMAIN(test_algo_find_blobs);
381