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-2015 Shark Development Team
14  *
15  * <BR><HR>
16  * This file is part of Shark.
17  * <http://image.diku.dk/shark/>
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 <iostream>
39 #include <exception>
40 
41 #include <stdio.h>
42 
43 #include <boost/format.hpp>
44 #include <boost/filesystem.hpp>
45 #include <boost/archive/text_oarchive.hpp>
46 
47 
48 #include <shark/LinAlg/Base.h>
49 #include <shark/Data/Dataset.h>
50 
51 namespace shark {
52 
53 namespace detail {
54 void importPGM( std::string const& fileName, unsigned char ** ppData, int & sx, int & sy )
55 {
56  FILE * fp = fopen(fileName.c_str(), "rb");
57 
58  if( !fp ) throw SHARKEXCEPTION( "[importPGM] cannot open file: " + fileName);
59 
60  char format[16];
61  const int nParamRead0 = fscanf(fp, "%s\n", (char *) &format);
62  if ( 0 == nParamRead0 ) throw SHARKEXCEPTION( "[importPGM] error reading file: " + fileName );
63 
64  // Ignore comments
65  char tmpCharBuf[256];
66  fpos_t position;
67  fgetpos (fp, &position);
68  while ( true ) {
69  char *s = fgets( tmpCharBuf, 255, fp );
70  if (!s) throw SHARKEXCEPTION( "[importPGM] error reading file: " + fileName );
71  const int cnt = strncmp( tmpCharBuf, "#", 1 );
72  if (0 != cnt) {
73  fsetpos(fp, &position);
74  break;
75  } else {
76  fgetpos (fp, &position);
77  }
78  }
79 
80  int nGrayValues;
81  const int nParamRead1 = fscanf( fp, "%d %d\n", &sx, &sy );
82  const int nParamRead2 = fscanf( fp, "%d\n", &nGrayValues );
83 
84  if ( (nParamRead1 != 2) || (nParamRead2 != 1) ) {
85  fclose(fp);
86  throw SHARKEXCEPTION( "[importPGM] file corrupted or format not recognized in file: " + fileName );
87  } else {
88  if ( (0 == strncmp("P5", format, 2)) && ( (255 == nGrayValues) || (256 == nGrayValues) ) ) {
89  //delete[] *ppData;
90  *ppData = new unsigned char[sx*sy];
91  fseek(fp, -sx*sy*sizeof(unsigned char), SEEK_END);
92 
93  const int readcount = (int)( fread(*ppData, sx*sizeof(unsigned char), sy, fp));
94  if (sy != readcount) {
95  fclose(fp);
96  throw SHARKEXCEPTION( "[importPGM] file corrupted or format not recognized in file: " + fileName );
97  }
98  } else {
99  fclose(fp);
100  throw SHARKEXCEPTION( "[importPGM] file corrupted or format not recognized in file: " + fileName );
101  }
102  }
103  fclose(fp);
104 }
105 
106 /**
107  * \ingroup shark_globals
108  *
109  * @{
110  */
111 
112 /// \brief Writes a PGM file.
113 ///
114 /// \param fileName File to write to
115 /// \param pData unsigned char pointer to the data
116 /// \param sx Width of image
117 /// \param sy Height of image
118 void writePGM( std::string const& fileName, unsigned char const* pData, std::size_t sx, std::size_t sy )
119 {
120  FILE* fp = fopen(fileName.c_str(), "wb");
121  if( !fp ) throw SHARKEXCEPTION( "[writePGM] cannot open file: " + fileName);
122 
123  fprintf(fp, "P5\n");
124  fprintf(fp, "%d %d\n255\n", (int)sx, (int)sy);
125 
126  if( 1 != fwrite(pData, sx*sy, 1, fp) ) {
127  fclose(fp);
128  throw SHARKEXCEPTION( "[writePGM] can not write data to file: "+ fileName );
129  }
130  fclose(fp);
131 }
132 } // end namespace detail
133 
134 /// \brief Import a PGM image from file
135 ///
136 /// \param fileName The file to read from
137 /// \param data Linear object for storing image
138 /// \param sx Width of imported image
139 /// \param sy Height of imported image
140 template <class T>
141 void importPGM( std::string const& fileName, T& data, std::size_t& sx, std::size_t& sy ) {
142  unsigned char *pData;
143  unsigned i=0;
144  int isx;
145  int isy;
146  detail::importPGM(fileName, &pData, isx, isy);
147  sx = std::size_t(isx);
148  sy = std::size_t(isy);
149  data.resize(sx*sy);
150  std::copy(pData, pData + sx*sy, data.begin());
151  delete [] pData;
152 }
153 
154 /// \brief Export a PGM image to file
155 ///
156 /// \param fileName File to write to
157 /// \param data Linear object storing image
158 /// \param sx Width of image
159 /// \param sy Height of image
160 /// \param normalize Adjust values to [0,255], default false
161 template <class T>
162 void exportPGM(std::string const& fileName, T const& data, std::size_t sx, std::size_t sy, bool normalize = false) {
163  unsigned i = 0;
164  unsigned char *pData = new unsigned char[data.size()];
165  typename T::const_iterator it = data.begin();
166  if(normalize) {
167  double lb = *std::min_element(data.begin(),data.end());
168  double ub = *std::max_element(data.begin(), data.end());
169  for( it = data.begin() ; it != data.end(); ++it, ++i )
170  pData[i] = (unsigned char)( (*it - lb) / (ub - lb) * 255 );
171  } else {
172  for( it = data.begin() ; it != data.end(); ++it, ++i )
173  pData[i] = (unsigned char)( *it );
174  }
175  detail::writePGM(fileName, pData, sx, sy);
176  delete [] pData;
177 }
178 
179 /// \brief Exports a set of filters as a grid image
180 ///
181 /// It is assumed that the filters each form a row in the filter-matrix.
182 /// Moreover, the sizes of the filter images has to be given and it must gold width*height=W.size2().
183 /// The filters a re printed on a single image as a grid. The grid will be close to square. And the
184 /// image are separated by a black 1 pixel wide line.
185 /// The output will be normalized so that all images are on the same scale.
186 /// \param basename File to write to. ".pgm" is appended to the filename
187 /// \param filters Matrix storing the filters row by row
188 /// \param width Width of the filter image
189 /// \param height Height of th filter image
190 inline void exportFiltersToPGMGrid(std::string const& basename, RealMatrix const& filters,std::size_t width, std::size_t height) {
191  SIZE_CHECK(filters.size2() == width*height);
192  //try to get a square image
193  std::size_t gridX = std::size_t(std::sqrt(double(filters.size1())));
194  std::size_t gridY = gridX;
195  while(gridX*gridY < filters.size1()) ++gridX;
196 
197  RealMatrix image((height+1)*gridY,(width+1)*gridX,min(filters));
198 
199  for(std::size_t filter = 0; filter != filters.size1(); ++filter){
200  //get grid position from filter
201  std::size_t i = filter/gridX;
202  std::size_t j = filter%gridX;
203  std::size_t startY = (height+1)*i;
204  std::size_t startX = (width+1)*j;
205  //copy images
206  noalias(subrange(image,startY,startY+height,startX,startX+width)) = to_matrix(row(filters,filter),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 
216 /// \brief Exports a set of filters as a grid image
217 ///
218 /// It is assumed that the filters each form a row in the filter-matrix.
219 /// Moreover, the sizes of the filter images has to be given and it must gold width*height=W.size2().
220 /// The filters a re printed on a single image as a grid. The grid will be close to square. And the
221 /// image are separated by a black 1 pixel wide line.
222 /// The output will be normalized so that all images are on the same scale.
223 /// \param basename File to write to. ".pgm" is appended to the filename
224 /// \param filters Matrix storing the filters row by row
225 /// \param width Width of the filter image
226 /// \param height Height of th filter image
227 inline void exportFiltersToPGMGrid(std::string const& basename, Data<RealVector> const& filters,std::size_t width, std::size_t height) {
228  SIZE_CHECK(dataDimension(filters) == width*height);
229  //try to get a square image
230  std::size_t numFilters = filters.numberOfElements();
231  std::size_t gridX = std::size_t(std::sqrt(double(numFilters)));
232  std::size_t gridY = gridX;
233  while(gridX*gridY < numFilters) ++gridX;
234 
235  double minimum = std::numeric_limits<double>::max();
236  for(std::size_t i = 0; i != filters.numberOfBatches(); ++i){
237  minimum =std::min(minimum,min(filters.batch(i)));
238  }
239 
240  RealMatrix image((height+1)*gridY,(width+1)*gridX,minimum);
241 
242  for(std::size_t filter = 0; filter != numFilters; ++filter){
243  //get grid position from filter
244  std::size_t i = filter/gridX;
245  std::size_t j = filter%gridX;
246  std::size_t startY = (height+1)*i;
247  std::size_t startX = (width+1)*j;
248  RealVector filterImage =filters.element(filter);
249  //copy images
250  noalias(subrange(image,startY,startY+height,startX,startX+width)) = to_matrix(filterImage,height,width);
251  }
252  exportPGM(
253  (basename+".pgm").c_str(),
254  blas::adapt_vector((height+1)*gridY*(width+1)*gridX,&image(0,0)),
255  (width+1)*gridX, (height+1)*gridY,
256  true);
257 }
258 
259 
260 /// \brief Stores name and size of image externally
261 ///
263  std::size_t x;
264  std::size_t y;
265  std::string name;
266 
267  template<typename Archive>
268  void serialize(Archive & ar, const unsigned int) {
269  ar & x;
270  ar & y;
271  ar & name;
272  }
273 };
274 
275 /// \brief Import PGM images scanning a directory recursively
276 ///
277 /// \param p Directory
278 /// \param container Container storing images
279 /// \param info Vector storing image informations
280 template<class T>
281 void importPGMDir(const std::string &p, T &container, std::vector<ImageInformation> &info)
282 {
283  typedef typename T::value_type InputType;
284 
285 
286  if (boost::filesystem::is_directory(p)) {
287  for (boost::filesystem::recursive_directory_iterator itr(p); itr!=boost::filesystem::recursive_directory_iterator(); ++itr) {
288  if (boost::filesystem::is_regular(itr->status())) {
289  if ((boost::filesystem::extension(itr->path()) == ".PGM") ||
290  (boost::filesystem::extension(itr->path()) == ".pgm")) {
291  InputType img;
292  ImageInformation imgInfo;
293  importPGM(itr->path().string().c_str(), img, imgInfo.x, imgInfo.y);
294  imgInfo.name = itr->path().filename().string().c_str();
295  container.push_back(img);
296  info.push_back(imgInfo);
297  }
298  }
299  }
300  } else {
301  throw( std::invalid_argument( "[importPGMDir] cannot open file" ) );
302  }
303 }
304 
305 /// \brief Import PGM images scanning a directory recursively
306 ///
307 /// \param p Directory
308 /// \param set Set storing images
309 /// \param setInfo Vector storing image informations
310 template<class T>
311 void importPGMSet(const std::string &p, Data<T> &set, Data<ImageInformation> &setInfo)
312 {
313  std::vector<T> tmp;
314  std::vector<ImageInformation> tmpInfo;
315  importPGMDir(p, tmp, tmpInfo);
316  set = createDataFromRange(tmp);
317  setInfo = createDataFromRange(tmpInfo);
318 }
319 
320 /** @}*/
321 
322 } // end namespace shark
323 #endif