how to output a Image in Django web page with PIL Image - python

I use Image.new create a image object in a django view,
I want to show it in webpages.
x = Image.new('RGB',(400,400))
return HttpResponse(x.show(), mimetype="image/png")
doesn't work.
How can I covert a image object to a image raw binary?

You can embed base64 images into an <img src= so you could try converting your PIL image to base64.
from PIL import Image
import StringIO
x = Image.new('RGB',(400,400))
output = StringIO.StringIO()
x.save(output, "PNG")
contents = output.getvalue().encode("base64")
output.close()
return HttpResponse('<img src="data:image/png;base64,' + contents + ' />')

You have 2 options:
Save your image in some place your web server can serve it
Encode it with base64 and show it directly
The first option is the prefered one. The code should be something like:
x = Image.new() # The same stuff than yours
x.save(MEDIA_ROOT + "/generated_images/the_name_of_the_image.jpg", "JPEG")
return HttpResponse(
"<img src="%s/%s />" % (MEDIA_URL, "/generated_images/the_name_of_the_image.jpg")
)
If you want, you can read that in base64 (see: Encoding an image file with base64)
And display it:
base64_img = get_base_64()
return HttpResponse('<img alt="Embedded Image" src="data:image/jpeg;base64," % base64_img />')

There is also a third way to accomplish this by having the view return the raw image so that the view's url can be referenced in the src attribute of the image tag.
Building on the examples others have used, the view that returns a dynamically generated image would look something like this:
from PIL import Image
import StringIO
def dynamic_image(request):
image = Image.new('RGB', (400, 400))
output = StringIO.StringIO()
image.save(output, 'PNG')
contents = output.getvalue()
output.close()
return HttpResponse(contents, content_type='image/png')
urls.py would reference this as:
urlpatterns = [
path('image', views.dynamic_image, name="dynamic_image")
]
Finally in the HTML, reference the view in the IMG tag:
<div>
<img src="{% url 'dynamic_image' %}" />
</div>
There is nothing wrong with the other approaches but they focus on returning HTML when Django views can return more than just HTML.

Related

How to generate a PNG image in PIL and display it in Jinja2 template using FastAPI?

I have a FastAPI endpoint that is generating PIL images. I want to then send the resulting image as a stream to a Jinja2 TemplateResponse. This is a simplified version of what I am doing:
import io
from PIL import Image
#api.get("/test_image", status_code=status.HTTP_200_OK)
def test_image(request: Request):
'''test displaying an image from a stream.
'''
test_img = Image.new('RGBA', (300,300), (0, 255, 0, 0))
# I've tried with and without this:
test_img = test_img.convert("RGB")
test_img = test_img.tobytes()
base64_encoded_image = base64.b64encode(test_img).decode("utf-8")
return templates.TemplateResponse("display_image.html", {"request": request, "myImage": base64_encoded_image})
With this simple html:
<html>
<head>
<title>Display Uploaded Image</title>
</head>
<body>
<h1>My Image<h1>
<img src="data:image/jpeg;base64,{{ myImage | safe }}">
</body>
</html>
I've been working from these answers and have tried multiple permutations of these:
How to display uploaded image in HTML page using FastAPI & Jinja2?
How to convert PIL Image.image object to base64 string?
How can I display PIL image to html with render_template flask?
This seems like it ought to be very simple but all I get is the html icon for an image that didn't render.
What am I doing wrong? Thank you.
I used Mark Setchell's answer, which clearly shows what I was doing wrong, but still am not getting an image in html. My FastAPI is:
#api.get("/test_image", status_code=status.HTTP_200_OK)
def test_image(request: Request):
# Create image
im = Image.new('RGB',(1000,1000),'red')
im.save('red.png')
print(im.tobytes())
# Create buffer
buffer = io.BytesIO()
# Tell PIL to save as PNG into buffer
im.save(buffer, 'PNG')
# get the PNG-encoded image from buffer
PNG = buffer.getvalue()
print()
print(PNG)
base64_encoded_image = base64.b64encode(PNG)
return templates.TemplateResponse("display_image.html", {"request": request, "myImage": base64_encoded_image})
and my html:
<html>
<head>
<title>Display Uploaded Image</title>
</head>
<body>
<h1>My Image 3<h1>
<img src="data:image/png;base64,{{ myImage | safe }}">
</body>
</html>
When I run, if I generate a 1x1 image I get the exact printouts in Mark's answer. If I run this version, with 1000x1000 image, it saves a red.png that I can open and see. But in the end, the html page has the heading and the icon for no image rendered. I'm clearly doing something wrong now in how I send to html.
There are a couple of issues here. I'll make a new section for each to keep it clearly divided up.
If you want to send a base64-encoded PNG, you need to change your HTML to:
<img src="data:image/png;base64,{{ myImage | safe }}">
If you create an image of a single red pixel like this:
im = Image.new('RGB',(1,1),'red')
print(im.tobytes())
you'll get:
b'\xff\x00\x00'
That is not a PNG-encoded image, how could it be - you haven't told PIL that you want a PNG, or a JPEG, or a TIFF, so it cannot know. It is just giving you the 3 raw RGB pixels as bytes #ff0000.
If you save that image to disk as a PNG and dump it you will get:
im.save('red.png')
Then dump it:
xxd red.png
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
00000010: 0000 0001 0000 0001 0802 0000 0090 7753 ..............wS
00000020: de00 0000 0c49 4441 5478 9c63 f8cf c000 .....IDATx.c....
00000030: 0003 0101 00c9 fe92 ef00 0000 0049 454e .............IEN
00000040: 44ae 4260 82 D.B`.
You can now see the PNG signature at the start. So we need to create that same thing, but just in memory without bothering the disk:
import io
import base64
from PIL import image
# Create image
im = Image.new('RGB',(1,1),'red')
# Create buffer
buffer = io.BytesIO()
# Tell PIL to save as PNG into buffer
im.save(buffer, 'PNG')
Now we can get the PNG-encoded image from the buffer:
PNG = buffer.getvalue()
And if we print it, it will look suspiciously identical to the PNG on disk:
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDATx\x9cc\xf8\xcf\xc0\x00\x00\x03\x01\x01\x00\xc9\xfe\x92\xef\x00\x00\x00\x00IEND\xaeB`\x82'
Now you can base64-encode it and send it:
base64_encoded_image = base64.b64encode(PNG)
Note: I only made 1x1 for demonstration purposes so I could show you the whole file. Make it bigger than 1x1 when you test, or you'll never see it 😀
I used Mark Setchell's answer and comments to come up with this full code. I thought it useful to show what works:
import base64
from PIL import Image
#api.get("/test_image", status_code=status.HTTP_200_OK)
def test_image(request: Request):
# Create image
im = Image.new('RGB',(1000,1000),'red')
# Create buffer
buffer = io.BytesIO()
# Tell PIL to save as PNG into buffer
im.save(buffer, 'PNG')
# get the PNG-encoded image from buffer
PNG = buffer.getvalue()
# the only difference is the .decode("utf-8") added here:
base64_encoded_image = base64.b64encode(PNG).decode("utf-8")
return templates.TemplateResponse("display_image.html", {"request": request, "myImage": base64_encoded_image})
<html>
<head>
<title>Display Uploaded Image</title>
</head>
<body>
<h1>My Image 3<h1>
<img src="data:image/png;base64,{{ myImage | safe }}">
</body>
</html>
This included some troubleshooting from:
How to display a bytes type image in HTML/Jinja2 template using FastAPI?

Getting an image back from Lambda is not shown on windows outlook

