{"action":"opened","number":628,"pull_request":{"url":"https://api.github.com/repos/menpo/menpo/pulls/628","id":43659358,"html_url":"https://github.com/menpo/menpo/pull/628","diff_url":"https://github.com/menpo/menpo/pull/628.diff","patch_url":"https://github.com/menpo/menpo/pull/628.patch","issue_url":"https://api.github.com/repos/menpo/menpo/issues/628","number":628,"state":"open","locked":false,"title":"Patches Visualization","user":{"login":"nontas","id":4224739,"avatar_url":"https://avatars.githubusercontent.com/u/4224739?v=3","gravatar_id":"","url":"https://api.github.com/users/nontas","html_url":"https://github.com/nontas","followers_url":"https://api.github.com/users/nontas/followers","following_url":"https://api.github.com/users/nontas/following{/other_user}","gists_url":"https://api.github.com/users/nontas/gists{/gist_id}","starred_url":"https://api.github.com/users/nontas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nontas/subscriptions","organizations_url":"https://api.github.com/users/nontas/orgs","repos_url":"https://api.github.com/users/nontas/repos","events_url":"https://api.github.com/users/nontas/events{/privacy}","received_events_url":"https://api.github.com/users/nontas/received_events","type":"User","site_admin":false},"body":"This PR adds functionality for visualizing a set of patches that are extracted from an image. \r\n\r\n### I. Patches representation\r\nAll the changes are made based on the assumption that patches are represented in one of the two formats that are returned by the `extract_patches()` method of Menpo's `Image` class, i.e.\r\n\r\n* A `numpy.array` with shape ``(n_centers, n_offsets, n_channels, patch_height, patch_width)``\r\n* A `list` of ``n_centers * n_offsets`` `Image` objects, each of which has shape ``(n_channels, patch_height, patch_width)``\r\n\r\nwhere `n_centers` is the number of patches (points) and `n_offsets` is the number of offsets per patch. Generally it is more efficient to use the first representation, that's why a `convert_patches_list_to_single_array(patches_list, n_centers)` method is defined in `menpo.image.base`. I also changed the default value of `as_single_array` to ``True`` in `extract_patches()` and `extract_patches_around_landmarks()` methods.\r\n\r\n### II. Set patches\r\nIn order to facilitate the patches visualization, I defined methods that are able to set patches to the correct locations of an image. Specifically,\r\n\r\n1. I defined a Cython __`set_patches()`__ method, similar to the existing `extract_patches()` one. \r\n2. The method is added to the old `menpo.image.extract_patches.pyx` file, which is now renamed to __`menpo.image.patches.pyx`__.\r\n3. The __`set_patches(patches, patch_centers, offset, offset_index)`__ and __`set_patches_around_landmarks(patches, group, label, offset, offset_index)`__ methods now exist in all image classes, i.e. `Image`, `MaskedImage` and `BooleanImage`. `offset` is a single offset value (``(1, 2`` `numpy.array` or `list` or `tuple`) that gets added to `patch_centers`. `offset_index` is the offset index value of `patches`.\r\n4. The way that the patches were constructed through __`build_mask_around_landmarks()`__ was different than `extract_patches()` and `extract_patches_around_landmarks()`. Thanks to @patricksnape this is now made consistent.\r\n\r\n### III. Visualize patches\r\nA patches `numpy.array` or `list` can be visualized in two ways:\r\n\r\n##### III.A. `view_patches()` from `menpo.image`\r\nThe basic arguments of this method are `patches` and `patch_centers`. The user can also pass in a `list` with the patches indices to get visualized as well as select the patches offset. The method can optionally visualize the patches centers (PointCloud) as well as draw the bounding boxes around the patches. Finally, the background colour can optionally be either black or white.\r\n\r\n##### III.B. `visualize_patches()` widget from `menpo.visualize`\r\nThe widget has a similar behaviour to the rest of the widgets. It includes options related to the channels and the renderer. However, it has an extra tab of options related to the patches. In there, the user can define the following:\r\n\r\n* The patches indices to be visualized using a valid Python slicing command, e.g. ``::3``, ``:3``, ``1:10:2``, ``1, 2, 3``, ``[1, 2, 3]``, ``range(10)``, ``range(2, 10)`` etc.\r\n* Offset index\r\n* Background colour, bounding boxes options, render patches and centers checkboxes\r\n\r\n### IV. Example code\r\n```\r\n%matplotlib inline\r\nimport menpo.io as mio\r\nfrom menpo.image import view_patches\r\nfrom menpo.visualize import visualize_patches\r\nimport matplotlib.pyplot as plt\r\n\r\nim = mio.import_builtin_asset.lenna_png()\r\npatches = im.extract_patches_around_landmarks(patch_shape=(21, 12))\r\n\r\n# Visualize patches with various options\r\nplt.subplot(131)\r\nview_patches(patches, im.landmarks['LJSON'].lms, render_patches=True,\r\n             background='white', render_centers=False, \r\n             render_patches_bboxes=False, figure_size=(16, 14))\r\n\r\nplt.subplot(132)\r\nview_patches(patches, im.landmarks['LJSON'].lms, render_patches=True,\r\n             background='black', render_centers=True, \r\n             render_patches_bboxes=True, figure_size=(16, 14))\r\n\r\nplt.subplot(133)\r\nview_patches(patches, im.landmarks['LJSON'].lms, render_patches=False,\r\n             background='white', render_centers=True, \r\n             render_patches_bboxes=True, figure_size=(16, 14))\r\n\r\n# Widget\r\nvisualize_patches(patches, im.landmarks['LJSON'].lms, style='coloured')\r\n```\r\n\r\n### V. Open questions\r\nThere are still some open questions regarding the logic that I followed, for which I need your feedback:\r\n1. Does the patches representation make sense? Should we define a `PathBasedImage` class? (I have already discussed these with @patricksnape )\r\n2. Does it make sense to have the `view_patches()` method in `menpo.image` or should it move to `menpo.visualize`?\r\n3. Does this solution make it easy to visualize things from `menpofit`, for example the reponse maps of CLMs?","created_at":"2015-08-28T18:48:20Z","updated_at":"2015-08-28T18:48:21Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/menpo/menpo/pulls/628/commits","review_comments_url":"https://api.github.com/repos/menpo/menpo/pulls/628/comments","review_comment_url":"https://api.github.com/repos/menpo/menpo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/menpo/menpo/issues/628/comments","statuses_url":"https://api.github.com/repos/menpo/menpo/statuses/8c21cccbd2e63b2c4afe42e8d4f4805790ebd836","head":{"label":"nontas:patches_visualization","ref":"patches_visualization","sha":"8c21cccbd2e63b2c4afe42e8d4f4805790ebd836","user":{"login":"nontas","id":4224739,"avatar_url":"https://avatars.githubusercontent.com/u/4224739?v=3","gravatar_id":"","url":"https://api.github.com/users/nontas","html_url":"https://github.com/nontas","followers_url":"https://api.github.com/users/nontas/followers","following_url":"https://api.github.com/users/nontas/following{/other_user}","gists_url":"https://api.github.com/users/nontas/gists{/gist_id}","starred_url":"https://api.github.com/users/nontas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nontas/subscriptions","organizations_url":"https://api.github.com/users/nontas/orgs","repos_url":"https://api.github.com/users/nontas/repos","events_url":"https://api.github.com/users/nontas/events{/privacy}","received_events_url":"https://api.github.com/users/nontas/received_events","type":"User","site_admin":false},"repo":{"id":18892071,"name":"menpo","full_name":"nontas/menpo","owner":{"login":"nontas","id":4224739,"avatar_url":"https://avatars.githubusercontent.com/u/4224739?v=3","gravatar_id":"","url":"https://api.github.com/users/nontas","html_url":"https://github.com/nontas","followers_url":"https://api.github.com/users/nontas/followers","following_url":"https://api.github.com/users/nontas/following{/other_user}","gists_url":"https://api.github.com/users/nontas/gists{/gist_id}","starred_url":"https://api.github.com/users/nontas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nontas/subscriptions","organizations_url":"https://api.github.com/users/nontas/orgs","repos_url":"https://api.github.com/users/nontas/repos","events_url":"https://api.github.com/users/nontas/events{/privacy}","received_events_url":"https://api.github.com/users/nontas/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/nontas/menpo","description":"Menpo - the Python facial modelling toolkit","fork":true,"url":"https://api.github.com/repos/nontas/menpo","forks_url":"https://api.github.com/repos/nontas/menpo/forks","keys_url":"https://api.github.com/repos/nontas/menpo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nontas/menpo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nontas/menpo/teams","hooks_url":"https://api.github.com/repos/nontas/menpo/hooks","issue_events_url":"https://api.github.com/repos/nontas/menpo/issues/events{/number}","events_url":"https://api.github.com/repos/nontas/menpo/events","assignees_url":"https://api.github.com/repos/nontas/menpo/assignees{/user}","branches_url":"https://api.github.com/repos/nontas/menpo/branches{/branch}","tags_url":"https://api.github.com/repos/nontas/menpo/tags","blobs_url":"https://api.github.com/repos/nontas/menpo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nontas/menpo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nontas/menpo/git/refs{/sha}","trees_url":"https://api.github.com/repos/nontas/menpo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nontas/menpo/statuses/{sha}","languages_url":"https://api.github.com/repos/nontas/menpo/languages","stargazers_url":"https://api.github.com/repos/nontas/menpo/stargazers","contributors_url":"https://api.github.com/repos/nontas/menpo/contributors","subscribers_url":"https://api.github.com/repos/nontas/menpo/subscribers","subscription_url":"https://api.github.com/repos/nontas/menpo/subscription","commits_url":"https://api.github.com/repos/nontas/menpo/commits{/sha}","git_commits_url":"https://api.github.com/repos/nontas/menpo/git/commits{/sha}","comments_url":"https://api.github.com/repos/nontas/menpo/comments{/number}","issue_comment_url":"https://api.github.com/repos/nontas/menpo/issues/comments{/number}","contents_url":"https://api.github.com/repos/nontas/menpo/contents/{+path}","compare_url":"https://api.github.com/repos/nontas/menpo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nontas/menpo/merges","archive_url":"https://api.github.com/repos/nontas/menpo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nontas/menpo/downloads","issues_url":"https://api.github.com/repos/nontas/menpo/issues{/number}","pulls_url":"https://api.github.com/repos/nontas/menpo/pulls{/number}","milestones_url":"https://api.github.com/repos/nontas/menpo/milestones{/number}","notifications_url":"https://api.github.com/repos/nontas/menpo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nontas/menpo/labels{/name}","releases_url":"https://api.github.com/repos/nontas/menpo/releases{/id}","created_at":"2014-04-17T20:13:29Z","updated_at":"2015-04-09T15:06:48Z","pushed_at":"2015-08-28T18:44:06Z","git_url":"git://github.com/nontas/menpo.git","ssh_url":"git@github.com:nontas/menpo.git","clone_url":"https://github.com/nontas/menpo.git","svn_url":"https://github.com/nontas/menpo","homepage":"http://ibug.doc.ic.ac.uk/","size":39316,"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":"master"}},"base":{"label":"menpo:master","ref":"master","sha":"6dc5b907ded8fd6b11f84f9e159e6bdd8a2a92d1","user":{"login":"menpo","id":4281647,"avatar_url":"https://avatars.githubusercontent.com/u/4281647?v=3","gravatar_id":"","url":"https://api.github.com/users/menpo","html_url":"https://github.com/menpo","followers_url":"https://api.github.com/users/menpo/followers","following_url":"https://api.github.com/users/menpo/following{/other_user}","gists_url":"https://api.github.com/users/menpo/gists{/gist_id}","starred_url":"https://api.github.com/users/menpo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/menpo/subscriptions","organizations_url":"https://api.github.com/users/menpo/orgs","repos_url":"https://api.github.com/users/menpo/repos","events_url":"https://api.github.com/users/menpo/events{/privacy}","received_events_url":"https://api.github.com/users/menpo/received_events","type":"Organization","site_admin":false},"repo":{"id":9731325,"name":"menpo","full_name":"menpo/menpo","owner":{"login":"menpo","id":4281647,"avatar_url":"https://avatars.githubusercontent.com/u/4281647?v=3","gravatar_id":"","url":"https://api.github.com/users/menpo","html_url":"https://github.com/menpo","followers_url":"https://api.github.com/users/menpo/followers","following_url":"https://api.github.com/users/menpo/following{/other_user}","gists_url":"https://api.github.com/users/menpo/gists{/gist_id}","starred_url":"https://api.github.com/users/menpo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/menpo/subscriptions","organizations_url":"https://api.github.com/users/menpo/orgs","repos_url":"https://api.github.com/users/menpo/repos","events_url":"https://api.github.com/users/menpo/events{/privacy}","received_events_url":"https://api.github.com/users/menpo/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/menpo/menpo","description":"Python framework for manipulating annotated data","fork":false,"url":"https://api.github.com/repos/menpo/menpo","forks_url":"https://api.github.com/repos/menpo/menpo/forks","keys_url":"https://api.github.com/repos/menpo/menpo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/menpo/menpo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/menpo/menpo/teams","hooks_url":"https://api.github.com/repos/menpo/menpo/hooks","issue_events_url":"https://api.github.com/repos/menpo/menpo/issues/events{/number}","events_url":"https://api.github.com/repos/menpo/menpo/events","assignees_url":"https://api.github.com/repos/menpo/menpo/assignees{/user}","branches_url":"https://api.github.com/repos/menpo/menpo/branches{/branch}","tags_url":"https://api.github.com/repos/menpo/menpo/tags","blobs_url":"https://api.github.com/repos/menpo/menpo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/menpo/menpo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/menpo/menpo/git/refs{/sha}","trees_url":"https://api.github.com/repos/menpo/menpo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/menpo/menpo/statuses/{sha}","languages_url":"https://api.github.com/repos/menpo/menpo/languages","stargazers_url":"https://api.github.com/repos/menpo/menpo/stargazers","contributors_url":"https://api.github.com/repos/menpo/menpo/contributors","subscribers_url":"https://api.github.com/repos/menpo/menpo/subscribers","subscription_url":"https://api.github.com/repos/menpo/menpo/subscription","commits_url":"https://api.github.com/repos/menpo/menpo/commits{/sha}","git_commits_url":"https://api.github.com/repos/menpo/menpo/git/commits{/sha}","comments_url":"https://api.github.com/repos/menpo/menpo/comments{/number}","issue_comment_url":"https://api.github.com/repos/menpo/menpo/issues/comments{/number}","contents_url":"https://api.github.com/repos/menpo/menpo/contents/{+path}","compare_url":"https://api.github.com/repos/menpo/menpo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/menpo/menpo/merges","archive_url":"https://api.github.com/repos/menpo/menpo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/menpo/menpo/downloads","issues_url":"https://api.github.com/repos/menpo/menpo/issues{/number}","pulls_url":"https://api.github.com/repos/menpo/menpo/pulls{/number}","milestones_url":"https://api.github.com/repos/menpo/menpo/milestones{/number}","notifications_url":"https://api.github.com/repos/menpo/menpo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/menpo/menpo/labels{/name}","releases_url":"https://api.github.com/repos/menpo/menpo/releases{/id}","created_at":"2013-04-28T14:52:38Z","updated_at":"2015-08-28T06:26:40Z","pushed_at":"2015-08-27T17:33:32Z","git_url":"git://github.com/menpo/menpo.git","ssh_url":"git@github.com:menpo/menpo.git","clone_url":"https://github.com/menpo/menpo.git","svn_url":"https://github.com/menpo/menpo","homepage":"http://www.menpo.org","size":47050,"stargazers_count":46,"watchers_count":46,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":27,"mirror_url":null,"open_issues_count":34,"forks":27,"open_issues":34,"watchers":46,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/menpo/menpo/pulls/628"},"html":{"href":"https://github.com/menpo/menpo/pull/628"},"issue":{"href":"https://api.github.com/repos/menpo/menpo/issues/628"},"comments":{"href":"https://api.github.com/repos/menpo/menpo/issues/628/comments"},"review_comments":{"href":"https://api.github.com/repos/menpo/menpo/pulls/628/comments"},"review_comment":{"href":"https://api.github.com/repos/menpo/menpo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/menpo/menpo/pulls/628/commits"},"statuses":{"href":"https://api.github.com/repos/menpo/menpo/statuses/8c21cccbd2e63b2c4afe42e8d4f4805790ebd836"}},"merged":false,"mergeable":null,"mergeable_state":"unknown","merged_by":null,"comments":0,"review_comments":0,"commits":31,"additions":2857,"deletions":194,"changed_files":26}}