Commit 382b5b92 authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Merge branch 'development/ndimensional-io' into 'development/ranges'

Support for load/save in any dimension.

See merge request !71
parents e46c04fd 9c318e8e
Pipeline #13459 failed with stages
in 92 minutes and 16 seconds
#pragma once
#include <mln/core/box.hpp>
#include <mln/core/image/experimental/ndimage_fwd.hpp>
#include <functional>
......@@ -13,4 +13,7 @@ namespace mln::canvas::details
void traverse_along_direction(mln::experimental::box2d roi,
mln::experimental::point2d direction,
std::function<void(mln::experimental::point2d, mln::experimental::point2d, std::size_t n)> callback);
/// \brief Traverse an image and calls f with a pointer to the data of each line
void apply_line(mln::ndbuffer_image& input, std::function<void(std::byte*)>);
}
......@@ -5,7 +5,7 @@
namespace mln::io::internal
{
class freeimage_reader_plugin : public plugin2d_reader
class freeimage_reader_plugin : public plugin_reader
{
public:
~freeimage_reader_plugin() final;
......@@ -13,11 +13,11 @@ namespace mln::io::internal
void close() final;
};
class freeimage_writer_plugin : public plugin2d_writer
class freeimage_writer_plugin : public plugin_writer
{
public:
~freeimage_writer_plugin() final;
void open(const char* filename, sample_type_id sample_type, int width, int height) final;
void open(const char* filename, sample_type_id sample_type, int nfim, const int dims[]) final;
void close() final;
private:
......
......@@ -11,20 +11,22 @@
namespace mln::io::internal
{
void load2d(plugin2d_reader* p, const char* filename, mln::ndbuffer_image& output);
template <class I>
void save2d(mln::experimental::Image<I>& input, plugin2d_writer* p, const char* filename);
void save2d(mln::experimental::Image<I>& input, plugin_writer* p, const char* filename);
template <class I>
void save2d(mln::experimental::Image<I>&& input, plugin2d_writer* p, const char* filename);
void save2d(mln::experimental::Image<I>&& input, plugin_writer* p, const char* filename);
template <class I>
void save2d(const mln::experimental::Image<I>& input, plugin2d_writer* p, const char* filename);
void save2d(const mln::experimental::Image<I>& input, plugin_writer* p, const char* filename);
// Specialization for types convertible to mln::ndbuffer_image
void save2d(const mln::ndbuffer_image& input, plugin2d_writer* p, const char* filename);
inline void save2d(const mln::ndbuffer_image& input, plugin_writer* p, const char* filename);
// Generic ndimension algorithm for buffer encoded image
void load(plugin_reader* p, const char* filename, mln::ndbuffer_image& output);
void save(const mln::ndbuffer_image& input, plugin_writer* p, const char* filename);
}
......@@ -34,7 +36,7 @@ namespace mln::io::internal
{
template <class Image>
void save2d(Image&& input, plugin2d_writer* p, const char* filename)
void save2d(Image&& input, plugin_writer* p, const char* filename)
{
using I = std::remove_reference_t<Image>;
using V = image_value_t<I>;
......@@ -49,7 +51,8 @@ namespace mln::io::internal
sample_type_id tid = sample_type_traits<V>::id();
// Read header
p->open(filename, tid, width, height);
int dims[2] = {width, height};
p->open(filename, tid, 2, dims);
auto buffer = std::make_unique<V[]>(width);
......@@ -71,22 +74,27 @@ namespace mln::io::internal
} // namespace impl
template <class I>
void save2d(mln::experimental::Image<I>&& input, plugin2d_writer* p, const char* filename)
void save2d(mln::experimental::Image<I>&& input, plugin_writer* p, const char* filename)
{
impl::save2d(static_cast<I&&>(input), p, filename);
}
template <class I>
void save2d(mln::experimental::Image<I>& input, plugin2d_writer* p, const char* filename)
void save2d(mln::experimental::Image<I>& input, plugin_writer* p, const char* filename)
{
impl::save2d(static_cast<I&>(input), p, filename);
}
template <class I>
void save2d(const mln::experimental::Image<I>& input, plugin2d_writer* p, const char* filename)
void save2d(const mln::experimental::Image<I>& input, plugin_writer* p, const char* filename)
{
impl::save2d(static_cast<const I&>(input), p, filename);
}
void save2d(const mln::ndbuffer_image& input, plugin_writer* p, const char* filename)
{
save(input, p, filename);
}
} // namespace mln::io::internal
......@@ -7,15 +7,16 @@
namespace mln::io::internal
{
class plugin2d_base
class plugin_base
{
public:
virtual ~plugin2d_base() = default;
virtual ~plugin_base() = default;
struct impl_t
{
int m_width;
int m_height;
int m_ndim;
int m_dims[16];
sample_type_id m_sample_type_id;
virtual ~impl_t() = default;
......@@ -28,26 +29,28 @@ namespace mln::io::internal
};
class plugin2d_reader : public plugin2d_base
class plugin_reader : public plugin_base
{
public:
virtual void open(const char* filename) = 0;
virtual void close() = 0;
int width() const { return m_impl->m_width; }
int height() const { return m_impl->m_height; }
int get_ndim() const { return m_impl->m_ndim; }
int get_dim(int k) const { return m_impl->m_dims[k]; }
const int* get_dim_array() const { return m_impl->m_dims; }
sample_type_id get_sample_type_id() const { return m_impl->m_sample_type_id; }
void read_next_line(std::byte* buffer) { return m_impl->read_next_line(buffer); }
};
class plugin2d_writer : public plugin2d_base
class plugin_writer : public plugin_base
{
public:
virtual void open(const char* filename, sample_type_id sample_type, int width, int height) = 0;
virtual void close() = 0;
virtual void open(const char* filename, sample_type_id sample_type, int ndim, const int sizes[]) = 0;
virtual void close() = 0;
void write_next_line(const std::byte* buffer) { return m_impl->write_next_line(buffer); }
};
} // namespace mln::io::internal
#include <mln/core/canvas/private/traverse2d.hpp>
#include <mln/core/image/experimental/ndimage.hpp>
namespace mln::canvas::details
{
......@@ -71,4 +72,36 @@ namespace mln::canvas::details
}
}
}
}
/// \brief Traverse an image and calls f with a pointer to the data of each line
void apply_line(mln::ndbuffer_image& input, std::function<void(std::byte*)> fun)
{
int pdim = input.pdim();
if (pdim > 4)
throw std::runtime_error("Not implemented for this number of dimensions.");
std::ptrdiff_t strides[4] = {0};
int dims[4] = {1, 1, 1, 1};
for (int k = 0; k < pdim; ++k)
{
dims[k] = input.size(k);
strides[k] = input.byte_stride(k);
}
for (int w = 0; w < dims[3]; ++w)
for (int z = 0; z < dims[2]; ++z)
{
std::byte* lineptr = input.buffer() + w * strides[3] + z * strides[2];
for (int y = 0; y < dims[1]; ++y)
{
fun(lineptr);
lineptr += strides[1];
}
}
}
} // namespace mln::canvas::details
......@@ -82,7 +82,7 @@ namespace mln::io::internal
struct impl_base_t : plugin2d_base::impl_t
struct impl_base_t : plugin_base::impl_t
{
const char* m_filename;
FIBITMAP* m_dib;
......@@ -97,14 +97,14 @@ namespace mln::io::internal
void read_next_line(std::byte* buffer) final
{
BYTE* lineptr = FreeImage_GetScanLine(m_dib, m_y);
std::memcpy(buffer, lineptr, m_width * m_bpp / 8);
std::memcpy(buffer, lineptr, m_dims[0] * m_bpp / 8);
m_y--;
}
void write_next_line(const std::byte* buffer) final
{
BYTE* lineptr = FreeImage_GetScanLine(m_dib, m_y);
std::memcpy(lineptr, buffer, m_width * m_bpp / 8);
std::memcpy(lineptr, buffer, m_dims[0] * m_bpp / 8);
m_y--;
}
};
......@@ -116,7 +116,7 @@ namespace mln::io::internal
void read_next_line(std::byte* __restrict buffer) final
{
std::uint8_t* lineptr = (std::uint8_t*) FreeImage_GetScanLine(m_dib, m_y);
for (int x = 0, t = 0; x < m_width; x += 8, t++)
for (int x = 0, t = 0; x < m_dims[0]; x += 8, t++)
{
buffer[x] = std::byte((lineptr[t] & 0x80) != 0);
buffer[x + 1] = std::byte((lineptr[t] & 0x40) != 0);
......@@ -136,11 +136,11 @@ namespace mln::io::internal
std::uint8_t* lineptr = (std::uint8_t*)FreeImage_GetScanLine(m_dib, m_y);
int x, t;
for (x = 0, t = 0; x + 8 < m_width; x += 8, t++)
for (x = 0, t = 0; x + 8 < m_dims[0]; x += 8, t++)
lineptr[t] = buffer[x + 0] << 7 | buffer[x + 1] << 6 | buffer[x + 2] << 5 | buffer[x + 3] << 4 |
buffer[x + 4] << 3 | buffer[x + 5] << 2 | buffer[x + 6] << 1 | buffer[x + 7] << 0;
int remainder = m_width - x;
int remainder = m_dims[0] - x;
if (remainder > 0)
{
lineptr[t] = 0;
......@@ -165,7 +165,7 @@ namespace mln::io::internal
void read_next_line(std::byte* __restrict buffer) final
{
std::uint8_t* lineptr = (std::uint8_t*) FreeImage_GetScanLine(m_dib, m_y);
for (int x = 0, t = 0; x < m_width; x += 8, t++)
for (int x = 0, t = 0; x < m_dims[0]; x += 8, t++)
{
buffer[x] = std::byte((lineptr[t] & 0x80) == 0);
buffer[x + 1] = std::byte((lineptr[t] & 0x40) == 0);
......@@ -191,7 +191,7 @@ namespace mln::io::internal
void read_next_line(std::byte* __restrict buffer) final
{
uint8_t* lineptr = (uint8_t*) FreeImage_GetScanLine(m_dib, m_y);
for (int x = 0; x < m_width; ++x)
for (int x = 0; x < m_dims[0]; ++x)
{
buffer[3 * x + 0] = std::byte(m_palette[lineptr[x]].rgbRed);
buffer[3 * x + 1] = std::byte(m_palette[lineptr[x]].rgbGreen);
......@@ -209,7 +209,7 @@ namespace mln::io::internal
void read_next_line(std::byte* __restrict buffer) final
{
std::byte* lineptr = (std::byte*) FreeImage_GetScanLine(m_dib, m_y);
for (int x = 0; x < m_width; ++x)
for (int x = 0; x < m_dims[0]; ++x)
{
buffer[3 * x + 0] = lineptr[3 * x + FI_RGBA_RED];
buffer[3 * x + 1] = lineptr[3 * x + FI_RGBA_GREEN];
......@@ -221,7 +221,7 @@ namespace mln::io::internal
void write_next_line(const std::byte* __restrict buffer) final
{
std::byte* lineptr = (std::byte*) FreeImage_GetScanLine(m_dib, m_y);
for (int x = 0; x < m_width; ++x)
for (int x = 0; x < m_dims[0]; ++x)
{
lineptr[3 * x + FI_RGBA_RED] = buffer[3 * x + 0];
lineptr[3 * x + FI_RGBA_GREEN] = buffer[3 * x + 1];
......@@ -237,7 +237,7 @@ namespace mln::io::internal
void read_next_line(std::byte* __restrict buffer) final
{
std::byte* lineptr = (std::byte*) FreeImage_GetScanLine(m_dib, m_y);
for (int x = 0; x < m_width; ++x)
for (int x = 0; x < m_dims[0]; ++x)
{
buffer[4 * x + 0] = lineptr[4 * x + FI_RGBA_RED];
buffer[4 * x + 1] = lineptr[4 * x + FI_RGBA_GREEN];
......@@ -250,7 +250,7 @@ namespace mln::io::internal
void write_next_line(const std::byte* __restrict buffer) final
{
std::byte* lineptr = (std::byte*) FreeImage_GetScanLine(m_dib, m_y);
for (int x = 0; x < m_width; ++x)
for (int x = 0; x < m_dims[0]; ++x)
{
lineptr[4 * x + FI_RGBA_RED] = buffer[4 * x + 0];
lineptr[4 * x + FI_RGBA_GREEN] = buffer[4 * x + 1];
......@@ -306,12 +306,13 @@ namespace mln::io::internal
impl->m_dib = dib;
impl->m_fif = fif;
impl->m_height = FreeImage_GetHeight(dib);
impl->m_width = FreeImage_GetWidth(dib);
impl->m_ndim = 2;
impl->m_dims[0] = FreeImage_GetWidth(dib);
impl->m_dims[1] = FreeImage_GetHeight(dib);
impl->m_sample_type_id = sid;
impl->m_bpp = bits_per_pixel;
impl->m_palette = FreeImage_GetPalette(dib);
impl->m_y = impl->m_height - 1;
impl->m_y = impl->m_dims[1] - 1;
m_impl = std::move(impl);
return;
......@@ -362,8 +363,14 @@ namespace mln::io::internal
this->close();
}
void freeimage_writer_plugin::open(const char* filename, sample_type_id sample_type, int width, int height)
void freeimage_writer_plugin::open(const char* filename, sample_type_id sample_type, int ndim, const int dims[])
{
if (ndim != 2)
throw std::runtime_error(fmt::format("Unsupported number of dimension (ndim={}, should be 2).", ndim));
const int width = dims[0];
const int height = dims[1];
std::unique_ptr<impl_base_t> impl;
FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(filename);
......@@ -412,7 +419,7 @@ namespace mln::io::internal
if (!dib)
throw std::runtime_error(
fmt::format("Unable to allocate image (type={}, bpp={}).", FreeImage_GetFormatFromFIF(fif), bits_per_pixel));
fmt::format("Unable to allocate image (type={}, bpp={}).", FreeImage_GetFormatFromFIF(fif), bits_per_pixel));
// Set the palette
RGBQUAD* palette = FreeImage_GetPalette(dib);
......@@ -427,8 +434,9 @@ namespace mln::io::internal
palette[i] = {(BYTE)i, (BYTE)i, (BYTE)i, 0u};
}
impl->m_width = width;
impl->m_height = height;
impl->m_ndim = 2;
impl->m_dims[0] = width;
impl->m_dims[1] = height;
impl->m_sample_type_id = sample_type;
impl->m_bpp = bits_per_pixel;
impl->m_dib = dib;
......
......@@ -16,6 +16,6 @@ namespace mln::io::experimental
void imread(const std::string& filename, mln::ndbuffer_image& out)
{
internal::freeimage_reader_plugin p;
internal::load2d(&p, filename.c_str(), out);
internal::load(&p, filename.c_str(), out);
}
}
#include <mln/io/private/io.hpp>
#include <mln/core/canvas/private/traverse2d.hpp>
#include <mln/core/image/experimental/ndimage.hpp>
#include <mln/io/private/plugin.hpp>
#include <functional>
#include <fmt/core.h>
namespace mln::io::internal
{
void load2d(plugin2d_reader* p, const char* filename, mln::ndbuffer_image& output)
void load(plugin_reader* p, const char* filename, mln::ndbuffer_image& output)
{
// Read header
p->open(filename);
// Allocate image
int height = p->height();
int width = p->width();
int pdim = p->get_ndim();
sample_type_id tid = p->get_sample_type_id();
if (output.sample_type() != sample_type_id::OTHER && output.sample_type() != tid)
throw std::runtime_error(
fmt::format("Sample type mismatch. The input is {} (requested: {}).", (int)tid, (int)output.sample_type()));
fmt::format("Sample type mismatch. The input is {} (requested: {}).", (int)tid, (int)output.sample_type()));
if (output.pdim() != 0 && output.pdim() != 2)
throw std::runtime_error(fmt::format("Number of dimensions mismath (={}) (should be 2).", output.pdim()));
if (output.pdim() != 0 && output.pdim() != pdim)
throw std::runtime_error(fmt::format("Number of dimensions mismath (={}) (should be {}).", output.pdim(), pdim));
output.resize(tid, width, height);
output.resize(tid, pdim, p->get_dim_array(), image_build_params{});
// Fill content
{
std::byte* lineptr = output.buffer();
std::ptrdiff_t stride = output.byte_stride();
for (int y = 0; y < height; ++y)
{
p->read_next_line(lineptr);
lineptr += stride;
}
}
// Fill content (up to 4 dimensions for now)
mln::canvas::details::apply_line(output, std::bind(&plugin_reader::read_next_line, p, std::placeholders::_1));
// Close handle
p->close();
}
void save2d(const mln::ndbuffer_image& input, plugin2d_writer* p, const char* filename)
void save(const mln::ndbuffer_image& input, plugin_writer* p, const char* filename)
{
if (input.pdim() != 2)
throw std::runtime_error(fmt::format("Invalid number of dimensions (={}) (should be 2).", input.pdim()));
int ndim = input.pdim();
int sizes[16];
// Allocate image
int height = input.size(1);
int width = input.size(0);
sample_type_id tid = input.sample_type();
for (int k = 0; k < ndim; ++k)
sizes[k] = input.size(k);
// Read header
p->open(filename, tid, width, height);
p->open(filename, input.sample_type(), ndim, sizes);
// Fill content
{
const std::byte* lineptr = input.buffer();
std::ptrdiff_t stride = input.byte_stride();
for (int y = 0; y < height; ++y)
{
p->write_next_line(lineptr);
lineptr += stride;
}
}
mln::canvas::details::apply_line(const_cast<mln::ndbuffer_image&>(input), std::bind(&plugin_writer::write_next_line, p, std::placeholders::_1));
// Close handle
p->close();
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment