Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Resample RGB images in C++#29453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
QuLogic wants to merge4 commits intomatplotlib:main
base:main
Choose a base branch
Loading
fromQuLogic:resample-rgb
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 9 additions & 21 deletionslib/matplotlib/image.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -160,8 +160,7 @@ def flush_images():
flush_images()


def _resample(
image_obj, data, out_shape, transform, *, resample=None, alpha=1):
def _resample(image_obj, data, out_shape, transform, *, resample=None, alpha=1):
"""
Convenience wrapper around `._image.resample` to resample *data* to
*out_shape* (with a third dimension if *data* is RGBA) that takes care of
Expand DownExpand Up@@ -204,7 +203,10 @@ def _resample(
interpolation = 'nearest'
else:
interpolation = 'hanning'
out = np.zeros(out_shape + data.shape[2:], data.dtype) # 2D->2D, 3D->3D.
if len(data.shape) == 3:
# Always output RGBA.
out_shape += (4, )
out = np.zeros(out_shape, data.dtype)
if resample is None:
resample = image_obj.get_resample()
_image.resample(data, out, transform,
Expand All@@ -216,20 +218,6 @@ def _resample(
return out


def _rgb_to_rgba(A):
"""
Convert an RGB image to RGBA, as required by the image resample C++
extension.
"""
rgba = np.zeros((A.shape[0], A.shape[1], 4), dtype=A.dtype)
rgba[:, :, :3] = A
if rgba.dtype == np.uint8:
rgba[:, :, 3] = 255
else:
rgba[:, :, 3] = 1.0
return rgba


class _ImageBase(mcolorizer.ColorizingArtist):
"""
Base class for images.
Expand DownExpand Up@@ -508,10 +496,10 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
# alpha channel below.
output_alpha = (255 * alpha) if A.dtype == np.uint8 else alpha
else:
output_alpha = _resample(# resample alpha channel
self, A[..., 3], out_shape, t, alpha=alpha)
output = _resample(# resample rgb channels
self,_rgb_to_rgba(A[..., :3]), out_shape, t, alpha=alpha)
# resample alpha channel
output_alpha = _resample(self, A[..., 3], out_shape, t, alpha=alpha)
# resample rgb channels
output = _resample(self, A[..., :3], out_shape, t, alpha=alpha)
output[..., 3] = output_alpha # recombine rgb and alpha

# output is now either a 2D array of normed (int or float) data
Expand Down
4 changes: 2 additions & 2 deletionslib/matplotlib/tests/test_image.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1577,8 +1577,8 @@ def test__resample_valid_output():
resample(np.zeros((9, 9)), np.zeros((9, 9, 4)))
with pytest.raises(ValueError, match="different dimensionalities"):
resample(np.zeros((9, 9, 4)), np.zeros((9, 9)))
with pytest.raises(ValueError, match="3D input array must beRGBA"):
resample(np.zeros((9, 9,3)), np.zeros((9, 9, 4)))
with pytest.raises(ValueError, match="3D input array must beRGB"):
resample(np.zeros((9, 9,2)), np.zeros((9, 9, 4)))
with pytest.raises(ValueError, match="3D output array must be RGBA"):
resample(np.zeros((9, 9, 4)), np.zeros((9, 9, 3)))
with pytest.raises(ValueError, match="mismatched types"):
Expand Down
90 changes: 55 additions & 35 deletionssrc/_image_resample.h
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@
#include "agg_image_accessors.h"
#include "agg_path_storage.h"
#include "agg_pixfmt_gray.h"
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgba.h"
#include "agg_renderer_base.h"
#include "agg_renderer_scanline.h"
Expand All@@ -16,6 +17,7 @@
#include "agg_span_allocator.h"
#include "agg_span_converter.h"
#include "agg_span_image_filter_gray.h"
#include "agg_span_image_filter_rgb.h"
#include "agg_span_image_filter_rgba.h"
#include "agg_span_interpolator_adaptor.h"
#include "agg_span_interpolator_linear.h"
Expand DownExpand Up@@ -496,16 +498,39 @@ typedef enum {
} interpolation_e;


// T isrgba if and only if it has an T::r field.
// T isrgb(a) if and only if it has an T::r field.
template<typename T, typename = void> struct is_grayscale : std::true_type {};
template<typename T> struct is_grayscale<T, std::void_t<decltype(T::r)>> : std::false_type {};
template<typename T> constexpr bool is_grayscale_v = is_grayscale<T>::value;


