/* -*- mona-c++ -*-
* Copyright (c) Leipzig, Madrid 2004 - 2008
* Max-Planck-Institute for Human Cognitive and Brain Science
* Max-Planck-Institute for Evolutionary Anthropology
* BIT, ETSI Telecomunicacion, UPM
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <libmona/monaIOStream.hh>
#include <libmona/monaException.hh>
#include <libmona/2DImageWrap.hh>
#include <map>
#include <stdexcept>
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost/type_traits.hpp>
using namespace mona;
using namespace std;
typedef pair<int, int> CClassRange;
typedef map<double, CClassRange> CClassMap;
class CMapImage {
public:
typedef C2DImageWrap result_type;
CMapImage(const vector<int>& map, int nclasses);
template <typename Image2D>
CMapImage::result_type operator ()(const Image2D& image)const;
private:
const vector<int>& _M_map;
int _M_nclasses;
};
class CSMapImage {
public:
typedef C2DImageList result_type;
CSMapImage(const vector<int>& map, int nclasses);
template <typename Image2D>
CSMapImage::result_type operator ()(const Image2D& image)const;
private:
const vector<int>& _M_map;
int _M_nclasses;
};
template <typename Image2D>
C2DImageWrap map_to_bit_image(const Image2D& image, const vector<int>& map)
{
typename Image2D::const_iterator ii = image.begin();
typename Image2D::const_iterator ie = image.end();
C2DBitImage *result = new C2DBitImage(image.get_size());
C2DBitImage::iterator t = result->begin();
while (ii != ie)
*t++ = map[(int)*ii++] == 1 ? true : false;
return C2DImageWrap(result);
}
template <typename OutImage, typename Image2D>
C2DImageWrap map_classes(const Image2D& image, const vector<int>& map)
{
typename Image2D::const_iterator ii = image.begin();
typename Image2D::const_iterator ie = image.end();
OutImage *result = new OutImage(image.get_size());
typename OutImage::iterator t = result->begin();
while (ii != ie)
*t++ = map[(int)*ii++];
return C2DImageWrap(result);
}
CMapImage::CMapImage(const vector<int>& map, int nclasses):
_M_map(map),
_M_nclasses(nclasses)
{
}
template <typename Image2D>
CMapImage::result_type CMapImage::operator ()(const Image2D& image) const
{
if (numeric_limits<typename Image2D::value_type>::max() > (typename Image2D::value_type)_M_map.size()) {
stringstream errmsg;
errmsg << "input image range larger then map span: " << (int)numeric_limits<typename Image2D::value_type>::max()
<<" vs." << _M_map.size();
throw mona_runtime_error(errmsg.str());
}
if (_M_nclasses < 3)
return map_to_bit_image(image, _M_map);
else if (_M_nclasses < 256)
return map_classes<C2DUBImage>(image, _M_map);
else
return map_classes<C2DUSImage>(image, _M_map);
}
CSMapImage::CSMapImage(const vector<int>& map, int nclasses):
_M_map(map),
_M_nclasses(nclasses)
{
}
template <typename Image2D, bool integral>
struct __dispatcher {
static CSMapImage::result_type apply(const Image2D& image, int nclasses, const vector<int>& map)
{
throw invalid_argument("unsupported input image type");
return CSMapImage::result_type();
}
};
template <typename Image2D>
struct __dispatcher<Image2D, true> {
static CSMapImage::result_type apply(const Image2D& image, int nclasses, const vector<int>& map)
{
vector<C2DBitImage*> r(nclasses);
vector<C2DBitImage::iterator> ov(nclasses);
for (int i = 0; i < nclasses; ++i) {
r[i] = new C2DBitImage(image.get_size());
ov[i] = r[i]->begin();
}
typename Image2D::const_iterator ii = image.begin();
typename Image2D::const_iterator ie = image.end();
while (ii != ie) {
*ov[map[*ii++]] = true;
for (int i = 0; i < nclasses; ++i)
++ov[i];
}
C2DImageList result;
for (int i = 0; i < nclasses; ++i)
result.push_back(C2DImageWrap(r[i]));
return result;
}
};
template <typename Image2D>
CSMapImage::result_type CSMapImage::operator ()(const Image2D& image)const
{
const bool is_integral = ::boost::is_integral<typename Image2D::value_type>::value;
return __dispatcher<Image2D, is_integral>::apply(image, _M_nclasses, _M_map);
}
CClassMap read_map(const string& filename)
{
ifstream inp(filename.c_str());
if (!inp.good()) {
stringstream errmsg;
errmsg << "unable to open " << filename << " for reading";
throw mona_fatal_error(errmsg.str());
}
int nclasses;
inp >> nclasses;
CClassMap result;
while (inp.good() && nclasses--) {
CClassRange range;
double center;
inp >> center >> range.first >> range.second;
result[center] = range;
}
if (nclasses>0) {
stringstream errmsg;
errmsg << "Could not read all classes from " << filename;
throw mona_fatal_error(errmsg.str());
}
return result;
}
vector<int> get_mapper(const CClassMap& map)
{
vector<int> result;
int iclass = 0;
int pos = 0;
CClassMap::const_iterator im = map.begin();
CClassMap::const_iterator em = map.end();
while (im != em) {
while (pos < im->second.second) {
result.push_back(iclass);
++pos;
}
++iclass;
++im;
}
return result;
}
int main(int argc, const char *args[])
{
string in_filename;
string out_filename;
string map_filename;
bool split;
popt::COptions options;
options.push_back(popt::option( in_filename, "in-file", 'i', "input image(s) to be remapped", NULL));
options.push_back(popt::option( out_filename, "out-file", 'o', "output image(s) to be remapped", NULL));
options.push_back(popt::option( map_filename, "map", 'm', "kmeans map file", NULL));
options.push_back(popt::option( split, "split", 's', "split the input file into a bit-file per class"));
try {
vector<string> non_args;
popt::parse_options(argc, args, options, non_args);
if (!non_args.empty()) {
throw mona_fatal_error("unrecognised arguments detected, try 'eva-remap --help'");
}
if (map_filename.empty())
throw mona_fatal_error("required argument --map missing");
if (in_filename.empty())
throw mona_fatal_error("required argument --in-file missing");
if (out_filename.empty())
throw mona_fatal_error("required argument --out-file missing");
CClassMap cmap = read_map(map_filename);
vector<int> map = get_mapper(cmap);
C2DImageIOPluginHandler imageio;
auto_ptr<C2DImageList> in_image_list(imageio.load(in_filename));
if (!in_image_list.get() || !in_image_list->size())
throw mona_runtime_error(string("no images found in ") + in_filename);
if (split) {
CSMapImage mapper(map, cmap.size());
C2DImageList out_list = wrap_filter(mapper, *in_image_list->begin());
if ( !imageio.save(in_image_list->get_sourceformat(), out_list, out_filename) ){
string not_save = ("unable to save result to ") + out_filename;
throw mona_runtime_error(not_save);
};
}else{
CMapImage mapper(map, cmap.size());
for (C2DImageList::iterator img = in_image_list->begin();
img != in_image_list->end(); ++img)
*img = wrap_filter(mapper, *img);
if ( !imageio.save(in_image_list->get_sourceformat(), *in_image_list, out_filename) ){
string not_save = ("unable to save result to ") + out_filename;
throw mona_runtime_error(not_save);
};
}
return EXIT_SUCCESS;
}
catch (const mona_runtime_error& e){
cerr << args[0] << " error: " << e.what() << "\n";
}
catch (const mona_fatal_error& e){
cerr << args[0] << " fatal: " << e.what() << "\n";
}
catch (const mona_exception& e){
cerr << args[0] << " error: " << e.what() << "\n";
}
catch (const runtime_error &e){
cerr << args[0] << " runtime: " << e.what() << "\n";
}
catch (const invalid_argument &e){
cerr << args[0] << " error: " << e.what() << "\n";
}
catch (const exception& e){
cerr << args[0] << " error: " << e.what() << "\n";
}
catch (...){
cerr << args[0] << " unknown exception" << "\n";
}
return EXIT_FAILURE;
}