How do I serve a dynamically generated image in Django?
I have an html tag
<html>
...
<img src="images/dynamic_chart.png" />
...
</html>
linked up to this request handler, which creates an in-memory image
def chart(request):
img = Image.new("RGB", (300,300), "#FFFFFF")
data = [(i,randint(100,200)) for i in range(0,300,10)]
draw = ImageDraw.Draw(img)
draw.polygon(data, fill="#000000")
# now what?
return HttpResponse(output)
I also plan to change the requests to AJAX, and add some sort of caching mechanism, but my understanding is that wouldn't affect this part of the solution.
I assume you're using PIL (Python Imaging Library). You need to replace your last line with (for example, if you want to serve a PNG image):
response = HttpResponse(mimetype="image/png")
img.save(response, "PNG")
return response
See here for more information.
I'm relatively new to Django myself. I haven't been able to find anything in Django itself, but I have stumbled upon a project on Google Code that may be of some help to you:
django-dynamic-media-serve
I was looking for a solution of the same problem
And for me this simple approach worked fine:
from django.http import FileResponse
def dyn_view(request):
response = FileResponse(open("image.png","rb"))
return response
Another way is to use BytesIO. BytesIO is like a buffer. So one can save the image (which is fast enough than writing to disk) in that buffer.
from PIL import Image, ImageDraw
import io
def chart(request):
img = Image.new('RGB', (240, 240), color=(250,160,170))
draw = ImageDraw.Draw(img)
draw.text((20, 40), 'some_text')
buff = io.BytesIO()
img.save(buff, 'jpeg')
return HttpResponse(buff.getvalue(), content_type='image/jpeg')
Related
I can do this OK both in js and php but not in python. I'm trying to pull a thumbnail image from google books api into a python variable.
The text objects are fine eg
newTitle = (parsed_json['items'][0]['volumeInfo']['title'])
isbn10 = (parsed_json['items'][0]['volumeInfo']['industryIdentifiers'][1]['identifier'])
isbn13 = (parsed_json['items'][0]['volumeInfo']['industryIdentifiers'][0]['identifier'])
The image is supplied in the api as follows. (if you put the http// url into a browser you see the image):
"imageLinks": {
"smallThumbnail": "http://books.google.com/books/content?id=XUnNDwAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api",
"thumbnail": "http://books.google.com/books/content?id=XUnNDwAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api"
I have tried the simple:
myImage = (parsed_json['items'][0]['volumeInfo']['imageLinks'][thumbnail])
which doesn't work.
I have installed pillow to provide image management:
from PIL import Image
img = Image.open("parsed_json['items'][0]['volumeInfo']['imageLinks'][thumbnail]") or
img = Image.open(parsed_json['items'][0]['volumeInfo']['imageLinks'][thumbnail])
which doesn't work. I have tried more complex arrangements:
from PIL import Image
import requests
from io import BytesIO
response = requests.get(parsed_json['items'][0]['volumeInfo']['imageLinks'][thumbnail])
img = Image.open(BytesIO(response.content))
but nothing seems to work. I have tried many other variations on these attempts. I have also, unsuccessfully tried to load the text that points to the thumbnail to try another route. I am confident that the "['items'][0]['volumeInfo']['imageLinks'][thumbnail]" is correct though my only way of testing whether the variable is properly loaded is to save it or if the line of code isn't working.
I didn't have problems downloading and opening the image.
I have use the following code
import requests
from PIL import Image
image_url = "https://books.google.com/books/content?id=XUnNDwAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api"
r = requests.get(image_url)
with open("demo_image",'wb') as f:
f.write(r.content)
img = Image.open("demo_image")
I am using python-docx-template (docxtpl) to generate a .docx file.
With this data:
docente= {
"id":145,
"lugar_de_nacimiento":"Loja",
"fecha_de_nacimiento":"1973-04-14",
"ciudad":"Loja",
"foto_web_low":"https://sica.utpl.edu.ec/media/uploads/docentes/fotos/web/low/1102904313_low.jpg"
}
I have a function where I pass the image docente['foto_web_low'] to the context and the path of the template:
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm
def generaraDocumento(request):
response = HttpResponse(content_type='application/msword')
response['Content-Disposition'] = 'attachment; filename="cv.docx"'
doc = DocxTemplate(str(settings.BASE_DIR) + '/cv_api/templates/docx_filename.docx')
imagen = docente['foto_web_low']
context = {'imagen': imagen}
doc.render(context)
doc.save(response)
return response
The template where I have the image that I want to show docx_filename.docx has this:
The template where I have the data that I want to show docx_filename.docx has this:
Image: {{ imagen }}
When the document is generated, I only get the URL address and not the image, in my template it returns this:
Image: https://sica.utpl.edu.ec/media/uploads/docentes/fotos/web/low/1102904313_low.jpg
How can I make the image appear in the document .docx (docxtpl). Thanks in advance.
The image has to be an instance of docxtpl.InlineImage (see docs).
Another important thing is that the image must be present on the disk. docxtpl doesn't support reading images from a url.
Example:
from docxtpl import InlineImage
from docx.shared import Mm
doc = DocxTemplate(str(settings.BASE_DIR) + '/cv_api/templates/docx_filename.docx')
# The image must be already saved on the disk
# reading images from url is not supported
imagen = InlineImage(doc, '/path/to/image/file.jpg', width=Mm(20)) # width is in millimetres
context = {'imagen': imagen}
# ... the rest of the code remains the same ...
Resize the image first, before add it
make it fit to your space
from PIL import Image
img = Image.open("image-path")
newimg = img.resize((250, 250))
newimg.save("tmplogo.png")
then use the new sized image which name tmplogo.png
doc = DocxTemplate("Base.docx")
dic = {"logo": InlineImage(doc, "tmplogo.png")}
doc.render(dic)
doc.save("test.docx")
You're currently linking the variable foto_web_low to the url and not to an actual image. You will need to download the image first, and then attach it. The code below is not tested, but should be in the right direction:
First, dowload the image:
response = requests.get("https://The_URL_of_the_picture.jpg")
file = open("the_image.png", "wb")
file.write(response.content)
file.close()
And then simply add the image to your variable in the context:
docente= {
"id":145,
...
"foto_web_low":"the_image.png",
...
}
Context
I have made a simple web app for uploading content to a blog. The front sends AJAX requests (using FormData) to the backend which is Bottle running on Python 3.7. Text content is saved to a MySQL database and images are saved to a folder on the server. Everything works fine.
Image processing and PIL/Pillow
Now, I want to enable processing of uploaded images to standardise them (I need them all resized and/or cropped to 700x400px).
I was hoping to use Pillow for this. My problem is creating a PIL Image object from the file object in Bottle. I cannot initialise a valid Image object.
Code
# AJAX sends request to this route
#post('/update')
def update():
# Form data
title = request.forms.get("title")
body = request.forms.get("body")
image = request.forms.get("image")
author = request.forms.get("author")
# Image upload
file = request.files.get("file")
if file:
extension = file.filename.split(".")[-1]
if extension not in ('png', 'jpg', 'jpeg'):
return {"result" : 0, "message": "File Format Error"}
save_path = "my/save/path"
file.save(save_path)
The problem
This all works as expected, but I cannot create a valid Image object with pillow for processing. I even tried reloading the saved image using the save path but this did not work either.
Other attempts
The code below did not work. It caused an internal server error, though I am having trouble setting up more detailed Python debugging.
path = save_path + "/" + file.filename
image_data = open(path, "rb")
image = Image.open(image_data)
When logged manually, the path is a valid relative URL ("../domain-folder/images") and I have checked that I am definitely importing PIL (Pillow) correctly using PIL.PILLOW_VERSION.
I tried adapting this answer:
image = Image.frombytes('RGBA', (128,128), image_data, 'raw')
However, I won’t know the size until I have created the Image object. I also tried using io:
image = Image.open(io.BytesIO(image_data))
This did not work either. In each case, it is only the line trying to initialise the Image object that causes problems.
Summary
The Bottle documentation says the uploaded file is a file-like object, but I am not having much success in creating an Image object that I can process.
How should I go about this? I do not have a preference about processing before or after saving. I am comfortable with the processing, it is initialising the Image object that is causing the problem.
Edit - Solution
I got this to work by adapting the answer from eatmeimadanish. I had to use a io.BytesIO object to save the file from Bottle, then load it with Pillow from there. After processing, it could be saved in the usual way.
obj = io.BytesIO()
file.save(obj) # This saves the file retrieved by Bottle to the BytesIO object
path = save_path + "/" + file.filename
# Image processing
im = Image.open(obj) # Reopen the object with PIL
im = im.resize((700,400))
im.save(path, optimize=True)
I found this from the Pillow documentation about a different function that may also be of use.
PIL.Image.frombuffer(mode, size, data, decoder_name='raw', *args)
Note that this function decodes pixel data only, not entire images.
If you have an entire image file in a string, wrap it in a BytesIO object, and use open() to load it.
Use StringIO instead.
From PIL import Image
try:
import cStringIO as StringIO
except ImportError:
import StringIO
s = StringIO.StringIO()
#save your in memory file to this instead of a regular file
file = request.files.get("file")
if file:
extension = file.filename.split(".")[-1]
if extension not in ('png', 'jpg', 'jpeg'):
return {"result" : 0, "message": "File Format Error"}
file.save(s)
im = Image.open(s)
im.resize((700,400))
im.save(s, 'png', optimize=True)
s64 = base64.b64encode(s.getvalue())
From what I understand, you're trying to resize the image after it has been saved locally (note that you could try to do the resize before it is saved). If this is what you want to achieve here, you can open the image directly using Pillow, it does the job for you (you do not have to open(path, "rb"):
image = Image.open(path)
image.resize((700,400)).save(path)
I want to do a practice that consists of capturing webs in jpg, but it did not just work (I am newbie), this is the code I use.
import numpy as np
import urllib
import cv2
def url_to_image("http://www.hereiputweb.com"):
resp = urllib.urlopen("http://www.hereiputweb.com")
image = np.asarray(bytearray(resp.read()), dtype="uint8")
image = cv2.imdecode(image, cv2.IMREAD_COLOR)
return image
The code I got it from a manual but it gives me fault in the line:
def url_to_image("http://www.hereiputweb.com"):
I think I indicated the web incorrectly, very far I should not be .. tried several forms but nothing .. what do I do wrong?
regards
There is a really brief tutorial (https://docs.python.org/3/tutorial/).
The relevant part would be https://docs.python.org/3/tutorial/controlflow.html#defining-functions
So, you should define your function as follows:
def url_to_image(url):
resp = urllib.urlopen(url)
image = np.asarray(bytearray(resp.read()), dtype="uint8")
image = cv2.imdecode(image, cv2.IMREAD_COLOR)
return image
I have not checked the implementation works ;)
Then you can use your function:
url = "http://www.hereiputweb.com"
my_image = url_to_image(url)
The problem is not with your implementation, it's with your URL!
This method require a functioning URL that returns an image. The URL you're using is not an image.
Try using an URL of an image (e.g: some URLs that end with .jpg) and it shall work!
Remember that the URL must be on-line!
I have a stored picture on my computer. I open it using the Python Image module. Then I crop this image into several pieces using this module. To conclude, I would like to upload the image via a POST request on a website.
Because that small images are PIL object, I converted each of them into StringIO to be able to send the form without having to save them on my PC.
Unfortunately, I encounter an error, whereas if the images are physically stored on my PC, there is no problem. I do not understand why.
You can visit the website here: http://www.noelshack.com/api.php
This is a very basic API that returns the link to the uploaded picture.
In my case, the problem is that returns nothing, at the end of the second image (no problem for the first).
Here is the programming code to crop the image into 100 pieces.
import requests
import Image
import StringIO
import os
image = Image.open("test.jpg")
width, height = image.size
images = []
for i in range(10):
for j in range(10):
crop = image.crop((i * 10, j * 10, (i + 1) * 10, (j + 1) * 10))
images.append(crop)
The function to upload an image:
def upload(my_file):
api_url = 'http://www.noelshack.com/api.php'
r = requests.post(api_url, files={'fichier': my_file})
if not 'www.noelshack.com' in r.text:
raise Exception(r.text)
return r.text
Now we have two possibilities. The first is to save each of the 100 images on disk and upload them.
if not os.path.exists("directory"):
os.makedirs("directory")
i = 0
for img in images:
img.save("directory/" + str(i) + ".jpg")
i += 1
for file in os.listdir("directory"):
with open("directory/" + file, "rb") as f:
print upload(f)
It works like a charm, but it is not very convenient. So, I thought to use StringIO.
for img in images:
my_file = StringIO.StringIO()
img.save(my_file, "JPEG")
print upload(my_file.getvalue())
# my_file.close() -> Does not change anything
The first link is printed, but the function raise the exception then.
I think the problem lies in the img.save(), because the same kind of for loop was not working to save to disk and then upload. In addition, if you add a time.sleep(1) between the uploads, it seems to work.
Any help would be welcome please, because I'm really stuck.
my_file.getvalue() returns a string. What you need is a file-like object, which my_file already is. And file like objects have a cursor, so to speak, which says where to read from or write to. So, if you do my_file.seek(0) before the upload, it should get fixed.
modify the code to:
for img in images:
my_file = StringIO.StringIO()
img.save(my_file, "JPEG")
my_file.seek(0)
print upload(my_file)