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

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

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)

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

Image processing after upload with Python Bottle

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)

ImageField from Django to PIL Image to send via HttpResponse

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

How to get image from response and save it in models.ImageField

Asking this I realize that topic is quite common and perhaps was discussed many times. But it still not clear for me, so I'm trying to get image file from response, resize it and save it in model. I deal with the error:
AttributeError at /saveimage/
'InMemoryUploadedFile' object has no attribute 'get'
my code is like so:
import Image
import cStringIO
from django.core.files.uploadedfile import InMemoryUploadedFile
def get_and_save_image():
file = cStringIO.StringIO()
size = (200,200)
color = (255,0,0,0)
image = Image.new("RGBA", size, color)
image.save(file, format='JPEG')
image_file = InMemoryUploadedFile(file, None, 'foo.jpg', 'jpeg', None, None)
image_file.seek(0)
return image_file
Http response:
FILES:
some_image = <InMemoryUploadedFile: image.jpg (image/jpeg)>
Is it the proper way to save image into ImageField? Thanks in advance.
Here are two ways you can do this:
Save the image in a models.ImageField, and then return the image's url in the response, and you can create the image element in javascript and add the url.
Encode the image's data in base64 and return it as a string, which you can easily use in the front end. Here's an example of this:
Backend:
from PIL import Image
from io import BytesIO
import base64
def get_and_save_image():
size = (200,200)
color = (255,0,0,0)
image = Image.new("RGBA", size, color)
img_buffer = BytesIO()
image.save(img_buffer, format='JPEG')
response_string = base64.b64encode(img_buffer.getvalue())
return response_string
Front end:
<img src="data:image/jpg;base64,{{ THE_RESPONSE_STRING }}">

Categories

Resources