imghdr.what() does not work on a BytesIO buffer - python

I am working on a feature that allows users to upload images to a Python-Flask web app. The uploaded image is converted into a BytesIO buffer and is never saved to disk. I want to use imghdr.what() to determine the image type (png, jpg, etc.) and see if it is a format that users are allowed to upload. If the format is not allowed, the upload will be rejected.
Following the imghdr.what() documentation, I wrote the below code,
image_data.seek(0)
image_type = imghdr.what(None, h=image_data.read())
image_data.seek(0)
Unfortunately,when I call this with a png image, it returns None for image_type. I tried the below variations with the same image.
image_data.seek(0)
image_type = imghdr.what('', h=image_data.read())
image_data.seek(0)
Again, the above returned None for image_type.
image_data.seek(0)
image_type = imghdr.what(None, h=image_data)
image_data.seek(0)
The above returns an error, TypeError: '_io.BytesIO' object is not subscriptable
image_data.seek(0)
image_type = imghdr.what('', h=image_data.read)
image_data.seek(0)
The above returns the same error, TypeError: '_io.BytesIO' object is not subscriptable
Here is the code from conftest.py where I create the mock png image,
#pytest.fixture
def test_image_BytesIO():
test_dir = os.path.dirname(os.path.realpath(__file__))
local_path = os.path.join(test_dir, 'images/204Cat.png')
img_bytes = Pimage.open(local_path).tobytes()
return BytesIO(img_bytes)
I've already looked at these resources:
Examples of how to use imghdr.what()
Determine the type of an image in Python using imghdr
Determing the type of an image using Python imghdr
TLDR: How do I get imghdr.what() to work with an image that is in BytesIO format?

Related

Not able to save Image by downloading in python

I need to download a png file from a website and save the same in local directory .
The code is as below :
import pytesseract
from PIL import Image
from pathlib import Path
k = requests.get('https://somewebsite.com/somefile.png',stream =True)
Img=Image.open(k) # <----
Img.save("/new.png")
while executing it in JupyterNotebook
If I execute, i always get an error "response object has no attribute seek"
On the other hand , if I change the code to
Img= Image.open(k.raw), it works fine
I need to understand why it is so
You can save image data from a link using open() and write() functions:
import requests
URL = "https://images.unsplash.com/photo-1574169207511-e21a21c8075a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=880&q=80"
name = "IMG.jpg" #The name of the image once saved
Picture_request = requests.get(URL)
if Picture_request.status_code == 200:
with open(name, 'wb') as f:
f.write(Picture_request.content)
Per pillow the docs:
:param fp: A filename (string), pathlib.Path object or a file object.
The file object must implement file.read,
file.seek, and file.tell methods,
and be opened in binary mode.
response itself is just the response object. Using response.raw implements read, seek, and tell.
However, you should use response.content to get the raw bytes of the image. If you want to open it, then use io.BytesIO (quick explanation here).
import requests
from PIL import Image
from io import BytesIO
URL = "whatever"
name = "image.jpg"
response = requests.get(URL)
mybytes = BytesIO()
mybytes.write(response.content) # write the bytes into `mybytes`
mybytes.seek(0) # set pointer back to the beginning
img = Image.open(mybytes) # now pillow reads from this io and gets all the bytes we want
# do things to img

Cannot open Image file got from request.files.get()

I am trying to make a face recognition API with Flask and deepface library.
But I am not able to open the image it's giving me different errors. Please give me a solution.
Code:
#app.route('/detect', methods=['POST'])
def recognize():
image_path = request.files.get('image')
try:
analysis = DeepFace.analyze(image_path)
return jsonify(analysis)
except Exception as e:
return jsonify({'error': str(e)})
error
"error": "object of type 'FileStorage' has no len()"
Tried to open the image
with open(image_path, 'r') as image:
analysis = DeepFace.analyze(image)
return jsonify(analysis)
I get the error
{"errors": "expected str, bytes or os.PathLike object, not FileStorage"}
I think image_path holds object of type FileStorage which knows location of a file and does not hold actual image data. First you need to load that image file and then try to analyze it.
A werkzeug FileStorage object has several methods as I've documented in another answer.
Sounds like you need to save the image to a temporary location first, then do the analysis.
I would create a directory called uploads on your server, then set this in the app config:
app.config['UPLOAD_FOLDER'] = 'uploads'
Then in your route, use the uuid.uuid4 function to create a temporary filename. Something like:
from uuid import uuid4
#app.route('/detect', methods=['POST'])
def recognize():
tmp_fname = os.path.join(app.config['UPLOAD_FOLDER'], uuid4().__str__())
image = request.files['image']
image.save(tmp_fname)
try:
analysis = DeepFace.analyze(tmp_fname)
return jsonify(analysis)
except Exception as e:
return jsonify({'error': str(e)})
This leaves the saved images on the filesystem, so you may wish to do some cleanup on this directory, or do something like os.remove('tmp_fname') after running the analysis.
I'm not sure whether Deepface.analyze accepts a stream as its first argument. The README suggests it only accepts a filename as a string. You could try doing Deepface.analyze(image.stream) to avoid having to deal with saving the temporary file (keep everything in memory instead), but this may not be supported.

