Pgm.h
Go to the documentation of this file.
1 //===========================================================================
2 /*!
3  *
4  *
5  * \brief Importing and exporting PGM images
6  *
7  *
8  *
9  * \author C. Igel
10  * \date 2011
11  *
12  *
13  * \par Copyright 1995-2017 Shark Development Team
14  *
15  * <BR><HR>
16  * This file is part of Shark.
17  * <http://shark-ml.org/>
18  *
19  * Shark is free software: you can redistribute it and/or modify
20  * it under the terms of the GNU Lesser General Public License as published
21  * by the Free Software Foundation, either version 3 of the License, or
22  * (at your option) any later version.
23  *
24  * Shark is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27  * GNU Lesser General Public License for more details.
28  *
29  * You should have received a copy of the GNU Lesser General Public License
30  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
31  *
32  */
33 //===========================================================================
34 
35 #ifndef SHARK_DATA_IMPORT_PGM_H
36 #define SHARK_DATA_IMPORT_PGM_H
37 
38 #include <fstream>
39 
40 #include <boost/format.hpp>
41 #include <boost/filesystem.hpp>
42 #include <boost/archive/text_oarchive.hpp>
43 
44 
45 #include <shark/LinAlg/Base.h>
46 #include <shark/Data/Dataset.h>
47 
48 namespace shark {
49 
50 namespace detail {
51 void importPGM( std::string const& fileName, std::vector<unsigned char>& ppData, std::size_t& sx, std::size_t& sy )
52 {
53  std::ifstream file(fileName.c_str(), std::ios::binary);
54  SHARK_RUNTIME_CHECK(file, "Can not open File");
55 
56  std::string id;
57  std::size_t nGrayValues = 0;
58  file>> id;
59  SHARK_RUNTIME_CHECK(id == "P5" , "File " + fileName+ "is not a pgm");
60  //ignore comments
61  file >> std::ws;//skip white space
62  while(file.peek() == '#'){
63  file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
64  }
65  file >> sx >> sy >> nGrayValues;
66  SHARK_RUNTIME_CHECK(file, "Error reading file!");
67  SHARK_RUNTIME_CHECK(nGrayValues <= 255, "File " + fileName+ "unsupported format");
68 
69  ppData.resize(sx*sy);
70  file.read((char*)ppData.data(),sx*sy);
71  SHARK_RUNTIME_CHECK(file, "Error reading file!");
72 }
73 
74 /**
75  * \ingroup shark_globals
76  *
77  * @{
78  */
79 
80 /// \brief Writes a PGM file.
81 ///
82 /// \param fileName File to write to
83 /// \param pData unsigned char pointer to the data
84 /// \param sx Width of image
85 /// \param sy Height of image
86 void writePGM( std::string const& fileName, std::vector<unsigned char> const& data, std::size_t sx, std::size_t sy )
87 {
88  std::ofstream file(fileName.c_str(), std::ios::binary);
89  SHARK_RUNTIME_CHECK(file, "Can not open File");
90 
91  file<<"P5\n"<<sx<<" "<<sy<<"\n"<<255<<"\n";
92  file.write((char*)data.data(),sx*sy);
93 }
94 } // end namespace detail
95 
96 /// \brief Import a PGM image from file
97 ///
98 /// \param fileName The file to read from
99 /// \param data Linear object for storing image
100 /// \param sx Width of imported image
101 /// \param sy Height of imported image
102 template <class T>
103 void importPGM( std::string const& fileName, T& data, std::size_t& sx, std::size_t& sy ) {
104  std::vector<unsigned char> rawData;
105  detail::importPGM(fileName, rawData, sx, sy);
106  data.resize(sx*sy);
107  std::copy(rawData.begin(), rawData.end(), data.begin());
108 }
109 
110 /// \brief Export a PGM image to file
111 ///
112 /// \param fileName File to write to
113 /// \param data Linear object storing image
114 /// \param sx Width of image
115 /// \param sy Height of image
116 /// \param normalize Adjust values to [0,255], default false
117 template <class T>
118 void exportPGM(std::string const& fileName, T const& data, std::size_t sx, std::size_t sy, bool normalize = false) {
119  SIZE_CHECK(sx*sy == data.size());
120  std::vector<unsigned char> rawData(data.size());
121  typename T::const_iterator it = data.begin();
122  std::size_t i = 0;
123  if(normalize) {
124  double lb = *std::min_element(data.begin(),data.end());
125  double ub = *std::max_element(data.begin(), data.end());
126  for( it = data.begin() ; it != data.end(); ++it, ++i )
127  rawData[i] = (unsigned char)( (*it - lb) / (ub - lb) * 255 );
128  } else {
129  for( it = data.begin() ; it != data.end(); ++it, ++i )
130  rawData[i] = (unsigned char)( *it );
131  }
132  detail::writePGM(fileName, rawData, sx, sy);
133 }
134 
135 /// \brief Exports a set of filters as a grid image
136 ///
137 /// It is assumed that the filters each form a row in the filter-matrix.
138 /// Moreover, the sizes of the filter images has to be given and it must gold width*height=W.size2().
139 /// The filters a re printed on a single image as a grid. The grid will be close to square. And the
140 /// image are separated by a black 1 pixel wide line.
141 /// The output will be normalized so that all images are on the same scale.
142 /// \param basename File to write to. ".pgm" is appended to the filename
143 /// \param filters Matrix storing the filters row by row
144 /// \param width Width of the filter image
145 /// \param height Height of th filter image
146 inline void exportFiltersToPGMGrid(std::string const& basename, RealMatrix const& filters,std::size_t width, std::size_t height) {
147  SIZE_CHECK(filters.size2() == width*height);
148  //try to get a square image
149  std::size_t gridX = std::size_t(std::sqrt(double(filters.size1())));
150  std::size_t gridY = gridX;
151  while(gridX*gridY < filters.size1()) ++gridX;
152 
153  RealMatrix image((height+1)*gridY,(width+1)*gridX,min(filters));
154 
155  for(std::size_t filter = 0; filter != filters.size1(); ++filter){
156  //get grid position from filter
157  std::size_t i = filter/gridX;
158  std::size_t j = filter%gridX;
159  std::size_t startY = (height+1)*i;
160  std::size_t startX = (width+1)*j;
161  //copy images
162  noalias(subrange(image,startY,startY+height,startX,startX+width)) = to_matrix(row(filters,filter),height,width);
163  }
164  exportPGM(
165  (basename+".pgm").c_str(),
166  blas::adapt_vector((height+1)*gridY*(width+1)*gridX,&image(0,0)),
167  (width+1)*gridX, (height+1)*gridY,
168  true
169  );
170 }
171 
172 /// \brief Exports a set of filters as a grid image
173 ///
174 /// It is assumed that the filters each form a row in the filter-matrix.
175 /// Moreover, the sizes of the filter images has to be given and it must gold width*height=W.size2().
176 /// The filters a re printed on a single image as a grid. The grid will be close to square. And the
177 /// image are separated by a black 1 pixel wide line.
178 /// The output will be normalized so that all images are on the same scale.
179 /// \param basename File to write to. ".pgm" is appended to the filename
180 /// \param filters Matrix storing the filters row by row
181 /// \param width Width of the filter image
182 /// \param height Height of th filter image
183 inline void exportFiltersToPGMGrid(std::string const& basename, Data<RealVector> const& filters,std::size_t width, std::size_t height) {
184  SIZE_CHECK(dataDimension(filters) == width*height);
185  //try to get a square image
186  std::size_t numFilters = filters.numberOfElements();
187  std::size_t gridX = std::size_t(std::sqrt(double(numFilters)));
188  std::size_t gridY = gridX;
189  while(gridX*gridY < numFilters) ++gridX;
190 
191  double minimum = std::numeric_limits<double>::max();
192  for(std::size_t i = 0; i != filters.numberOfBatches(); ++i){
193  minimum =std::min(minimum,min(filters.batch(i)));
194  }
195 
196  RealMatrix image((height+1)*gridY,(width+1)*gridX,minimum);
197 
198  for(std::size_t filter = 0; filter != numFilters; ++filter){
199  //get grid position from filter
200  std::size_t i = filter/gridX;
201  std::size_t j = filter%gridX;
202  std::size_t startY = (height+1)*i;
203  std::size_t startX = (width+1)*j;
204  RealVector filterImage =filters.element(filter);
205  //copy images
206  noalias(subrange(image,startY,startY+height,startX,startX+width)) = to_matrix(filterImage,height,width);
207  }
208  exportPGM(
209  (basename+".pgm").c_str(),
210  blas::adapt_vector((height+1)*gridY*(width+1)*gridX,&image(0,0)),
211  (width+1)*gridX, (height+1)*gridY,
212  true);
213 }
214 
215 /// \brief Import PGM images scanning a directory recursively
216 ///
217 /// All images are required to have the same size. the shape of the images is stored in set.shape()
218 ///
219 /// \param p Directory
220 /// \param set Set storing images
221 template<class T>
222 void importPGMSet(std::string const&p, Data<T> &set){
223  std::vector<T> container;
224  std::vector<std::pair<std::size_t,std::size_t> > info;
225  if (boost::filesystem::is_directory(p)) {
226  for (boost::filesystem::recursive_directory_iterator itr(p); itr!=boost::filesystem::recursive_directory_iterator(); ++itr) {
227  if (boost::filesystem::is_regular(itr->status())) {
228  if ((boost::filesystem::extension(itr->path()) == ".PGM") ||
229  (boost::filesystem::extension(itr->path()) == ".pgm")) {
230  T img;
231  std::pair<std::size_t,std::size_t> imgInfo;
232  importPGM(itr->path().string().c_str(), img, imgInfo.first, imgInfo.second);
233  container.push_back(img);
234  info.push_back(imgInfo);
235  }
236  }
237  }
238  } else {
239  throw( std::invalid_argument( "[importPGMDir] cannot open file" ) );
240  }
241 
242  //check all images have same size
243  for(auto const& i: info){
244  if(i.first != info.front().first || i.second != info.front().second){
245  throw SHARKEXCEPTION("[importPGMSet] all images are required to have the same size");
246  }
247  }
248  set = createDataFromRange(container);
249  set.shape() = {info.front().second,info.front().first};
250 }
251 
252 /** @}*/
253 
254 } // end namespace shark
255 #endif