Skip to content

Tps adaptive mesh refinement#132

Merged
fcollman merged 14 commits intoAllenInstitute:developfrom
djkapner:TPS_adaptive_mesh_refinement
Jan 25, 2019
Merged

Tps adaptive mesh refinement#132
fcollman merged 14 commits intoAllenInstitute:developfrom
djkapner:TPS_adaptive_mesh_refinement

Conversation

@djkapner
Copy link
Copy Markdown
Contributor

Do you have a thin plate spline mesh that you suspect is overkill and is really chapping your hide? Maybe, something like this?
image

This PR adds an adaptive mesh estimator to create a new smaller transform that matches the original one on the control points. This can reduce the computation time associated with the tform() method, for example.

image

code below provided for example to reproduce this plot

from renderapi.transform import thin_plate_spline as tps
from scipy.spatial import Delaunay
import matplotlib.pyplot as plt
import json
reload(tps)

with open('/home/danielk/djk_render-python/test/test_files/thin_plate_spline.json', 'r') as f:
    tpsj = json.load(f)

tf = tps.ThinPlateSplineTransform(json=tpsj)
ntf = tf.adaptive_mesh_estimate(
        tol=1.0,
        max_iter=100,
        starting_grid=7,
        nworst=10)

d=[]
d.append(Delaunay(tf.srcPts.transpose()))
d.append(Delaunay(ntf.srcPts.transpose()))

plt.figure(1)
plt.clf()
for i in range(2):
    plt.subplot(1, 2, i + 1)
    plt.triplot(d[i].points[:, 0], d[i].points[:, 1], d[i].simplices.copy(), color='k', alpha=0.25)
    plt.scatter(d[i].points[:, 0], d[i].points[:, 1], marker='.', color='r')
    plt.gca().set_aspect('equal')
    plt.title('%d points' % d[i].points.shape[0])

@djkapner
Copy link
Copy Markdown
Contributor Author

note: this PR started from #129 and should be rebased/merged after that one.

@codecov
Copy link
Copy Markdown

codecov bot commented Dec 14, 2018

Codecov Report

Merging #132 into develop will decrease coverage by 0.06%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           develop    #132      +/-   ##
==========================================
- Coverage    95.66%   95.6%   -0.07%     
==========================================
  Files           29      29              
  Lines         2005    2023      +18     
==========================================
+ Hits          1918    1934      +16     
- Misses          87      89       +2
Impacted Files Coverage Δ
renderapi/transform/leaf/thin_plate_spline.py 100% <100%> (ø) ⬆️
renderapi/utils.py 94% <0%> (-2%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6029528...daf8185. Read the comment docs.

@codecov
Copy link
Copy Markdown

codecov bot commented Dec 14, 2018

Codecov Report

Merging #132 into develop will increase coverage by 0.06%.
The diff coverage is 100%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #132      +/-   ##
===========================================
+ Coverage    95.65%   95.71%   +0.06%     
===========================================
  Files           29       29              
  Lines         2000     2029      +29     
===========================================
+ Hits          1913     1942      +29     
  Misses          87       87
Impacted Files Coverage Δ
renderapi/transform/leaf/thin_plate_spline.py 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 05719f1...88b5f5e. Read the comment docs.

@djkapner djkapner force-pushed the TPS_adaptive_mesh_refinement branch from a73c24c to e6ba38c Compare December 17, 2018 23:02
@djkapner
Copy link
Copy Markdown
Contributor Author

rebased to master

sortind = np.argsort(delta[ind])

if niter == max_iter:
logger.warning(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we sure you want to not raise an exception here, seems the client of this should be in control of whether to accept the result and move on. It could raise an Exception and return the newtf in the exception.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this should be tested.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the exception... not sure how to raise and return the newtf at the same time

return newtf

src = np.vstack((src, self.src[ind[sortind[0: nworst]]]))
return self.adaptive_mesh_estimate(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we wrap the computation here as a class/static method? It looks like there is a lot of setup (relating to origin points, etc) that can be run once and passed to another recursive method, which can make this more readable for the future.

logger.addHandler(logging.StreamHandler(sys.stdout))


class AdaptiveMeshEstimationError(Exception):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry to be nitpicky, but this should subclass EstimationError, or we can make EstimationError have an keyword argument of transform=None, so that it works whether you want to pass on the transform calculated or not, but code can be written to generically catch all EstimationErrors

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's fine, I can't quite imagine a use case where you would/should use a failed result... but you seem to. So, I don't mind making it what you're after.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh! well.. actually I was intuiting because you had it as a warning before that you had a use case in mind to use the transform, so i was trying to accommodate that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so what do we want to do... 1) leave this as is 2) make it subclass EstimateError 3) simply remove all this pass up the transform in the exception business.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a scipy iterative solver situation, I've come to expect the behavior that a solution is always returned, but a max iter message pops up. That's what I was originally going for with the logger message:

from scipy.optimize import minimize, rosen, rosen_der
x0 = [1.3, 0.7, 0.8, 1.9, 1.2]
res = minimize(rosen, x0, method='Nelder-Mead', tol=1e-6, options={'maxiter': 1000, 'disp': True})
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 295
         Function evaluations: 494

print(res.__class__)
<class 'scipy.optimize.optimize.OptimizeResult'>

del res

res = minimize(rosen, x0, method='Nelder-Mead', tol=1e-6, options={'maxiter': 10, 'disp': True})
Warning: Maximum number of iterations has been exceeded.

print(res.__class__)
<class 'scipy.optimize.optimize.OptimizeResult'>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i like bombing out better personally.



class AdaptiveMeshEstimationError(EstimationError):
def __init__(self, value, transform):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a tricky thing to do in python. I'm pretty sure that trying to raise something like this in e.g. a multiprocessing pool will cause hanging since the reduction function pickle uses will not treat it nicely. There is a way around this, but correct behavior should be tested if we go this route (which I think is okay).

@djkapner
Copy link
Copy Markdown
Contributor Author

nudge.
I'd like to use this in a TEMCA/GPU lens correction thing pretty soon

@fcollman fcollman merged commit 43d2387 into AllenInstitute:develop Jan 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants