menpo/menpo — pull request #369 by jabooth (2014-05-21 15:19:29+00:00)
Title: warp_to_mask, warp_to_shape, fast resizing of images

This PR changes the warping interface in Menpo.

We've always had `warp_to`, a method that took a boolean mask and used it to warp `Image` s and `MaskedImage` s.

Unfortunately, this wasn't well tested, and suffered from a number of shortcomings:

1. warping an `Image` with anything except an all-true mask actually led to an Exception. This meant that it wasn't possible to fit an `Image` to an AAM - something that doesn't make any sense as a new image to fit has no need for a mask.

2. Even though the above was true we were using this method when rescaling `Images`. It just so happened that because the mask was all true, it worked.

3. The behavior was complex. A kwarg (`warp_mask`) controlled if the mask on a masked image should be warped or not. By default it was false, so the mask on a masked image actually had no effect on the warping.

4. `skimage.transform.resize` was over twice as fast as our resize on a plain `Image`, because they have a custom Cython interpolator for affine warps that is simply faster than `map_coordinates`. `resize` is used **a lot** - every fitting method starts by getting the image to be fitted to be of the same units as the model, which requires a resize. Before this PR, resizing at the start of the SDM fitting loop was dominating, taking about 40% of the time for a fit.

### Proposal

This PR fixes this by splitting warp_to into two methods - `warp_to_mask` and `warp_to_shape`. `warp_to_mask` behaves just like `warp_to` did, with a few changes:

1. The type of return for both `Image.warp_to_mask` and `MaskedImage.warp_to_mask` is always
`MaskedImage`. This means AAM can call warp_to_mask on an input image and know for sure the result is masked, fixing a longstanding bug.

2. `warp_to_mask` on a `MaskedImage` now **always warps the mask**. This is equivalent to forcing the `warp_mask=True` kwarg on `warp_to`. This is much more natural. If you don't care about the mask, you should efficiently convert the `MaskedImage` to an `Image` and call `warp_to_mask` on that (more about that in a sec). Note that this is **the biggest breaking change in the PR**. We need to analyse where we are currently calling `warp_to` and decide how it should be handled now.

`warp_to_shape` is a new warping method. It always returns the same type as what it is called on. This makes it much more friendly to be called from methods like `resize` on the image package.

Instead of taking a template mask this variant takes a template shape. This matches exactly the interface to `skimage.transform.warp` and in fact actually calls that method (basically, it's a thin wrapper around their method which handles converting our transform types into callables they expect). This can be much more efficient than the masking case (and indeed it is, it gives us an over 2x performance increase in resize).

## To do
The work isn't quite done, but I wanted to put this up to get feedback. Still outstanding we have
- [ ] Fix PWA issues that we are having in LK
- [ ] Allow boolean ndarray as well as `BooleanImage` for mask in `warp_to_mask`
- [ ] Adjust kwargs on `warp_to_image` to match `warp_to_mask`
- [ ] Decide on how `interpolator` kwarg should be handled