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.
Related
I am new in python and django, right now I would like to upload an image from postman (content type: form-data) and then save it in server. So far I have doing this
#csrf_exempt
def detector(request):
data = {"success": False}
if request.method == "POST":
print("oke0")
# check to see if an image was uploaded
if request.FILES.get("image", None) is not None:
...# here I would like to save the image
else:
return JsonResponse(data)
return JsonResponse(data)
so the flow is: upload image from postman and then directory 'media' will be created and the image will be stored there
so far I have been following this https://www.geeksforgeeks.org/python-uploading-images-in-django/ but I don't know how to try it in postman, anyone knows step by step to save image file to server in django?
Here is the code, but keep in mind that I didn't test it, since I wrote it on the fly.
I also assume you use Python3.2+, since you won't be able to use os.makedirs with exist_ok flag.
If you're using Python3.5+, you can also replace that code with something like Pathlib which can create folder, but won't raise an exception like so:
import pathlib
pathlib.Path('save_path').mkdir(parents=True, exist_ok=True)
and you can replace the os.makedirs in the code bellow with this call in that case.
import os
import uuid
#csrf_exempt
def detector(request):
data = {"success": False}
if request.method == "POST":
print("oke0")
if request.FILES.get("image", None) is not None:
#So this would be the logic
img = request.FILES["image"]
img_extension = os.path.splitext(img.name)[1]
# This will generate random folder for saving your image using UUID
save_path = "static/" + str(uuid.uuid4())
if not os.path.exists(save_path):
# This will ensure that the path is created properly and will raise exception if the directory already exists
os.makedirs(os.path.dirname(save_path), exist_ok=True)
# Create image save path with title
img_save_path = "%s/%s%s" % (save_path, "image", img_extension)
with open(img_save_path, "wb+") as f:
for chunk in img.chunks():
f.write(chunk)
data = {"success": True}
else:
return JsonResponse(data)
return JsonResponse(data)
Now, that's an easy part.
For Postman, simply follow this answer -> https://stackoverflow.com/a/49210637/1737811
Hope this answers your question.
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?
Following multiple suggestions from other StackOverflow questions and the mutagen documentation, I was able to come up with code to get and set every ID3 tag in both MP3 and MP4 files. The issue I have is with setting the cover art for M4B files.
I have reproduced the code exactly like it is laid out in this answer:
Embedding album cover in MP4 file using Mutagen
But I am still receiving errors when I attempt to run the code. If I run the code with the 'albumart' value by itself I receive the error:
MP4file.tags['covr'] = albumart
Exception has occurred: TypeError
can't concat int to bytes
However, if I surround the albumart variable with brackets like is shown in the aforementioned StackOverflow question I get this output:
MP4file.tags['covr'] = [albumart]
Exception has occurred: struct.error
required argument is not an integer
Here is the function in it's entirety. The MP3 section works without any problems.
from mutagen.mp3 import MP3
from mutagen.mp4 import MP4, MP4Cover
def set_cover(filename, cover):
r = requests.get(cover)
with open('C:/temp/cover.jpg', 'wb') as q:
q.write(r.content)
if(filename.endswith(".mp3")):
MP3file = MP3(filename, ID3=ID3)
if cover.endswith('.jpg') or cover.endswith('.jpeg'):
mime = 'image/jpg'
else:
mime = 'image/png'
with open('C:/temp/cover.jpg', 'rb') as albumart:
MP3file.tags.add(APIC(encoding=3, mime=mime, type=3, desc=u'Cover', data=albumart.read()))
MP3file.save(filename)
else:
MP4file = MP4(filename)
if cover.endswith('.jpg') or cover.endswith('.jpeg'):
cover_format = 'MP4Cover.FORMAT_JPEG'
else:
cover_format = 'MP4Cover.FORMAT_PNG'
with open('C:/temp/cover.jpg', 'rb') as f:
albumart = MP4Cover(f.read(), imageformat=cover_format)
MP4file.tags['covr'] = [albumart]
I have been trying to figure out what I am doing wrong for two days now. If anyone can help me spot the problem I would be in your debt.
Thanks!
In the source code of mutagen at the location where the exception is being raised I've found the following lines:
def __render_cover(self, key, value):
...
for cover in value:
try:
imageformat = cover.imageformat
except AttributeError:
imageformat = MP4Cover.FORMAT_JPEG
...
Atom.render(b"data", struct.pack(">2I", imageformat, 0) + cover))
...
There key is the name for the cover tag and value is the data read from the image, wrapped into an MP4Cover object. Well, it turns out that if you iterates over an MP4Cover object, as the above code does, the iteration yields one byte of the image per iteration as int.
Moreover, in Python version 3+, struct.pack returns an object of type bytes. I think the cover argument was intended to be the collection of bytes taken from the cover image.
In the code you've given above the bytes of the cover image are wrapped inside an object of type MP4Cover that cannot be added to bytes as done in the second argument of Atom.render.
To avoid having to edit or patch the mutagen library source code, the trick is converting the 'MP4Cover' object to bytes and wrapping the result inside a collection as shown below.
import requests
from mutagen.mp3 import MP3
from mutagen.mp4 import MP4, MP4Cover
def set_cover(filename, cover):
r = requests.get(cover)
with open('C:/temp/cover.jpg', 'wb') as q:
q.write(r.content)
if(filename.endswith(".mp3")):
MP3file = MP3(filename, ID3=ID3)
if cover.endswith('.jpg') or cover.endswith('.jpeg'):
mime = 'image/jpg'
else:
mime = 'image/png'
with open('C:/temp/cover.jpg', 'rb') as albumart:
MP3file.tags.add(APIC(encoding=3, mime=mime, type=3, desc=u'Cover', data=albumart.read()))
MP3file.save(filename)
else:
MP4file = MP4(filename)
if cover.endswith('.jpg') or cover.endswith('.jpeg'):
cover_format = 'MP4Cover.FORMAT_JPEG'
else:
cover_format = 'MP4Cover.FORMAT_PNG'
with open('C:/temp/cover.jpg', 'rb') as f:
albumart = MP4Cover(f.read(), imageformat=cover_format)
MP4file.tags['covr'] = [bytes(albumart)]
MP4file.save(filename)
I've also added MP4file.save(filename) as the last line of the code to persists the changes done to the file.
I am trying to add some validation for user uploaded files. This requires running through a custom script I made called "sumpin", which only takes a filepath as a variable and sends back JSON data that will verify. Everything inside my script is working independently, putting it together where the error occurs.
Since this is file validation, I decided to expand my file_extension validator that was already working.
models.py
from allauthdemo.fileuploadapp.slic3rcheck import sumpin
def user_directory_path_files(instance, filename):
return os.path.join('uploads', str(instance.objectid), filename)
def validate_file_extension(value):
ext = os.path.splitext(value.name)[1]
valid_extensions = ['.stl','.STL']
if not ext in valid_extensions:
raise ValidationError(u'Please upload a .stl file type only')
data = sumpin(value.path)
print (data)
class subfiles(models.Model):
STL = models.FileField(_('STL Upload'),
upload_to=user_directory_path_files, validators=[validate_file_extension])
The error that I get is that the path (value.path) is not valid.
This is the incorrect path because the upload_to tag must change this at a later point. This may be obvious, but I also need to have the file at the filepath location when my script is called. So essentially my questions are...
How can pass the "upload_to" path into my validator to run through my custom script?
Is there a better method to deal with uploaded files, like in the main class with a "save" or "clean" function?
I've found my own answer, but I'll post it here in case someone runs across this issue in the future.
I was incorrect, a validator wouldn't actually download the file. I need to use a file upload handler, which is shown below.
import os
from django.core.files.storage import default_storage
from allauthdemo.fileuploadapp.slic3rcheck import sumpin
def handle_uploaded_file(f):
with open(default_storage.path('tmp/'+f.name), 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
data = sumpin(default_storage.path('tmp/'+f.name))
os.remove(default_storage.path('tmp/'+f.name))
return data
then I call this inside my views.py.
from allauthdemo.fileuploadapp.uploadhandler import handle_uploaded_file
#login_required
def STLupload(request):
# Handle file upload
if request.method == 'POST':
formA = ObjectUp(request.POST, request.FILES)
if formA is_valid():
data = handle_uploaded_file(request.FILES['STL'])
This will return whatever I called to return within handle_upload_file, which worked perfect for my issues. Hopefully someone will find this useful the future.
I am using a Flask app to receive a mutipart/form-data request with an uploaded file (a video, in this example).
I don't want to save the file in the local directory because this app will be running on a server, and saving it will slow things down.
I am trying to use the file object created by the Flask request.files[''] method, but it doesn't seem to be working.
Here is that portion of the code:
#bp.route('/video_upload', methods=['POST'])
def VideoUploadHandler():
form = request.form
video_file = request.files['video_data']
if video_file:
s3 = boto3.client('s3')
s3.upload_file(video_file.read(), S3_BUCKET, 'video.mp4')
return json.dumps('DynamoDB failure')
This returns an error:
TypeError: must be encoded string without NULL bytes, not str
on the line:
s3.upload_file(video_file.read(), S3_BUCKET, 'video.mp4')
I did get this to work by first saving the file and then accessing that saved file, so it's not an issue with catching the request file. This works:
video_file.save(form['video_id']+".mp4")
s3.upload_file(form['video_id']+".mp4", S3_BUCKET, form['video_id']+".mp4")
What would be the best method to handle this file data in memory and pass it to the s3.upload_file() method? I am using the boto3 methods here, and I am only finding examples with the filename used in the first parameter, so I'm not sure how to process this correctly using the file in memory. Thanks!
First you need to be able to access the raw data sent to Flask. This is not as easy as it seems, since you're reading a form. To be able to read the raw stream you can use flask.request.stream, which behaves similarly to StringIO. The trick here is, you cannot call request.form or request.file because accessing those attributes will load the whole stream into memory or into a file.
You'll need some extra work to extract the right part of the stream (which unfortunately I cannot help you with because it depends on how your form is made, but I'll let you experiment with this).
Finally you can use the set_contents_from_file function from boto, since upload_file does not seem to deal with file-like objects (StringIO and such).
Example code:
from boto.s3.key import Key
#bp.route('/video_upload', methods=['POST'])
def VideoUploadHandler():
# form = request.form <- Don't do that
# video_file = request.files['video_data'] <- Don't do that either
video_file_and_metadata = request.stream # This is a file-like object which does not only contain your video file
# This is what you need to implement
video_title, video_stream = extract_title_stream(video_file_and_metadata)
# Then, upload to the bucket
s3 = boto3.client('s3')
bucket = s3.create_bucket(bucket_name, location=boto.s3.connection.Location.DEFAULT)
k = Key(bucket)
k.key = video_title
k.set_contents_from_filename(video_stream)