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 * <https://shark-ml.github.io/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 <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
48namespace shark {
49
50/**
51 * \addtogroup shark_globals
52 * @{
53 */
54
55namespace detail {
56void importPGM( std::string const& fileName, std::vector<unsigned char>& ppData, std::size_t& sx, std::size_t& sy )
57{
58 std::ifstream file(fileName.c_str(), std::ios::binary);
59 SHARK_RUNTIME_CHECK(file, "Can not open File");
60
61 std::string id;
62 std::size_t nGrayValues = 0;
63 file>> id;
64 SHARK_RUNTIME_CHECK(id == "P5" , "File " + fileName+ "is not a pgm");
65 //ignore comments
66 file >> std::ws;//skip white space
67 while(file.peek() == '#'){
68 file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
69 }
70 file >> sx >> sy >> nGrayValues;
71 SHARK_RUNTIME_CHECK(file, "Error reading file!");
72 SHARK_RUNTIME_CHECK(nGrayValues <= 255, "File " + fileName+ "unsupported format");
73
74 ppData.resize(sx*sy);
75 file.read((char*)ppData.data(),sx*sy);
76 SHARK_RUNTIME_CHECK(file, "Error reading file!");
77}
78
79/// \brief Writes a PGM file.
80///
81/// \param fileName File to write to
82/// \param pData unsigned char pointer to the data
83/// \param sx Width of image
84/// \param sy Height of image
85void writePGM( std::string const& fileName, std::vector<unsigned char> const& data, std::size_t sx, std::size_t sy )
86{
87 std::ofstream file(fileName.c_str(), std::ios::binary);
88 SHARK_RUNTIME_CHECK(file, "Can not open File");
89
90 file<<"P5\n"<<sx<<" "<<sy<<"\n"<<255<<"\n";
91 file.write((char*)data.data(),sx*sy);
92}
93} // end namespace detail
94
95/// \brief Import a PGM image from file
96///
97/// \param fileName The file to read from
98/// \param data Linear object for storing image
99/// \param sx Width of imported image
100/// \param sy Height of imported image
101template <class T>
102void importPGM( std::string const& fileName, T& data, std::size_t& sx, std::size_t& sy ) {
103 std::vector<unsigned char> rawData;
104 detail::importPGM(fileName, rawData, sx, sy);
105 data.resize(sx*sy);
106 std::copy(rawData.begin(), rawData.end(), data.begin());
107}
108
109/// \brief Export a PGM image to file
110///
111/// \param fileName File to write to
112/// \param data Linear object storing image
113/// \param sx Width of image
114/// \param sy Height of image
115/// \param normalize Adjust values to [0,255], default false
116template <class T>
117void exportPGM(std::string const& fileName, T const& data, std::size_t sx, std::size_t sy, bool normalize = false) {
118 SIZE_CHECK(sx*sy == data.size());
119 std::vector<unsigned char> rawData(data.size());
120 typename T::const_iterator it = data.begin();
121 std::size_t i = 0;
122 if(normalize) {
123 double lb = *std::min_element(data.begin(),data.end());
124 double ub = *std::max_element(data.begin(), data.end());
125 for( it = data.begin() ; it != data.end(); ++it, ++i )
126 rawData[i] = (unsigned char)( (*it - lb) / (ub - lb) * 255 );
127 } else {
128 for( it = data.begin() ; it != data.end(); ++it, ++i )
129 rawData[i] = (unsigned char)( *it );
130 }
131 detail::writePGM(fileName, rawData, sx, sy);
132}
133
134/// \brief Exports a set of filters as a grid image
135///
136/// It is assumed that the filters each form a row in the filter-matrix.
137/// Moreover, the sizes of the filter images has to be given and it must gold width*height=W.size2().
138/// The filters a re printed on a single image as a grid. The grid will be close to square. And the
139/// image are separated by a black 1 pixel wide line.
140/// The output will be normalized so that all images are on the same scale.
141/// \param basename File to write to. ".pgm" is appended to the filename
142/// \param filters Matrix storing the filters row by row
143/// \param width Width of the filter image
144/// \param height Height of th filter image
145inline void exportFiltersToPGMGrid(std::string const& basename, RealMatrix const& filters,std::size_t width, std::size_t height) {
146 SIZE_CHECK(filters.size2() == width*height);
147 //try to get a square image
148 std::size_t gridX = std::size_t(std::sqrt(double(filters.size1())));
149 std::size_t gridY = gridX;
150 while(gridX*gridY < filters.size1()) ++gridX;
151
152 RealMatrix image((height+1)*gridY,(width+1)*gridX,min(filters));
153
154 for(std::size_t filter = 0; filter != filters.size1(); ++filter){
155 //get grid position from filter
156 std::size_t i = filter/gridX;
157 std::size_t j = filter%gridX;
158 std::size_t startY = (height+1)*i;
159 std::size_t startX = (width+1)*j;
160 //copy images
161 noalias(subrange(image,startY,startY+height,startX,startX+width)) = to_matrix(row(filters,filter),height,width);
162 }
163 exportPGM(
164 (basename+".pgm").c_str(),
165 blas::adapt_vector((height+1)*gridY*(width+1)*gridX,&image(0,0)),
166 (width+1)*gridX, (height+1)*gridY,
167 true
168 );
169}
170
171/// \brief Exports a set of filters as a grid image
172///
173/// It is assumed that the filters each form a row in the filter-matrix.
174/// Moreover, the sizes of the filter images has to be given and it must gold width*height=W.size2().
175/// The filters a re printed on a single image as a grid. The grid will be close to square. And the
176/// image are separated by a black 1 pixel wide line.
177/// The output will be normalized so that all images are on the same scale.
178/// \param basename File to write to. ".pgm" is appended to the filename
179/// \param filters Matrix storing the filters row by row
180/// \param width Width of the filter image
181/// \param height Height of th filter image
182inline void exportFiltersToPGMGrid(std::string const& basename, Data<RealVector> const& filters,std::size_t width, std::size_t height) {
183 SIZE_CHECK(dataDimension(filters) == width*height);
184 //try to get a square image
185 std::size_t numFilters = filters.numberOfElements();
186 std::size_t gridX = std::size_t(std::sqrt(double(numFilters)));
187 std::size_t gridY = gridX;
188 while(gridX*gridY < numFilters) ++gridX;
189
190 double minimum = std::numeric_limits<double>::max();
191 for(std::size_t i = 0; i != filters.numberOfBatches(); ++i){
192 minimum =std::min(minimum,min(filters.batch(i)));
193 }
194
195 RealMatrix image((height+1)*gridY,(width+1)*gridX,minimum);
196
197 for(std::size_t filter = 0; filter != numFilters; ++filter){
198 //get grid position from filter
199 std::size_t i = filter/gridX;
200 std::size_t j = filter%gridX;
201 std::size_t startY = (height+1)*i;
202 std::size_t startX = (width+1)*j;
203 RealVector filterImage =filters.element(filter);
204 //copy images
205 noalias(subrange(image,startY,startY+height,startX,startX+width)) = to_matrix(filterImage,height,width);
206 }
207 exportPGM(
208 (basename+".pgm").c_str(),
209 blas::adapt_vector((height+1)*gridY*(width+1)*gridX,&image(0,0)),
210 (width+1)*gridX, (height+1)*gridY,
211 true);
212}
213
214/// \brief Import PGM images scanning a directory recursively
215///
216/// All images are required to have the same size. the shape of the images is stored in set.shape()
217///
218/// \param p Directory
219/// \param set Set storing images
220template<class T>
221void importPGMSet(std::string const&p, Data<T> &set){
222 std::vector<T> container;
223 std::vector<std::pair<std::size_t,std::size_t> > info;
224 if (boost::filesystem::is_directory(p)) {
225 for (boost::filesystem::recursive_directory_iterator itr(p); itr!=boost::filesystem::recursive_directory_iterator(); ++itr) {
226 if (boost::filesystem::is_regular(itr->status())) {
227 if ((boost::filesystem::extension(itr->path()) == ".PGM") ||
228 (boost::filesystem::extension(itr->path()) == ".pgm")) {
229 T img;
230 std::pair<std::size_t,std::size_t> imgInfo;
231 importPGM(itr->path().string().c_str(), img, imgInfo.first, imgInfo.second);
232 container.push_back(img);
233 info.push_back(imgInfo);
234 }
235 }
236 }
237 } else {
238 throw( std::invalid_argument( "[importPGMDir] cannot open file" ) );
239 }
240
241 //check all images have same size
242 for(auto const& i: info){
243 if(i.first != info.front().first || i.second != info.front().second){
244 throw SHARKEXCEPTION("[importPGMSet] all images are required to have the same size");
245 }
246 }
247 set = createDataFromRange(container);
248 set.shape() = {info.front().second,info.front().first};
249}
250
251/** @}*/
252
253} // end namespace shark
254#endif