1 #include <algorithm>
2 #include <cmath>
3 #include <sstream>
4 #include "vil_pyramid_image_list.h"
5 //:
6 // \file
7 #include <cassert>
8 #ifdef _MSC_VER
9 #  include "vcl_msvc_warnings.h"
10 #endif
11 #include "vil/vil_stream_fstream.h"
12 #include "vil/vil_image_list.h"
13 #include "vil/vil_blocked_image_facade.h"
14 #include "vil/vil_cached_image_resource.h"
15 #include "vil/vil_new.h"
16 #include "vil/vil_load.h"
17 #include "vil/vil_copy.h"
18 
19 //:Load a pyramid image.  The path should correspond to a directory.
20 // If not, return a null resource.
21 vil_pyramid_image_resource_sptr
make_input_pyramid_image(char const * directory)22 vil_pyramid_image_list_format::make_input_pyramid_image(char const * directory)
23 {
24   vil_image_list il(directory);
25   std::vector<vil_image_resource_sptr> rescs = il.resources();
26   if (rescs.size() < 2L)
27     return nullptr;
28   auto * pil = new vil_pyramid_image_list(rescs);
29   pil->set_directory(directory);
30   return pil;
31 }
32 
33 vil_pyramid_image_resource_sptr
make_pyramid_output_image(char const * file)34 vil_pyramid_image_list_format::make_pyramid_output_image(char const * file)
35 {
36   if (!vil_image_list::vil_is_directory(file))
37     return nullptr;
38   return new vil_pyramid_image_list(file);
39 }
40 
41 static bool
copy_base_resc(vil_image_resource_sptr const & base_image,const std::string & full_filename,char const * file_format,vil_blocked_image_resource_sptr & copy)42 copy_base_resc(vil_image_resource_sptr const & base_image,
43                const std::string & full_filename,
44                char const * file_format,
45                vil_blocked_image_resource_sptr & copy)
46 {
47   { // scope for closing resource
48     // Create a new blocked base image resource
49     std::cout << "Copying base resource\n";
50     vil_blocked_image_resource_sptr brsc = blocked_image_resource(base_image);
51     if (!brsc || brsc->size_block_i() % 2 != 0 || brsc->size_block_i() % 2 != 0)
52       brsc = new vil_blocked_image_facade(base_image);
53     // handle case of RGBA as four planes
54     vil_pixel_format fmt = vil_pixel_format_component_format(brsc->pixel_format());
55     vil_blocked_image_resource_sptr out_resc = vil_new_blocked_image_resource(full_filename.c_str(),
56                                                                               brsc->ni(),
57                                                                               brsc->nj(),
58                                                                               brsc->nplanes(),
59                                                                               fmt,
60                                                                               brsc->size_block_i(),
61                                                                               brsc->size_block_j(),
62                                                                               file_format);
63     if (!out_resc)
64       return false;
65     for (unsigned int j = 0; j < brsc->n_block_j(); ++j)
66       for (unsigned int i = 0; i < brsc->n_block_i(); ++i)
67       {
68         vil_image_view_base_sptr blk = brsc->get_block(i, j);
69         if (!blk)
70           return false;
71         if (!out_resc->put_block(i, j, *blk))
72           return false;
73       }
74   } // end scope for out resource
75   //
76   // reopen the resource for reading.
77   vil_image_resource_sptr temp = vil_load_image_resource(full_filename.c_str());
78   copy = blocked_image_resource(temp);
79   return (bool)copy;
80 }
81 
82 static std::string
level_filename(std::string & directory,std::string & filename,float level)83 level_filename(std::string & directory, std::string & filename, float level)
84 {
85   std::string slash;
86 
87 #ifdef _WIN32
88   slash = "\\";
89 #else
90   slash = "/";
91 #endif
92   std::stringstream cs;
93   cs << level;
94   return directory + slash + filename + "_" + cs.str();
95 }
96 
97 //: Construct pyramid image files in the directory
98 //  Each level has the same scale ratio (0.5) to the preceding level.
99 //  Level 0 is the original base image.
100 vil_pyramid_image_resource_sptr
make_pyramid_image_from_base(char const * directory,vil_image_resource_sptr const & base_image,unsigned int nlevels,bool copy_base,char const * level_file_format,char const * filename)101 vil_pyramid_image_list_format::make_pyramid_image_from_base(char const * directory,
102                                                             vil_image_resource_sptr const & base_image,
103                                                             unsigned int nlevels,
104                                                             bool copy_base,
105                                                             char const * level_file_format,
106                                                             char const * filename)
107 {
108   if (!vil_image_list::vil_is_directory(directory))
109     return nullptr;
110   std::string d = directory;
111   std::string fn = filename;
112   std::string full_filename = level_filename(d, fn, 0.0f) + '.' + level_file_format;
113   vil_blocked_image_resource_sptr blk_base;
114   if (copy_base)
115   {
116     if (!copy_base_resc(base_image, full_filename, level_file_format, blk_base))
117       return nullptr;
118   }
119   else
120   {
121     blk_base = blocked_image_resource(base_image);
122     if (!blk_base)
123       return nullptr;
124   }
125   // Create the other pyramid levels
126   { // program scope to close resource files
127     vil_image_resource_sptr image = blk_base.ptr();
128     for (unsigned int L = 1; L < nlevels; ++L)
129     {
130       std::cout << "Decimating Level " << L << std::endl;
131       full_filename = level_filename(d, fn, float(L)) + '.' + level_file_format;
132       image = vil_pyramid_image_resource::decimate(image, full_filename.c_str());
133     }
134   } // end program scope to close resource files
135   vil_image_list il(directory);
136   std::vector<vil_image_resource_sptr> rescs = il.resources();
137   return new vil_pyramid_image_list(rescs);
138 }
139 
140 ///==============  start vil_pyramid_image_list  =========================
141 // comparison of level scales
142 static bool
level_compare(pyramid_level * const l1,pyramid_level * const l2)143 level_compare(pyramid_level * const l1, pyramid_level * const l2)
144 {
145   assert(l1 && l2);
146   return l1->image_->ni() > l2->image_->ni();
147 }
148 
149 
vil_pyramid_image_list()150 vil_pyramid_image_list::vil_pyramid_image_list()
151   : directory_("")
152 {}
153 
vil_pyramid_image_list(char const * directory)154 vil_pyramid_image_list::vil_pyramid_image_list(char const * directory)
155   : directory_(directory)
156 {}
157 
vil_pyramid_image_list(std::vector<vil_image_resource_sptr> const & images)158 vil_pyramid_image_list::vil_pyramid_image_list(std::vector<vil_image_resource_sptr> const & images)
159   : directory_("")
160 {
161   for (const auto & image : images)
162   {
163     // if the resource is blocked use a cached access
164     vil_blocked_image_resource_sptr brsc = blocked_image_resource(image);
165     if (!brsc)
166       brsc = new vil_blocked_image_facade(image);
167     vil_cached_image_resource * cimr = new vil_cached_image_resource(brsc, 100);
168     vil_image_resource_sptr ir = (vil_image_resource *)cimr;
169     auto * level = new pyramid_level(ir);
170     levels_.push_back(level);
171   }
172   // sort on image width
173   std::sort(levels_.begin(), levels_.end(), level_compare);
174   this->normalize_scales();
175 }
176 
~vil_pyramid_image_list()177 vil_pyramid_image_list::~vil_pyramid_image_list()
178 {
179   auto nlevels = (unsigned int)(levels_.size());
180   for (unsigned int i = 0; i < nlevels; ++i)
181     delete levels_[i];
182 }
183 
184 //: Assumes that the image in level 0 is the largest
185 void
normalize_scales()186 vil_pyramid_image_list::normalize_scales()
187 {
188   auto nlevels = (unsigned int)(levels_.size());
189   if (nlevels == 0)
190     return;
191   levels_[0]->scale_ = 1.0f;
192   if (nlevels == 1)
193     return;
194   auto ni0 = static_cast<float>(levels_[0]->image_->ni());
195   for (unsigned int i = 1; i < nlevels; ++i)
196     levels_[i]->scale_ = static_cast<float>(levels_[i]->image_->ni()) / ni0;
197 }
198 
199 bool
is_same_size(vil_image_resource_sptr const & image)200 vil_pyramid_image_list::is_same_size(vil_image_resource_sptr const & image)
201 {
202   unsigned int ni = image->ni(), nj = image->nj();
203   for (unsigned int L = 0; L < this->nlevels(); ++L)
204     if (levels_[L]->image_->ni() == ni && levels_[L]->image_->nj() == nj)
205       return true;
206   return false;
207 }
208 
209 bool
add_resource(vil_image_resource_sptr const & image)210 vil_pyramid_image_list::add_resource(vil_image_resource_sptr const & image)
211 {
212   if (this->is_same_size(image))
213     return false;
214 
215   auto * level = new pyramid_level(image);
216   levels_.push_back(level);
217 
218   // is this the first image added?
219   if (levels_.size() == 1)
220     return true;
221   // sort the pyramid
222   std::sort(levels_.begin(), levels_.end(), level_compare);
223   // normalize the scales
224   this->normalize_scales();
225   return true;
226 }
227 
228 //: Find an appropriate filename extension for the image.
229 // If the size of the image lies between existing scales then use
230 // the fractional amount in the name
231 float
find_next_level(vil_image_resource_sptr const & image)232 vil_pyramid_image_list::find_next_level(vil_image_resource_sptr const & image)
233 {
234   unsigned int nlevels = this->nlevels();
235   if (nlevels == 0)
236     return 0.0f;
237   auto base_ni = static_cast<float>(levels_[0]->image_->ni());
238   return static_cast<float>(image->ni()) / base_ni;
239 }
240 
241 //: This method copies the resource into the pyramid.
242 // Use add_resource if the existing resource is to be just inserted into the level stack.
243 bool
put_resource(vil_image_resource_sptr const & image)244 vil_pyramid_image_list::put_resource(vil_image_resource_sptr const & image)
245 {
246   if (this->is_same_size(image))
247     return false;
248   float level = this->find_next_level(image);
249   std::string copy_name = "copyR";
250   std::string file = level_filename(directory_, copy_name, level);
251   std::string ffmt = "pgm";
252   if (image->file_format())
253     ffmt = image->file_format();
254   file = file + '.' + ffmt;
255   unsigned int sbi = 0, sbj = 0;
256   vil_blocked_image_resource_sptr bir = blocked_image_resource(image);
257   if (bir)
258   {
259     sbi = bir->size_block_i();
260     sbj = bir->size_block_j();
261   }
262   vil_image_resource_sptr copy;
263   if (sbi == 0 || sbj == 0)
264   {
265 #ifdef VIL_USE_FSTREAM64
266     vil_stream_fstream64 * os = new vil_stream_fstream64(file.c_str(), "w");
267 #else  // VIL_USE_FSTREAM64
268     auto * os = new vil_stream_fstream(file.c_str(), "w");
269 #endif // VIL_USE_FSTREAM64
270     copy = vil_new_image_resource(os, image->ni(), image->nj(), image->nplanes(), image->pixel_format(), ffmt.c_str());
271   }
272   else
273     copy = vil_new_blocked_image_resource(
274              file.c_str(), image->ni(), image->nj(), image->nplanes(), image->pixel_format(), sbi, sbj, ffmt.c_str())
275              .ptr();
276   if (!vil_copy_deep(image, copy))
277     return false;
278   return this->add_resource(copy);
279 }
280 
281 //:find the level closest to the specified scale
282 pyramid_level *
closest(const float scale) const283 vil_pyramid_image_list::closest(const float scale) const
284 {
285   auto nlevels = (unsigned int)(levels_.size());
286   if (nlevels == 0)
287     return nullptr;
288 
289   if (nlevels == 1)
290     return levels_[0];
291   float mind = 1.0e08f; // huge scale;
292   unsigned int lmin = 0;
293   for (unsigned int i = 0; i < nlevels; ++i)
294   {
295     float ds = std::fabs(std::log(levels_[i]->scale_ / scale));
296     if (ds < mind)
297     {
298       mind = ds;
299       lmin = i;
300     }
301   }
302   pyramid_level * pl = levels_[lmin];
303   if (pl)
304     pl->cur_level_ = lmin;
305   return pl;
306 }
307 
308 vil_image_view_base_sptr
get_copy_view(unsigned int i0,unsigned int n_i,unsigned int j0,unsigned int n_j,unsigned int level) const309 vil_pyramid_image_list::get_copy_view(unsigned int i0,
310                                       unsigned int n_i,
311                                       unsigned int j0,
312                                       unsigned int n_j,
313                                       unsigned int level) const
314 {
315   if (level >= this->nlevels())
316   {
317     std::cerr << "pyramid_image_list::get_copy_view(.) level = " << level << " max level = " << this->nlevels() - 1
318               << '\n';
319     return nullptr;
320   }
321   pyramid_level * pl = levels_[level];
322   float actual_scale = pl->scale_;
323 
324   float fi0 = actual_scale * i0, fni = actual_scale * n_i, fj0 = actual_scale * j0, fnj = actual_scale * n_j;
325   // transform image coordinates by actual scale
326   auto si0 = static_cast<unsigned int>(fi0);
327   auto sni = static_cast<unsigned int>(fni);
328   if (sni == 0)
329     sni = 1; // can't have less than one pixel
330   auto sj0 = static_cast<unsigned int>(fj0);
331   auto snj = static_cast<unsigned int>(fnj);
332   if (snj == 0)
333     snj = 1; // can't have less than one pixel
334   vil_image_view_base_sptr v = pl->image_->get_copy_view(si0, sni, sj0, snj);
335   if (!v)
336   {
337     std::cerr << "pyramid_image_list::get_copy_view(.) level = " << level << "(i0,j0):(" << i0 << ' ' << j0
338               << ") (ni, nj):(" << n_i << ' ' << n_j << ")\n"
339               << "Get copy view from level image failed\n";
340     return nullptr;
341   }
342   return v;
343 }
344 
345 //:return a view with image scale that is closest to scale.
346 vil_image_view_base_sptr
get_copy_view(unsigned int i0,unsigned int n_i,unsigned int j0,unsigned int n_j,const float scale,float & actual_scale) const347 vil_pyramid_image_list::get_copy_view(unsigned int i0,
348                                       unsigned int n_i,
349                                       unsigned int j0,
350                                       unsigned int n_j,
351                                       const float scale,
352                                       float & actual_scale) const
353 {
354   // find the resource that is closest to the specified scale
355   pyramid_level * pl = this->closest(scale);
356   if (!pl)
357   {
358     actual_scale = 0;
359     return nullptr;
360   }
361   actual_scale = pl->scale_;
362   unsigned int level = pl->cur_level_;
363   return this->get_copy_view(i0, n_i, j0, n_j, level);
364 }
365