template<typename color_type>
// rgb_step is only used if input_has_alpha=false.
template<typename color_type, bool input_has_alpha, int rgb_step=3>
struct type_mapping
{
using blender_type = std::conditional_t<
using input_blender_type = std::conditional_t<
is_grayscale_v<color_type>,
agg::blender_gray<color_type>,
std::conditional_t<
input_has_alpha,
std::conditional_t<
std::is_same_v<color_type, agg::rgba8>,
fixed_blender_rgba_plain<color_type, agg::order_rgba>,
agg::blender_rgba_plain<color_type, agg::order_rgba>
>,
agg::blender_rgb<color_type, agg::order_rgb>
>
>;
using input_pixfmt_type = std::conditional_t<
is_grayscale_v<color_type>,
agg::pixfmt_alpha_blend_gray<input_blender_type, agg::rendering_buffer>,
std::conditional_t<
input_has_alpha,
agg::pixfmt_alpha_blend_rgba<input_blender_type, agg::rendering_buffer>,
agg::pixfmt_alpha_blend_rgb<input_blender_type, agg::rendering_buffer, rgb_step>
>
>;
using output_blender_type = std::conditional_t<
is_grayscale_v<color_type>,
agg::blender_gray<color_type>,
std::conditional_t<
Expand All@@ -514,36 +539,37 @@ struct type_mapping
agg::blender_rgba_plain<color_type, agg::order_rgba>
>
>;
usingpixfmt_type = std::conditional_t<
usingoutput_pixfmt_type = std::conditional_t<
is_grayscale_v<color_type>,
agg::pixfmt_alpha_blend_gray<blender_type, agg::rendering_buffer>,
agg::pixfmt_alpha_blend_rgba<blender_type, agg::rendering_buffer>
>;
using pixfmt_pre_type = std::conditional_t<
is_grayscale_v<color_type>,
pixfmt_type,
agg::pixfmt_alpha_blend_rgba<
std::conditional_t<
std::is_same_v<color_type, agg::rgba8>,
fixed_blender_rgba_pre<color_type, agg::order_rgba>,
agg::blender_rgba_pre<color_type, agg::order_rgba>
>,
agg::rendering_buffer>
agg::pixfmt_alpha_blend_gray<output_blender_type, agg::rendering_buffer>,
agg::pixfmt_alpha_blend_rgba<output_blender_type, agg::rendering_buffer>
>;
template<typename A> using span_gen_affine_type = std::conditional_t<
is_grayscale_v<color_type>,
agg::span_image_resample_gray_affine<A>,
agg::span_image_resample_rgba_affine<A>
std::conditional_t<
input_has_alpha,
agg::span_image_resample_rgba_affine<A>,
agg::span_image_resample_rgb_affine<A>
>
>;
template<typename A, typename B> using span_gen_filter_type = std::conditional_t<
is_grayscale_v<color_type>,
agg::span_image_filter_gray<A, B>,
agg::span_image_filter_rgba<A, B>
std::conditional_t<
input_has_alpha,
agg::span_image_filter_rgba<A, B>,
agg::span_image_filter_rgb<A, B>
>
>;
template<typename A, typename B> using span_gen_nn_type = std::conditional_t<
is_grayscale_v<color_type>,
agg::span_image_filter_gray_nn<A, B>,
agg::span_image_filter_rgba_nn<A, B>
std::conditional_t<
input_has_alpha,
agg::span_image_filter_rgba_nn<A, B>,
agg::span_image_filter_rgb_nn<A, B>
>
>;
};

Expand DownExpand Up@@ -697,16 +723,17 @@ static void get_filter(const resample_params_t &params,
}


