On 17.11.18 13:46, Even Rouault wrote:
Thanks a lot for the in-depth analysis.
You liked that? I'll give you more, and I hope that I can get my points
across. It will be another long post.
First, I'm not really a DSP *expert*, but I'm doing my best to share my
limited understanding. But it so happens that I know a lot about b-splines:
https://bitbucket.org/kfj/vspline
The prefilter is a high-pass filter which
'sharpens' the original signal by just so much that applying the
reconstruction filter will produce the original signal. The problem with
prefiltering is that it's done with an IIR filter and has theoretically
infinite support (usually it's enough to have a handful of support
either end, forgive my sloppy formulation), which makes margin treatment
and handling of no-data value a dicey issue. This is probably one reason
why prefiltering tends to be omitted.
So there would be a need for high-pass prefilter after a low-pass one, before
applying the resampling function ?
How would that high-pass prefilter would be implemented ? Do you have
references for that ?
Does that high-pass prefilter only applies to cubic bspline, or to other
methods as welll ? currently the code logic is common for Bicubic (Catmull-
Rom), Bicubic-BSpline and Lanczos. They only differ by the kernel radius and
the weighting function applied.
I see that this is confusing, so I'll try and clarify.
1. low-pass filtering
Before downsampling, high frequencies have to be removed from the signal
if they are present. This is a simple fact of digital signal processing.
You have to look at the signal, see if high frequencies are present, and
remove them from the signal before proceeding. This is done with a
low-pass filter and can only be omitted if the signal has no relevant
high-frequency content. And, as I said, if you have a good low-pass
filter, you may apply it anyway: if there are no high frequencies in the
signal, the low-pass filter should have no effect.
This is the theory. In reality, though, low-pass filters are never truly
perfect, so applying them on principle will always degrade the
low-frequency content as well. An ideal low-pass filter would completely
remove all frequencies above a given limit (this part of the spectrum is
called the stop band) and pass all other frequencies unchanged (this is
called the pass band) - with no in-between (the in-between is called the
transition band). Real filters differ from this ideal, so usually the
transition band is not zero-length, stop-band attenuation is not
perfect, and there is some attenuation in the pass band as well.
So how can we approach an ideal low-pass? One approach of digital
filtering is convolution. It's well-understood, easy to implement and
has some advantages, especially for specific classes of kernels. But
even with quite large kernels, to get near-ideal filter characteristics
is hard. Much more effective than convolution is the use of 'IIR'
filters. They tend to give very strong stop-band attenuation and short
transition bands. But they are hard to design for a given set of
requirements - there are many approaches, but it's all DSP black magic,
and most of it is beyond my horizon, so here we'd have to call for help
from the real experts.
Let's assume we have settled on a specific filter and applied it to the
signal - or we have found the signal has no significant high-frequency
content so we have omitted the filter. Let's call what we have now the
'clean' signal. We are now ready for the next step.
It's important to acknowledge that the initial low-pass filter is
needed. gdal does not use a low-pass filter before downsampling and
requires users to know what they are doing: they have to apply the
filter themselves. But most users will be unaware of this fact and rely
on gdal to 'do things right'. But it doesn't.
2. downsampling the 'clean' signal
There are simple cases of downsampling, where we needn't think much
about how to proceed. These cases occur when the downsampling can be
done by simply picking every other, every third etc. sample from the
clean signal. Doing so is certain not to 'miss' part of the signal,
since the only thing we could miss here are the high frequencies, which
are by definition absent from the clean signal. So this subsampling is
safe. This special type of subsampling is called decimation, and if one
can apply it, it's great because it's simple and mathematically correct.
Most of the time, though, things are not so easy: The subsampling is by
a non-integer factor, or the points at which we want to pick up the
subsamples aren't precisely at the locations where the samples of the
input signal are located (in gdal term, ULLR for input and output may
differ). So what needs to be done? We need to *interpolate*.
Interpolation is a guess about how the signal looks where we don't have
data. We only have data at the sampling locations, in between, we're
reduced to guesswork, but we can make informed guesses guided by
mathematical criteria, accepting that each way of guessing may have
advantages and disadvantages. gdal offers quite a few interpolation
methods, starting with the very fast but signal-degrading
nearest-neighbour 'interpolation' and proceeding via linear
interpolation to more sophisticated techniques like b-splines.
What do we expect of an interpolator? It should reproduce the input when
it is evaluated at the sampling locations. This is called the
'interpolation criterion'. 'in-between', it's informed guesswork, so it
depends on the specific interpolator. Obviously, nearest-neighbour
'interpolation' fulfills the interpolation criterion, as does linear
interpolation. b-splines also do, but you can't simply apply the
b-spline evaluation to the clean signal itself. b-spline mathematics
require that the input signal be 'prefiltered' to obtain a set of
'coefficients' which are then fed to the evaluation process. This
prefilter is a high-pass filter which has to be applied to the input
signal before the b-spline can be evaluated.
Let' stick with the cubic b-spline for now, and do a sample evaluation
at an integral position (so, at a sample loaction). Let the clean signal
be ... 0 , 1 , 0 ... and we'll evaluate right where the '1' is.
The b-spline reconstruction kernel (or, the set of weights at integral
positions) is 1/6 2/3 1/6. If we convolve, we get
0 * 1/6 + 1 * 2/3 + 0*1/6 = 2/3
You can see that the interpolation criterion is *not* fulfilled, because
we'd expect to see '1' as the result. Why is this? because we haven't
prefiltered. I'll not go into prefiltering now, but a *prefiltered*
signal at this position is
-0.464285714 1.732142857 -0.464285714
and, voila, we get
-0.464285714 * 1/6 + 1.732142857 * 2/3 + -0.464285714 * 1/6 = 1
When the b-spline is used for interpolation, so when it's evaluated at
locations which are not the sample locations, the maths are less simple;
the set of weights to apply to the coefficients has to be calculated,
which is what the gdal code you have pointed me to does (in
GWKBSpline4Values; it produces four weights, three weights as in my
example above are a special case for evaluating at integral positions).
But applying this set of weights to the unmodified clean signal will, as
in my little example, *not* reproduce the clean signal. Only forming the
weighted sum from the b-spline coefficients will do so. Using b-spline
evaluation on the input signal without prefiltering is, strictly
speaking, wrong.
So what happens if you do it nevertheless? Obviously you lose some of
the signal. And what you lose is some high-frequency content. The
prefiltering boosts high-frequency content to compensate for the effect,
and the boosting is fine-tuned to *precisely* counteract it, so that the
interpolation criterion is fulfilled. If we don't apply the prefilter (a
high-pass filter) before using the b-spline evaluation formula (a
low-pass filter), we effectively low-pass-filter the signal.
Back to our processing chain. We have the 'clean' signal which has been
stripped of high frequency content in step 1 above. Now we want to use
b-spline interpolation on it to get our target signal. So before we can
use b-spline evaluation, we need to prefilter the clean signal. This is
something we do to the clean signal, and it has to be done, because
otherwise we can't correctly interpolate. And this is what is missing in
gdal, where the prefiltering is not done. What gdal does do instead is
applying a slight low-pass filter instead of precisely interpolating.
This low-pass has nothing to to with the low-pass in step 1, it simply
happens to be yet another effect which is also a low-pass. The effect is
mild and oftentimes it won't be easy to spot - And oftentimes it's even
desired. It can even mask the problems arising from omitting step 1 above.
3. correct procedure
So, to finish this off, if you want to do everything right, how do you
do it?
- first apply a low-pass filter to the signal to get the 'clean' signal
- then interpolate over the clean signal
And, if you want to use b-spline interpolation over the clean signal,
you have to 'prefilter' it before using the b-spline evaluation formula.
Other interpolators do not require this extra step, but usually their
results are not as good.
2.
I'd encourage GDAL to consider
adopting this method to avoid the worst of downsampling errors that come
with '-r average'.
I somehow remember people mentionning this in the past too. (ah and "GDAL"
would be an abstract concept, if not embodied by its contributors, like you
potentially ;-) )
I'd like to help, but rather by explaining things. I'd not really like
to get involved in coding, as I have a lot of work on my hands already.
I do owe the gdal project a debt, though. I am creating extremely
detailed GPS maps of the Piedmont from government data using free
software only, and if it weren't for QGIS, gdal and GRASS, my work would
be very difficult indeed.
3.
It's also possible to apply a low-pass filter no matter
if there is high-frequency content: if there is no high-frequency
content, a good low-pass filter has no effect.
Would probably be faster from a computation point of view to just apply the
filter than prior checking if it is needed. One point to have in mind is that
for memory reasons, and also because users also want to process big images by
chunks, GDAL operates by chunks and not on the whole image, so "infinite
support" is at best limited to the source window being passed to the function.
Jukka's hint to use kernel-filtered sources in a VRT file does do the
trick and shows the way to go, but requires an intermediate file of the
same size as the initial input. The maths is already there. Doing the
step-1 low-pass this way is a reasonable choice, even if IIR filters may
perform better. If the process is done in chunks, you need to cut
overlapping chunks. The overlap has to be large enough to give support
for the calculation of the pixels at the edge of the actual tiles. When
using convolution, the size of the overlap has to be half the kernel
size, which is not too much. With IIR filters, you can choose how much
error you find acceptable and select the overlap size accordingly.
What has to be understood here is that the filter has to match the
desired scale change. The kernel has to be produced by a process taking
the scale change into account, which can be done for a gaussian by
choosing an appropriate standard deviation.
For a down-to-earth approach, any recommended formulas to apply ? Does it
depend only on the scaling factor ?
Theoretically, it does depend only on the scaling factor. But as I have
outlined above, digital filters are not perfect. They have transition
bands, pass-band and stop-band ripple - so, in reality, it depends. I
know this is a frustrating answer, but this is a fact of life. In the
real world, you have to compromise and find a solution which is 'good
enough' for what you have in mind.
When using gaussians for low-pass filtering, there is a rule of thumb,
but just now I can't seem to find it, sorry. I'll try and come up with
the rule of thumb, which gives a standard deviation which will remove a
'significant portion' of frequencies above a given limit. Given that
standard deviation, you can truncate the gaussian and then normalize it.
But there are many different low-pass filters, each with it's own
special characteristics, and gaussians aren't necessarily the ones which
are best suited for all tasks.
Don't you have a DSP specialist in your community? Would be nice to have
someone else joining the discussion. My DSP knowledge is mainly through
image processing, so I may miss fine points with geodata.
Still with me? Thanks for your patience! I hope I have understood all
the source code and response posts correctly, don't hesitate to correct me.
While we are at assessing correctness of the implementation, one point that
has sometimes been raised is the potential impact of the "pixel-is-area"
versus "pixel-is-point" convention. Typically an imaging sensor will reflect
the number of photons collected over an area in a range of wave-length, so the
value of the pixel applies to the whole area it covers. Other datasets might
be the result of a variable evaluated at the center of the pixel. GDAL
currently computes the distances to the pixel centre when computing the
weights of the resampling function, so it would seem to me that it is the
natural way for "pixel-is-point" datasets, and I'm wondering how good it is
for "pixel-is-area" ones.
We could fill a whole new thread discussing this ;)
First the sensor. When it comes to images, you are right: the sensor has
an area. But it's a smallish area, definitely smaller than the area a
pixel 'occupies' later on (because the sensor chip has other stuff on
it's surface), and the incoming signal is already low-pass filtered by
the lambda filter, and then the light goes through a color filter to
produce a bayer pattern which only produces 'pixel' values after a very
complex process of demosaicking. All of this is so complex that it's
more of an art than a science. But if you accept the set of pixels as a
starting point, the pixel-as-area metaphor is usable just as the
pixel-as-point metaphor. If you use pixel-as-area and you subsample,
picking precisely the area which a target pixel would occupy in the
source image is a reasonable choice and much better than simply
averaging over all pixels-as-points which are nearer than a given
maximum distance. Now what you do with the data in this area is a
different matter. Averaging over the area is one possibility, but you
might do a weighted sum to get better results. Again, it's a matter of
what you want to achieve and what errors you can accept. There is no
silver bullet. It's a bit like the duality of light. Is it a wave or a
particle?
And when it comes to DEM data derived from LIDAR scans, which is what I
am currently working on, the pixel-as-area approach is very valid: the
LIDAR scan produces a point cloud, which is submitted to binning, so
that all measurements happening to occur in a given small rectangular
area are put together and averaged. Interpreting such a bin as something
which has an area over which the signal is uniform is, again, an
understandable choice, but it's not necessary. You might, for example,
use weights on the point measurements when binning them. Or use a median
filter or a specific quartile or or or to get data for a specific
purpose. It's important that you understand what you're doing and pick
your tools accordingly. And this is much easier said than done.
On top of that, using other people's data, you often don't precisely
know how they have already 'messed' with the data. pixel-as-area is one
way which is often quite close to a good choice. But it's important to
understand that 'area' interpolation is, as every other method, a
digital filter and produces certain errors. If that is okay with what
you want to achieve, it's okay to use it.
So what's the upshot? If you are writing software for a large audience
of people who mostly don't understand the fine points, you want to
shield them from the worst mistakes by making it hard for them to make
these mistakes. An automatic low-pass filter before downsampling is just
such a shield. And using area interpolation instead of the average over
contributing pixels would be another one. So having code in place to
perform these tasks is good, making them the default is sensible, and
allowing users to overrule the defaults is courteous to advanced users,
whom you don't want to put into a straightjacket of what you think is
'right'. And when it comes to b-splines, when they are used without
prefiltering the data, this should be said in the documentation, because
then it's no longer strictly speaking an interpolation but produces mild
blur.
Kay
_______________________________________________
gdal-dev mailing list
[email protected]
https://lists.osgeo.org/mailman/listinfo/gdal-dev