Skip to content

Expanded functionality of oiiotool --resize #3751

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
40 changes: 38 additions & 2 deletions src/doc/oiiotool.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3167,11 +3167,33 @@ current top image.

*wscale% x hscale%*

if `width` or `height` is 0, that dimension will be
automatically computed so as to preserve the original aspect ratio.
If `width` or `height` is 0, that dimension will be automatically computed
so as to preserve the original aspect ratio.

By default, the scaling that occurs is to map the *full/display* window
area of the input image to the full/display window of the output image
(determined by the `size` argument). However, a more general warping
can be specified using any of the `:from=`, `:to=`, or `:offset=`
optional modifiers. (These modifiers were added in OpenmageIO 2.5.)

Optional appended modifiers include:

`:from=` *size*
The region (specified in any of the same forms as the *size* argument,
with decimal / partial pixel sizes and offsets allowed) of the source
image that defines the transformational mapping. This defaults to the
full/display window of the source image.

`:to=` *size*
The region (specified in any of the same forms as the *size* argument,
with decimal / partial pixel sizes and offsets allowed) of the
destination image that defines the transformational mapping. This
defaults to the full/display window of the destination image.

`:offset=` [+-] *xoffset* [+-] *yoffset*
An additional offset (after the resize) to adjust the placement of the
result. Fractional pixel offsets are allowed.

`:filter=` *name*
Filter name. The default is `blackman-harris` when increasing
resolution, `lanczos3` when decreasing resolution.
Expand All @@ -3182,6 +3204,11 @@ current top image.
`--rangeexpand`, which can reduce visible ringing artifacts when a
filter with negative lobes is used on a very high-contrast HDR image.

`:edgeclamp=` *bool*
If nonzero, clamp the image to the edge pixels before filtering.
This might help with certain edge ringing situations. The default is
0 (off).

`:subimages=` *indices-or-names*
Include/exclude subimages (see :ref:`sec-oiiotool-subimage-modifier`).

Expand All @@ -3192,6 +3219,15 @@ current top image.
--resize 300% # increase resolution to 1920x1440
--resize 400x0 # new resolution will be 400x300

# Create a 1024x768 image that is a resized and shifted version
# of the original, where the upper left 100x100 section of the
# original maps to a 200x200 region starting at the 50,50
# coordinates of the new image.
--resize:from=100x100:to=200x200+50+50 1024x768

# Resize to 320x240, but with an additional 1/2 pixel shift in
# each direction.
--resize:offset=+0.5+0.5 320x240

.. option:: --fit <size>

Expand Down
24 changes: 24 additions & 0 deletions src/include/OpenImageIO/imagebufalgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,9 @@ bool OIIO_API resize (ImageBuf &dst, const ImageBuf &src, Filter2D *filter,
/// For "deep" images, this function returns copies the closest source pixel
/// needed, rather than attempting to interpolate deep pixels (regardless of
/// the value of `interpolate`).
///
/// @see ImageBufAlgo::resize()

ImageBuf OIIO_API resample (const ImageBuf &src, bool interpolate = true,
ROI roi={}, int nthreads=0);
/// Write to an existing image `dst` (allocating if it is uninitialized).
Expand Down Expand Up @@ -790,6 +793,27 @@ bool OIIO_API warp (ImageBuf &dst, const ImageBuf &src, M33fParam M,
const Filter2D *filter, bool recompute_roi = false,
ImageBuf::WrapMode wrap = ImageBuf::WrapDefault,
ROI roi = {}, int nthreads=0);

#ifdef OIIO_INTERNAL /* experimental -- not part of public API yet */
ImageBuf OIIO_API warp (const ImageBuf &src, M33fParam M,
string_view filtername,
float filterwidth, bool recompute_roi,
ImageBuf::WrapMode wrap, bool edgeclamp,
ROI roi={}, int nthreads=0);
ImageBuf OIIO_API warp (const ImageBuf &src, M33fParam M,
const Filter2D *filter, bool recompute_roi,
ImageBuf::WrapMode wrap, bool edgeclamp,
ROI roi = {}, int nthreads=0);
bool OIIO_API warp (ImageBuf &dst, const ImageBuf &src, M33fParam M,
string_view filtername,
float filterwidth, bool recompute_roi,
ImageBuf::WrapMode wrap, bool edgeclamp,
ROI roi={}, int nthreads=0);
bool OIIO_API warp (ImageBuf &dst, const ImageBuf &src, M33fParam M,
const Filter2D *filter, bool recompute_roi,
ImageBuf::WrapMode wrap, bool edgeclamp,
ROI roi = {}, int nthreads=0);
#endif // OIIO_INTERNAL
/// @}


Expand Down
63 changes: 62 additions & 1 deletion src/libOpenImageIO/imagebufalgo_xform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ warp_impl(ImageBuf& dst, const ImageBuf& src, const Imath::M33f& M,
// filter we allocate here is properly destroyed.
std::shared_ptr<Filter2D> filterptr((Filter2D*)NULL, Filter2D::destroy);
if (filter == NULL) {
// If no filter was provided, punt and just linearly interpolate.
// If no filter was provided, punt and use lanczos3
filterptr.reset(Filter2D::create("lanczos3", 6.0f, 6.0f));
filter = filterptr.get();
}
Expand Down Expand Up @@ -355,6 +355,67 @@ ImageBufAlgo::warp(const ImageBuf& src, M33fParam M, string_view filtername,



bool
ImageBufAlgo::warp(ImageBuf& dst, const ImageBuf& src, M33fParam M,
const Filter2D* filter, bool recompute_roi,
ImageBuf::WrapMode wrap, bool edgeclamp, ROI roi,
int nthreads)
{
return warp_impl(dst, src, M, filter, recompute_roi, wrap, edgeclamp, roi,
nthreads);
}



bool
ImageBufAlgo::warp(ImageBuf& dst, const ImageBuf& src, M33fParam M,
string_view filtername, float filterwidth,
bool recompute_roi, ImageBuf::WrapMode wrap, bool edgeclamp,
ROI roi, int nthreads)
{
// Set up a shared pointer with custom deleter to make sure any
// filter we allocate here is properly destroyed.
auto filter = get_warp_filter(filtername, filterwidth, dst);
if (!filter) {
return false; // error issued in get_warp_filter
}
return warp(dst, src, M, filter.get(), recompute_roi, wrap, edgeclamp, roi,
nthreads);
}



ImageBuf
ImageBufAlgo::warp(const ImageBuf& src, M33fParam M, const Filter2D* filter,
bool recompute_roi, ImageBuf::WrapMode wrap, bool edgeclamp,
ROI roi, int nthreads)
{
ImageBuf result;
bool ok = warp(result, src, M, filter, recompute_roi, wrap, edgeclamp, roi,
nthreads);
if (!ok && !result.has_error())
result.errorfmt("ImageBufAlgo::warp() error");
return result;
}



ImageBuf
ImageBufAlgo::warp(const ImageBuf& src, M33fParam M, string_view filtername,
float filterwidth, bool recompute_roi,
ImageBuf::WrapMode wrap, bool edgeclamp, ROI roi,
int nthreads)
{
ImageBuf result;
bool ok = warp(result, src, M, filtername, filterwidth, recompute_roi, wrap,
edgeclamp, roi, nthreads);
if (!ok && !result.has_error())
result.errorfmt("ImageBufAlgo::warp() error");
return result;
}



bool
ImageBufAlgo::rotate(ImageBuf& dst, const ImageBuf& src, float angle,
float center_x, float center_y, Filter2D* filter,
Expand Down
Loading