How to save image in-memory and upload using PIL?

I'm fairly new to Python. Currently I'm making a prototype that takes an image, creates a thumbnail out of it and and uploads it to the ftp server.
So far I got the get image, convert and resize part ready.
The problem I run into is that using the PIL (pillow) Image library converts the image is a different type than that can be used when uploading using storebinary()
I already tried some approaches like using StringIO or BufferIO to save the image in-memory. But I'm getting errors all the time. Sometimes the image does get uploaded but the file appears to be empty (0 bytes).
Here is the code I'm working with:
import os
import io
import StringIO
import rawpy
import imageio
import Image
import ftplib
# connection part is working
ftp = ftplib.FTP('bananas.com')
ftp.login(user="banana", passwd="bananas")
ftp.cwd("/public_html/upload")
def convert_raw():
files = os.listdir("/home/pi/Desktop/photos")
for file in files:
if file.endswith(".NEF") or file.endswith(".CR2"):
raw = rawpy.imread(file)
rgb = raw.postprocess()
im = Image.fromarray(rgb)
size = 1000, 1000
im.thumbnail(size)
ftp.storbinary('STOR Obama.jpg', img)
temp.close()
ftp.quit()
convert_raw()
What I tried:
temp = StringIO.StringIO
im.save(temp, format="png")
img = im.tostring()
temp.seek(0)
imgObj = temp.getvalue()
The error I'm getting lies on the line ftp.storbinary('STOR Obama.jpg', img).
Message:
buf = fp.read(blocksize)
attributeError: 'str' object has no attribute read
For Python 3.x use BytesIO instead of StringIO:
temp = BytesIO()
im.save(temp, format="png")
ftp.storbinary('STOR Obama.jpg', temp.getvalue())
Do not pass a string to storbinary. You should pass a file or file object (memory-mapped file) to it instead. Also, this line should be temp = StringIO.StringIO(). So:
temp = StringIO.StringIO() # this is a file object
im.save(temp, format="png") # save the content to temp
ftp.storbinary('STOR Obama.jpg', temp) # upload temp

Upload Image To Imgur After Resizeing In PIL

I am writing a script which will get an image from a link. Then the image will be resized using the PIL module and the uploaded to Imgur using pyimgur. I dont want to save the image on disk, instead manipulate the image in memory and then upload it from memory to Imgur.
The Script:
from pyimgur import Imgur
import cStringIO
import requests
from PIL import Image
LINK = "http://pngimg.com/upload/cat_PNG106.png"
CLIENT_ID = '29619ae5d125ae6'
im = Imgur(CLIENT_ID)
def _upload_image(img, title):
uploaded_image = im.upload_image(img, title=title)
return uploaded_image.link
def _resize_image(width, height, link):
#Retrieve our source image from a URL
fp = requests.get(link)
#Load the URL data into an image
img = cStringIO.StringIO(fp.content)
im = Image.open(img)
#Resize the image
im2 = im.resize((width, height), Image.NEAREST)
#saving the image into a cStringIO object to avoid writing to disk
out_im2 = cStringIO.StringIO()
im2.save(out_im2, 'png')
return out_im2.getvalue()
When I run this script I get this error: TypeError: file() argument 1 must be encoded string without NULL bytes, not str
Anyone has a solution in mind?
It looks like the same problem as this, and the solution is to use StringIO.
A common tip for searching such issues is to search using the generic part of the error message/string.

Django content type of an image file

I want to check a file type before uploading it by:
content = self.cleaned_data['picture']
content_type = content.content_type.split('/')[0]
When I upload the picture I get an error:
'NoneType' object has no attribute 'content_type'
What can be wrong here?
imghdr.what will return image format as string (gif, png etc.) if the image is valid, or None otherwise.
Usage:
import imghdr
imghdr.what(value)
In the above example value can be either a file(-like) object or a filename.

Categories