//===========================================================================
/*!
*
*
* \brief Importing and exporting PGM images
*
*
*
* \author C. Igel
* \date 2011
*
*
* \par Copyright 1995-2017 Shark Development Team
*
*
* This file is part of Shark.
*
*
* Shark is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Shark is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Shark. If not, see .
*
*/
//===========================================================================
#ifndef SHARK_DATA_IMPORT_PGM_H
#define SHARK_DATA_IMPORT_PGM_H
#include
#include
#include
#include
#include
#include
namespace shark {
namespace detail {
void importPGM( std::string const& fileName, std::vector& ppData, std::size_t& sx, std::size_t& sy )
{
std::ifstream file(fileName.c_str(), std::ios::binary);
SHARK_RUNTIME_CHECK(file, "Can not open File");
std::string id;
std::size_t nGrayValues = 0;
file>> id;
SHARK_RUNTIME_CHECK(id == "P5" , "File " + fileName+ "is not a pgm");
//ignore comments
file >> std::ws;//skip white space
while(file.peek() == '#'){
file.ignore(std::numeric_limits::max(), '\n');
}
file >> sx >> sy >> nGrayValues;
SHARK_RUNTIME_CHECK(file, "Error reading file!");
SHARK_RUNTIME_CHECK(nGrayValues <= 255, "File " + fileName+ "unsupported format");
ppData.resize(sx*sy);
file.read((char*)ppData.data(),sx*sy);
SHARK_RUNTIME_CHECK(file, "Error reading file!");
}
/**
* \ingroup shark_globals
*
* @{
*/
/// \brief Writes a PGM file.
///
/// \param fileName File to write to
/// \param pData unsigned char pointer to the data
/// \param sx Width of image
/// \param sy Height of image
void writePGM( std::string const& fileName, std::vector const& data, std::size_t sx, std::size_t sy )
{
std::ofstream file(fileName.c_str(), std::ios::binary);
SHARK_RUNTIME_CHECK(file, "Can not open File");
file<<"P5\n"<
void importPGM( std::string const& fileName, T& data, std::size_t& sx, std::size_t& sy ) {
std::vector rawData;
detail::importPGM(fileName, rawData, sx, sy);
data.resize(sx*sy);
std::copy(rawData.begin(), rawData.end(), data.begin());
}
/// \brief Export a PGM image to file
///
/// \param fileName File to write to
/// \param data Linear object storing image
/// \param sx Width of image
/// \param sy Height of image
/// \param normalize Adjust values to [0,255], default false
template
void exportPGM(std::string const& fileName, T const& data, std::size_t sx, std::size_t sy, bool normalize = false) {
SIZE_CHECK(sx*sy == data.size());
std::vector rawData(data.size());
typename T::const_iterator it = data.begin();
std::size_t i = 0;
if(normalize) {
double lb = *std::min_element(data.begin(),data.end());
double ub = *std::max_element(data.begin(), data.end());
for( it = data.begin() ; it != data.end(); ++it, ++i )
rawData[i] = (unsigned char)( (*it - lb) / (ub - lb) * 255 );
} else {
for( it = data.begin() ; it != data.end(); ++it, ++i )
rawData[i] = (unsigned char)( *it );
}
detail::writePGM(fileName, rawData, sx, sy);
}
/// \brief Exports a set of filters as a grid image
///
/// It is assumed that the filters each form a row in the filter-matrix.
/// Moreover, the sizes of the filter images has to be given and it must gold width*height=W.size2().
/// The filters a re printed on a single image as a grid. The grid will be close to square. And the
/// image are separated by a black 1 pixel wide line.
/// The output will be normalized so that all images are on the same scale.
/// \param basename File to write to. ".pgm" is appended to the filename
/// \param filters Matrix storing the filters row by row
/// \param width Width of the filter image
/// \param height Height of th filter image
inline void exportFiltersToPGMGrid(std::string const& basename, RealMatrix const& filters,std::size_t width, std::size_t height) {
SIZE_CHECK(filters.size2() == width*height);
//try to get a square image
std::size_t gridX = std::size_t(std::sqrt(double(filters.size1())));
std::size_t gridY = gridX;
while(gridX*gridY < filters.size1()) ++gridX;
RealMatrix image((height+1)*gridY,(width+1)*gridX,min(filters));
for(std::size_t filter = 0; filter != filters.size1(); ++filter){
//get grid position from filter
std::size_t i = filter/gridX;
std::size_t j = filter%gridX;
std::size_t startY = (height+1)*i;
std::size_t startX = (width+1)*j;
//copy images
noalias(subrange(image,startY,startY+height,startX,startX+width)) = to_matrix(row(filters,filter),height,width);
}
exportPGM(
(basename+".pgm").c_str(),
blas::adapt_vector((height+1)*gridY*(width+1)*gridX,&image(0,0)),
(width+1)*gridX, (height+1)*gridY,
true
);
}
/// \brief Exports a set of filters as a grid image
///
/// It is assumed that the filters each form a row in the filter-matrix.
/// Moreover, the sizes of the filter images has to be given and it must gold width*height=W.size2().
/// The filters a re printed on a single image as a grid. The grid will be close to square. And the
/// image are separated by a black 1 pixel wide line.
/// The output will be normalized so that all images are on the same scale.
/// \param basename File to write to. ".pgm" is appended to the filename
/// \param filters Matrix storing the filters row by row
/// \param width Width of the filter image
/// \param height Height of th filter image
inline void exportFiltersToPGMGrid(std::string const& basename, Data const& filters,std::size_t width, std::size_t height) {
SIZE_CHECK(dataDimension(filters) == width*height);
//try to get a square image
std::size_t numFilters = filters.numberOfElements();
std::size_t gridX = std::size_t(std::sqrt(double(numFilters)));
std::size_t gridY = gridX;
while(gridX*gridY < numFilters) ++gridX;
double minimum = std::numeric_limits::max();
for(std::size_t i = 0; i != filters.numberOfBatches(); ++i){
minimum =std::min(minimum,min(filters.batch(i)));
}
RealMatrix image((height+1)*gridY,(width+1)*gridX,minimum);
for(std::size_t filter = 0; filter != numFilters; ++filter){
//get grid position from filter
std::size_t i = filter/gridX;
std::size_t j = filter%gridX;
std::size_t startY = (height+1)*i;
std::size_t startX = (width+1)*j;
RealVector filterImage =filters.element(filter);
//copy images
noalias(subrange(image,startY,startY+height,startX,startX+width)) = to_matrix(filterImage,height,width);
}
exportPGM(
(basename+".pgm").c_str(),
blas::adapt_vector((height+1)*gridY*(width+1)*gridX,&image(0,0)),
(width+1)*gridX, (height+1)*gridY,
true);
}
/// \brief Import PGM images scanning a directory recursively
///
/// All images are required to have the same size. the shape of the images is stored in set.shape()
///
/// \param p Directory
/// \param set Set storing images
template
void importPGMSet(std::string const&p, Data &set){
std::vector container;
std::vector > info;
if (boost::filesystem::is_directory(p)) {
for (boost::filesystem::recursive_directory_iterator itr(p); itr!=boost::filesystem::recursive_directory_iterator(); ++itr) {
if (boost::filesystem::is_regular(itr->status())) {
if ((boost::filesystem::extension(itr->path()) == ".PGM") ||
(boost::filesystem::extension(itr->path()) == ".pgm")) {
T img;
std::pair imgInfo;
importPGM(itr->path().string().c_str(), img, imgInfo.first, imgInfo.second);
container.push_back(img);
info.push_back(imgInfo);
}
}
}
} else {
throw( std::invalid_argument( "[importPGMDir] cannot open file" ) );
}
//check all images have same size
for(auto const& i: info){
if(i.first != info.front().first || i.second != info.front().second){
throw SHARKEXCEPTION("[importPGMSet] all images are required to have the same size");
}
}
set = createDataFromRange(container);
set.shape() = {info.front().second,info.front().first};
}
/** @}*/
} // end namespace shark
#endif