265

What I'm trying to do is fairly simple when we're dealing with a local file, but the problem comes when I try to do this with a remote URL.

Basically, I'm trying to create a PIL image object from a file pulled from a URL. Sure, I could always just fetch the URL and store it in a temp file, then open it into an image object, but that feels very inefficient.

Here's what I have:

Image.open(urlopen(url))

It flakes out complaining that seek() isn't available, so then I tried this:

Image.open(urlopen(url).read())

But that didn't work either. Is there a Better Way to do this, or is writing to a temporary file the accepted way of doing this sort of thing?

Daniel Quinn
  • 5,364
  • 4
  • 34
  • 54
  • 1
    See also: [How to save an image locally using Python whose URL address I already know?](http://stackoverflow.com/q/8286352/562769) – Martin Thoma Mar 14 '16 at 11:25
  • There must be an issue where the requests is not able to fetch the image from the url. Try the same ( just for testing purpose) from another url. – Aashish Chaubey Nov 17 '21 at 11:46

13 Answers13

385

In Python3 the StringIO and cStringIO modules are gone.

In Python3 you should use:

from PIL import Image
import requests
from io import BytesIO

response = requests.get(url)
img = Image.open(BytesIO(response.content))
Andres Kull
  • 4,226
  • 2
  • 14
  • 13
  • 1
    How to get back the image from response.content ? – Amresh Giri Feb 07 '19 at 11:33
  • `requests` package throws 503 status code while fetching an image from a URL. Instead, I had to resort to `http.client` to get the image. – MSS Aug 08 '19 at 06:46
  • When I try this I get: AttributeError: module 'requests' has no attribute 'get'. – apiljic Aug 26 '19 at 23:21
  • 20
    Manually wrapping in BytesIO is no longer needed since PIL >= 2.8.0. Just use ```Image.open(response.raw)```. PIL automatically checks for that now and does the BytesIO wrapping under the hood. From: https://pillow.readthedocs.io/en/3.0.x/releasenotes/2.8.0.html – Vinícius M Feb 06 '20 at 15:21
  • @ViníciusM your answer should be at the top! thank you – alanho Jul 26 '20 at 10:53
  • 1
    Kinda annoying that 3 libraries are necessary... Pillow should just add this functionality! – Nic Scozzaro Apr 29 '21 at 22:15
169

Using a StringIO

import urllib, cStringIO

file = cStringIO.StringIO(urllib.urlopen(URL).read())
img = Image.open(file)
iacob
  • 14,010
  • 5
  • 54
  • 92
Fábio Diniz
  • 9,719
  • 3
  • 36
  • 45
143

The following works for Python 3:

from PIL import Image
import requests

im = Image.open(requests.get(url, stream=True).raw)

References:

Nic Scozzaro
  • 5,117
  • 1
  • 31
  • 42
Giovanni Cappellotto
  • 4,137
  • 1
  • 29
  • 31
  • 5
    urllib2 was for Python2 I think, which is outdated. For Python 3 it's urllib.requests: `urllib.request.urlopen(url).read()` – wordsforthewise Jan 24 '21 at 01:03
  • 2
    As mentioned by @wordsforthewise urllib is outdated. I used the second option as I was using 'requests' anyway in my code and it worked, so upvoting. Should the urllib part of the solution be removed so that readers don't spend time on trying the first approach just to realize that it doesn't work and then move to the next one? – Mugdha Mar 04 '21 at 13:40
59

Using requests:

from PIL import Image
import requests
from StringIO import StringIO

response = requests.get(url)
img = Image.open(StringIO(response.content))
iacob
  • 14,010
  • 5
  • 54
  • 92
Saurav
  • 3,036
  • 3
  • 18
  • 12
  • 3
    For some reason urllib didn't work for some URLs, but requests worked where that failed – mirri66 Mar 04 '15 at 08:17
  • I couldn't find the PIL package, but it looks like pillow have taken over the PIL effort and you can install for python3 with `pip3.4 install pillow`. – disruptive Nov 23 '15 at 09:02
  • 3
    Note that requests will load the entire response into memory, and then PIL will load the entire thing again as an image, so you have two full copies resident in memory. The previous answer using urllib method streams the data, so you only end up with one copy plus the streaming buffer size. You can stream the data with requests too, but because the response does not support read() semantics, you would have to build an adapter. – sirdodger Feb 02 '16 at 22:14
  • @sirdodger Are you referring to urllib2 or urllib? – CMCDragonkai May 28 '18 at 02:59
  • @CMCDragonkai I was referring to the accepted urllib answer. If memory overhead is a concern, it is better than using this requests answer. (However, like I mentioned, a different solution using requests could achieve the same effect.) – sirdodger May 29 '18 at 08:01
  • @sirdodger PIL Apparently now supports the streaming too. =) https://pillow.readthedocs.io/en/3.0.x/releasenotes/2.8.0.html – Vinícius M Feb 06 '20 at 15:24
