Commit 15a05717 authored by Roland Levillain's avatar Roland Levillain
Browse files

Fix and improve the Magick++ I/O API wrapper.

	* mln/io/magick/load.hh
	(io::magick::do_it): Enclose these helpers...
	(io::magick::impl::do_it): ...in a sub-namespace.
	(io::magick::load): Ensure a Magick++'s Quantum is an 8-bit value.
	Use a pixel view (Magick::Pixels) to access to pixel values.
	No longer pass the input filename to the `do_it' helper.
	Simplify the code.
	Improve the documentation.
	Aesthetic changes.
	* mln/io/magick/save.hh
	(io::magick::get_color): Enclose these helpers...
	(io::magick::impl::get_color): ...in a sub-namespace.
	Properly use Magick::Color.
	Properly pass the width and the height of the image.
	Use a pixel view (Magick::Pixels) to access to pixel values.
	Simplify the code.
	Aesthetic changes.
	* tests/io/magick/load.cc,
	* tests/io/magick/save.cc:
	Properly initialize Magick++.
	Exercice more cases.
	* tests/io/magick/Makefile.am (MOSTLYCLEANFILES):
	Update the list of files created by tests.
parent 71c9c144
2010-07-19 Roland Levillain <roland@lrde.epita.fr>
Fix and improve the Magick++ I/O API wrapper.
* mln/io/magick/load.hh
(io::magick::do_it): Enclose these helpers...
(io::magick::impl::do_it): ...in a sub-namespace.
(io::magick::load): Ensure a Magick++'s Quantum is an 8-bit value.
Use a pixel view (Magick::Pixels) to access to pixel values.
No longer pass the input filename to the `do_it' helper.
Simplify the code.
Improve the documentation.
Aesthetic changes.
* mln/io/magick/save.hh
(io::magick::get_color): Enclose these helpers...
(io::magick::impl::get_color): ...in a sub-namespace.
Properly use Magick::Color.
Properly pass the width and the height of the image.
Use a pixel view (Magick::Pixels) to access to pixel values.
Simplify the code.
Aesthetic changes.
* tests/io/magick/load.cc,
* tests/io/magick/save.cc:
Properly initialize Magick++.
Exercice more cases.
* tests/io/magick/Makefile.am (MOSTLYCLEANFILES):
Update the list of files created by tests.
2010-04-26 Roland Levillain <roland@lrde.epita.fr>
 
