forked from edrosten/libcvd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
load_and_save.h
337 lines (282 loc) · 10.5 KB
/
load_and_save.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
#ifndef CVD_LOAD_AND_SAVE_H
#define CVD_LOAD_AND_SAVE_H
#include <iostream>
#include <string>
#include <typeinfo>
#include <map>
#include <memory>
#include <cvd/exceptions.h>
#include <cvd/image_convert.h>
#include <cvd/colourspace_convert.h>
#include <cvd/internal/convert_pixel_types.h>
#include <cvd/internal/name_CVD_rgb_types.h>
#include <cvd/internal/io/parameter.h>
namespace CVD {
namespace Exceptions
{
/// %Exceptions specific to image loading and saving
/// @ingroup gException
namespace Image_IO
{
/// Base class for all Image_IO exceptions
/// @ingroup gException
struct All: public CVD::Exceptions::All
{};
/// This image type is not supported
/// @ingroup gException
struct UnsupportedImageType: public All
{
UnsupportedImageType();
};
/// The file ended before the image
/// @ingroup gException
struct EofBeforeImage: public All
{
EofBeforeImage();
};
/// The ifstream which the file is being read from is not open
/// @ingroup gException
struct IfstreamNotOpen: public All
{
IfstreamNotOpen();
};
/// The image was incorrect
/// @ingroup gException
struct MalformedImage: public All
{
MalformedImage(const std::string &); ///< Construct from a message string
};
/// The loaded image is not the right size
/// @ingroup gException
struct ImageSizeMismatch: public All
{
ImageSizeMismatch(const ImageRef& src, const ImageRef& dest); ///< Construct from the two sizes
};
/// Error writing the image
/// @ingroup gException
struct WriteError: public All
{
WriteError(const std::string& err); ///< Construct from a message string
};
/// Cannot seek in this stream
/// @ingroup gException
struct UnseekableIstream: public All
{
UnseekableIstream(const std::string& type); ///< Construct from a message string
};
/// Type mismatch reading the image (image data is either 8- or 16-bit, and it must be the same in the file)
/// @ingroup gException
struct ReadTypeMismatch: public All
{
ReadTypeMismatch(bool read8); ///< Constructor is passed <code>true</code> if it was trying to read 8-bit data
ReadTypeMismatch(const std::string& available, const std::string& requested);
};
/// Type mismatch reading the image (image data is either 8- or 16-bit, and it must be the same in the file)
/// @ingroup gException
struct WriteTypeMismatch: public All
{
WriteTypeMismatch(const std::string& available, const std::string& requested);
};
/// An error occurred in one of the helper libraries
/// @ingroup gException
struct InternalLibraryError: public All
{
InternalLibraryError(const std::string& lib, const std::string err); ///< Construct from the library name and the error string
};
/// This image subtype is not supported
/// @ingroup gException
struct UnsupportedImageSubType: public All
{
UnsupportedImageSubType(const std::string &, const std::string&); ///< Construct from a subtype string and an error string
};
/// Error in opening file
/// @ingroup gException
struct OpenError: public All
{
OpenError(const std::string&, const std::string&, int); ///< Construct from the filename and the error number
};
}
}
namespace Internal
{
template<class C, int i> struct save_default_
{
static const bool use_16bit=1;
};
template<class C> struct save_default_<C,1>
{
static const bool use_16bit=(CVD::Pixel::traits<typename CVD::Pixel::Component<C>::type>::bits_used) > 8;
};
template<class C> struct save_default
{
static const bool use_16bit = save_default_<C, CVD::Pixel::traits<typename CVD::Pixel::Component<C>::type>::integral>::use_16bit;
};
////////////////////////////////////////////////////////////////////////////////
//Mechanisms for generic image loading
//
// Image readers are duck-types and must provide the following:
// typedef Types Typelist containing types which can be loaded
// string datatype() Stringified name of datatype on disk
// string name() Name of the reader (JPEG, TIFF, etc)
// ImageRef size() size()
// void get_raw_pixel_line(T) Where T is available for everything in Types
// Constructor accepting istream;
//Basic typelist.
struct Head{};
template<class A, class B> struct TypeList
{
typedef A Type;
typedef B Next;
};
////////////////////////////////////////////////////////////////////////////////
//
// Read data and process if necessary.
// In the case where the in-memory and on-disk datatypes match, no processing
// is performed.
template<class PixelType, class DiskPixelType, class ImageLoader> struct read_and_maybe_process
{
static void exec(BasicImage<PixelType>& im, ImageLoader& r)
{
Image<DiskPixelType> rowbuf(ImageRef(r.size().x, 1));
for(int row = 0; row < r.size().y; row++)
{
r.get_raw_pixel_line(rowbuf.data());
PixelType* rowptr;
if(r.top_row_first())
rowptr = im[row];
else
rowptr = im[im.size().y - row-1];
Pixel::ConvertPixels<DiskPixelType, PixelType>::convert(rowbuf.data(), rowptr, r.size().x);
}
}
};
template<class PixelType, class ImageLoader> struct read_and_maybe_process<PixelType, PixelType, ImageLoader>
{
static void exec(BasicImage<PixelType>& im, ImageLoader& r)
{
for(int row = 0; row < r.size().y; row++)
if(r.top_row_first())
r.get_raw_pixel_line(im[row]);
else
r.get_raw_pixel_line(im[im.size().y - row-1]);
}
};
template<class PixelType, class DiskPixelType, class ImageLoader> struct read_and_then_process
{
static void exec(BasicImage<PixelType>& im, ImageLoader& r)
{
Image<DiskPixelType> imgbuf(r.size());
read_and_maybe_process<DiskPixelType,DiskPixelType, ImageLoader>::exec(imgbuf, r);
convert_image(imgbuf, im);
}
};
////////////////////////////////////////////////////////////////////////////////
//
// Iterate over the typelist, and decide which type to load.
//
template<class PixelType, class ImageLoader, class List > struct Reader
{
static void read(BasicImage<PixelType>& im, ImageLoader& r)
{
if(r.datatype() == PNM::type_name<typename List::Type>::name())
{
//std::cout << "converting " << r.datatype() << " -> " << PNM::type_name<PixelType>::name() << " PixelByPixel: " << PixelByPixelConvertible<typename List::Type, PixelType>::is << " Convertible: " << IsConvertible<typename List::Type, PixelType>::is << std::endl;
if (PixelByPixelConvertible<typename List::Type, PixelType>::is)
read_and_maybe_process<PixelType, typename List::Type, ImageLoader>::exec(im, r);
else if (IsConvertible<typename List::Type, PixelType>::is)
read_and_then_process<PixelType, typename List::Type, ImageLoader>::exec(im, r);
else
throw CVD::Exceptions::Image_IO::ReadTypeMismatch(r.datatype(), PNM::type_name<PixelType>::name());
}
else
Reader<PixelType, ImageLoader, typename List::Next>::read(im, r);
}
};
template<class PixelType, class ImageLoader> struct Reader<PixelType, ImageLoader, Head>
{
static void read(BasicImage<PixelType>&, ImageLoader& r)
{
throw Exceptions::Image_IO::UnsupportedImageSubType(r.name(), r.datatype() + " not yet supported");
}
};
////////////////////////////////////////////////////////////////////////////////
//
// Driver functions for loading images.
//
template<class T, class ImageLoader> void readImage(BasicImage<T>& im, ImageLoader& r)
{
Reader<T, ImageLoader, typename ImageLoader::Types>::read(im, r);
}
template <class T, class ImageLoader> void readImage(BasicImage<T>& im, std::istream& in)
{
ImageLoader loader(in);
ImageRef size = loader.size();
if (im.size() != size)
throw Exceptions::Image_IO::ImageSizeMismatch(size, im.size());
readImage(im, loader);
}
template <class T, class ImageLoader> void readImage(Image<T>& im, std::istream& in)
{
ImageLoader loader(in);
im.resize(loader.size());
readImage(im, loader);
}
////////////////////////////////////////////////////////////////////////////////
//
// Functions for attempting to choose an image type based on the datatype.
// The template code provides information detailing the ideal image type.
// The writer object is initialized with this information, and returns the type
// that it will write.
//
// Unlike loading, where the type on disk is known only dynamically, the type
// being saved and any necessary conversions can be deduced statically.
//
// The writer objects provide an interface with the following parts:
//
// template<class C> Outgoing::type For a given incoming pixel type C, this is the outgoing type
// constructor(ostream&, ImageRef, string type) Construct an image writer for a given type
// void write_raw_pixel_line(T*) Write pixels of type T.
////////////////////////////////////////////////////////////////////////////////
//
// Select an outgoing type, convert if necessary and then save.
//
template<class Pixel, class ImageWriter, class OutgoingPixel> struct maybe_process_and_write
{
static void write(std::ostream& os, const BasicImage<Pixel>& im, const std::map<std::string, Parameter<> >& p)
{
ImageWriter w(os, im.size(), CVD::PNM::type_name<OutgoingPixel>::name(), p);
Image<OutgoingPixel> row(ImageRef(im.size().x, 1));
if(w.top_row_first)
for(int r=0; r < im.size().y; r++)
{
CVD::Pixel::ConvertPixels<Pixel, OutgoingPixel>::convert(im[r], row.data(), im.size().x);
w.write_raw_pixel_line(row.data());
}
else
for(int r=im.size().y-1; r >= 0; r--)
{
CVD::Pixel::ConvertPixels<Pixel, OutgoingPixel>::convert(im[r], row.data(), im.size().x);
w.write_raw_pixel_line(row.data());
}
}
};
template<class Pixel, class ImageWriter> struct maybe_process_and_write<Pixel, ImageWriter, Pixel>
{
static void write(std::ostream& os, const BasicImage<Pixel>& im, const std::map<std::string, Parameter<> >& p)
{
ImageWriter w(os, im.size(), CVD::PNM::type_name<Pixel>::name(), p);
if(w.top_row_first)
for(int r=0; r < im.size().y; r++)
w.write_raw_pixel_line(im[r]);
else
for(int r=im.size().y-1; r >= 0; r--)
w.write_raw_pixel_line(im[r]);
}
};
template<class Pixel, class Writer> void writeImage(const BasicImage<Pixel>& im, std::ostream& o, const std::map<std::string, Parameter<> >& p)
{
maybe_process_and_write<Pixel, Writer, typename Writer::template Outgoing<Pixel>::type>::write(o, im, p);
}
}
}
#endif