how to pass image from memory to flask variable - python

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 }}">

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?

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 display a saved,dynamically created image in django template?

I have created a plot in a view and saved the plot as a png inside the templates folder. But when I try to display this saved image using an <img> tag in a template html file, the image is not displayed.
Here is an image of my folder structure:
Folder Structure
This is how I save my plot inside a view:
def result(request):
if request.POST and request.FILES:
mycsv=pd.read_csv(request.FILES['csv_file'])
c=mycsv.X
#We divide by the class height(basically) so that Xi+1-Xi=1
x = [d / 5 for d in c]
n=len(x)
b=mycsv.Y
divi=np.sum(b)
f = [e / divi for e in b]
#Thus sum(f)=1, makes calculation of mean simpler
#PLOTTING BEGINS
fig = plt.figure()
ax = plt.subplot(111)
ax.plot(x, f)
plt.title('Pearson Type 1 ')
ax.legend()
#plt.show()
fig.savefig('polls/templates/polls/plot.png')
context = {'n':n,}
return render(request, 'polls/result.html', context)
else:
return HttpResponse("Form Not Submitted")
My result.html file where I try to get the image is:
<h1>Graph with {{n}} points</h1>
<img src='./plot.png' />
I'm running this on my localhost, is there a problem with permissions?
I've just started learning django and wanted to test this thing. Thank you for your help!
There are many things wrong about your approach but I'll try to give you some advice on how to proceed, although you may want to reconsider it.
First of all, saving your file to the templates directory won't make it available to your template. The templates directory is a special configuration that allows you to use Django's template loader, it doesn't load static files like your image.
You could save your image to static files and use the {% static %} template tag to recover it, but, again, it would be the wrong approach, as your image is not static content, it's created dynamically.
As you have no use for the data in the image after it's created, my recommendation would be to store the image in a temporary file using TempFile, or (if it's light enough) in memory using StringIO and then load those bytes in the context as base64
from StringIO import StringIO
import base64
img_in_memory = StringIO()
fig.savefig(img_in_memory, format="png") #dunno if your library can do that.
context['image'] = base64.b64encode(img_in_memory.getvalue())
Then, in your template you'd do something like:
<img src="data:image/png;base64,{{image}}" />
Or simply convert the image to string by
with open(original_image_path, "rb") as img_file:
base64_img = base64.b64encode(img_file.read()).decode('utf-8')
mimetype = mimetypes.guess_type(original_image_path)[0]
once done render this variable on HTML as
<img src="data:{{ mimetype }};base64,{{ base64_img }}"/>
It will handle both jpeg and png both.

Converting binary file into PIL Image datatype in Google App Engine

I am using Google App Engine with Python and Jinja for the templates. In my HTML template, I have this piece of code, which allows the user to choose a file (Image):
<form action="/step2" enctype="multipart/form-data" method="post">
<input type="file" name="datafile" size="40">
<input type="submit" value="upload" >
</form>
Upon post, I am able to get the image via self.request.get('datafile'), and this appears to be a binary data type ('str'). I put this into a database, and I can display this with:
self.response.out.write('<div><img src="img?img_id=%s"></img>' % userimages.key())
I am struggling to find a way to convert this into a PIL.Image data type, in order to do image processing with it. Thanks in advance!
Put the string in a StringIO object:
from cStringIO import StringIO
imgfile = StringIO(self.request.get('datafile'))
img = Image.open(imgfile)
All PIL needs is a file-like object; StringIO provides this source the actual data from the given string.
In the other direction, have PIL write to a StringIO object, but you do need to specify the format used:
imgfile = StringIO()
img.save(imgfile, format='PNG')
imagestring = imgfile.getvalue()

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

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.

Categories