OPAL
ImageDatabase.h
1 #pragma once
2 
3 #include "Image.h"
4 #include "ImageIO/ImageIO.h"
5 
6 #include "util/fs/File.h"
7 #include "util/fs/Directory.h"
8 #include "util/json/json.h"
9 
10 #include <stdexcept>
11 #include <cassert>
12 #include <vector>
13 #include <string>
14 #include <fstream>
15 
16 
17 template<class I, class S>
19 
20 public:
21  using ImgPixelType = I;
22  using SegPixelType = S;
23 
26 
27  using ImgContainerType = std::vector<ImgType>;
28  using SegContainerType = std::vector<SegType>;
29 
30  using ConstImgIterator = typename ImgContainerType::const_iterator;
31  using ConstSegIterator = typename SegContainerType::const_iterator;
32 
33 public:
34  ImageDatabase() = default;
35 
36 public:
45  void Add(const std::string &imgFileName, const std::string &segFileName);
46 
64  void Add(const ImgType &imgMat, const SegType &segMat,
65  const std::string &imgFileName = "",
66  const std::string &segFileName = "");
67 
68  // Read a JSON config file and fill the database accordingly.
69  void ReadFromConfig(const std::string &fileName);
70 
81  void AppendFilesFromList(const std::string &fileName);
82 
92  void ReadFilesFromList(const std::string &fileName);
93 
95  void Clear();
96 
97  // Getters.
98 
99  // Get images.
100  inline const ImgType & GetImage(size_t i) const;
101  inline const SegType & GetSegmentation(size_t i) const;
102 
103  inline std::string GetImageName(size_t i) const;
104  inline std::string GetSegmentationName(size_t j) const;
105 
106  inline size_t GetImageCount() const;
107  inline bool IsEmpty() const;
108 
109  // Get image dimensions.
110  size_t GetImageHeight() const { return imageHeight; }
111  size_t GetImageWidth() const { return imageWidth; }
112 
113  // Iterators.
114  inline ConstImgIterator img_cbegin() const { return images.cbegin(); }
115  inline ConstSegIterator seg_cbegin() const { return segmentations.cbegin(); }
116  inline ConstImgIterator img_cend() const { return images.end(); }
117  inline ConstSegIterator seg_cend() const { return segmentations.cend(); }
118 
119 private:
120  void ReadFromJson(const util::Json &jsonConfig);
121 
122  // Parse 'images' or 'segmentation' section. Section is required.
123  // files and folder are cleared first.
124  //
125  // Throws std::runtime_error if section is not present in the config.
126  void ParseConfigSection(const util::Json &config,
127  const std::string &section,
128  std::vector<std::string> &absFiles);
129 
130 private:
131  ImgContainerType images;
132  SegContainerType segmentations;
133 
134  std::vector<std::string> imgNames;
135  std::vector<std::string> segNames;
136 
137  size_t imageHeight;
138  size_t imageWidth;
139 
140  std::string dbName;
141 };
142 
143 
144 template<class I, class S>
145 void ImageDatabase<I, S>::Add(const std::string &imgFileName,
146  const std::string &segFileName)
147 {
148  auto imgMat = ImageIO::ReadImage<ImgPixelType>(imgFileName);
149  auto segMat = ImageIO::ReadImage<SegPixelType>(segFileName);
150 
151  Add(imgMat, segMat, imgFileName, segFileName);
152 }
153 
154 
155 template <class I, class S>
157  const SegType &segMat,
158  const std::string &imgFileName,
159  const std::string &segFileName)
160 {
161  // Don't add empty images.
162  if (imgMat.isEmpty() || segMat.isEmpty())
163  throw std::runtime_error("Loaded empty image or segmentation!");
164 
165  // Don't add images with different dimensions.
166  if (imgMat.getHeight() != segMat.getHeight() ||
167  imgMat.getWidth() != segMat.getWidth())
168  throw std::runtime_error("Image/segmentation size mismatch!");
169 
170  // Check size of images.
171  if (images.size() == 0) {
172  // This must be the first pair of images.
173  imageHeight = imgMat.getHeight();
174  imageWidth = imgMat.getWidth();
175  } else {
176  assert(imageHeight != 0 && imageWidth != 0 &&
177  "Only one of image dimensions is 0!");
178 
179  // Don't add images which dimensions don't match the base.
180  if (imgMat.getHeight() != imageHeight || imgMat.getWidth() != imageWidth)
181  throw std::runtime_error(
182  "Size of new image/segmentation doesn't suit the database!");
183  }
184 
185  // Add images.
186  images.push_back(imgMat);
187  segmentations.push_back(segMat);
188 
189  // Add image names.
190  imgNames.push_back(imgFileName);
191  segNames.push_back(segFileName);
192 }
193 
194 
195 template<class I, class S>
196 void ImageDatabase<I, S>::ReadFromConfig(const std::string &fileName)
197 {
198  return ReadFromJson(util::JsonFromFile(fileName));
199 }
200 
201 
202 template <class I, class S>
203 void ImageDatabase<I, S>::ReadFromJson(const util::Json &jsonConfig)
204 {
205  // Database name (optional).
206  auto name = jsonConfig["name"];
207  if (!name.is_null())
208  dbName = name.string_value();
209 
210  std::vector<std::string> imagesFiles;
211  ParseConfigSection(jsonConfig, "images", imagesFiles);
212 
213  std::vector<std::string> segFiles;
214  ParseConfigSection(jsonConfig, "segmentations", segFiles);
215 
216  if (imagesFiles.size() != segFiles.size())
217  throw std::runtime_error("Images / segmentations files count mismatch");
218 
219  // Read files.
220  for (size_t i = 0; i < imagesFiles.size(); ++i)
221  Add(imagesFiles[i], segFiles[i]);
222 }
223 
224 
225 template<class I, class S>
227  const std::string &section,
228  std::vector<std::string> &absFiles)
229 {
230  std::string folder = "";
231  std::string extension = "";
232  std::vector<std::string> configFiles;
233  absFiles.clear();
234 
235  // Section is required.
236  auto jsonSection = config[section];
237  if (jsonSection.is_null())
238  throw std::runtime_error("'" + section + "' section not found in config");
239 
240  // Example:
241  // "images": {
242  // "files": [
243  // "1.img",
244  // "2.img"
245  // ],
246  // "folder": "/home/d-zobnin/images"
247  // }
248 
249  // At least one of 'files' and 'folder' subsections must be given.
250  auto folderSection = jsonSection["folder"];
251  auto filesSection = jsonSection["files"];
252  auto extensionSection = jsonSection["extension"];
253  if (!folderSection.is_string() && !filesSection.is_array())
254  throw std::runtime_error("'files' and 'folder' subsections not found for '"
255  + section + "' section");
256 
257  if (folderSection.is_string())
258  folder = folderSection.string_value();
259 
260  if (extensionSection.is_string())
261  extension = extensionSection.string_value();
262 
263  if (filesSection.is_array())
264  for (const auto &item : filesSection.array_items()) {
265  if (!item.is_string())
266  throw std::runtime_error("'files' section must contain only strings");
267  configFiles.push_back(item.string_value());
268  }
269 
270  // If 'files' is not given, read all files from 'folder'.
271  if (configFiles.empty()) {
272  absFiles = util::ListDir(folder, extension, /*sorted=*/ true);
273  } else {
274  for (const auto &f : configFiles)
275  absFiles.push_back(util::ConcatPaths(folder, f));
276  }
277 }
278 
279 
280 template<class I, class S>
281 void ImageDatabase<I, S>::AppendFilesFromList(const std::string &fileName)
282 {
283  std::ifstream ifs(fileName);
284 
285  std::string imgFileName, segFileName;
286  while (ifs >> imgFileName) {
287  if (!(ifs >> segFileName))
288  break;
289  Add(imgFileName, segFileName);
290  }
291  ifs.close();
292 }
293 
294 
295 template<class I, class S>
296 void ImageDatabase<I, S>::ReadFilesFromList(const std::string &fileName)
297 {
298  Clear();
299  AppendFilesFromList(fileName);
300 }
301 
302 
303 template<class I, class S>
305 {
306  images.clear();
307  segmentations.clear();
308  imageHeight = imageWidth = 0;
309 }
310 
311 // Throws std::out_of_range.
312 template<class I, class S>
313 const typename ImageDatabase<I, S>::ImgType &
314 ImageDatabase<I, S>::GetImage(size_t i) const
315 {
316  assert(i < images.size() && "Image index is out of range!");
317 
318  return images[i];
319 }
320 
321 
322 // Throws std::out_of_range.
323 template<class I, class S>
324 const typename ImageDatabase<I, S>::SegType &
326 {
327  assert(i < segmentations.size() && "Segmentation index is out of range!");
328 
329  return segmentations[i];
330 }
331 
332 
333 template <class I, class S>
334 std::string ImageDatabase<I, S>::GetImageName(size_t i) const
335 {
336  assert(i < imgNames.size() && "Index of image name is out of range!");
337 
338  return imgNames[i];
339 }
340 
341 
342 template <class I, class S>
343 std::string ImageDatabase<I, S>::GetSegmentationName(size_t i) const
344 {
345  assert(i < segNames.size() && "Index of segmentation name is out of range!");
346 
347  return segNames[i];
348 }
349 
350 
351 template<class I, class S>
353 {
354  assert(images.size() == segmentations.size() &&
355  "Image and segmentation databases must have the same size!");
356  return images.size();
357 }
358 
359 
360 template<class I, class S>
361 inline bool ImageDatabase<I, S>::IsEmpty() const
362 {
363  assert(images.size() == segmentations.size() &&
364  "Image and segmentation databases must have the same size!");
365  return images.size() == 0;
366 }
367 
void AppendFilesFromList(const std::string &fileName)
Read a list of filenames as &#39;image, segmentation&#39; pairs and add them to the database.
Definition: ImageDatabase.h:281
Definition: ImageDatabase.h:18
void Clear()
Clear the contents of the database.
Definition: ImageDatabase.h:304
Definition: config.py:1
Header file declaring interface for various operations with JSON.
void Add(const std::string &imgFileName, const std::string &segFileName)
Adds a pair of image and its segmentation.
Definition: ImageDatabase.h:145
Definition: json11.hpp:79
void ReadFilesFromList(const std::string &fileName)
Clear the database and read from list.
Definition: ImageDatabase.h:296
Definition: Image.h:11