/* -*- 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: downscale3d.cc,v 1.14 2006-07-12 13:44:28 wollny Exp $
/*! \brief eva-downscale
\sa eva-downscale3d.cc
\file downscale.cc
\author G. Wollny, wollny eva.mpg.de, 2005
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string>
#include <stdexcept>
#include <libmona/monaHistory.hh>
#include <libmona/monaException.hh>
#include <libmona/monaPopt.hh>
#include <libmona/3DVector.hh>
#include <libmona/filter.hh>
#include <libmona/filter_plugin2d.hh>
#include <libmona/2DImageWrap.hh>
#include <libmona/fnameana.hh>
#include <libmona/filetools.hh>
using namespace std;
using namespace mona;
class CAdder {
public:
typedef void result_type;
CAdder(const T1DFilterKernel& filter, const C2DBounds& size);
void reset();
const C2DFImage& get_result()const;
template <typename Image>
result_type operator() (Image& image);
private:
const T1DFilterKernel _M_filter;
C2DFImage _M_image;
int _M_pos;
};
CAdder::CAdder(const T1DFilterKernel& filter, const C2DBounds& size):
_M_filter(filter),
_M_image(size),
_M_pos(0)
{
}
void CAdder::reset()
{
fill(_M_image.begin(), _M_image.end(), 0.0f);
_M_pos = 0;
}
const C2DFImage& CAdder::get_result()const
{
return _M_image;
}
struct apfy {
apfy(float f):
_M_f(f)
{
}
float operator () (float a, float b) {
return a + _M_f * b;
}
private:
float _M_f;
};
template <typename Image>
CAdder::result_type CAdder::operator() (Image& image)
{
assert(_M_pos < _M_filter.get_size());
if (_M_image.get_size() != image.get_size()) {
stringstream errmsg;
errmsg << "images differ in size: (" << _M_image.get_size().x << ", " << _M_image.get_size().y << ") vs. (" <<
image.get_size().x << ", " << image.get_size().y << ")\n";
throw invalid_argument(errmsg.str());
}
apfy f(_M_filter[_M_pos++]);
transform(_M_image.begin(), _M_image.end(), image.begin(), _M_image.begin(), f);
}
template <typename Out>
struct Convert {
template <typename In>
Out operator ()(In x) {
return Out(x);
}
};
struct CConverter {
typedef C2DImageWrap result_type;
CConverter(const C2DFImage& orig):
_M_image(orig)
{
}
template <typename Image>
C2DImageWrap operator ()(const Image& prototype)
{
Image *result = new Image(prototype.get_size());
Convert<typename Image::value_type> convert;
transform(_M_image.begin(), _M_image.end(), result->begin(), convert);
return C2DImageWrap(result);
}
private:
const C2DFImage& _M_image;
};
C2DImageWrap convolute(const list<C2DImageWrap>& in_imagebuffer, CAdder& adder)
{
adder.reset();
for (list<C2DImageWrap>::const_iterator i = in_imagebuffer.begin();
i != in_imagebuffer.end(); ++i)
wrap_filter(adder, *i);
CConverter converter(adder.get_result());
return wrap_filter(converter, *in_imagebuffer.begin());
}
int main(int argc, const char *args[])
{
string in_filename;
string out_path;
string file_type;
vector<int> new_size;
C2DImageIOPluginHandler imageio;
// define options
popt::COptions options;
options.push_back(popt::option( in_filename, "in-file", 'i', "input file name", ""));
options.push_back(popt::option( out_path, "out-file", 'o', "output file name", "out-downscale3d%08d.png"));
options.push_back(popt::option( file_type, imageio.get_set(), "type", 't',"output file type" , NULL));
options.push_back(popt::option( new_size, "size", 's', "target 3D image size", "256,256,256"));
vector<string> unknows_args;
popt::parse_options(argc, args, options, unknows_args);
if (!unknows_args.empty()) {
cverr() << "Unknown arguments: ";
for (vector<string>::const_iterator i = unknows_args.begin(); i != unknows_args.end();
++i)
cverb << *i << " ";
cverb << "\n";
return -1;
}
if (in_filename.empty()) {
cverr() << "No input file given, run '" << args[0] << " --help' to get help\n";
return -1;
}
if (out_path.empty()) {
cverr() << "No output filename given, run '" << args[0] << " --help' to get help\n";
return -1;
}
if (new_size.size() != 3) {
cverr() << "new-size needs 3 values\n";
return -1;
}
C3DBounds newsize(new_size[0],new_size[1],new_size[2]);
// now start the fun part
//first count the number of slices
try {
vector<string> src_names = get_consecutive_numbered_files(in_filename);
if (newsize.z > src_names.size())
throw invalid_argument("upscaling not supported");
// create z filter kernel
int factor = ( src_names.size() + newsize.z - 1 ) / newsize.z;
int hwidth_z = ::mona::log2(factor);
if (hwidth_z == 0)
throw invalid_argument("nothing to filter in z-direction, please use a batched downscaler filter call");
// z-scale filtered
T1DGaussFilterKernel kernel(hwidth_z);
cvdebug() << "kernel size = " << kernel.get_size() << "\n";
list<C2DImageWrap> in_imagebuffer;
// int out_i = 0;
// create downscaler
stringstream filter;
filter << "downscaler:dx=" << newsize.x << ",dy=" << newsize.y;
C2DImageFilterHandler filter_plugins;
cvdebug() << "create downscale filter:" << filter.str() << "\n";
C2DImageFilterFactory::ProductPtr downscaler(C2DImageFilterFactory::produce(filter.str().c_str(), filter_plugins));
CAdder adder(kernel, C2DBounds(newsize.x, newsize.y));
int out_num = 0;
const int n_files = src_names.size();
int in_cnt = n_files;
for (size_t i = 0; i < src_names.size(); ++i) {
cvmsg() << "Load: " << src_names[i] << "\n";
auto_ptr<C2DImageList> in_image_list(imageio.load(src_names[i]));
if (!in_image_list.get() || !in_image_list->size()) {
cverr() << "expected " << src_names.size() << " images, got only" << i <<"\n";
break;
}
// downscale the image
downscaler->filter_inplace(*in_image_list->begin());
// initialise the adder
in_imagebuffer.push_back(*in_image_list->begin());
if ((int)in_imagebuffer.size() > kernel.get_size())
in_imagebuffer.pop_front();
if ((int)in_imagebuffer.size() == kernel.get_size()) {
in_cnt += newsize.z;
if (in_cnt > n_files) {
in_cnt -= n_files;
// create a new image based on the convolution of the input
in_image_list->resize(0);
in_image_list->push_back(convolute(in_imagebuffer, adder));
string out_filename = create_filename(out_path.c_str(), out_num++);
cvmsg() << "write: " << out_filename << "\n";
if ( !imageio.save(file_type, *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() << endl;
}
catch (const mona_fatal_error& e){
cerr << args[0] << " fatal: " << e.what() << endl;
}
catch (const mona_exception& e){
cerr << args[0] << " error: " << e.what() << endl;
}
catch (const runtime_error &e){
cerr << args[0] << " runtime: " << e.what() << endl;
}
catch (const invalid_argument &e){
cerr << args[0] << " error: " << e.what() << endl;
}
catch (const exception& e){
cerr << args[0] << " error: " << e.what() << endl;
}
catch (...){
cerr << args[0] << " unknown exception" << endl;
}
return EXIT_FAILURE;
}