Boost logo

Boost :

From: Lubomir Bourdev (lbourdev_at_[hidden])
Date: 2006-11-13 18:40:28


Hi Hans,

> On Tuesday, 07. November 2006 01:58, Lubomir Bourdev wrote:
> > My rule of thumb: when I provide functionality that is
> similar but not
> > identical to existing functionality, I go out of my way to indicate
> > that they are different. This is why GIL calls 2D 'iterators'
> > locators, although they are almost identical to traditional
> iterators.
> For the same reason, they're called traversers in VIGRA nowadays.
> (Actually, I prefer traverser vs. locator - doesn't the
> latter imply some kind of searching?)

If you associate Locator with "locate", it does imply search. But if you
associate it with "location" it does not.

When searching for appropriate names, we thought about Traverser too,
and we like it. It emphasizes more the navigation aspect, whereas
Locator emphasizes the access aspect. Both Locator and Traverser have
precedence. We ended up choosing Locator because it is a bit shorter.
Another term we considered is Position. Someone also brought up the term
Cursor.

I don't have a strong opinion about sticking with Locator; other than
the work to change the name everywhere. If people overwhelmingly prefer
one term over another we can change it.

>
> > Similarly,
> > I prefer names like resize_and_clobber(), resize_and_invalidate(),
> > create_with_new_dimensions(), recreate(), or reset() than resize().
> > What do other people think?
> What about reshape()?

Reshape is used in Matlab to change the dimensions of an array, but it
does not invalidate the elements of the array. So I wouldn't recommend
using it for the same reason I don't like resize() - because there are
precedents with the same name and different semantics. How about
image::recreate(width,height)?

> > > > Right now GIL hard-codes the commonly used types to their
> > >
> > > commonly used
> > >
> > > > ranges - float is 0..1.
> > >
> > > Really? I didn't realize this and don't like it at all as
> a default
> > > setting. IMHO, the pleasant property of floats is that
> one (mostly)
> > > doesn't need to take care of ranges and loss of precision.
> >
> > Think of these as part of the channel traits specifying the legal
> > range of the channel, which is often smaller than its
> physical range.
> > You are, of course, free to ignore them (and it may make
> sense to do
> > so in intermediate computations), but they are implicitly
> always defined.
> > Having them explicitly defined makes certain operations
> much easier,
> > such as converting from one channel type to another. Not
> having them
> > defined would force you to propagate them to every function
> that might
> > need them.
> In VIGRA, a
> copyImage(srcImageRange(my8bitImage),
> destImage(some16bitOrFloatImage)) will simply copy the values
> without any linear transformation.
> This looks to me like the principle of least surprise.

So what happens if you want to copy the other way around, from float to
8-bit image? It isn't clear if you want to scale, truncate, round, etc.
You could disallow copying in the other direction, but that would make
copy asymmetric and thus less intuitive.

This is why I think the GIL rule is simple - if the channels are
incompatible, copy is not defined. You can transform, convert, etc. but
you may not use copy_pixels. Two channel types should only be defined as
compatible if there is one-to-one mapping between them.

> If
> GIL transforms byte ranges 0..255 to float 0.f..1.f, will it
> also shift by 8 bits for 8->16 bit conversions?

For non-compatible channels you must use copy_and_convert_pixels, not
copy_pixels, to emphasize that the copy may be lossy. And your channel
types must be convertible. That is, there must be a function:

DstChannel channel_convert<DstChannel>(SrcChannel c);

It will define how to convert one channel type to another (scale, shift
by 8 bits, etc.). The default simply transforms the range of one to the
other and rounds to the nearest element.
If you don't like this transformation, you may choose to create a custom
channel type, you may provide custom conversion function object, or you
may choose to use transform_pixels instead of copy_and_convert_pixels,
depending on your design.

>
> When discussing the stacking of views, your argument was:
> > I am in favor of letting the programmer choose what is best rather
> > than imposing one strategy, which in my opinion is the style of C++
> > and STL as compared to other languages.
>
> Does this argument not hold for the above case?

The two contexts are not the same.
I was arguing that we shouldn't prevent creating color_converted_view
with certain color spaces simply because color conversion with them is
expensive. We should let the programmer decide whether it makes sense to
use color converted view or explicitly color convert.

But the semantics of color_converted_view with LAB is very clear - it is
the same as with any other color space, whereas how to copy between two
incompatible images is not always obvious.

It comes down to some basic principles:

1. We want equality comparison to be an equivalence relation.
2. We also want equality comparison and copying to be defined on the
same set of types.
3. The two operations should work as expected. In particular, after a=b,
you can assert that a==b.

I hope you will agree that these are fundamental rules that hold in
mathematics and violating them can lead to unintuitive and bug-prone
systems.

But the first principle requires that the types be compatible (i.e.
there must be one-to-one correspondence between them). To see why, lets
assume we can define operator== between int and float, and define it to
round to the nearest int when comparing an int to a float. Let:

float a=5.1;
int b=5;
float c=5.2;

In this case a==b and b==c but a!=c. That suggests our operation is not
transitive, which means it is not an equivalence relation.

You may define operator== to not round but "promote" the int to a float.
That will make your equality comparison an equivalence relation. The
problem then will shift to rule 3 because you cannot do the same
promotion when copying. Consider this:

float a=5.1;
int b=a;
assert(b==a); // fails!

So if equality comparison is only defined between compatible types,
then, by rule 2, so should be the copy operation.
That is, "a=b" should be defined for exactly the same types for which
"a==b" is defined. Therefore, copy should only be defined between
compatible images.

> > For example channel_convert will need to be provided the
> minimum and
> > maximum value for both source and destination, and so will
> the color
> > conversion functions, the color converted view, etc.
>
> I don't think so - I would imagine a linear_transformation or
> linear_scaling view, which could be used explicitly in case
> the domain shall be changed.
>

Regardless of what we do, there is always a notion of the operating
range of a channel, whether it is implicit or explicit.
You are essentially suggesting that the range of a channel be defined at
run time, rather than at compile time, right?

But you still need to know the range for many operations.
For example, converting between additive and subtractive color spaces
requires inverting the channel, which requires knowing its range. If
your range is defined at run time, don't you need to provide four
additional parameters every time you need to color convert? In addition
to complicating the interfaces, this may result in less efficient code,
as we are substituting a compile-time constant with a runtime value.

Lubomir


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk