DigitalSlideArchive / HistomicsTK

A Python toolkit for pathology image analysis algorithms.
https://digitalslidearchive.github.io/HistomicsTK/
Apache License 2.0
389 stars 114 forks source link

How to download thumbnail (or other non-JSON-encoded) using python REST API? #528

Closed kheffah closed 6 years ago

kheffah commented 6 years ago

Pardon my ignorance, my knowledge of server requests is quite limited. I am trying to get the thumbnail and tiles of a slide from HTK to my python interpreter so I can do some analysis. I am getthing this error because it is not JSON-encoded and girder_client assumes JSON (if my understanding is correct). I would appreciate any help with this. Thanks!

Code I am using:

import requests
import json
import girder_client

# Connect to API
apiUrl = 'http://candygram.neurology.emory.edu:8080/api/v1/'
gc= girder_client.GirderClient(apiUrl = apiUrl)
gc.authenticate(interactive=True)

thumbnail = gc.get("item/%s/tiles/thumbnail" % slide_id)

Error message

JSONDecodeError                           Traceback (most recent call last)
<ipython-input-60-acc36e14b24f> in <module>()
----> 1 gc.get("item/%s/tiles/thumbnail" % slide_id)

~/.conda/envs/wsi/lib/python3.5/site-packages/girder_client/__init__.py in get(self, path, parameters, jsonResp)
    501         Convenience method to call :py:func:`sendRestRequest` with the 'GET' HTTP method.
    502         """
--> 503         return self.sendRestRequest('GET', path, parameters, jsonResp=jsonResp)
    504 
    505     def post(self, path, parameters=None, files=None, data=None, json=None, headers=None,

~/.conda/envs/wsi/lib/python3.5/site-packages/girder_client/__init__.py in sendRestRequest(self, method, path, parameters, data, files, json, headers, jsonResp, **kwargs)
    489         if result.status_code in (200, 201):
    490             if jsonResp:
--> 491                 return result.json()
    492             else:
    493                 return result

~/.conda/envs/wsi/lib/python3.5/site-packages/requests/models.py in json(self, **kwargs)
    894                     # used.
    895                     pass
--> 896         return complexjson.loads(self.text, **kwargs)
    897 
    898     @property

~/.conda/envs/wsi/lib/python3.5/json/__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    317             parse_int is None and parse_float is None and
    318             parse_constant is None and object_pairs_hook is None and not kw):
--> 319         return _default_decoder.decode(s)
    320     if cls is None:
    321         cls = JSONDecoder

~/.conda/envs/wsi/lib/python3.5/json/decoder.py in decode(self, s, _w)
    337 
    338         """
--> 339         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    340         end = _w(s, end).end()
    341         if end != len(s):

~/.conda/envs/wsi/lib/python3.5/json/decoder.py in raw_decode(self, s, idx)
    355             obj, end = self.scan_once(s, idx)
    356         except StopIteration as err:
--> 357             raise JSONDecodeError("Expecting value", s, err.value) from None
    358         return obj, end

JSONDecodeError: Expecting value: line 1 column 1 (char 0)
kheffah commented 6 years ago

Alternatively, I tried using requests to get the thumbnail, as follows:

Code:

headers = {
    'Content-Type': 'image/jpeg',
    'Accept': 'image/jpeg',
    'Girder-Token': 'xqv5O7OP7TCWl5ZlKmFIcEbEBW1evvxB'+
                    'ZnDNOm6HQFqQ1sxWGyYASdpHrli5DbLq',
}

response = requests.get(htk.apiUrl + "item/%s/tiles/thumbnail" % slide_id, 
                 headers=headers, auth=("kheffah", "**********"))

print(response.content)

Response:

<Response [401]>
b'{"message": "Read access denied for folder 5b81c6ade62914001aed7234 (user None).", "type": "access"}'

Note that I have access to the folder, only this request says I don't ... I tried admin priviliges with the same problem persisting.

kheffah commented 6 years ago

Update:

When I try using requests directly but with a public image (to ignore the authentication issue for now), I get a response of 200 (success) but I get the same JSONDecodeError when I try to decode the result using either:

json.loads(response.text) or response.json()

