ImageField from Django to PIL Image to send via HttpResponse - python

Hi I'm a newbie to Django (and I'm non-native English speaker). I'm trying to get an image (I get the id of the image using a form via POST) from the database(ImageField), crop it with PIL Image and send it via HttpResponse. It doesn't give an error but when I receive the image "piece.jpg", it is a corrupt file. If I open it using notepad I can only see:
<PIL.Image.Image image mode=RGB size=46x75 at 0x3DEA790>
This is my code in views.py:
from PIL import Image
def function(request):
if request.method == 'POST':
id = request.POST.get('id')
object = Document.objects.get(pk=id)
im = Image.open(object.docfile)
left = 10
top = 10
right = 100
bottom = 100
cropped_image = im.crop( (left, top, right, bottom) )
response = HttpResponse(cropped_image, content_type='image/jpg')
response['Content-Disposition'] = 'attachment; filename="piece.jpg"'
return response
I tried sending just "object.docfile" and it works. But if I try to send "im" (which is a PIL image), it doesnt work.
I need to send it via HttpResponse because I want it as a downloadable on that page.

HttpResponse expects string or an iterator. More details in docs:
https://docs.djangoproject.com/en/1.9/ref/request-response/#django.http.HttpResponse.init
Did you try something like this?
response = HttpResponse(mimetype='image/jpg')
cropped_image.save(response, "JPEG")
return response

This helped me to find the solution! Your code opens the image in the browser (which is ok, because I didnt say I wanted it as a download). To get a download dialog, the code is:
response = HttpResponse(content_type='image/jpg')
cropped_image.save(response, "JPEG")
response['Content-Disposition'] = 'attachment; filename="piece.jpg"'
return response
Thanks kind sir :)

Related

How to get response data consisting both image file and values in flask server post response?

Sending image to the flask server and do object detection on that and send back that image as a response and no of object detected variable value. Flask route function is as below
#app.route('/upload', methods=['POST'])
def upload_file():
file = request.files['image']
im = Image.open(file)
total_count = 0
for i in keypoints:
total_count = total_count + 1
im_with_keypoints = cv2.drawKeypoints(im, keypoints, np.array([]), (0, 0, 255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
file_object = io.BytesIO()
img = Image.fromarray(im_with_keypoints.astype('uint8'))
# write PNG in file-object
img.save(file_object, 'JPEG')
# move to beginning of file so `send_file()` it will read from start
file_object.seek(0)
return send_file(file_object, mimetype='image/jpeg')
when I do post a request by uploading an image I get the processed image back on POSTMAN
but I need to get the value of variable total_count along with the image.
I tried calling like this
return '{} {}'.format(total_count,send_file(file_object, mimetype='image/jpeg'))
but I got output as 11 <Response streamed [200 OK]>
where the image is stored as object form, is there a way to get the image along with the total_count value as a response?
Any suggestion or example on the way of getting both response which is Image and variable total_count?
mclslee's suggestions are quite creative/clever but they feel a bit hacky to me. Here are my ideas:
Base64-encoded image
Code stolen from https://stackoverflow.com/a/31826470/220652
from flask import jsonify
import base64
# other imports
# ...
base64_image = base64.b64encode(file_object.getvalue())
return jsonify(image=base64_image, total_count=total_count)
And it doesn't need to be JSON:
return f"{total_count} {base64_image}"
You haven't said anything about the client, it'd obviously need to know how to decode base64 data but most platforms including JS in the browser can do that.
Image with EXIF tag
Idea stolen from https://stackoverflow.com/a/10834464/220652
Requirement: https://pypi.org/project/exif/
import json
from exif import Image as ExifImage
# other imports
# ...
exif_image = ExifImage(file_object)
# Either ... or ... (no idea whether either works)
exif_image.maker_note = json.dumps(dict(total_count=total_count))
exif_image.user_comment = json.dumps(dict(total_count=total_count))
return send_file(io.BytesIO(exif_image.get_file()), mimetype='image/jpeg')
Here as well the client needs to know how to read EXIF tags from images and again most platforms including JS in the browser can do that.
Off the top of my head, there are a couple of things you could do, below are pseudocoded-ish versions:
Make a custom header in the response and grab it from the headers with whatever is calling this endpoint
from flask import make_response, send_file
...
response = make_response(send_file(file_object, mimetype='image/jpeg'))
response.headers['Total-Count'] = total_count #might have to make it a string
return response
or
Send the file with the total_count as part of the filename and parse it out from whatever is calling this endpoint
from flask import send_file
...
file_name = f"{total_count}_image.jpeg"
return send_file(file_object, mimetype="image/jpeg", as_attachment=True, attachment_filename=file_name)

read() image file with Python and send it to HttpResponse

I am trying to return an image to HttpResponse(image_data), but I get a page contain weird symbols.
I can use PIL to .show() image perfectly from that path, but I want to display the image in browser instead of paint or an photo viewer app.
image_data = open("media/hello.jpg", "rb").read()
return HttpResponse(image_data, content_type="image")
weird symbols: ÿØÿá�Exif��II*������������ÿì�Ducky�����d��ÿî�Adobe�dÀ���ÿÛ�„�
Make use of FileResponse
from django.http import FileResponse
def send_file(response):
img = open('media/hello.jpg', 'rb')
response = FileResponse(img)
return response

How can I fetch an image from a URL returned from the Unsplash API?

import requests
def linkFetch():
url = "https://api.unsplash.com/photos/random/?client_id=MyAccessKey"
response = requests.get(url)
data = response.json()["urls"]["raw"]
return data
def imageFetch(data):
print(data)
imageFetch(linkFetch())
Here my code runs and fetches a url for an image but how can I automatically open the photo in small window. the linkFetch() function actually gets the image link and I want imageFetch() to actually open the photo. I'm new to using apis so any help will be useful. I already tried using another request.get() but I may have used it incorrectly. Other solutions seem to want to download the image indefinitely where I want to just open it.
Note: MyAccessKey replaces my actual key
You have to first get the image data by sending a request then pass it to the pillow package to display the image.
from io import BytesIO
from PIL import Image
import requests
img_url = imageFetch(linkFetch())
response = requests.get(img_url)
img = Image.open(BytesIO(response.content))
img.show()

Return PNG image from Django views is damaged

I'm working on a Django(2) project in which I need to return a PNG image as HttpResponse when I return this image in the form of a zip archive it returns the image correctly, but when I return the PNG image directly it damaged the image.
Here's my code:
How it's writing the image:
img_resized = cv2.resize(seg_image, dsize)
cv2.imwrite(os.path.join(settings.BASE_DIR, 'img/MaskedImage.png'), img_resized)
How it's returning the Image:
response = HttpResponse(os.path.join(settings.BASE_DIR, 'img/MaskedImage.png'), content_type='image/png')
response['Content-Disposition'] = 'attachment; filename=MaskedImage.png'
return response
It returns an Image with the name MaskedImage.pn but the image is damaged, not able to open.
What can be wrong here?
Thanks in advance!
You will need to read the image data. You're currently returning a response with just the image path.
with open(os.path.join(settings.BASE_DIR, 'img/MaskedImage.png'), 'rb') as fp:
response = HttpResponse(fp.read(), content_type='image/png')
response['Content-Disposition'] = 'attachment; filename=MaskedImage.png'
return response

Serve a dynamically generated image with Django

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')

Categories

Resources