/* -*- 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
*
*/
// $Id: oprojection.cc,v 1.2 2006-07-24 13:08:27 wollny Exp $
/*! \brief eva-2dimagefilter
\sa 3va-2dimagefilter.cc
\file mask.cc
\author G. Wollny, wollny eva.mpg.de, 2005
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <iostream>
#include <iomanip>
#include <string>
#include <stdexcept>
#include <dlfcn.h>
#include <boost/shared_ptr.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/lambda/lambda.hpp>
#include <libmona/filter_plugin2d.hh>
#include <libmona/2DImageWrap.hh>
#include <libmona/monaException.hh>
#include <libmona/monaHistory.hh>
#include <libmona/filetools.hh>
#include <libmona/fnameana.hh>
using namespace std;
using namespace mona;
using namespace boost::lambda;
class CMaxIntenseFilter: public TUnaryImageFilter<void> {
public:
CMaxIntenseFilter(size_t dz);
template <class T>
typename CMaxIntenseFilter::result_type operator () (const T2DImage<T>& data);
bool save(const string& filebase, const string& type, const C2DImageIOPluginHandler& imageio) const;
private:
bool save(const string& filebase, const char *view, const string& type, const C2DImageWrap& image, const C2DImageIOPluginHandler& imageio) const;
bool _M_initialized;
size_t _M_slice;
size_t _M_dz;
C2DImageWrap _M_axial;
C2DImageWrap _M_coronal;
C2DImageWrap _M_saggital;
};
class CAvgIntenseFilter: public TUnaryImageFilter<void> {
public:
CAvgIntenseFilter(size_t dz);
template <class T>
typename CAvgIntenseFilter::result_type operator () (const T2DImage<T>& data);
bool save(const string& filebase, const string& type, const C2DImageIOPluginHandler& imageio) const;
private:
template <class T>
typename CAvgIntenseFilter::result_type operator () (T2DImage<T>& data, const C2DFImage& input);
bool save(const string& filebase, const char *view, const string& type, const C2DFImage& input, const C2DImageIOPluginHandler& imageio) const;
bool _M_initialized;
size_t _M_slice;
size_t _M_dz;
boost::shared_ptr<C2DFImage> _M_axial_sum;
boost::shared_ptr<C2DFImage> _M_coronal_sum;
boost::shared_ptr<C2DFImage> _M_saggital_sum;
C2DImageWrap _M_prototype;
};
CMaxIntenseFilter::CMaxIntenseFilter(size_t dz):
_M_initialized(false),
_M_slice(0),
_M_dz(dz)
{
}
bool CMaxIntenseFilter::save(const string& filebase, const char *view, const string& type, const C2DImageWrap& image, const C2DImageIOPluginHandler& imageio) const
{
C2DImageList list;
list.push_back(image);
stringstream ss;
ss << filebase << view << "." << type;
return imageio.save(type, list, ss.str());
}
bool CMaxIntenseFilter::save(const string& filebase, const string& type, const C2DImageIOPluginHandler& imageio) const
{
return save(filebase, "axial", type, _M_axial, imageio) &&
save(filebase, "coronal", type, _M_coronal, imageio) &&
save(filebase, "sagggital", type, _M_saggital, imageio);
}
template <class T>
typename CMaxIntenseFilter::result_type CMaxIntenseFilter::operator () (const T2DImage<T>& data)
{
if (!_M_initialized) {
_M_axial = C2DImageWrap(new T2DImage<T>(data.get_size()));
_M_coronal = C2DImageWrap(new T2DImage<T>(C2DBounds(data.get_size().x, _M_dz)));
_M_saggital = C2DImageWrap(new T2DImage<T>(C2DBounds(data.get_size().y, _M_dz)));
_M_initialized = true;
cvdebug() << "3D size is " << data.get_size().x << 'x' << data.get_size().y << 'x' << _M_dz << '\n';
}
T2DImage<T> *axial = _M_axial.get<T>();
T2DImage<T> *coronal = _M_coronal.get<T>();
T2DImage<T> *saggital = _M_saggital.get<T>();
if ( !axial || !coronal || !saggital) {
throw runtime_error("input images differ in type");
}
if (axial->get_size() != data.get_size())
throw runtime_error("input images differ in size");
assert(_M_slice < _M_dz);
typename T2DImage<T>::const_iterator inp = data.begin();
// axial aximum intensity
for (typename T2DImage<T>::iterator ai = axial->begin(), ae = axial->end();
ai != ae; ++inp, ++ai) {
if (*ai < *inp)
*ai = *inp;
}
// coronal maximum intensity
typename T2DImage<T>::iterator ci = coronal->begin();
advance(ci, coronal->get_size().x * _M_slice);
inp = data.begin();
for (size_t y = 0; y < data.get_size().y; ++y) {
typename T2DImage<T>::iterator cii = ci;
for (size_t x = 0; x < data.get_size().x; ++x, ++inp, ++cii) {
if (*cii < *inp)
*cii = *inp;
}
}
// saggital maximum intensity
typename T2DImage<T>::iterator si = saggital->begin();
advance(si, saggital->get_size().x * _M_slice);
inp = data.begin();
for (size_t y = 0; y < data.get_size().y; ++y, ++si) {
typename T2DImage<T>::value_type val = 0;
for (size_t x = 0; x < data.get_size().x; ++x, ++inp) {
if (val < *inp)
val = *inp;
}
*si = val;
}
++_M_slice;
}
CAvgIntenseFilter::CAvgIntenseFilter(size_t dz):
_M_initialized(false),
_M_slice(0),
_M_dz(dz)
{
}
class CCopyConvert : public TUnaryImageFilter<C2DImageWrap> {
const C2DFImage& _M_src;
public:
CCopyConvert(const C2DFImage& src):
_M_src(src)
{
}
template <class T>
typename CCopyConvert::result_type operator () (const T2DImage<T>& prototype)const
{
T2DImage<T> *output = new T2DImage<T>(_M_src.get_size());
typename T2DImage<T>::iterator outp = output->begin();
for (C2DFImage::const_iterator inp = _M_src.begin(), inpe = _M_src.end();
inp != inpe; ++inp, ++outp)
*outp = boost::numeric_cast<T>(*inp);
return C2DImageWrap(output);
}
};
bool CAvgIntenseFilter::save(const string& filebase, const char *view, const string& type,
const C2DFImage& input, const C2DImageIOPluginHandler& imageio) const
{
CCopyConvert cc(input);
C2DImageWrap image = wrap_filter(cc, _M_prototype);
C2DImageList list;
list.push_back(image);
stringstream ss;
ss << filebase << view << "." << type;
return imageio.save(type, list, ss.str());
}
bool CAvgIntenseFilter::save(const string& filebase, const string& type, const C2DImageIOPluginHandler& imageio) const
{
transform(_M_axial_sum->begin(), _M_axial_sum->end(), _M_axial_sum->begin(), _1 / _M_dz);
transform(_M_coronal_sum->begin(), _M_coronal_sum->end(), _M_coronal_sum->begin(), _1 / _M_axial_sum->get_size().x);
transform(_M_saggital_sum->begin(), _M_saggital_sum->end(), _M_saggital_sum->begin(), _1 / _M_axial_sum->get_size().y);
return save(filebase, "axial", type, *_M_axial_sum, imageio) &&
save(filebase, "coronal", type, *_M_coronal_sum, imageio) &&
save(filebase, "sagggital", type, *_M_saggital_sum, imageio);
}
template <class T>
typename CAvgIntenseFilter::result_type CAvgIntenseFilter::operator () (const T2DImage<T>& data)
{
if (!_M_initialized) {
_M_axial_sum.reset(new C2DFImage(data.get_size()));
_M_coronal_sum.reset(new C2DFImage(C2DBounds(data.get_size().x, _M_dz)));
_M_saggital_sum.reset(new C2DFImage(C2DBounds(data.get_size().y, _M_dz)));
_M_prototype = C2DImageWrap(new T2DImage<T>(C2DBounds(1,1)));
_M_initialized = true;
cvdebug() << "3D size is " << data.get_size().x << 'x' << data.get_size().y << 'x' << _M_dz << '\n';
}
if (_M_axial_sum->get_size() != data.get_size())
throw runtime_error("input images differ in size");
assert(_M_slice < _M_dz);
typename T2DImage<T>::const_iterator inp = data.begin();
// axial aximum intensity
for (C2DFImage::iterator ai = _M_axial_sum->begin(), ae = _M_axial_sum->end();
ai != ae; ++inp, ++ai) {
*ai += *inp;
}
// coronal maximum intensity
C2DFImage::iterator ci = _M_coronal_sum->begin();
advance(ci, _M_coronal_sum->get_size().x * _M_slice);
inp = data.begin();
for (size_t y = 0; y < data.get_size().y; ++y) {
C2DFImage::iterator cii = ci;
for (size_t x = 0; x < data.get_size().x; ++x, ++inp, ++cii) {
*cii += *inp;
}
}
// saggital maximum intensity
C2DFImage::iterator si = _M_saggital_sum->begin();
advance(si, _M_saggital_sum->get_size().x * _M_slice);
inp = data.begin();
for (size_t y = 0; y < data.get_size().y; ++y, ++si) {
for (size_t x = 0; x < data.get_size().x; ++x, ++inp) {
*si += *inp;
}
}
++_M_slice;
}
template <class F>
int filter(F filter, const vector<string>& src_files, C2DImageIOPluginHandler& imageio,
bool use_src_format, const string& cout_type, const string& out_filebase)
{
char new_line = cverb.show_debug() ? '\n' : '\r';
string out_type = cout_type;
for (vector<string>::const_iterator fname = src_files.begin(),
fend = src_files.end(); fname != fend; ++fname) {
cvmsg() << "Filter: " << *fname << new_line;
auto_ptr<C2DImageList> in_image_list(imageio.load(*fname));
if (in_image_list.get() && in_image_list->size()) {
if (use_src_format) {
out_type = in_image_list->get_sourceformat();
use_src_format = false;
}
wrap_filter(filter, *in_image_list->begin());
}
}
// write the three images
return filter.save(out_filebase, out_type, imageio) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* Revision string */
const char revision[] = "not specified";
int main( int argc, const char *argv[] )
{
string in_filename;
string out_filebase;
string out_type;
vector<string> filter_chain;
bool help_plugins = false;
C2DImageFilterHandler filter_plugins;
C2DImageIOPluginHandler imageio;
enum EOps {op_avg, op_max};
const popt::option::Dictionary op_dict[] = {
{"average", op_avg},
{"max", op_max},
{NULL, 0},
};
int op;
popt::COptions options;
options.push_back(popt::option( in_filename, "in-file", 'i', "input image(s) to be filtered", NULL));
options.push_back(popt::option( out_filebase, "out-base", 'o', "projection output file name base", "maximum"));
options.push_back(popt::option( out_type, imageio.get_set(), "type", 't', "output file type", NULL));
options.push_back(popt::option( op, op_dict, "op", 'p', "operation to apply", "max"));
try {
popt::parse_options(argc, argv, options, filter_chain);
cvdebug() << "IO supported types: " << imageio.plugin_names() << "\n";
if (help_plugins) {
filter_plugins.print_help();
return EXIT_SUCCESS;
}
if ( in_filename.empty() )
throw mona_runtime_error("'--in-file' ('i') option required");
if ( out_filebase.empty() )
throw mona_runtime_error("'--out-base' ('o') option required");
bool use_src_format = out_type.empty();
if (!use_src_format && !imageio.find_plugin_by_name(out_type)) {
cvwarn() << "Output file format not supported, revert to input file format\n";
use_src_format = true;
}
CHistory::instance().append(argv[0], revision, options);
vector<string> src_files = get_consecutive_numbered_files(in_filename);
if (src_files.empty())
throw invalid_argument(string("no files match pattern ") + in_filename);
cvdebug() << "filter " << src_files.size() << "images\n";
switch (op) {
case op_avg: return filter(CAvgIntenseFilter(src_files.size()), src_files, imageio, use_src_format, out_type, out_filebase);
case op_max: return filter(CMaxIntenseFilter(src_files.size()), src_files, imageio, use_src_format, out_type, out_filebase);
default: assert(!"unknown filter option returned - not possible at this point");
}
return EXIT_SUCCESS;
}
catch (const mona_runtime_error& e){
cerr << argv[0] << " error: " << e.what() << endl;
}
catch (const mona_fatal_error& e){
cerr << argv[0] << " fatal: " << e.what() << endl;
}
catch (const mona_exception& e){
cerr << argv[0] << " error: " << e.what() << endl;
}
catch (const runtime_error &e){
cerr << argv[0] << " runtime: " << e.what() << endl;
}
catch (const invalid_argument &e){
cerr << argv[0] << " error: " << e.what() << endl;
}
catch (const exception& e){
cerr << argv[0] << " error: " << e.what() << endl;
}
catch (...){
cerr << argv[0] << " unknown exception" << endl;
}
return EXIT_FAILURE;
}