GitHub PullRequestReviewCommentEvent at 2014-07-16 22:33:52
Repository: imagen
Actor: jbednar
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/ioam/imagen/pulls/comments/15032296","id":15032296,"diff_hunk":"@@ -365,49 +413,261 @@ class FileImage(GenericImage):\n     details of supported image file formats.\n     \"\"\"\n \n-    filename = param.Filename(default='images/ellen_arthur.pgm',precedence=0.9,doc=\"\"\"\n-        File path (can be relative to Param's base path) to a bitmap image.\n-        The image can be in any format accepted by PIL, e.g. PNG, JPG, TIFF, or PGM.\n+    filename = param.Filename(default='images/ellen_arthur.pgm', precedence=0.9,doc=\"\"\"\n+        File path (can be relative to Param's base path) to a bitmap\n+        image.  The image can be in any format accepted by PIL,\n+        e.g. PNG, JPG, TIFF, or PGM as well or numpy save files (.npy\n+        or .npz) containing 2D or 3D arrays (where the third dimension\n+        is used for each channel).\n         \"\"\")\n \n \n     def __init__(self, **params):\n         super(FileImage,self).__init__(**params)\n-        # Saves the last filename loaded, to avoid unnecessary reloading\n-        self.last_filename = None\n+        self.last_filename = None  # Cached to avoid unnecessary reloading for each channel\n+\n+\n+    def __call__(self,**params_to_override):\n+        params_to_override['cache_image']=True\n+        p = param.ParamOverrides(self,params_to_override)\n+        # Cache image to prevent channel_data to be deleted before channel specific processing has been finished.\n+        gray = super(FileImage,self).__call__(**params_to_override)\n+\n+        self._channel_data = self._process_channels(p,**params_to_override)\n+\n+        if self.channel_transform:\n+            self._channel_data = self.channel_transform(p, self._channel_data)\n+\n+        if p.cache_image is False:\n+            self._image = None\n+\n+        return gray\n \n \n     def _get_image(self,p):\n-        if p.filename!=self.last_filename or self._image is None:\n-            self.last_filename=p.filename\n-            self._image = ImageOps.grayscale(Image.open(p.filename))\n+        file_, ext = splitext(p.filename)\n+        npy = (ext.lower() == \".npy\")\n+        reload_image = (p.filename!=self.last_filename or self._image is None)\n+\n+        self.last_filename = p.filename\n+\n+        if reload_image:\n+            if npy:\n+                self._load_npy(p.filename)\n+            else:\n+                self._load_pil_image(p.filename)\n+\n         return self._image\n \n \n+    def _load_pil_image(self, filename):\n+        \"\"\"\n+        Load image using PIL.\n+        \"\"\"\n+        self._channel_data = []\n+        im = Image.open(filename)\n+        self._image = ImageOps.grayscale(im)\n+        im.load()\n \n-class NumpyFile(GenericImage):\n+        file_data = np.asarray(im, float)\n+        file_data = file_data / file_data.max()\n+\n+        # if the image has more than one channel, load it\n+        if( len(file_data.shape) == 3 ):\n+            num_channels = file_data.shape[2]\n+            for i in range(num_channels):\n+                self._channel_data.append( file_data[:, :, i])\n+\n+\n+    def _load_npy(self, filename):\n+        \"\"\"\n+        Load image using Numpy.\n+        \"\"\"\n+        self._channel_data = []\n+        file_channel_data = np.load(filename)\n+        file_channel_data = file_channel_data / file_channel_data.max()\n+\n+        for i in range(file_channel_data.shape[2]):\n+            self._channel_data.append(file_channel_data[:, :, i])\n+\n+        self._image = file_channel_data.sum(2) / file_channel_data.shape[2]\n+\n+\n+\n+\n+class RGBChannelTransform(ChannelTransform):\n     \"\"\"\n-    Read an array from a Numpy-format file.\n+    PostProcessor for the specific case of 3-channel (RED/GREEN/BLUE)\n+    color images.  Color-specific processing is applied, in particular\n+    to rotate the hue of each image at random, thus achieving a\n+    balanced color input across many pattern presentations.\n     \"\"\"\n \n-    filename = param.Filename(default='images/numpy_array_feret_photo.npy',precedence=0.9,doc=\"\"\"\n-        File path (can be relative to Param's base path) to the Numpy file.\"\"\")\n+    saturation = param.Number(default=1.0)\n+\n+    apply_hue_jitter = param.Boolean(default=True, doc=\"\"\"\n+        Whether to apply a random uniform jitter to the image,\n+        eg, to perform random hue rotation.\"\"\")\n+\n+    random_hue_jitter = param.ClassSelector(numbergen.RandomDistribution,\n+        default=numbergen.UniformRandom(name='hue_jitter',lbound=0,ubound=1,seed=1048921), doc=\"\"\"\n+        Numbergen random generator to be used to create a distribution in hue jitter,\n+        ie, to perform hue rotation on the images.\"\"\")\n+\n+    _hack_recording = param.Parameter(default=None)\n+\n+\n+    def __call__(self,p, channel_data):\n+        if(self.apply_hue_jitter):\n+            im2pg = color_conversion.image2receptors\n+            pg2analysis = color_conversion.receptors2analysis\n+            analysis2pg = color_conversion.analysis2receptors\n+            jitterfn = color_conversion.jitter_hue\n+            satfn = color_conversion.multiply_sat\n+\n+            channs_in  = np.dstack(channel_data)\n+            channs_out = im2pg(channs_in)\n+            analysis_space = pg2analysis(channs_out)\n+\n+            if self._hack_recording is not None:\n+                self._hack_recording(self,channs=channs_in,extra=analysis_space)\n+\n+            jitterfn(analysis_space,self.random_hue_jitter())\n+            satfn(analysis_space,self.saturation)\n+\n+            channs_out = analysis2pg(analysis_space)\n+\n+            channel_data = np.dsplit(channs_out, 3) # must be RGB!\n+            for a in channel_data:\n+                a.shape = a.shape[0:2]\n+\n+\n+        return channel_data\n+\n+\n+\n+class RGBImage(FileImage):\n+    \"\"\"\n+    For backwards compatibility.\n+    \"\"\"\n+    channel_transform = param.Callable(default=RGBChannelTransform)\n+\n+\n+class NumpyFile(FileImage):\n+    \"\"\"\n+    For backwards compatibility.\n+    \"\"\"\n \n-    # Inherits from GenericImage, overriding defaults to disable rescaling\n     pattern_sampler = param.ClassSelector(class_=ImageSampler,\n         default=PatternSampler(background_value_fn=edge_average,\n                                size_normalization='original',\n-                               whole_pattern_output_fns=[]))\n+                               whole_pattern_output_fns=[]),doc=\"\"\"\n+        The PatternSampler to use to resample/resize the image.\"\"\")\n \n \n-    def __init__(self, **params):\n-        super(NumpyFile,self).__init__(**params)\n-        # Saves the last filename loaded, to avoid unnecessary reloading\n-        self.last_filename = None\n \n \n-    def _get_image(self,p):\n-        if p.filename!=self.last_filename or self._image is None:\n-            self.last_filename=p.filename\n-            self._image = np.load((p.filename))\n-        return self._image\n+class CompositeImage(GenericImage):\n+    \"\"\"\n+    Wrapper for any PatternGenerator to support multiple\n+    channels.\n+\n+    If the specified generator itself has a 'generator' attribute,\n+    ExtendToNChannel will attempt to get the channels' data from\n+    generator.generator (e.g. ColorImage inside a Selector);\n+    otherwise, ExtendToNChannel will attempt to get channel_data\n+    from generator. If no channel_data is found in this\n+    ways, ExtendToNChannel will synthesize the channels from the traditional\n+    single channel generator.\n+\n+    After finding or synthesizing the channels, they are\n+    scaled according to relative_channel_strengths.\n+    \"\"\"\n+\n+    generator = param.ClassSelector(class_=PatternGenerator,default=Constant(),doc=\"\"\"\n+        PatternGenerator to be converted to N-Channels.\"\"\")\n+\n+    channel_factors = param.Dynamic(default=[1.,1.,1],doc=\"\"\"\n+        Channel scaling factors. The length of this list sets the number of channels to be\n+        created, unless the input_generator is alreay NChannel (in which case the number of\n+        its channels is used).\"\"\")\n+\n+    correlate_channels = param.Parameter(default=None, doc=\"\"\"\n+        List which contains 3 values:\n+          correlate_channels = ( dest_channel_to_correlate, channel_to_correlate_to, correlation_strength )\"\"\")\n+\n+\n+    def __init__(self,**params):\n+        super(CompositeImage,self).__init__(**params)\n+        self._channel_data = []\n+\n+        for i in range(len(self.channel_factors)):\n+            self._channel_data.append( None )\n+\n+\n+    def channels(self):\n+        if(len(self._channel_data)>0):\n+            if(self._channel_data[0] is None):\n+                fake = copy.deepcopy(self)\n+                fake()\n+                return fake._channel_data\n+\n+                #self()\n+\n+        return self._channel_data\n+\n+\n+\n+    def post_process(self):  # old hack_hook1\n+        pass","path":"imagen/image.py","position":383,"original_position":383,"commit_id":"e032efc8046a387408ed31f6c638f698975c17a6","original_commit_id":"e032efc8046a387408ed31f6c638f698975c17a6","user":{"login":"jbednar","id":1695496,"avatar_url":"https://avatars.githubusercontent.com/u/1695496?","gravatar_id":"007608f5b5d533a8279761721fb975c4","url":"https://api.github.com/users/jbednar","html_url":"https://github.com/jbednar","followers_url":"https://api.github.com/users/jbednar/followers","following_url":"https://api.github.com/users/jbednar/following{/other_user}","gists_url":"https://api.github.com/users/jbednar/gists{/gist_id}","starred_url":"https://api.github.com/users/jbednar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jbednar/subscriptions","organizations_url":"https://api.github.com/users/jbednar/orgs","repos_url":"https://api.github.com/users/jbednar/repos","events_url":"https://api.github.com/users/jbednar/events{/privacy}","received_events_url":"https://api.github.com/users/jbednar/received_events","type":"User","site_admin":false},"body":"I believe that they are necessary for Chris's models that Giacomo is using.  But please remove anything about old hack_hooks; that can be emailed to Chris directly, and doesn't need to be in our code.","created_at":"2014-07-16T22:33:52Z","updated_at":"2014-07-16T22:33:52Z","html_url":"https://github.com/ioam/imagen/pull/31#discussion_r15032296","pull_request_url":"https://api.github.com/repos/ioam/imagen/pulls/31","_links":{"self":{"href":"https://api.github.com/repos/ioam/imagen/pulls/comments/15032296"},"html":{"href":"https://github.com/ioam/imagen/pull/31#discussion_r15032296"},"pull_request":{"href":"https://api.github.com/repos/ioam/imagen/pulls/31"}}},"pull_request":{"url":"https://api.github.com/repos/ioam/imagen/pulls/31","id":18482667,"html_url":"https://github.com/ioam/imagen/pull/31","diff_url":"https://github.com/ioam/imagen/pull/31.diff","patch_url":"https://github.com/ioam/imagen/pull/31.patch","issue_url":"https://api.github.com/repos/ioam/imagen/issues/31","number":31,"state":"open","title":"New image.py, added support for channels() to PG, Selector and all from GenericImage on","user":{"login":"spiglerg","id":1992305,"avatar_url":"https://avatars.githubusercontent.com/u/1992305?","gravatar_id":"edb2cd5184b872ec3fb2f46c415cf251","url":"https://api.github.com/users/spiglerg","html_url":"https://github.com/spiglerg","followers_url":"https://api.github.com/users/spiglerg/followers","following_url":"https://api.github.com/users/spiglerg/following{/other_user}","gists_url":"https://api.github.com/users/spiglerg/gists{/gist_id}","starred_url":"https://api.github.com/users/spiglerg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spiglerg/subscriptions","organizations_url":"https://api.github.com/users/spiglerg/orgs","repos_url":"https://api.github.com/users/spiglerg/repos","events_url":"https://api.github.com/users/spiglerg/events{/privacy}","received_events_url":"https://api.github.com/users/spiglerg/received_events","type":"User","site_admin":false},"body":"","created_at":"2014-07-16T17:35:14Z","updated_at":"2014-07-16T22:33:52Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cc34f29267d58157ee0a6f5ecb884e21619a45ee","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ioam/imagen/pulls/31/commits","review_comments_url":"https://api.github.com/repos/ioam/imagen/pulls/31/comments","review_comment_url":"https://api.github.com/repos/ioam/imagen/pulls/comments/{number}","comments_url":"https://api.github.com/repos/ioam/imagen/issues/31/comments","statuses_url":"https://api.github.com/repos/ioam/imagen/statuses/e032efc8046a387408ed31f6c638f698975c17a6","head":{"label":"spiglerg:master","ref":"master","sha":"e032efc8046a387408ed31f6c638f698975c17a6","user":{"login":"spiglerg","id":1992305,"avatar_url":"https://avatars.githubusercontent.com/u/1992305?","gravatar_id":"edb2cd5184b872ec3fb2f46c415cf251","url":"https://api.github.com/users/spiglerg","html_url":"https://github.com/spiglerg","followers_url":"https://api.github.com/users/spiglerg/followers","following_url":"https://api.github.com/users/spiglerg/following{/other_user}","gists_url":"https://api.github.com/users/spiglerg/gists{/gist_id}","starred_url":"https://api.github.com/users/spiglerg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spiglerg/subscriptions","organizations_url":"https://api.github.com/users/spiglerg/orgs","repos_url":"https://api.github.com/users/spiglerg/repos","events_url":"https://api.github.com/users/spiglerg/events{/privacy}","received_events_url":"https://api.github.com/users/spiglerg/received_events","type":"User","site_admin":false},"repo":{"id":21909593,"name":"imagen","full_name":"spiglerg/imagen","owner":{"login":"spiglerg","id":1992305,"avatar_url":"https://avatars.githubusercontent.com/u/1992305?","gravatar_id":"edb2cd5184b872ec3fb2f46c415cf251","url":"https://api.github.com/users/spiglerg","html_url":"https://github.com/spiglerg","followers_url":"https://api.github.com/users/spiglerg/followers","following_url":"https://api.github.com/users/spiglerg/following{/other_user}","gists_url":"https://api.github.com/users/spiglerg/gists{/gist_id}","starred_url":"https://api.github.com/users/spiglerg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spiglerg/subscriptions","organizations_url":"https://api.github.com/users/spiglerg/orgs","repos_url":"https://api.github.com/users/spiglerg/repos","events_url":"https://api.github.com/users/spiglerg/events{/privacy}","received_events_url":"https://api.github.com/users/spiglerg/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/spiglerg/imagen","description":"ImaGen: Generic Python library for 0D, 1D and 2D pattern distributions","fork":true,"url":"https://api.github.com/repos/spiglerg/imagen","forks_url":"https://api.github.com/repos/spiglerg/imagen/forks","keys_url":"https://api.github.com/repos/spiglerg/imagen/keys{/key_id}","collaborators_url":"https://api.github.com/repos/spiglerg/imagen/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/spiglerg/imagen/teams","hooks_url":"https://api.github.com/repos/spiglerg/imagen/hooks","issue_events_url":"https://api.github.com/repos/spiglerg/imagen/issues/events{/number}","events_url":"https://api.github.com/repos/spiglerg/imagen/events","assignees_url":"https://api.github.com/repos/spiglerg/imagen/assignees{/user}","branches_url":"https://api.github.com/repos/spiglerg/imagen/branches{/branch}","tags_url":"https://api.github.com/repos/spiglerg/imagen/tags","blobs_url":"https://api.github.com/repos/spiglerg/imagen/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/spiglerg/imagen/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/spiglerg/imagen/git/refs{/sha}","trees_url":"https://api.github.com/repos/spiglerg/imagen/git/trees{/sha}","statuses_url":"https://api.github.com/repos/spiglerg/imagen/statuses/{sha}","languages_url":"https://api.github.com/repos/spiglerg/imagen/languages","stargazers_url":"https://api.github.com/repos/spiglerg/imagen/stargazers","contributors_url":"https://api.github.com/repos/spiglerg/imagen/contributors","subscribers_url":"https://api.github.com/repos/spiglerg/imagen/subscribers","subscription_url":"https://api.github.com/repos/spiglerg/imagen/subscription","commits_url":"https://api.github.com/repos/spiglerg/imagen/commits{/sha}","git_commits_url":"https://api.github.com/repos/spiglerg/imagen/git/commits{/sha}","comments_url":"https://api.github.com/repos/spiglerg/imagen/comments{/number}","issue_comment_url":"https://api.github.com/repos/spiglerg/imagen/issues/comments/{number}","contents_url":"https://api.github.com/repos/spiglerg/imagen/contents/{+path}","compare_url":"https://api.github.com/repos/spiglerg/imagen/compare/{base}...{head}","merges_url":"https://api.github.com/repos/spiglerg/imagen/merges","archive_url":"https://api.github.com/repos/spiglerg/imagen/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/spiglerg/imagen/downloads","issues_url":"https://api.github.com/repos/spiglerg/imagen/issues{/number}","pulls_url":"https://api.github.com/repos/spiglerg/imagen/pulls{/number}","milestones_url":"https://api.github.com/repos/spiglerg/imagen/milestones{/number}","notifications_url":"https://api.github.com/repos/spiglerg/imagen/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/spiglerg/imagen/labels{/name}","releases_url":"https://api.github.com/repos/spiglerg/imagen/releases{/id}","created_at":"2014-07-16T17:05:44Z","updated_at":"2014-07-16T17:13:11Z","pushed_at":"2014-07-16T17:34:39Z","git_url":"git://github.com/spiglerg/imagen.git","ssh_url":"git@github.com:spiglerg/imagen.git","clone_url":"https://github.com/spiglerg/imagen.git","svn_url":"https://github.com/spiglerg/imagen","homepage":"http://ioam.github.com/imagen/","size":2867,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"ioam:master","ref":"master","sha":"069152a2d4187f8b004efcc880bf58a8631aac67","user":{"login":"ioam","id":1693676,"avatar_url":"https://avatars.githubusercontent.com/u/1693676?","gravatar_id":"345c6a8565c8968e401c52f34c98ebb8","url":"https://api.github.com/users/ioam","html_url":"https://github.com/ioam","followers_url":"https://api.github.com/users/ioam/followers","following_url":"https://api.github.com/users/ioam/following{/other_user}","gists_url":"https://api.github.com/users/ioam/gists{/gist_id}","starred_url":"https://api.github.com/users/ioam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ioam/subscriptions","organizations_url":"https://api.github.com/users/ioam/orgs","repos_url":"https://api.github.com/users/ioam/repos","events_url":"https://api.github.com/users/ioam/events{/privacy}","received_events_url":"https://api.github.com/users/ioam/received_events","type":"Organization","site_admin":false},"repo":{"id":4191983,"name":"imagen","full_name":"ioam/imagen","owner":{"login":"ioam","id":1693676,"avatar_url":"https://avatars.githubusercontent.com/u/1693676?","gravatar_id":"345c6a8565c8968e401c52f34c98ebb8","url":"https://api.github.com/users/ioam","html_url":"https://github.com/ioam","followers_url":"https://api.github.com/users/ioam/followers","following_url":"https://api.github.com/users/ioam/following{/other_user}","gists_url":"https://api.github.com/users/ioam/gists{/gist_id}","starred_url":"https://api.github.com/users/ioam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ioam/subscriptions","organizations_url":"https://api.github.com/users/ioam/orgs","repos_url":"https://api.github.com/users/ioam/repos","events_url":"https://api.github.com/users/ioam/events{/privacy}","received_events_url":"https://api.github.com/users/ioam/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ioam/imagen","description":"ImaGen: Generic Python library for 0D, 1D and 2D pattern distributions","fork":false,"url":"https://api.github.com/repos/ioam/imagen","forks_url":"https://api.github.com/repos/ioam/imagen/forks","keys_url":"https://api.github.com/repos/ioam/imagen/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ioam/imagen/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ioam/imagen/teams","hooks_url":"https://api.github.com/repos/ioam/imagen/hooks","issue_events_url":"https://api.github.com/repos/ioam/imagen/issues/events{/number}","events_url":"https://api.github.com/repos/ioam/imagen/events","assignees_url":"https://api.github.com/repos/ioam/imagen/assignees{/user}","branches_url":"https://api.github.com/repos/ioam/imagen/branches{/branch}","tags_url":"https://api.github.com/repos/ioam/imagen/tags","blobs_url":"https://api.github.com/repos/ioam/imagen/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ioam/imagen/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ioam/imagen/git/refs{/sha}","trees_url":"https://api.github.com/repos/ioam/imagen/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ioam/imagen/statuses/{sha}","languages_url":"https://api.github.com/repos/ioam/imagen/languages","stargazers_url":"https://api.github.com/repos/ioam/imagen/stargazers","contributors_url":"https://api.github.com/repos/ioam/imagen/contributors","subscribers_url":"https://api.github.com/repos/ioam/imagen/subscribers","subscription_url":"https://api.github.com/repos/ioam/imagen/subscription","commits_url":"https://api.github.com/repos/ioam/imagen/commits{/sha}","git_commits_url":"https://api.github.com/repos/ioam/imagen/git/commits{/sha}","comments_url":"https://api.github.com/repos/ioam/imagen/comments{/number}","issue_comment_url":"https://api.github.com/repos/ioam/imagen/issues/comments/{number}","contents_url":"https://api.github.com/repos/ioam/imagen/contents/{+path}","compare_url":"https://api.github.com/repos/ioam/imagen/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ioam/imagen/merges","archive_url":"https://api.github.com/repos/ioam/imagen/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ioam/imagen/downloads","issues_url":"https://api.github.com/repos/ioam/imagen/issues{/number}","pulls_url":"https://api.github.com/repos/ioam/imagen/pulls{/number}","milestones_url":"https://api.github.com/repos/ioam/imagen/milestones{/number}","notifications_url":"https://api.github.com/repos/ioam/imagen/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ioam/imagen/labels{/name}","releases_url":"https://api.github.com/repos/ioam/imagen/releases{/id}","created_at":"2012-05-01T12:12:16Z","updated_at":"2014-06-25T13:23:11Z","pushed_at":"2014-07-16T22:05:41Z","git_url":"git://github.com/ioam/imagen.git","ssh_url":"git@github.com:ioam/imagen.git","clone_url":"https://github.com/ioam/imagen.git","svn_url":"https://github.com/ioam/imagen","homepage":"http://ioam.github.com/imagen/","size":6356,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"forks_count":7,"mirror_url":null,"open_issues_count":8,"forks":7,"open_issues":8,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ioam/imagen/pulls/31"},"html":{"href":"https://github.com/ioam/imagen/pull/31"},"issue":{"href":"https://api.github.com/repos/ioam/imagen/issues/31"},"comments":{"href":"https://api.github.com/repos/ioam/imagen/issues/31/comments"},"review_comments":{"href":"https://api.github.com/repos/ioam/imagen/pulls/31/comments"},"review_comment":{"href":"https://api.github.com/repos/ioam/imagen/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/ioam/imagen/pulls/31/commits"},"statuses":{"href":"https://api.github.com/repos/ioam/imagen/statuses/e032efc8046a387408ed31f6c638f698975c17a6"}}}}