/* -*- 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: croproi.cc,v 1.13 2006-07-12 13:44:26 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 <sstream>
#include <libmona/monaHistory.hh>
#include <libmona/monaException.hh>
#include <libmona/monaPopt.hh>
#include <libmona/3DVector.hh>
#include <libmona/filter_plugin2d.hh>
#include <libmona/2DImageWrap.hh>
#include <libmona/fnameana.hh>
#include <libmona/filetools.hh>
using namespace std;
using namespace mona;
// size
const size_t size_align = 16;
class CCheckBounds {
public:
typedef bool result_type;
CCheckBounds(C2DBounds& start, C2DBounds& end, int thresh);
template <typename Image>
CCheckBounds::result_type operator() (const Image& image);
private:
C2DBounds& _M_start;
C2DBounds& _M_end;
int _M_thresh;
};
CCheckBounds::CCheckBounds(C2DBounds& start, C2DBounds& end, int thresh):
_M_start(start),
_M_end(end),
_M_thresh(thresh)
{
}
template <typename Image>
CCheckBounds::result_type CCheckBounds::operator() (const Image& image)
{
bool above_thresh = false;
typename Image::const_iterator pixel = image.begin();
for (size_t y = 0; y < image.get_size().y; ++y) {
bool above_thresh_y = false;
for (size_t x = 0; x < image.get_size().x; ++x, ++pixel) {
if (*pixel > _M_thresh) {
above_thresh_y = true;
if (_M_start.x > x)
_M_start.x = x;
if (_M_end.x < x)
_M_end.x = x;
}
}
if (above_thresh_y) {
if (_M_start.y > y)
_M_start.y = y;
if (_M_end.y < y)
_M_end.y = y;
above_thresh = true;
}
}
return above_thresh;
}
void save_incement(C2DBounds& val, int up)
{
while (up--) {
if (val.x < numeric_limits<size_t>::max())
++val.x;
if (val.y < numeric_limits<size_t>::max())
++val.y;
}
}
void save_decrement(C2DBounds& val, int down)
{
while (down--) {
if (val.x > 0 )
--val.x;
if (val.y > 0 )
--val.y;
}
}
void adjust_size(const C2DBounds& start, C2DBounds& end, size_t factor)
{
assert(factor > 0);
C2DBounds h(factor - 1, factor - 1);
C2DBounds f = end - start + h;
f.x /= factor; f.x *= factor;
f.y /= factor; f.y *= factor;
end = start + f;
}
bool check_file(const string& fname, CCheckBounds& checker, const C2DImageIOPluginHandler& imageio)
{
cvmsg() << "read file "<< fname << "\r";
auto_ptr<C2DImageList> image_list(imageio.load(fname));
if (!image_list.get() || !image_list->size())
return false;
bool has_pixels = false;
for (C2DImageList::const_iterator i = image_list->begin();
i != image_list->end(); ++i)
has_pixels |= wrap_filter(checker, *i);
return has_pixels;
}
void crop_file(const string& fname, const string& out_path, int out_z,
const string& out_type,
const C2DImageFilterBase& crop,
const C2DImageIOPluginHandler& imageio)
{
cvmsg() << "read file "<< fname << "\r";
auto_ptr<C2DImageList> image_list(imageio.load(fname));
if (!image_list.get() || !image_list->size()) {
stringstream errmsg;
errmsg << "crop_file: unable to read " << fname << "for cropping\n";
throw runtime_error(errmsg.str());
}
for (C2DImageList::iterator i = image_list->begin(); i != image_list->end(); ++i)
crop.filter_inplace(*i);
string out_filename = create_filename(out_path.c_str(), out_z);
cvdebug() << "save to " << out_filename << "\n";
if ( !imageio.save(out_type, *image_list, out_filename) ){
string not_save = ("unable to save result to ") + out_filename;
throw mona_runtime_error(not_save);
};
}
int main(int argc, const char *args[])
{
// define options
string in_filename;
string out_path;
string out_type;
stringstream io_types;
int bg_thresh;
int borderwidth;
C2DImageIOPluginHandler imageio;
io_types << "Image formats supported: " << imageio.plugin_names();
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-crop-roi%08d.tif"));
options.push_back(popt::option( bg_thresh, "bg-thresh", 'T', "background color thresh", "1"));
options.push_back(popt::option( borderwidth, "borderwidth", 'b', "width of background border to preserve", "2"));
options.push_back(popt::option( out_type, imageio.get_set(), "type", 't',"output file type" , NULL));
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";
return -1;
}
if (out_path.empty()) {
cverr() << "No output filename given, run '" << args[0] << " --help' to get help";
return -1;
}
C2DBounds start(numeric_limits<size_t>::max(),
numeric_limits<size_t>::max());
C2DBounds end(0,0);
try {
vector<string> src_names = get_consecutive_numbered_files(in_filename);
if (src_names.size() > 1) {
vector<string>::const_iterator fbegin = src_names.begin();
vector<string>::const_iterator end_names = src_names.end();
CCheckBounds checker(start, end, bg_thresh);
while (fbegin != end_names && !check_file(*fbegin, checker, imageio))
++fbegin;
if (fbegin == end_names)
throw invalid_argument("Input data all below given background threshold");
vector<string>::const_iterator fend_h = fbegin;
++fend_h;
vector<string>::const_iterator fend = fend_h;
while (fend_h != end_names) {
if (check_file(*fend_h, checker, imageio))
fend = fend_h;
++fend_h;
}
if (borderwidth> 0) {
// add the boundary savely
save_incement(end, borderwidth);
save_decrement(start, borderwidth);
for (int i = 0; i < borderwidth; ++i) {
if (fbegin != src_names.begin())
--fbegin;
if (fend != end_names)
++fend;
}
}
adjust_size(start, end, size_align);
stringstream filter;
filter << "crop:sx=" << start.x << ",sy=" << start.y <<
",ex=" << end.x << ",ey=" << end.y;
C2DImageFilterHandler filter_plugins;
cvdebug() << "create crop filter:" << filter.str() << "\n";
C2DImageFilterFactory::ProductPtr crop(C2DImageFilterFactory::produce(filter.str().c_str(), filter_plugins));
size_t out_num = 0;
for (; fbegin != fend; ++fbegin, ++out_num) {
crop_file(*fbegin, out_path, out_num, out_type, *crop, imageio);
}
}else{
// only one file
throw mona_fatal_error("single file crop-analysis not yet supported");
}
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;
}