I also tried decoding the string response using:

np.fromstring(response.content)

but I get the following error:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-123-d660066cc581> in <module>()
----> 1 np.fromstring(response.content)

ValueError: string size must be a multiple of element size

Any thoughts?

jbeezley commented 6 years ago

The response from that endpoint is image/jpeg not application/json. To use girder client to read it, try:

resp = gc.get('item/%s/tiles/thumbnail' % '<item id>', jsonResp=False)
resp.content

To get a numpy array, you will need to decode it. Using PIL you can do something like this

import StringIO
from PIL import Image
import numpy as np

image_content = StringIO.StringIO(resp.content)
image_content.seek(0)
Image.open(image_content)
image = Image.open(image_content)
array = np.array(image)
dgutman commented 6 years ago

Mohamed we have multiple python notebooks showing how to do this... chat with me or JC tomorrow and we will point you in the right direction...

On Sat, Aug 25, 2018 at 8:05 PM Mohamed Amgad Tageldin < notifications@github.com> wrote:

Pardon my ignorance, my knowledge of server requests is quite limited. I am trying to get the thumbnail and tiles of a slide from HTK to my python interpreter so I can do some analysis. I am getthing this error because it is not JSON-encoded and girder_client assumes JSON (if my understanding is correct). I would appreciate any help with this. Thanks!

Code I am using:

import requests import json import girder_client

Connect to API

apiUrl = 'http://candygram.neurology.emory.edu:8080/api/v1/' gc= girder_client.GirderClient(apiUrl = apiUrl) gc.authenticate(interactive=True)

thumbnail = gc.get("item/%s/tiles/thumbnail" % slide_id)

Error message

JSONDecodeError Traceback (most recent call last)

in () ----> 1 gc.get("item/%s/tiles/thumbnail" % slide_id) ~/.conda/envs/wsi/lib/python3.5/site-packages/girder_client/__init__.py in get(self, path, parameters, jsonResp) 501 Convenience method to call :py:func:`sendRestRequest` with the 'GET' HTTP method. 502 """ --> 503 return self.sendRestRequest('GET', path, parameters, jsonResp=jsonResp) 504 505 def post(self, path, parameters=None, files=None, data=None, json=None, headers=None, ~/.conda/envs/wsi/lib/python3.5/site-packages/girder_client/__init__.py in sendRestRequest(self, method, path, parameters, data, files, json, headers, jsonResp, **kwargs) 489 if result.status_code in (200, 201): 490 if jsonResp: --> 491 return result.json() 492 else: 493 return result ~/.conda/envs/wsi/lib/python3.5/site-packages/requests/models.py in json(self, **kwargs) 894 # used. 895 pass --> 896 return complexjson.loads(self.text, **kwargs) 897 898 @property ~/.conda/envs/wsi/lib/python3.5/json/__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw) 317 parse_int is None and parse_float is None and 318 parse_constant is None and object_pairs_hook is None and not kw): --> 319 return _default_decoder.decode(s) 320 if cls is None: 321 cls = JSONDecoder ~/.conda/envs/wsi/lib/python3.5/json/decoder.py in decode(self, s, _w) 337 338 """ --> 339 obj, end = self.raw_decode(s, idx=_w(s, 0).end()) 340 end = _w(s, end).end() 341 if end != len(s): ~/.conda/envs/wsi/lib/python3.5/json/decoder.py in raw_decode(self, s, idx) 355 obj, end = self.scan_once(s, idx) 356 except StopIteration as err: --> 357 raise JSONDecodeError("Expecting value", s, err.value) from None 358 return obj, end JSONDecodeError: Expecting value: line 1 column 1 (char 0) — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub , or mute the thread . -- David A Gutman MD PhD Assistant Professor of Neurology, Psychiatry & Biomedical Informatics Emory University School of Medicine Staff Physician, Mental Health Service Line Atlanta VA Medical Center
kheffah commented 6 years ago

@jbeezley Thank you so much for your help!! This is great!

kheffah commented 6 years ago

@dgutman Thanks David, that's great to hear. I will pass by.

kheffah commented 5 years ago

Just as a follow up, one should use io.BytesIO instead for python 3.x.