Clean Milena's tests' outputs during `make mostlyclean'.
// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
......@@ -28,13 +28,21 @@
/// \file
///
/// Define a function which loads an image of kind magick with
/// given path.
/// \brief Image intput routines based on Magick++.
///
/// Do not forget to call Magick::InitializeMagick(*argv)
/// <em>before</em> using any of these functions, as advised by the
/// GraphicsMagick documentation
/// (http://www.graphicsmagick.org/Magick++/Image.html).
# include <cstdlib>
# include <Magick++.h>
# include <mln/core/image/image2d.hh>
# include <mln/value/int_u8.hh>
# include <mln/value/rgb8.hh>
# include <Magick++.h>
namespace mln
......@@ -46,66 +54,83 @@ namespace mln
namespace magick
{
/*! Load a magick image in a Milena image.
*
* \param[out] ima A reference to the image which will receive
* data.
* \param[in] filename The source.
*/
/** Load data from a file into a Milena image using Magick++.
\param[out] ima The image data are loaded into.
\param[in] filename The name of the input file. */
template <typename I>
void load(Image<I>& ima, const std::string& filename);
/*! Load a magick image in a tiled image.
*
* \param[out] ima A reference to the image which will receive
* data.
* \param[in] filename The source.
*/
/*template <typename T>
void load(Image<tiled2d<T> >& ima, const std::string& filename);*/
// FIXME: Unfinished?
#if 0
/** Load data from a file into a Milena tiled image using
Magick++.
\param[out] ima The image data are loaded into.
\param[in] filename The name of the input file. */
template <typename T>
void load(Image<tiled2d<T> >& ima, const std::string& filename);
#endif
# ifndef MLN_INCLUDE_ONLY
inline
bool do_it(const value::rgb8& in, bool& out, const std::string& filename)
namespace impl
{
if (in.red() == 255u && in.green() == 255u && in.blue() == 255u)
inline
bool
do_it(const value::rgb8& in, bool& out)
{
out = true;
if (in.red() != in.green() || in.green() != in.blue())
{
std::cerr <<
"error: attempt to load what looks like a color\n"
"(mln::value::rgb8) image into a Boolean (bool) image" <<
std::endl;
return false;
}
if (in.red() != 0 &&
in.red() != mln_max(value::rgb8::red_t))
{
std::cerr <<
"error: attempt to load what looks like a grayscale\n"
"(mln::value::int_u8) image into a Boolean (bool) image" <<
std::endl;
return false;
}
out = (in.red() != 0);
return true;
}
if (in.red() == 0u && in.green() == 0u && in.blue() == 0u)
inline
bool
do_it(const value::rgb8& in, value::int_u8& out)
{
out = false;
if (in.red() != in.green() || in.green() != in.blue())
{
std::cerr <<
"error: attempt to load what looks like a color\n"
"(mln::value::rgb8) image into a grayscale\n"
"(mln::int_u8 values) image" << std::endl;
return false;
}
out = in.red();
return true;
}
if (in.red() == in.green() && in.green() == in.blue())
std::cerr << "error: trying to load '" << filename << "' which is a grayscale image into a bool image" << std::endl;
else
std::cerr << "error: trying to load '" << filename << "' which is a truecolor image into a bool image" << std::endl;
return false;
}
inline
bool do_it(const value::rgb8& in, value::int_u8& out, const std::string& filename)
{
if (in.red() == in.green() && in.green() == in.blue())
inline
bool
do_it(const value::rgb8& in, value::rgb8& out)
{
out = in.red();
out = in;
return true;
}
std::cerr << "error: trying to load '" << filename << "' which is a truecolor image into a grayscale image" << std::endl;
return false;
}
inline
bool do_it(const value::rgb8& in, value::rgb8& out, const std::string& filename)
{
(void) filename;
out = in;
return true;
}
} // end of namespace mln::io::magick::impl
template <typename I>
......@@ -114,58 +139,51 @@ namespace mln
{
trace::entering("mln::io::magick::load");
// Ensure a Magick++'s Quantum is an 8-bit value.
mln::metal::equal<Magick::Quantum, unsigned char>::check();
I& ima = exact(ima_);
//std::ifstream file(filename.c_str());
//if (! file)
//{
// std::cerr << "error: cannot open file '" << filename << "'!";
// abort();
//}
Magick::Image im_file(filename);
im_file.modifyImage();
im_file.type(Magick::TrueColorType);
int columns = im_file.columns();
int rows = im_file.rows();
/*std::cout << "width: " <<columns << std::endl;
std::cout << "height: " <<rows << std::endl;
std::cout << "depth: " <<im_file.depth() << std::endl;
std::cout << "format: " <<im_file.format() << std::endl;
std::cout << "magick: " <<im_file.magick() << std::endl;*/
const Magick::PixelPacket *pixel_cache = im_file.getConstPixels(0, 0, columns, rows);
algebra::vec<mln_site_(I)::dim, unsigned int> vmin;
algebra::vec<mln_site_(I)::dim, unsigned int> vmax;
vmin[0] = 0;
vmin[1] = 0;
vmax[0] = rows - 1;
vmax[1] = columns - 1;
mln_site(I) pmin(vmin);
mln_site(I) pmax(vmax);
// FIXME: Handle Magick++'s exceptions (see either
// ImageMagick++'s or GraphicsMagick++'s documentation).
Magick::Image magick_ima(filename);
magick_ima.read(filename);
magick_ima.type(Magick::TrueColorType);
int nrows = magick_ima.rows();
int ncols = magick_ima.columns();
mln_site(I) pmin(0, 0);
mln_site(I) pmax(nrows - 1, ncols - 1);
mln_concrete(I) result(box<mln_site(I)>(pmin, pmax));
initialize(ima, result);
Magick::Pixels view(magick_ima);
// Note that `ncols' is passed before `nrows'.
Magick::PixelPacket* pixels = view.get(0, 0, ima.ncols(), ima.nrows());
mln_piter(I) p(ima.domain());
for_all(p)
{
const Magick::PixelPacket *pixel = pixel_cache + (int) p.to_site().to_vec()[0] * columns
+ (int) p.to_site().to_vec()[1];
// FIXME: Quantum = 16bits but rgb is 8bits
value::rgb8 pix(pixel->red % 256, pixel->green % 256, pixel->blue % 256);
value::rgb8 c(pixels->red, pixels->green, pixels->blue);
mln_value(I) res;
if (!do_it(pix, res, filename))
abort();
if (!impl::do_it(c, res))
{
std::cerr << "while trying to load `" << filename << "'"
<< std::endl;
abort();
}
ima(p) = res;
++pixels;
}
trace::exiting("mln::io::magick::load");
}
/*template<typename T>
// FIXME: Unfinished?
#if 0
template<typename T>
inline
void load(Image<tiled2d<T> >& ima_, const std::string& filename)
void
load(Image<tiled2d<T> >& ima_, const std::string& filename)
{
trace::entering("mln::io::magick::load");
......@@ -175,8 +193,8 @@ namespace mln
ima = result;
trace::exiting("mln::io::magick::load");
}*/
}
#endif
# endif // ! MLN_INCLUDE_ONLY
......
// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
......@@ -28,16 +28,23 @@
/// \file
///
/// Define a function which saves an image of kind magick with
/// given path.
/// \brief Image output routines based on Magick++.
///
/// \todo At the moment it works; is it a miracle?
/// Do not forget to call Magick::InitializeMagick(*argv)
/// <em>before</em> using any of these functions, as advised by the
/// GraphicsMagick documentation
/// (http://www.graphicsmagick.org/Magick++/Image.html).
# include <cstdlib>
# include <Magick++.h>
# include <mln/core/image/image2d.hh>
# include <mln/metal/equal.hh>
# include <mln/core/image/image2d.hh>
# include <mln/value/int_u8.hh>
# include <mln/value/rgb8.hh>
# include <Magick++.h>
namespace mln
......@@ -49,84 +56,106 @@ namespace mln
namespace magick
{
/*! Save a Milena image in a magick image.
*
* \param[out] ima A reference to the image to save.
* \param[in] filename The output.
*/
/** Save a Milena image into a file using Magick++.
\param[out] ima The image to save.
\param[in] filename The name of the output file. */
template <typename I>
void save(const Image<I>& ima,
const std::string& filename);
void
save(const Image<I>& ima, const std::string& filename);
/*! Save a Milena tiled image in a magick image.
*
* \param[out] ima A reference to the image to save.
* \param[in] filename The output.
*/
/*template <typename T>
void save(Image< tiled2d<T> >& ima,
const std::string& filename);*/
// FIXME: Unfinished?
#if 0
/** Save a Milena tiled image into a file using Magick++.
\param[out] ima The image to save.
\param[in] filename The name of the output file. */
template <typename T>
void
save(const Image< tiled2d<T> >& ima, const std::string& filename);
#endif
# ifndef MLN_INCLUDE_ONLY
inline
Magick::Color get_color(bool value)
namespace impl
{
return Magick::ColorMono(value);
}
inline
Magick::Color get_color(const value::int_u8& value)
{
return Magick::ColorGray(256 - value);
}
inline
Magick::Color get_color(bool value)
{
return Magick::ColorMono(value);
}
inline
Magick::Color get_color(const value::int_u8& value)
{
// Ensure a Magick++'s Quantum is an 8-bit value.
mln::metal::equal<Magick::Quantum, unsigned char>::check();
return Magick::Color(value, value, value);
}
inline
Magick::Color get_color(const value::rgb8& value)
{
// Ensure a Magick++'s Quantum is an 8-bit value.
mln::metal::equal<Magick::Quantum, unsigned char>::check();
return Magick::Color(value.red(), value.green(), value.blue());
}
} // end of namespace mln::io::magick::impl
inline
Magick::Color get_color(const value::rgb8& value)
{
return Magick::ColorRGB(256 - value.red(),
256 - value.green(),
256 - value.blue());
}
template <typename I>
inline
void save(const Image<I>& ima_, const std::string& filename)
void
save(const Image<I>& ima_, const std::string& filename)
{
trace::entering("mln::io::magick::save");
mln_precondition(mln_site_(I)::dim == 2);
const I& ima = exact(ima_);
// Turn this into a static check?
if (!(mln::metal::equal<mln_value(I), bool>::value ||
mln::metal::equal<mln_value(I), value::int_u8>::value ||
mln::metal::equal<mln_value(I), value::rgb8>::value))
{
std::cerr << "error: trying to save an unsupported format" << std::endl;
std::cerr << "supported formats: binary, 8bits grayscale (int_u8), 8bits truecolor (rgb8)" << std::endl;
std::cerr <<
"error: trying to save an unsupported format\n"
"supported formats are:\n"
" binary (bool)\n"
" 8-bit grayscale (mln::value::int_u8)\n"
" 3x8-bit truecolor (rgb8)" << std::endl;
abort();
}
Magick::Image im_file;
im_file.size(Magick::Geometry(ima.nrows(), ima.ncols()));
const I& ima = exact(ima_);
Magick::PixelPacket* pixel_cache = im_file.getPixels(0, 0, ima.nrows(), ima.ncols());
Magick::PixelPacket* pixel;
Magick::Image magick_ima;
// In the construction of a Geometry object, the width (i.e.
// `ncols') comes first, then the height (i.e. `nrows')
// follows.
magick_ima.size(Magick::Geometry(ima.ncols(), ima.nrows()));
Magick::Pixels view(magick_ima);
// As above, `ncols' is passed before `nrows'.
Magick::PixelPacket* pixels = view.get(0, 0, ima.ncols(), ima.nrows());
mln_piter(I) p(ima.domain());
for_all(p)
{
pixel = pixel_cache + (int) p.to_site().to_vec()[0] * ima.ncols()
+ (int) p.to_site().to_vec()[1];
*pixel = get_color(ima(p));
}
im_file.syncPixels();
im_file.write(filename);
*pixels++ = impl::get_color(ima(p));
view.sync();
magick_ima.write(filename);
trace::exiting("mln::io::magick::save");
}
/*template <typename T>
void save(Image< tiled2d<T> >& ima_, const std::string& filename)
// FIXME: Unfinished?
#if 0
template <typename T>
void
save(const Image< tiled2d<T> >& ima_, const std::string& filename)
{
trace::entering("mln::io::magick::save");
......@@ -135,7 +164,8 @@ namespace mln
ima.buffer().write(filename);
trace::exiting("mln::io::magick::save");
}*/
}
#endif
# endif // ! MLN_INCLUDE_ONLY
......
......@@ -29,5 +29,9 @@ save_SOURCES = save.cc
TESTS = $(check_PROGRAMS)
MOSTLYCLEANFILES = \
save-tiny.ppm \
save-tiny.png
save-tiny-temp.pbm \
save-tiny-temp.pgm \
save-tiny-temp.png \
save-tiny.pbm \
save-tiny.pgm \
save-tiny.ppm
// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
......@@ -23,26 +23,78 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
#include <mln/core/image/image2d.hh>
#include <mln/io/magick/load.hh>
#include <mln/io/pbm/load.hh>
#include <mln/io/pgm/load.hh>
#include <mln/io/ppm/load.hh>
#include <mln/data/compare.hh>
#include <mln/io/magick/load.hh>
#include <mln/io/ppm/load.hh>
#include <mln/core/image/image2d.hh>
#include <mln/value/rgb8.hh>
#include "tests/data.hh"
int main()
int main(int /* argc */, char* argv[])
{
using namespace mln;
image2d<value::rgb8> lena_ppm;
io::ppm::load(lena_ppm, MLN_IMG_DIR "/tiny.ppm");
/* From http://www.graphicsmagick.org/Magick++/Image.html:
The InitializeMagick() function MUST be invoked before
constructing any Magick++ objects. This used to be optional,
but now it is absolutely required. This function initalizes
semaphores and configuration information necessary for the
software to work correctly. Failing to invoke
InitializeMagick() is likely to lead to a program crash or
thrown assertion. If the program resides in the same directory
as the GraphicsMagick files, then argv[0] may be passed as an
argument so that GraphicsMagick knows where its files reside,
otherwise NULL may be passed and GraphicsMagick will try to use
other means (if necessary). */
Magick::InitializeMagick(*argv);
// Compare Milena's built-in PBM loaded and Magick++'s support for PBM.
{
typedef image2d<bool> I;
I mln_lena;
io::pbm::load(mln_lena, MLN_IMG_DIR "/tiny.pbm");
I magick_lena;
io::magick::load(magick_lena, MLN_IMG_DIR "/tiny.pbm");
mln_assertion(mln_lena == magick_lena);
}
// Compare Milena's built-in PGM loaded and Magick++'s support for PGM.
{
typedef image2d<value::int_u8> I;
I mln_lena;
io::pgm::load(mln_lena, MLN_IMG_DIR "/tiny.pgm");
I magick_lena;
io::magick::load(magick_lena, MLN_IMG_DIR "/tiny.pgm");
mln_assertion(mln_lena == magick_lena);
}
// Compare Milena's built-in PPM loaded and Magick++'s support for PPM.
{
typedef image2d<value::rgb8> I;
I mln_lena;
io::ppm::load(mln_lena, MLN_IMG_DIR "/tiny.ppm");
I magick_lena;
io::magick::load(magick_lena, MLN_IMG_DIR "/tiny.ppm");
mln_assertion(mln_lena == magick_lena);
}
image2d<value::rgb8> lena_png;
io::magick::load(lena_png, MLN_TESTS_IMG_DIR "/tiny.png");
mln_assertion(lena_png == lena_ppm);
// Check Magick++'s support for PNG.
{
typedef image2d<value::rgb8> I;
I lena_ppm;
io::ppm::load(lena_ppm, MLN_IMG_DIR "/tiny.ppm");
I lena_png;
io::magick::load(lena_png, MLN_TESTS_IMG_DIR "/tiny.png");
mln_assertion(lena_ppm == lena_png);
}
}
......@@ -24,34 +24,92 @@
// executable file might be covered by the GNU General Public License.
#include <mln/core/image/image2d.hh>
#include <mln/data/compare.hh>
#include <mln/io/magick/load.hh>
#include <mln/io/magick/save.hh>
#include <mln/io/pbm/load.hh>
#include <mln/io/pbm/save.hh>
#include <mln/io/pgm/load.hh>
#include <mln/io/pgm/save.hh>
#include <mln/io/ppm/load.hh>
#include <mln/io/ppm/save.hh>
#include <mln/data/compare.hh>
#include "tests/data.hh"
#include <mln/io/magick/load.hh>
using namespace mln;
int main()
{
using namespace mln;
template <typename T>
image2d<T>
test(const image2d<T>& lena_mln, const std::string& temp_filename)
{
point2d p(0,0);
image2d<value::rgb8> lena_mln;