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