38

Python 3

from urllib.request import urlopen
from PIL import Image

img = Image.open(urlopen(url))
img

Jupyter Notebook and IPython

import IPython
url = 'https://newevolutiondesigns.com/images/freebies/colorful-background-14.jpg'
IPython.display.Image(url, width = 250)

Unlike other methods, this method also works in a for loop!

Miladiouss
  • 3,532
  • 1
  • 21
  • 30
27

Use StringIO to turn the read string into a file-like object:

from StringIO import StringIO
import urllib

Image.open(StringIO(urllib.requests.urlopen(url).read()))
Asclepius
  • 49,954
  • 14
  • 144
  • 128
Dan D.
  • 70,581
  • 13
  • 96
  • 116
23

For those doing some sklearn/numpy post processing (i.e. Deep learning) you can wrap the PIL object with np.array(). This might save you from having to Google it like I did:

from PIL import Image
import requests
import numpy as np
from StringIO import StringIO

response = requests.get(url)
img = np.array(Image.open(StringIO(response.content)))
Ben
  • 481
  • 4
  • 6
19

The arguably recommended way to do image input/output these days is to use the dedicated package ImageIO. Image data can be read directly from a URL with one simple line of code:

from imageio import imread
image = imread('https://cdn.sstatic.net/Sites/stackoverflow/img/logo.png')

Many answers on this page predate the release of that package and therefore do not mention it. ImageIO started out as component of the Scikit-Image toolkit. It supports a number of scientific formats on top of the ones provided by the popular image-processing library PILlow. It wraps it all in a clean API solely focused on image input/output. In fact, SciPy removed its own image reader/writer in favor of ImageIO.

john-hen
  • 3,720
  • 2
  • 19
  • 38
5

select the image in chrome, right click on it, click on Copy image address, paste it into a str variable (my_url) to read the image:

import shutil
import requests

my_url = 'https://www.washingtonian.com/wp-content/uploads/2017/06/6-30-17-goat-yoga-congressional-cemetery-1-994x559.jpg'
response = requests.get(my_url, stream=True)
with open('my_image.png', 'wb') as file:
    shutil.copyfileobj(response.raw, file)
del response

open it;

from PIL import Image

img = Image.open('my_image.png')
img.show()
Shivid
  • 1,185
  • 1
  • 20
  • 34
4

Manually wrapping in BytesIO is no longer needed since PIL >= 2.8.0. Just use Image.open(response.raw)

Adding on top of Vinícius's comment:

You should pass stream=True as noted https://requests.readthedocs.io/en/master/user/quickstart/#raw-response-content

So

img = Image.open(requests.get(url, stream=True).raw)
ibigbug
  • 94
  • 1
  • 7
2
from PIL import Image
import cv2
import numpy as np
import requests
image=Image.open(requests.get("https://previews.123rf.com/images/darrenwhi/darrenwhi1310/darrenwhi131000024/24022179-photo-of-many-cars-with-one-a-different-color.jpg", stream=True).raw)
#image =resize((420,250))

image_array=np.array(image)
image 
1

To directly get image as numpy array without using PIL

import requests, io
import matplotlib.pyplot as plt 

response = requests.get(url).content
img = plt.imread(io.BytesIO(response), format='JPG')
plt.imshow(img)
AdithyaM
  • 11
  • 2
1

USE urllib.request.urlretrieve() AND PIL.Image.open() TO DOWNLOAD AND READ IMAGE DATA :

import requests

import urllib.request

import PIL
urllib.request.urlretrieve("https://i.imgur.com/ExdKOOz.png", "sample.png")
img = PIL.Image.open("sample.png")
img.show()

or Call requests.get(url) with url as the address of the object file to download via a GET request. Call io.BytesIO(obj) with obj as the content of the response to load the raw data as a bytes object. To load the image data, call PIL.Image.open(bytes_obj) with bytes_obj as the bytes object:

import io
response = requests.get("https://i.imgur.com/ExdKOOz.png")
image_bytes = io.BytesIO(response.content)
img = PIL.Image.open(image_bytes)
img.show()