% July 1992 %
% %
% %
-% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
#include "magick/pixel.h"
#include "magick/option.h"
#include "magick/resample.h"
+#include "magick/resample-private.h"
#include "magick/resize.h"
#include "magick/resize-private.h"
#include "magick/string_.h"
window_support, /* window support, usally equal to support (expert only) */
scale, /* dimension scaling to fit window support (usally 1.0) */
blur, /* x-scale (blur-sharpen) */
- coeff[8]; /* cubic coefficents for smooth Cubic filters */
+ coefficient[7]; /* cubic coefficents for BC-cubic spline filters */
size_t
signature;
{
/*
Cubic Filters using B,C determined values:
- Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
- Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
- Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
- Hermite B= 0 C= 0 Quadratic Spline (support = 1)
+ Mitchell-Netravali B= 1/3 C= 1/3 "Balanced" cubic spline filter
+ Catmull-Rom B= 0 C= 1/2 Interpolatory and exact on linears
+ Cubic B-Spline B= 1 C= 0 Spline approximation of Gaussian
+ Hermite B= 0 C= 0 Spline with small support (= 1)
See paper by Mitchell and Netravali, Reconstruction Filters in Computer
Graphics Computer Graphics, Volume 22, Number 4, August 1988
Mitchell.pdf.
Coefficents are determined from B,C values:
- P0 = ( 6 - 2*B )/6
+ P0 = ( 6 - 2*B )/6 = coeff[0]
P1 = 0
- P2 = (-18 +12*B + 6*C )/6
- P3 = ( 12 - 9*B - 6*C )/6
- Q0 = ( 8*B +24*C )/6
- Q1 = ( -12*B -48*C )/6
- Q2 = ( 6*B +30*C )/6
- Q3 = ( - 1*B - 6*C )/6
+ P2 = (-18 +12*B + 6*C )/6 = coeff[1]
+ P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
+ Q0 = ( 8*B +24*C )/6 = coeff[3]
+ Q1 = ( -12*B -48*C )/6 = coeff[4]
+ Q2 = ( 6*B +30*C )/6 = coeff[5]
+ Q3 = ( - 1*B - 6*C )/6 = coeff[6]
which are used to define the filter:
P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
- Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
+ Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2
- which ensures function is continuous in value and derivative (slope).
+ which ensures function is continuous in value and derivative
+ (slope).
*/
if (x < 1.0)
- return(resize_filter->coeff[0]+x*(resize_filter->coeff[1]+x*
- (resize_filter->coeff[2]+x*resize_filter->coeff[3])));
+ return(resize_filter->coefficient[0]+x*(x*
+ (resize_filter->coefficient[1]+x*resize_filter->coefficient[2])));
if (x < 2.0)
- return(resize_filter->coeff[4]+x*(resize_filter->coeff[5]+x*
- (resize_filter->coeff[6]+x*resize_filter->coeff[7])));
+ return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
+ (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
return(0.0);
}
/*
Gaussian with a fixed sigma = 1/2
- Gaussian Formula...
- exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
+ Gaussian Formula (1D) ...
+ exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI)sigma^2))
The constants are pre-calculated...
exp( -coeff[0]*(x^2)) ) * coeff[1]
- However the multiplier coefficent is not needed and not used.
+ However the multiplier coefficent (1) is not needed and not used.
- This separates the gaussian 'sigma' value from the 'blur/support' settings
- allows for its use in special 'small sigma' gaussians, without the filter
- 'missing' pixels when blur and thus support becomes too small.
+ Gaussian Formula (2D) ...
+ exp( -(x^2)/((2.0*sigma^2) ) / (PI*sigma^2) )
+ Note that it is only a change in the normalization multiplier
+ which is not needed or used when gausian is used as a filter.
+
+ This separates the gaussian 'sigma' value from the 'blur/support'
+ settings allowing for its use in special 'small sigma' gaussians,
+ without the filter 'missing' pixels because the support becomes too
+ small.
*/
- return(exp((double)(-resize_filter->coeff[0]*x*x))); }
+ return(exp((double)(-resize_filter->coefficient[0]*x*x)));
+}
static MagickRealType Hanning(const MagickRealType x,
const ResizeFilter *magick_unused(resize_filter))
{
/*
1st order (linear) B-Spline, bilinear interpolation, Tent 1D
- filter, or a Bartlett 2D Cone filter.
+ filter, or a Bartlett 2D Cone filter. Also used as a
+ Bartlett Windowing function for Sinc().
*/
if (x < 1.0)
return(1.0-x);
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
-% from these filters:
+% AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
+% these filters:
%
% FIR (Finite impulse Response) Filters
% Box Triangle Quadratic
% Kaiser Lanczos
%
% Special purpose Filters
-% SincFast Lanczos2D Robidoux
+% SincFast LanczosSharp Lanczos2D Lanczos2DSharp Robidoux
%
% The users "-filter" selection is used to lookup the default 'expert'
% settings for that filter from a internal table. However any provided
% 'expert' settings (see below) may override this selection.
%
-% FIR filters are used as is, and are limited to that filters support
-% window (unless over-ridden). 'Gaussian' while classed as an IIR
-% filter, is also simply clipped by its support size (currently 1.5
-% or approximatally 3*sigma as recommended by many references)
-%
-% The selection is typically either a windowed Sinc, or interpolated
-% filter, for use by functions such as ResizeImage(). However if a
-% 'cylindrical' filter flag is requested, any default Sinc weighting
-% and windowing functions will be promoted to cylindrical Jinc form of
-% function.
-%
-% Directly requesting 'Sinc' or 'Jinc' will force the use of that
-% filter function without any windowing. This is not recommended,
-% except by image processing experts or in expert options. Selecting a
-% window filtering version of these functions is better.
-%
-% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
-% the cylindrical case) but defaulting to 3-lobe support, rather that
-% the default 4 lobe support of the other windowed sinc/jinc filters.
-%
-% Two forms of the 'Sinc' function are available: Sinc and SincFast.
-% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
-% selected if the user specifically specifies the use of a Sinc
-% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
-% and rational (high Q) approximations, and will be used by default in
-% most cases.
-%
-% The Lanczos2D and Robidoux filters are tuned for cylindrical
-% (radial) EWA (Elliptical Weighted Average) distortion. Lanczos2D
-% is a 2 lobe Lanczos-like filter using Jinc (for EWA) or Sinc.
-% Robidoux used to be a sharpened version of Lanczos2D (with
-% blur=0.958033808). Now, it is the unique Cubic 'Keys' filter that
-% exactly preserves images with only vertical or horizontal features
-% when performing 'no-op" with EWA distortion. It turns out to be
-% close to both plain Mitchell and "sharpened" Lanczos2D.
-%
-% Special 'expert' options can be used to override any and all filter
-% settings. This is not advised unless you have expert knowledge of
-% the use of resampling filtered techniques. Check on the results of
-% your selections using the "filter:verbose" setting to make sure you
-% get the exact filter that you are tring to achieve.
-%
-% "filter:filter" Select the main function associated with
-% this filter name, as the weighting function of the filter.
-% This can be used to set a windowing function as a weighting
-% function, for special purposes, such as graphing.
-%
-% If a "filter:window" operation has not been provided, then a 'Box'
-% windowing function will be set to denote that no windowing function
-% is being used.
-%
-% "filter:window" Select this windowing function for the filter.
-% While any filter could be used as a windowing function, using the
-% 'first lobe' of that filter over the whole support window, using a
-% non-windowing function is not advisible. If no weighting filter
-% function is specifed a 'SincFast' filter will be used.
-%
-% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
-% This a simpler method of setting filter support size that will
-% correctly handle the Sinc/Jinc switch for an operators filtering
-% requirements. Only integers should be given.
-%
-% "filter:support" Set the support size for filtering to the size given
-% This not recommended for Sinc/Jinc windowed filters (lobes should
-% be used instead). This will override any 'filter:lobes' option.
-%
-% "filter:win-support" Scale windowing function to this size instead.
-% This causes the windowing (or self-windowing Lagrange filter) to act
-% is if the support window it much much larger than what is actually
-% supplied to the calling operator. The filter however is still
-% clipped to the real support size given, by the support range suppiled
-% to the caller. If unset this will equal the normal filter support
-% size.
-%
-% "filter:blur" Scale the filter and support window by this amount.
-% A value >1 will generally result in a more burred image with
-% more ringing effects, while a value <1 will sharpen the
-% resulting image with more aliasing and Morie effects.
-%
-% "filter:sigma" The sigma value to use for the Gaussian filter only.
-% Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
-% usage. It effectially provides a alturnative to 'blur' for Gaussians
-% without it also effecting the final 'practical support' size.
+% FIR filters are used as is, and are limited to that filters support window
+% (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
+% simply clipped by its support size (currently 1.5 or approximatally 3*sigma
+% as recommended by many references)
+%
+% The special a 'cylindrical' filter flag will promote the default 4-lobed
+% Windowed Sinc filter to a 3-lobed Windowed Jinc equivelent, which is better
+% suited to this style of image resampling. This typically happens when using
+% such a filter for images distortions.
+%
+% Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
+% of function without any windowing, or promotion for cylindrical usage. This
+% is not recommended, except by image processing experts, especially as part
+% of expert option filter function selection.
+%
+% Two forms of the 'Sinc' function are available: Sinc and SincFast. Sinc is
+% computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
+% specifically specifies the use of a Sinc filter. SincFast uses highly
+% accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
+% and will be used by default in most cases.
+%
+% The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
+% to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
+% The Sinc version is the most popular windowed filter.
+%
+% LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
+% the Lanczos filter, specifically designed for EWA distortion (as a
+% Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
+% (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
+% satisfying the following condition without changing the character of the
+% corresponding EWA filter:
+%
+% 'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
+% only vertical or horizontal features are preserved when performing 'no-op"
+% with EWA distortion.
+%
+% The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
+% filters. The 'sharp' version uses a blur factor of 0.9549963639785485,
+% again chosen because the resulting EWA filter comes as close as possible to
+% satisfying the above condition.
+%
+% Robidoux is another filter tuned for EWA. It is the Keys cubic filter
+% defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
+% Vertical and Horizontal Line Preservation Condition" exactly, and it
+% moderately blurs high frequency 'pixel-hash' patterns under no-op. It turns
+% out to be close to both Mitchell and Lanczos2Sharp. For example, its first
+% crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
+% first crossing of Mitchell and Lanczos2Sharp.
+%
+% 'EXPERT' OPTIONS:
+%
+% These artifact "defines" are not recommended for production use without
+% expert knowledge of resampling, filtering, and the effects they have on the
+% resulting resampled (resize ro distorted) image.
+%
+% They can be used to override any and all filter default, and it is
+% recommended you make good use of "filter:verbose" to make sure that the
+% overall effect of your selection (before and after) is as expected.
+%
+% "filter:verbose" controls whether to output the exact results of the
+% filter selections made, as well as plotting data for graphing the
+% resulting filter over the filters support range.
+%
+% "filter:filter" select the main function associated with this filter
+% name, as the weighting function of the filter. This can be used to
+% set a windowing function as a weighting function, for special
+% purposes, such as graphing.
+%
+% If a "filter:window" operation has not been provided, a 'Box'
+% windowing function will be set to denote that no windowing function is
+% being used.
+%
+% "filter:window" Select this windowing function for the filter. While any
+% filter could be used as a windowing function, using the 'first lobe' of
+% that filter over the whole support window, using a non-windowing
+% function is not advisible. If no weighting filter function is specifed
+% a 'SincFast' filter is used.
+%
+% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter. This a
+% simpler method of setting filter support size that will correctly
+% handle the Sinc/Jinc switch for an operators filtering requirements.
+% Only integers should be given.
+%
+% "filter:support" Set the support size for filtering to the size given.
+% This not recommended for Sinc/Jinc windowed filters (lobes should be
+% used instead). This will override any 'filter:lobes' option.
+%
+% "filter:win-support" Scale windowing function to this size instead. This
+% causes the windowing (or self-windowing Lagrange filter) to act is if
+% the support window it much much larger than what is actually supplied
+% to the calling operator. The filter however is still clipped to the
+% real support size given, by the support range suppiled to the caller.
+% If unset this will equal the normal filter support size.
+%
+% "filter:blur" Scale the filter and support window by this amount. A value
+% > 1 will generally result in a more burred image with more ringing
+% effects, while a value <1 will sharpen the resulting image with more
+% aliasing effects.
+%
+% "filter:sigma" The sigma value to use for the Gaussian filter only.
+% Defaults to '1/2'. Using a different sigma effectively provides a
+% method of using the filter as a 'blur' convolution. Particularly when
+% using it for Distort.
%
% "filter:b"
-% "filter:c" Override the preset B,C values for a Cubic type of filter
-% If only one of these are given it is assumes to be a 'Keys'
-% type of filter such that B+2C=1, where Keys 'alpha' value = C
+% "filter:c" Override the preset B,C values for a Cubic type of filter.
+% If only one of these are given it is assumes to be a 'Keys' type of
+% filter such that B+2C=1, where Keys 'alpha' value = C.
%
-% "filter:verbose" Output the exact results of the filter selections
-% made, as well as plotting data for graphing the resulting filter
-% over support range (blur adjusted).
+% Examples:
%
-% Set a true un-windowed Sinc filter with 10 lobes (very slow)
+% Set a true un-windowed Sinc filter with 10 lobes (very slow):
% -define filter:filter=Sinc
% -define filter:lobes=8
%
-% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
+% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
% -filter Lanczos
% -define filter:lobes=8
%
%
% o image: the image.
%
-% o filter: the filter type, defining a preset filter, window and
-% support. The artifact settings listed above will override
-% those selections.
+% o filter: the filter type, defining a preset filter, window and support.
+% The artifact settings listed above will override those selections.
%
% o blur: blur the filter by this amount, use 1.0 if unknown. Image
-% artifact "filter:blur" will override this API call usage, including
-% any internal change (such as for cylindrical usage).
+% artifact "filter:blur" will override this API call usage, including any
+% internal change (such as for cylindrical usage).
%
-% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
-% (radial) filter (Jinc)
+% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
+% filter (Jinc).
%
% o exception: return any errors or warnings in this structure.
%
register ResizeFilter
*resize_filter;
- ssize_t
- option;
-
/*
- Table Mapping given Filter, into Weighting and Windowing functions.
- A 'Box' windowing function means its a simble non-windowed filter.
- An 'SincFast' filter function could be upgraded to a 'Jinc' filter
- if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
- specifically requested.
+ Table Mapping given Filter, into Weighting and Windowing functions. A
+ 'Box' windowing function means its a simble non-windowed filter. An
+ 'SincFast' filter function could be upgraded to a 'Jinc' filter if a
+ "cylindrical", unless a 'Sinc' or 'SincFast' filter was specifically
+ requested.
- WARNING: The order of this tabel must match the order of the
- FilterTypes enumeration specified in "resample.h", or the filter
- names will not match the filter being setup.
+ WARNING: The order of this tabel must match the order of the FilterTypes
+ enumeration specified in "resample.h", or the filter names will not match
+ the filter being setup.
You can check filter setups with the "filter:verbose" setting.
*/
window;
} const mapping[SentinelFilter] =
{
- { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
- { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
- { BoxFilter, BoxFilter }, /* Box averaging filter */
- { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
- { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
- { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
- { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
- { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
- { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
- { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
- { CubicFilter, BoxFilter }, /* Cubic B-Spline */
- { CatromFilter, BoxFilter }, /* Cubic interpolator */
- { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
- { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
- { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
- { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
- { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
- { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
- { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
- { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
- { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
- { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
- { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
- { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
- { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
- { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
+ { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
+ { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
+ { BoxFilter, BoxFilter }, /* Box averaging filter */
+ { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
+ { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
+ { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
+ { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
+ { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
+ { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
+ { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
+ { CubicFilter, BoxFilter }, /* Cubic B-Spline */
+ { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
+ { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
+ { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
+ { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
+ { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
+ { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
+ { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
+ { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
+ { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
+ { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
+ { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
+ { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
+ { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
+ { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
+ { Lanczos2SharpFilter,Lanczos2SharpFilter },
+ { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
};
/*
- Table mapping the filter/window from the above table to an actual
- function. The default support size for that filter as a weighting
- function, the range to scale with to use that function as a sinc
- windowing function, (typ 1.0).
+ Table mapping the filter/window from the above table to an actual function.
+ The default support size for that filter as a weighting function, the range
+ to scale with to use that function as a sinc windowing function, (typ 1.0).
Note that the filter_type -> function is 1 to 1 except for Sinc(),
- SincFast(), and CubicBC() functions, which may have multiple
- filter to function associations.
+ SincFast(), and CubicBC() functions, which may have multiple filter to
+ function associations.
- See "filter:verbose" handling below for the function -> filter
- mapping.
+ See "filter:verbose" handling below for the function -> filter mapping.
*/
static struct
{
MagickRealType
(*function)(const MagickRealType, const ResizeFilter*),
- lobes, /* default lobes/support size of the weighting filter */
- scale, /* windowing function range, for scaling windowing function */
- B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
+ lobes, /* Default lobes/support size of the weighting filter. */
+ scale, /* Support when function used as a windowing function
+ Typically equal to the location of the first zero crossing. */
+ B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
} const filters[SentinelFilter] =
{
{ Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
{ Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
{ CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
{ CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
- { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
- { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
- { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
+ { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
+ { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
{ Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
+ { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
{ Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
{ Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
{ CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
- { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
{ Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
{ Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
- { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
- { Jinc, 2.0, 1.21966989, 0.0, 0.0 }, /* Lanczos2D (Jinc-Jinc) */
- { Jinc, 2.0, 1.16848499, 0.0, 0.0 }, /* Lanczos2D Sharpened */
- { CubicBC, 2.0, 1.16848499, 0.37821575509399862, 0.31089212245300069 }
- /* Robidoux: Keys cubic close to Lanczos2D with blur=0.958033808 */
+ { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
+ { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
+ { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
+ { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
+ { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
+ { CubicBC, 2.0, 1.1685777620836932,
+ 0.37821575509399867, 0.31089212245300067 }
+ /* Robidoux: Keys cubic close to Lanczos2D sharpened */
};
/*
The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
- function being used as a filter. It is used by the "filter:lobes" and for
- the 'lobes' number in the above, the for support selection, so users do
- not have to deal with the highly irrational sizes of the 'lobes' of the
- Jinc filter.
+ function being used as a filter. It is used by the "filter:lobes" expert
+ setting and for 'lobes' for Jinc functions in the previous table. This way
+ users do not have to deal with the highly irrational lobe sizes of the Jinc
+ filter.
Values taken from
- http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
- using Jv-function with v=1, then dividing by PI.
+ http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp using
+ Jv-function with v=1, then dividing by PI.
*/
static MagickRealType
jinc_zeros[16] =
{
- 1.21966989126651,
- 2.23313059438153,
- 3.23831548416624,
- 4.24106286379607,
- 5.24276437687019,
- 6.24392168986449,
- 7.24475986871996,
- 8.24539491395205,
- 9.24589268494948,
- 10.2462933487549,
- 11.2466227948779,
- 12.2468984611381,
- 13.2471325221811,
- 14.2473337358069,
+ 1.2196698912665045,
+ 2.2331305943815286,
+ 3.2383154841662362,
+ 4.2410628637960699,
+ 5.2427643768701817,
+ 6.2439216898644877,
+ 7.244759868719957,
+ 8.2453949139520427,
+ 9.2458926849494673,
+ 10.246293348754916,
+ 11.246622794877883,
+ 12.246898461138105,
+ 13.247132522181061,
+ 14.247333735806849,
15.2475085630373,
- 16.247661874701
+ 16.247661874700962
};
/*
*/
filter_type=mapping[filter].filter;
window_type=mapping[filter].window;
- resize_filter->blur = blur;
- sigma = 0.5;
- /* Cylindrical Filters should use Jinc instead of Sinc */
- if (cylindrical != MagickFalse)
- switch (filter_type)
- {
- case SincFilter:
- /* Promote 1D Sinc Filter to a 2D Jinc filter. */
- if ( filter != SincFilter )
- filter_type=JincFilter;
- break;
- case SincFastFilter:
- /* Ditto for SincFast variant */
- if ( filter != SincFastFilter )
- filter_type=JincFilter;
- break;
- case LanczosFilter:
- /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
- filter_type=JincFilter;
- window_type=JincFilter;
- break;
- case Lanczos2DSharpFilter:
- /* Sharpened by Nicholas Robidoux so as to optimize for
- * minimal blurring of orthogonal lines
- */
- resize_filter->blur *= 0.958033808;
- break;
- case GaussianFilter:
- sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
- break;
- default:
- break;
- }
- else
- switch (filter_type)
- {
- case Lanczos2DFilter:
- case Lanczos2DSharpFilter:
- /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
- window_type=SincFastFilter;
- break;
- default:
- break;
- }
-
+ resize_filter->blur = blur; /* function argument blur factor */
+ sigma = 0.5; /* guassian sigma of half a pixel by default */
+ /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
+ if (cylindrical != MagickFalse && filter_type == SincFastFilter
+ && filter != SincFastFilter )
+ filter_type=JincFilter;
+
+ /* Expert filter setting override */
artifact=GetImageArtifact(image,"filter:filter");
if (artifact != (const char *) NULL)
{
- option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
+ ssize_t
+ option;
+
+ option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
if ((UndefinedFilter < option) && (option < SentinelFilter))
{ /* Raw filter request - no window function. */
filter_type=(FilterTypes) option;
window_type=BoxFilter;
}
- if (option == LanczosFilter)
- { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
- filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
- window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
- }
/* Filter override with a specific window function. */
artifact=GetImageArtifact(image,"filter:window");
if (artifact != (const char *) NULL)
{
- option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
+ option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
if ((UndefinedFilter < option) && (option < SentinelFilter))
- {
- if (option != LanczosFilter)
- window_type=(FilterTypes) option;
- else
- window_type=cylindrical != MagickFalse ? JincFilter :
- SincFastFilter;
- }
+ window_type=(FilterTypes) option;
}
}
else
artifact=GetImageArtifact(image,"filter:window");
if (artifact != (const char *) NULL)
{
- option=ParseMagickOption(MagickFilterOptions,MagickFalse,
+ ssize_t
+ option;
+
+ option=ParseCommandOption(MagickFilterOptions,MagickFalse,
artifact);
if ((UndefinedFilter < option) && (option < SentinelFilter))
{
}
}
}
+
/* Assign the real functions to use for the filters selected. */
resize_filter->filter=filters[filter_type].function;
resize_filter->support=filters[filter_type].lobes;
if (cylindrical != MagickFalse)
switch (filter_type)
{
- case PointFilter:
case BoxFilter:
/* Support for Cylindrical Box should be sqrt(2)/2 */
resize_filter->support=(MagickRealType) MagickSQ1_2;
break;
- default:
- break;
- }
- else
- switch (filter_type)
- {
- case Lanczos2DFilter:
- case Lanczos2DSharpFilter:
- /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
- resize_filter->filter=SincFast;
+ case LanczosFilter:
+ case LanczosSharpFilter:
+ case Lanczos2Filter:
+ case Lanczos2SharpFilter:
+ resize_filter->filter=filters[JincFilter].function;
+ resize_filter->window=filters[JincFilter].function;
+ resize_filter->scale=filters[JincFilter].scale;
+ /* number of lobes (support window size) remain unchanged */
break;
default:
break;
}
+ /* Global Sharpening (regardless of orthoginal/cylindrical) */
+ switch (filter_type)
+ {
+ case LanczosSharpFilter:
+ resize_filter->blur *= 0.9812505644269356;
+ break;
+ case Lanczos2SharpFilter:
+ resize_filter->blur *= 0.9549963639785485;
+ break;
+ default:
+ break;
+ }
/*
- ** More Expert Option Modifications
+ ** Other Expert Option Modifications
*/
/* User Sigma Override - no support change */
artifact=GetImageArtifact(image,"filter:sigma");
if (artifact != (const char *) NULL)
sigma=StringToDouble(artifact);
- /* Define coefficents for Gaussian (assumes no cubic window) */
+ /* Define coefficents for Gaussian */
if ( GaussianFilter ) {
- resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
- resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
+ resize_filter->coefficient[0]=1.0/(2.0*sigma*sigma);
+ resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*sigma*
+ sigma)); /* Normalization Multiplier - unneeded for filters */
}
/* Blur Override */
artifact=GetImageArtifact(image,"filter:blur");
if (artifact != (const char *) NULL)
- resize_filter->blur=StringToDouble(artifact);
+ resize_filter->blur *= StringToDouble(artifact);
if (resize_filter->blur < MagickEpsilon)
resize_filter->blur=(MagickRealType) MagickEpsilon;
lobes=1;
resize_filter->support=(MagickRealType) lobes;
}
- /* convert Jinc lobes to a real support value */
+ /* Convert a Jinc function lobes value to a real support value */
if (resize_filter->filter == Jinc)
{
if (resize_filter->support > 16)
if (artifact != (const char *) NULL)
resize_filter->window_support=fabs(StringToDouble(artifact));
/*
- Adjust window function scaling to the windowing support for
+ Adjust window function scaling to match windowing support for
weighting function. This avoids a division on every filter call.
*/
resize_filter->scale /= resize_filter->window_support;
if (artifact != (const char *) NULL)
{
B=StringToDouble(artifact);
- C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
+ C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
artifact=GetImageArtifact(image,"filter:c"); /* user C override */
if (artifact != (const char *) NULL)
C=StringToDouble(artifact);
if (artifact != (const char *) NULL)
{
C=StringToDouble(artifact);
- B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
+ B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
}
}
- /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
- resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
- resize_filter->coeff[1]=0.0;
- resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
- resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
- resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
- resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
- resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
- resize_filter->coeff[7]=(-B-6.0*C)/6.0;
- }
+ /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
+ {
+ const double twoB = B+B;
+ resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
+ resize_filter->coefficient[1]=-3.0+twoB+C;
+ resize_filter->coefficient[2]=2.0-1.5*B-C;
+ resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
+ resize_filter->coefficient[4]=-8.0*C-twoB;
+ resize_filter->coefficient[5]=B+5.0*C;
+ resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
+ }
+ }
/*
Expert Option Request for verbose details of the resulting filter.
*/
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp single
+ #pragma omp master
{
#endif
artifact=GetImageArtifact(image,"filter:verbose");
- if (artifact != (const char *) NULL)
+ if (IsMagickTrue(artifact))
{
double
support,
/*
Set the weighting function properly when the weighting
function may not exactly match the filter of the same name.
- EG: a Point filter really uses a Box weighting function
+ EG: a Point filter is really uses a Box weighting function
with a different support than is typically used.
-
*/
if (resize_filter->filter == Box) filter_type=BoxFilter;
if (resize_filter->filter == Sinc) filter_type=SincFilter;
if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
if (resize_filter->filter == Jinc) filter_type=JincFilter;
if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
+ if (resize_filter->window == Box) window_type=BoxFilter;
+ if (resize_filter->window == Sinc) window_type=SincFilter;
+ if (resize_filter->window == SincFast) window_type=SincFastFilter;
+ if (resize_filter->window == Jinc) window_type=JincFilter;
+ if (resize_filter->window == CubicBC) window_type=CubicFilter;
/*
Report Filter Details.
*/
support=GetResizeFilterSupport(resize_filter); /* practical_support */
(void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
- (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
- MagickFilterOptions,filter_type));
- (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
- MagickFilterOptions, window_type));
- (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
- (double) resize_filter->support);
- (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
- (double) resize_filter->window_support);
- (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
- (double) resize_filter->blur);
+ (void) fprintf(stdout,"# filter = %s\n",
+ CommandOptionToMnemonic(MagickFilterOptions,filter_type));
+ (void) fprintf(stdout,"# window = %s\n",
+ CommandOptionToMnemonic(MagickFilterOptions, window_type));
+ (void) fprintf(stdout,"# support = %.*g\n",
+ GetMagickPrecision(),(double) resize_filter->support);
+ (void) fprintf(stdout,"# win-support = %.*g\n",
+ GetMagickPrecision(),(double) resize_filter->window_support);
+ (void) fprintf(stdout,"# scale_blur = %.*g\n",
+ GetMagickPrecision(), (double)resize_filter->blur);
if ( filter_type == GaussianFilter )
- (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
- (double) sigma);
- (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
- (double) support);
+ (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
+ GetMagickPrecision(), (double)sigma);
+ (void) fprintf(stdout,"# practical_support = %.*g\n",
+ GetMagickPrecision(), (double)support);
if ( filter_type == CubicFilter || window_type == CubicFilter )
- (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
- (double) B,GetMagickPrecision(),(double) C);
+ (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
+ GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
(void) fprintf(stdout,"\n");
/*
Output values of resulting filter graph -- for graphing
0.0);
}
/* Output the above once only for each image - remove setting */
- (void) DeleteImageArtifact((Image *) image,"filter:verbose");
+ (void) DeleteImageArtifact((Image *) image,"filter:verbose");
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- }
+ }
#endif
-
return(resize_filter);
}
\f
#define AdaptiveResizeImageTag "Resize/Image"
CacheView
+ *image_view,
*resize_view;
Image
*resize_image;
MagickBooleanType
- proceed;
-
- MagickPixelPacket
- pixel;
-
- PointInfo
- offset;
+ status;
- ResampleFilter
- *resample_filter;
+ MagickOffsetType
+ progress;
ssize_t
y;
resize_image=DestroyImage(resize_image);
return((Image *) NULL);
}
- GetMagickPixelPacket(image,&pixel);
- resample_filter=AcquireResampleFilter(image,exception);
- (void) SetResampleFilter(resample_filter,PointFilter,1.0);
- (void) SetResampleFilterInterpolateMethod(resample_filter,
- MeshInterpolatePixel);
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
resize_view=AcquireCacheView(resize_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
+#endif
for (y=0; y < (ssize_t) resize_image->rows; y++)
{
+ MagickPixelPacket
+ pixel;
+
+ PointInfo
+ offset;
+
register IndexPacket
*restrict resize_indexes;
- register ssize_t
- x;
-
register PixelPacket
*restrict q;
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
exception);
if (q == (PixelPacket *) NULL)
- break;
+ continue;
resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
- offset.y=((MagickRealType) y*image->rows/resize_image->rows);
+ offset.y=((MagickRealType) (y+0.5)*image->rows/resize_image->rows);
+ GetMagickPixelPacket(image,&pixel);
for (x=0; x < (ssize_t) resize_image->columns; x++)
{
- offset.x=((MagickRealType) x*image->columns/resize_image->columns);
- (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
- &pixel);
+ offset.x=((MagickRealType) (x+0.5)*image->columns/resize_image->columns);
+ (void) InterpolateMagickPixelPacket(image,image_view,
+ MeshInterpolatePixel,offset.x-0.5,offset.y-0.5,&pixel,exception);
SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
q++;
}
if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
- break;
- proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
- image->rows);
- if (proceed == MagickFalse)
- break;
+ continue;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_AdaptiveResizeImage)
+#endif
+ proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
}
- resample_filter=DestroyResampleFilter(resample_filter);
resize_view=DestroyCacheView(resize_view);
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ resize_image=DestroyImage(resize_image);
return(resize_image);
}
\f
{
MagickRealType
scale,
+ weight,
x_blur;
/*
scale=resize_filter->scale;
scale=resize_filter->window(x_blur*scale,resize_filter);
}
- return(scale*resize_filter->filter(x_blur,resize_filter));
+ weight=scale*resize_filter->filter(x_blur,resize_filter);
+ return(weight);
}
\f
/*
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
- minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
- 1.0,exception);
+ minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0,
+ exception);
return(minify_image);
}
\f
pixel.index+=alpha*indexes[j];
}
resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
- GetIndexPixelComponent(&pixel));
+ GetIndexPixelComponent(&pixel));
}
}
if ((resize_image->storage_class == PseudoClass) &&
span=(MagickSizeType) (filter_image->columns+rows);
status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
&offset,exception);
- resize_filter=DestroyResizeFilter(resize_filter);
- resize_filter=AcquireResizeFilter(filter_image,filter_type,blur,
- MagickFalse,exception);
status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
span,&offset,exception);
}
span=(MagickSizeType) (filter_image->rows+columns);
status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
&offset,exception);
- resize_filter=DestroyResizeFilter(resize_filter);
- resize_filter=AcquireResizeFilter(filter_image,filter_type,blur,
- MagickFalse,exception);
status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
span,&offset,exception);
}
exception);
if (q == (PixelPacket *) NULL)
break;
- scale_indexes=GetAuthenticIndexQueue(scale_image);
+ scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
if (scale_image->rows == image->rows)
{
/*