I have a lambda function that returns an image
When creating a new email with an image that its source is the lambda function i was able to see the image on all of the existing mail clients including linux and mac outlook
but on windows outlook it is not shown, I get a red X sign with the text "The picture can't be displayed"
any ideas what should be changed in the lambda's code in order to be able to see the image on Windows Outlook as well?
My Code:
from PIL import Image
from io import BytesIO
from flask import Flask, request, send_file
#app.route('/test')
def test():
img = Image.new('RGB', (100, 100))
imgByteArr = BytesIO()
format = 'JPEG'
mimetype = 'image/jpg'
img.save(imgByteArr, format=format, quality=90)
imgByteArr.seek(0)
return send_file(imgByteArr, mimetype=mimetype)
The Image code in the email:
<img style="display:block" src="my-lambda..." alt="" width="500" height="auto" border="0">
The Email on Windows outlook:
HTML messages are rendered in Outlook by Word, and (at least in the older versions), it does not support msg tags with inlined image data.
You'd need to add the image as an attachment, set its Content-ID MIME header, and refer to that image in the HTML body through <img src="cid:MyContenttId">

How do I create a QR code in Python without saving it as an image

I am trying to make Qr Codes using Python on a Django applicaiton using this code :
def generate_qr_code (reference):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(reference)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white").convert('RGB')
filename = reference+".jpeg"
img.save("C:\\qrs\\"+filename)
Now, this function is called when I click on a "Generate Qr Code" Button. My problem is that I would like the Qr Code to be displayed on a new tab on my browser instead of it being saved as an image, as I only need to print them on paper at that moment and I have no need to keep the images.
Thank you for your help.
convert the image to base64 and show it in your html like this
import base64
b64 = base64.b64encode(image).decode("utf-8")
update:
ofc you don't need to save your image as png to use this feature, you can change the format in html and you can also change the image format without saving it to a file like this
After all, I managed to do so by using this simple line in my HTML:
<img id='barcode' src="https://api.qrserver.com/v1/create-qr-code/?data={{ref}}" alt="" title="{{ref}}" width="150" height="150"/>
You can use SVG format
import qrcode
import qrcode.image.svg
from io import BytesIO
def generate_qr_code (reference):
factory = qrcode.image.svg.SvgImage
qr_string = "sample text"
img = qrcode.make(qr_string, image_factory=factory, box_size=10)
stream = BytesIO()
img.save(stream)
context = {
'qrcode': stream.getvalue().decode()
}
return render(request, 'YOUR HTML.html', context)
and then you can use it in html file:
{{qrcode|safe}}

how to pass image from memory to flask variable

I want to generate a plot and save to memory and then pass it flask as a variable but I am stuck. I have written this code and it seems to work in google colab, when the function is called it generates the plot. However I want now to pass the variable buffer to flask render template but I am totally stuck
import io
def image_plot():
plt.figure()
my_ax = sns.violinplot(x=df_tweet["compound"])
plt.title('this is the twitter sentiment analysis for')
buffer = io.BytesIO()
my_ax.figure.savefig(buffer, format="png")
buffer.seek(0)
return buffer
return render_template("index.html", buffer=.....)
and the html part should be...
<body>
<img id="picture" src="{{ buffer }}">
</body>
I have figured out the following and it seems to work basing it this tutorial
https://buraksenol.medium.com/pass-images-to-html-without-saving-them-as-files-using-python-flask-b055f29908a
import io
import base64
plt.figure()
my_ax = sns.violinplot(x=df_tweet["compound"])
plt.title('this is saved to memory')
buffer = io.BytesIO()
my_ax.figure.savefig(buffer, format="png")
buffer.seek(0)
image_memory = base64.b64encode(buffer.getvalue())
return render_template("index.html", img_data=image_memory.decode('utf-8'))
on the html page
<img id="picture" src="data:image/png;base64,{{ img_data }}">

Python display a server-side image in a html

I have a image in the server folder. In my do_GET() function which handles the Get request I would like to send a image back. I choose to use the self.wfile.write(''). Can anyone tell me how to include the source of the image in to the img tag? Or is there a better way to do this? Thanks.
You can include the "source" of an image in the img tag using a data URI like this:
<img alt="Embedded Image" src="data:image/png;base64,<your base64 encoding here>" />
Generate the base64 string using the base64 python standard lib:
import base64
with open("image.png", "rb") as image:
encoded_string = base64.b64encode(image.read())

Categories