{"action":"created","comment":{"url":"https://api.github.com/repos/PyAbel/PyAbel/pulls/comments/100859184","pull_request_review_id":21568539,"id":100859184,"diff_hunk":"@@ -0,0 +1,236 @@\n+# -*- coding: utf-8 -*-\n+from __future__ import division\n+import numpy as np\n+from scipy import ndimage\n+from scipy.interpolate import UnivariateSpline\n+from scipy.optimize import leastsq\n+\n+import abel\n+\n+# see https://github.com/PyAbel/PyAbel/issues/186 for discussion\n+\n+\n+def circularize_image(IM, method=\"lsq\", center=None, radial_range=None,\n+                      zoom=1, smooth=0, nslices=32, ref_angle=None, \n+                      inverse=False, return_correction=False):\n+    \"\"\"\n+    Remove radial distortion from a velocity-map-image through radial scaling\n+    of the Newton-rings to enforce circularity.\n+\n+    The image is divided into angular slices, adjacent radial intensity\n+    profile slices compared to give a radial scaling factor that best\n+    aligns common structure.\n+\n+    The resultant radial scaling factor vs angle is interpolated to apply the\n+    correction to the whole image grid. The image is remapped to the\n+    correct cartesian grid.\n+\n+    Parameters\n+    ----------\n+    IM : numpy 2D array\n+\n+    method: str\n+        method to align slice profiles,\n+        \"argmax\" - compare intensity-profile.argmax() of each radial slice.\n+        \"lsq\" - determine radial scaling factor by minimizing the\n+                intensity-prfoile with an adjacent slice intensity-profile.\n+\n+    center: str, or None\n+        pre-center image using PyAbel centering methods,\n+        \"com\", \"convolution\", \"gaussian\", \"image_center\", \"slice\"\n+\n+    radial_range: tuple, or None\n+        limit slice comparison to the radial range (rmin, rmax).\n+\n+    zoom: float\n+        nmimage.zoom image before analysis. This may help improve the low\n+        radius slice profile.\n+\n+    smooth: float\n+        smoothing for spline interpolation of the determined radial scaling\n+        factor vs angle\n+\n+    nslices: int\n+        divide the image into nslices. Ideally, this should be a divisor of","path":"abel/tools/circularize.py","position":54,"original_position":54,"commit_id":"2128d4f76aee83736c6fa310e05ec031c3f98e61","original_commit_id":"2128d4f76aee83736c6fa310e05ec031c3f98e61","user":{"login":"DanHickstein","id":1107796,"avatar_url":"https://avatars.githubusercontent.com/u/1107796?v=3","gravatar_id":"","url":"https://api.github.com/users/DanHickstein","html_url":"https://github.com/DanHickstein","followers_url":"https://api.github.com/users/DanHickstein/followers","following_url":"https://api.github.com/users/DanHickstein/following{/other_user}","gists_url":"https://api.github.com/users/DanHickstein/gists{/gist_id}","starred_url":"https://api.github.com/users/DanHickstein/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DanHickstein/subscriptions","organizations_url":"https://api.github.com/users/DanHickstein/orgs","repos_url":"https://api.github.com/users/DanHickstein/repos","events_url":"https://api.github.com/users/DanHickstein/events{/privacy}","received_events_url":"https://api.github.com/users/DanHickstein/received_events","type":"User","site_admin":false},"body":"What happens if it's not an even divisor? Should we throw a warning?","created_at":"2017-02-13T18:14:47Z","updated_at":"2017-02-13T18:57:47Z","html_url":"https://github.com/PyAbel/PyAbel/pull/195#discussion_r100859184","pull_request_url":"https://api.github.com/repos/PyAbel/PyAbel/pulls/195","_links":{"self":{"href":"https://api.github.com/repos/PyAbel/PyAbel/pulls/comments/100859184"},"html":{"href":"https://github.com/PyAbel/PyAbel/pull/195#discussion_r100859184"},"pull_request":{"href":"https://api.github.com/repos/PyAbel/PyAbel/pulls/195"}}},"pull_request":{"url":"https://api.github.com/repos/PyAbel/PyAbel/pulls/195","id":105565397,"html_url":"https://github.com/PyAbel/PyAbel/pull/195","diff_url":"https://github.com/PyAbel/PyAbel/pull/195.diff","patch_url":"https://github.com/PyAbel/PyAbel/pull/195.patch","issue_url":"https://api.github.com/repos/PyAbel/PyAbel/issues/195","number":195,"state":"open","locked":false,"title":"image circularization, distortion correction","user":{"login":"stggh","id":10932229,"avatar_url":"https://avatars.githubusercontent.com/u/10932229?v=3","gravatar_id":"","url":"https://api.github.com/users/stggh","html_url":"https://github.com/stggh","followers_url":"https://api.github.com/users/stggh/followers","following_url":"https://api.github.com/users/stggh/following{/other_user}","gists_url":"https://api.github.com/users/stggh/gists{/gist_id}","starred_url":"https://api.github.com/users/stggh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stggh/subscriptions","organizations_url":"https://api.github.com/users/stggh/orgs","repos_url":"https://api.github.com/users/stggh/repos","events_url":"https://api.github.com/users/stggh/events{/privacy}","received_events_url":"https://api.github.com/users/stggh/received_events","type":"User","site_admin":false},"body":"See issue #186 \r\n> For velocity-map imaging experiments the charged particles emanate as Newton spheres that have spherical symmetry, and thus the 2D projection should be perfectly circular. Experimental velocity-map images may have distortion in the circular charged particle energy structure, due to, for example, stray magnetic fields, or optical distortion of the camera lens that images the particle detector. The effect of distortion is to reduce the radial (or velocity or kinetic energy) resolution, since a particular energy peak will \"walk\" in radial position, depending on the particular angular position on the detector. Imposing a physical circular distribution of particles, improves the kinetic energy resolution, at the expense of uncertainly in the absolution kinetic-energy position of the transition.\r\n\r\n`abel.tools.circularize.py` with `circularize_image()` function to circularize an image, removing distortion, to improve the resolution of spectral structure. Evaluates a scaling factor to align adjacent angular slice intensity profiles, either via `argmax` following maximum intensity, or `lsq` that radially scales the profiles to align. This radial scaling factor is interpolated in angle to provide the actual image grid coordinates `(Yactual, Xactual)`, that are then remapped back to grid coordinates `(Y, X)`.\r\n\r\n`examples/example_circularize_image.py` provided. Unit test `tests/test_circularize_image.py`. Docs `circularize_image.rst`.\r\n\r\nWorks well, thanks to the great inputs from @DanHickstein:  \r\n![o-circ-eg](https://cloud.githubusercontent.com/assets/10932229/22814302/82b1e68e-efa6-11e6-880d-3daa9b1ee751.png)\r\n\r\n\r\n**Additional changes**\r\n- Bug in the `origin` coordinate [determination](https://github.com/PyAbel/PyAbel/issues/186#issuecomment-278217784) (introduced by @stggh #134 :-( ):\r\n   ```python\r\n    origin = (nx//2 + nx % 2, ny//2 + ny % 2)   # % handles odd size image\r\n    ```\r\n   should be `origin = (nx//2, ny//2)` for odd size images.\r\n   Fixed all occurrences. Predominantly in `tools/center.py`, `tools/polar.py`, and Abel transform `direct.py`. \r\n   Slight tweaks to some unit tests.\r\n\r\n- added directories (empty) `examples/bases`, `examples/output` as file holders for the outputs generated by the examples code. Modified all examples to use `basis_dir='bases'` and `savefig('output/xxx')`. NB added `**kwargs` to `hansenlaw.py`, `direct.py`, `onion_bordas.py` to allow `basis_dir` parameter. Verified all examples. \r\n\r\n- added @DanHickstein's `flower_distort()` to `abel.tools.analytical.py`\r\n\r\n- added utility function `abel.tools.vmi.toPES()`to provide conversion of speed profile to photoelectron spectrum, as for the figure above. The tricky part is the intensity scaling.\r\n\r\n(Edited to fix typos, correct meaning)","created_at":"2017-02-10T04:41:00Z","updated_at":"2017-02-13T18:57:47Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f60a7df10ae0fc717721b00189c3d463b787edaa","assignee":{"login":"stggh","id":10932229,"avatar_url":"https://avatars.githubusercontent.com/u/10932229?v=3","gravatar_id":"","url":"https://api.github.com/users/stggh","html_url":"https://github.com/stggh","followers_url":"https://api.github.com/users/stggh/followers","following_url":"https://api.github.com/users/stggh/following{/other_user}","gists_url":"https://api.github.com/users/stggh/gists{/gist_id}","starred_url":"https://api.github.com/users/stggh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stggh/subscriptions","organizations_url":"https://api.github.com/users/stggh/orgs","repos_url":"https://api.github.com/users/stggh/repos","events_url":"https://api.github.com/users/stggh/events{/privacy}","received_events_url":"https://api.github.com/users/stggh/received_events","type":"User","site_admin":false},"assignees":[{"login":"stggh","id":10932229,"avatar_url":"https://avatars.githubusercontent.com/u/10932229?v=3","gravatar_id":"","url":"https://api.github.com/users/stggh","html_url":"https://github.com/stggh","followers_url":"https://api.github.com/users/stggh/followers","following_url":"https://api.github.com/users/stggh/following{/other_user}","gists_url":"https://api.github.com/users/stggh/gists{/gist_id}","starred_url":"https://api.github.com/users/stggh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stggh/subscriptions","organizations_url":"https://api.github.com/users/stggh/orgs","repos_url":"https://api.github.com/users/stggh/repos","events_url":"https://api.github.com/users/stggh/events{/privacy}","received_events_url":"https://api.github.com/users/stggh/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/PyAbel/PyAbel/pulls/195/commits","review_comments_url":"https://api.github.com/repos/PyAbel/PyAbel/pulls/195/comments","review_comment_url":"https://api.github.com/repos/PyAbel/PyAbel/pulls/comments{/number}","comments_url":"https://api.github.com/repos/PyAbel/PyAbel/issues/195/comments","statuses_url":"https://api.github.com/repos/PyAbel/PyAbel/statuses/2128d4f76aee83736c6fa310e05ec031c3f98e61","head":{"label":"stggh:circ","ref":"circ","sha":"2128d4f76aee83736c6fa310e05ec031c3f98e61","user":{"login":"stggh","id":10932229,"avatar_url":"https://avatars.githubusercontent.com/u/10932229?v=3","gravatar_id":"","url":"https://api.github.com/users/stggh","html_url":"https://github.com/stggh","followers_url":"https://api.github.com/users/stggh/followers","following_url":"https://api.github.com/users/stggh/following{/other_user}","gists_url":"https://api.github.com/users/stggh/gists{/gist_id}","starred_url":"https://api.github.com/users/stggh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stggh/subscriptions","organizations_url":"https://api.github.com/users/stggh/orgs","repos_url":"https://api.github.com/users/stggh/repos","events_url":"https://api.github.com/users/stggh/events{/privacy}","received_events_url":"https://api.github.com/users/stggh/received_events","type":"User","site_admin":false},"repo":{"id":46905320,"name":"PyAbel","full_name":"stggh/PyAbel","owner":{"login":"stggh","id":10932229,"avatar_url":"https://avatars.githubusercontent.com/u/10932229?v=3","gravatar_id":"","url":"https://api.github.com/users/stggh","html_url":"https://github.com/stggh","followers_url":"https://api.github.com/users/stggh/followers","following_url":"https://api.github.com/users/stggh/following{/other_user}","gists_url":"https://api.github.com/users/stggh/gists{/gist_id}","starred_url":"https://api.github.com/users/stggh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stggh/subscriptions","organizations_url":"https://api.github.com/users/stggh/orgs","repos_url":"https://api.github.com/users/stggh/repos","events_url":"https://api.github.com/users/stggh/events{/privacy}","received_events_url":"https://api.github.com/users/stggh/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/stggh/PyAbel","description":"A python package for Abel and inverse Abel transforms","fork":true,"url":"https://api.github.com/repos/stggh/PyAbel","forks_url":"https://api.github.com/repos/stggh/PyAbel/forks","keys_url":"https://api.github.com/repos/stggh/PyAbel/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stggh/PyAbel/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stggh/PyAbel/teams","hooks_url":"https://api.github.com/repos/stggh/PyAbel/hooks","issue_events_url":"https://api.github.com/repos/stggh/PyAbel/issues/events{/number}","events_url":"https://api.github.com/repos/stggh/PyAbel/events","assignees_url":"https://api.github.com/repos/stggh/PyAbel/assignees{/user}","branches_url":"https://api.github.com/repos/stggh/PyAbel/branches{/branch}","tags_url":"https://api.github.com/repos/stggh/PyAbel/tags","blobs_url":"https://api.github.com/repos/stggh/PyAbel/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stggh/PyAbel/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stggh/PyAbel/git/refs{/sha}","trees_url":"https://api.github.com/repos/stggh/PyAbel/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stggh/PyAbel/statuses/{sha}","languages_url":"https://api.github.com/repos/stggh/PyAbel/languages","stargazers_url":"https://api.github.com/repos/stggh/PyAbel/stargazers","contributors_url":"https://api.github.com/repos/stggh/PyAbel/contributors","subscribers_url":"https://api.github.com/repos/stggh/PyAbel/subscribers","subscription_url":"https://api.github.com/repos/stggh/PyAbel/subscription","commits_url":"https://api.github.com/repos/stggh/PyAbel/commits{/sha}","git_commits_url":"https://api.github.com/repos/stggh/PyAbel/git/commits{/sha}","comments_url":"https://api.github.com/repos/stggh/PyAbel/comments{/number}","issue_comment_url":"https://api.github.com/repos/stggh/PyAbel/issues/comments{/number}","contents_url":"https://api.github.com/repos/stggh/PyAbel/contents/{+path}","compare_url":"https://api.github.com/repos/stggh/PyAbel/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stggh/PyAbel/merges","archive_url":"https://api.github.com/repos/stggh/PyAbel/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stggh/PyAbel/downloads","issues_url":"https://api.github.com/repos/stggh/PyAbel/issues{/number}","pulls_url":"https://api.github.com/repos/stggh/PyAbel/pulls{/number}","milestones_url":"https://api.github.com/repos/stggh/PyAbel/milestones{/number}","notifications_url":"https://api.github.com/repos/stggh/PyAbel/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stggh/PyAbel/labels{/name}","releases_url":"https://api.github.com/repos/stggh/PyAbel/releases{/id}","deployments_url":"https://api.github.com/repos/stggh/PyAbel/deployments","created_at":"2015-11-26T05:12:44Z","updated_at":"2016-12-20T23:16:10Z","pushed_at":"2017-02-11T23:27:15Z","git_url":"git://github.com/stggh/PyAbel.git","ssh_url":"git@github.com:stggh/PyAbel.git","clone_url":"https://github.com/stggh/PyAbel.git","svn_url":"https://github.com/stggh/PyAbel","homepage":"","size":32105,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"circ"}},"base":{"label":"PyAbel:master","ref":"master","sha":"d7a08c395b9ae296e9f10ca1329d7f4e177a2cd9","user":{"login":"PyAbel","id":15894666,"avatar_url":"https://avatars.githubusercontent.com/u/15894666?v=3","gravatar_id":"","url":"https://api.github.com/users/PyAbel","html_url":"https://github.com/PyAbel","followers_url":"https://api.github.com/users/PyAbel/followers","following_url":"https://api.github.com/users/PyAbel/following{/other_user}","gists_url":"https://api.github.com/users/PyAbel/gists{/gist_id}","starred_url":"https://api.github.com/users/PyAbel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PyAbel/subscriptions","organizations_url":"https://api.github.com/users/PyAbel/orgs","repos_url":"https://api.github.com/users/PyAbel/repos","events_url":"https://api.github.com/users/PyAbel/events{/privacy}","received_events_url":"https://api.github.com/users/PyAbel/received_events","type":"Organization","site_admin":false},"repo":{"id":30170345,"name":"PyAbel","full_name":"PyAbel/PyAbel","owner":{"login":"PyAbel","id":15894666,"avatar_url":"https://avatars.githubusercontent.com/u/15894666?v=3","gravatar_id":"","url":"https://api.github.com/users/PyAbel","html_url":"https://github.com/PyAbel","followers_url":"https://api.github.com/users/PyAbel/followers","following_url":"https://api.github.com/users/PyAbel/following{/other_user}","gists_url":"https://api.github.com/users/PyAbel/gists{/gist_id}","starred_url":"https://api.github.com/users/PyAbel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PyAbel/subscriptions","organizations_url":"https://api.github.com/users/PyAbel/orgs","repos_url":"https://api.github.com/users/PyAbel/repos","events_url":"https://api.github.com/users/PyAbel/events{/privacy}","received_events_url":"https://api.github.com/users/PyAbel/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/PyAbel/PyAbel","description":"A python package for Abel and inverse Abel transforms","fork":false,"url":"https://api.github.com/repos/PyAbel/PyAbel","forks_url":"https://api.github.com/repos/PyAbel/PyAbel/forks","keys_url":"https://api.github.com/repos/PyAbel/PyAbel/keys{/key_id}","collaborators_url":"https://api.github.com/repos/PyAbel/PyAbel/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/PyAbel/PyAbel/teams","hooks_url":"https://api.github.com/repos/PyAbel/PyAbel/hooks","issue_events_url":"https://api.github.com/repos/PyAbel/PyAbel/issues/events{/number}","events_url":"https://api.github.com/repos/PyAbel/PyAbel/events","assignees_url":"https://api.github.com/repos/PyAbel/PyAbel/assignees{/user}","branches_url":"https://api.github.com/repos/PyAbel/PyAbel/branches{/branch}","tags_url":"https://api.github.com/repos/PyAbel/PyAbel/tags","blobs_url":"https://api.github.com/repos/PyAbel/PyAbel/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/PyAbel/PyAbel/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/PyAbel/PyAbel/git/refs{/sha}","trees_url":"https://api.github.com/repos/PyAbel/PyAbel/git/trees{/sha}","statuses_url":"https://api.github.com/repos/PyAbel/PyAbel/statuses/{sha}","languages_url":"https://api.github.com/repos/PyAbel/PyAbel/languages","stargazers_url":"https://api.github.com/repos/PyAbel/PyAbel/stargazers","contributors_url":"https://api.github.com/repos/PyAbel/PyAbel/contributors","subscribers_url":"https://api.github.com/repos/PyAbel/PyAbel/subscribers","subscription_url":"https://api.github.com/repos/PyAbel/PyAbel/subscription","commits_url":"https://api.github.com/repos/PyAbel/PyAbel/commits{/sha}","git_commits_url":"https://api.github.com/repos/PyAbel/PyAbel/git/commits{/sha}","comments_url":"https://api.github.com/repos/PyAbel/PyAbel/comments{/number}","issue_comment_url":"https://api.github.com/repos/PyAbel/PyAbel/issues/comments{/number}","contents_url":"https://api.github.com/repos/PyAbel/PyAbel/contents/{+path}","compare_url":"https://api.github.com/repos/PyAbel/PyAbel/compare/{base}...{head}","merges_url":"https://api.github.com/repos/PyAbel/PyAbel/merges","archive_url":"https://api.github.com/repos/PyAbel/PyAbel/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/PyAbel/PyAbel/downloads","issues_url":"https://api.github.com/repos/PyAbel/PyAbel/issues{/number}","pulls_url":"https://api.github.com/repos/PyAbel/PyAbel/pulls{/number}","milestones_url":"https://api.github.com/repos/PyAbel/PyAbel/milestones{/number}","notifications_url":"https://api.github.com/repos/PyAbel/PyAbel/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/PyAbel/PyAbel/labels{/name}","releases_url":"https://api.github.com/repos/PyAbel/PyAbel/releases{/id}","deployments_url":"https://api.github.com/repos/PyAbel/PyAbel/deployments","created_at":"2015-02-02T03:59:28Z","updated_at":"2017-01-07T00:41:52Z","pushed_at":"2017-02-11T23:27:16Z","git_url":"git://github.com/PyAbel/PyAbel.git","ssh_url":"git@github.com:PyAbel/PyAbel.git","clone_url":"https://github.com/PyAbel/PyAbel.git","svn_url":"https://github.com/PyAbel/PyAbel","homepage":"","size":20713,"stargazers_count":11,"watchers_count":11,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":11,"mirror_url":null,"open_issues_count":11,"forks":11,"open_issues":11,"watchers":11,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/PyAbel/PyAbel/pulls/195"},"html":{"href":"https://github.com/PyAbel/PyAbel/pull/195"},"issue":{"href":"https://api.github.com/repos/PyAbel/PyAbel/issues/195"},"comments":{"href":"https://api.github.com/repos/PyAbel/PyAbel/issues/195/comments"},"review_comments":{"href":"https://api.github.com/repos/PyAbel/PyAbel/pulls/195/comments"},"review_comment":{"href":"https://api.github.com/repos/PyAbel/PyAbel/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/PyAbel/PyAbel/pulls/195/commits"},"statuses":{"href":"https://api.github.com/repos/PyAbel/PyAbel/statuses/2128d4f76aee83736c6fa310e05ec031c3f98e61"}}}}
{"actor":{"display_login":"DanHickstein"}}