template<typename color_type>
// rgb_step is only used if input_has_alpha=false.
template<typename color_type, bool input_has_alpha = true, int rgb_step = 3>
void resample(
const void *input, int in_width, int in_height,
void *output, int out_width, int out_height,
const void *input, int in_width, int in_height, int in_stride,
void *output, int out_width, int out_height, int out_stride,
resample_params_t &params)
{
using type_mapping_t = type_mapping<color_type>;
using type_mapping_t = type_mapping<color_type, input_has_alpha, rgb_step>;

using input_pixfmt_t = typename type_mapping_t::pixfmt_type;
using output_pixfmt_t = typename type_mapping_t::pixfmt_type;
using input_pixfmt_t = typename type_mapping_t::input_pixfmt_type;
using output_pixfmt_t = typename type_mapping_t::output_pixfmt_type;

using renderer_t = agg::renderer_base<output_pixfmt_t>;
using rasterizer_t = agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl>;
Expand All@@ -722,11 +749,6 @@ void resample(
using arbitrary_interpolator_t =
agg::span_interpolator_adaptor<agg::span_interpolator_linear<>, lookup_distortion>;

size_t itemsize = sizeof(color_type);
if (is_grayscale<color_type>::value) {
itemsize /= 2; // agg::grayXX includes an alpha channel which we don't have.
}

if (params.interpolation != NEAREST &&
params.is_affine &&
fabs(params.affine.sx) == 1.0 &&
Expand All@@ -743,14 +765,12 @@ void resample(
span_conv_alpha_t conv_alpha(params.alpha);

agg::rendering_buffer input_buffer;
input_buffer.attach(
(unsigned char *)input, in_width, in_height, in_width * itemsize);
input_buffer.attach((unsigned char *)input, in_width, in_height, in_stride);
input_pixfmt_t input_pixfmt(input_buffer);
image_accessor_t input_accessor(input_pixfmt);

agg::rendering_buffer output_buffer;
output_buffer.attach(
(unsigned char *)output, out_width, out_height, out_width * itemsize);
output_buffer.attach((unsigned char *)output, out_width, out_height, out_stride);
output_pixfmt_t output_pixfmt(output_buffer);
renderer_t renderer(output_pixfmt);

Expand Down
79 changes: 57 additions & 22 deletionssrc/_image_wrapper.cpp
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,14 +106,30 @@ image_resample(py::array input_array,
throw std::invalid_argument("Input array must be a 2D or 3D array");
}

if (ndim == 3 && input_array.shape(2) != 4) {
throw std::invalid_argument(
"3D input array must be RGBA with shape (M, N, 4), has trailing dimension of {}"_s.format(
input_array.shape(2)));
py::ssize_t ncomponents = 0;
int rgb_step = 0;
if (ndim == 3) {
ncomponents = input_array.shape(2);
if (ncomponents == 3) {
// We special-case a few options in order to avoid copying in the common case.
auto rgb_stride = input_array.strides(1);
auto item_stride = input_array.strides(2);
if (rgb_stride == 3 * item_stride) {
rgb_step = 3;
} else if (rgb_stride == 4 * item_stride) {
rgb_step = 4;
}
} else if (ncomponents != 4) {
throw std::invalid_argument(
"3D input array must be RGB with shape (M, N, 3) or RGBA with shape (M, N, 4), "
"has trailing dimension of {}"_s.format(ncomponents));
}
}

// Ensure input array is contiguous, regardless of dtype
input_array = py::array::ensure(input_array, py::array::c_style);
if (rgb_step == 0) {
// Ensure input array is contiguous, regardless of dtype
input_array = py::array::ensure(input_array, py::array::c_style);
}

// Validate output array
auto out_ndim = output_array.ndim();
Expand DownExpand Up@@ -173,25 +189,44 @@ image_resample(py::array input_array,

if (auto resampler =
(ndim == 2) ? (
(dtype.equal(py::dtype::of<std::uint8_t>())) ? resample<agg::gray8> :
(dtype.equal(py::dtype::of<std::int8_t>())) ? resample<agg::gray8> :
(dtype.equal(py::dtype::of<std::uint16_t>())) ? resample<agg::gray16> :
(dtype.equal(py::dtype::of<std::int16_t>())) ? resample<agg::gray16> :
(dtype.equal(py::dtype::of<float>())) ? resample<agg::gray32> :
(dtype.equal(py::dtype::of<double>())) ? resample<agg::gray64> :
dtype.equal(py::dtype::of<std::uint8_t>()) ? resample<agg::gray8> :
dtype.equal(py::dtype::of<std::int8_t>()) ? resample<agg::gray8> :
dtype.equal(py::dtype::of<std::uint16_t>()) ? resample<agg::gray16> :
dtype.equal(py::dtype::of<std::int16_t>()) ? resample<agg::gray16> :
dtype.equal(py::dtype::of<float>()) ? resample<agg::gray32> :
dtype.equal(py::dtype::of<double>()) ? resample<agg::gray64> :
nullptr) : (
// ndim == 3
(dtype.equal(py::dtype::of<std::uint8_t>())) ? resample<agg::rgba8> :
(dtype.equal(py::dtype::of<std::int8_t>())) ? resample<agg::rgba8> :
(dtype.equal(py::dtype::of<std::uint16_t>())) ? resample<agg::rgba16> :
(dtype.equal(py::dtype::of<std::int16_t>())) ? resample<agg::rgba16> :
(dtype.equal(py::dtype::of<float>())) ? resample<agg::rgba32> :
(dtype.equal(py::dtype::of<double>())) ? resample<agg::rgba64> :
nullptr)) {
// ndim == 3
(ncomponents == 4) ? (
dtype.equal(py::dtype::of<std::uint8_t>()) ? resample<agg::rgba8, true> :
dtype.equal(py::dtype::of<std::int8_t>()) ? resample<agg::rgba8, true> :
dtype.equal(py::dtype::of<std::uint16_t>()) ? resample<agg::rgba16, true> :
dtype.equal(py::dtype::of<std::int16_t>()) ? resample<agg::rgba16, true> :
dtype.equal(py::dtype::of<float>()) ? resample<agg::rgba32, true> :
dtype.equal(py::dtype::of<double>()) ? resample<agg::rgba64, true> :
nullptr
) : (
(rgb_step == 4) ? (
dtype.equal(py::dtype::of<std::uint8_t>()) ? resample<agg::rgba8, false, 4> :
dtype.equal(py::dtype::of<std::int8_t>()) ? resample<agg::rgba8, false, 4> :
dtype.equal(py::dtype::of<std::uint16_t>()) ? resample<agg::rgba16, false, 4> :
dtype.equal(py::dtype::of<std::int16_t>()) ? resample<agg::rgba16, false, 4> :
dtype.equal(py::dtype::of<float>()) ? resample<agg::rgba32, false, 4> :
dtype.equal(py::dtype::of<double>()) ? resample<agg::rgba64, false, 4> :
nullptr
) : (
dtype.equal(py::dtype::of<std::uint8_t>()) ? resample<agg::rgba8, false, 3> :
dtype.equal(py::dtype::of<std::int8_t>()) ? resample<agg::rgba8, false, 3> :
dtype.equal(py::dtype::of<std::uint16_t>()) ? resample<agg::rgba16, false, 3> :
dtype.equal(py::dtype::of<std::int16_t>()) ? resample<agg::rgba16, false, 3> :
dtype.equal(py::dtype::of<float>()) ? resample<agg::rgba32, false, 3> :
dtype.equal(py::dtype::of<double>()) ? resample<agg::rgba64, false, 3> :
nullptr))))
{
Py_BEGIN_ALLOW_THREADS
resampler(
input_array.data(), input_array.shape(1), input_array.shape(0),
output_array.mutable_data(), output_array.shape(1), output_array.shape(0),
input_array.data(), input_array.shape(1), input_array.shape(0), input_array.strides(0),
output_array.mutable_data(), output_array.shape(1), output_array.shape(0), output_array.strides(0),
params);
Py_END_ALLOW_THREADS
} else {
Expand Down
40 changes: 0 additions & 40 deletionssrc/agg_workaround.h
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,46 +8,6 @@
blending of RGBA32 pixels does not preserve enough precision
*/

template<class ColorT, class Order>
struct fixed_blender_rgba_pre : agg::conv_rgba_pre<ColorT, Order>
{
typedef ColorT color_type;
typedef Order order_type;
typedef typename color_type::value_type value_type;
typedef typename color_type::calc_type calc_type;
typedef typename color_type::long_type long_type;
enum base_scale_e
{
base_shift = color_type::base_shift,
base_mask = color_type::base_mask
};

//--------------------------------------------------------------------
static AGG_INLINE void blend_pix(value_type* p,
value_type cr, value_type cg, value_type cb,
value_type alpha, agg::cover_type cover)
{
blend_pix(p,
color_type::mult_cover(cr, cover),
color_type::mult_cover(cg, cover),
color_type::mult_cover(cb, cover),
color_type::mult_cover(alpha, cover));
}

//--------------------------------------------------------------------
static AGG_INLINE void blend_pix(value_type* p,
value_type cr, value_type cg, value_type cb,
value_type alpha)
{
alpha = base_mask - alpha;
p[Order::R] = (value_type)(((p[Order::R] * alpha) >> base_shift) + cr);
p[Order::G] = (value_type)(((p[Order::G] * alpha) >> base_shift) + cg);
p[Order::B] = (value_type)(((p[Order::B] * alpha) >> base_shift) + cb);
p[Order::A] = (value_type)(base_mask - ((alpha * (base_mask - p[Order::A])) >> base_shift));
}
};


template<class ColorT, class Order>
struct fixed_blender_rgba_plain : agg::conv_rgba_plain<ColorT, Order>
{
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp