AttributeError: read in Python - python

I'm trying to get an image then turn it into an object Python understands then upload.
This is what I have tried:
# Read the image using .count to get binary
image_binary = requests.get(
"http://danealva143.files.wordpress.com/2014/03/2012-08-girls-920-26.jpg").content
string_buffer = io.BytesIO()
string_buffer.write(image_binary)
string_buffer.seek(0)
files = {}
files['image'] = Image.open(string_buffer)
payload = {}
results = requests.patch(url="http://127.0.0.1:8000/api/profile/94/", data=payload, files=files)
I get this error:
File "/Users/user/Documents/workspace/test/django-env/lib/python2.7/site-packages/PIL/Image.py", line 605, in __getattr__
raise AttributeError(name)
AttributeError: read
Why?

You cannot post a PIL.Image object; requests expects a file object.
If you are not altering the image, there is no point in loading the data into an Image object either. Just send the image_binary data instead:
files = {'image': image_binary}
results = requests.patch(url="http://127.0.0.1:8000/api/profile/94/", data=payload, files=files)
You may want to include the mime-type for the image binary:
image_resp = requests.get(
"http://danealva143.files.wordpress.com/2014/03/2012-08-girls-920-26.jpg")
files = {
'image': (image_resp.url.rpartition('/')[-1], image_resp.content, image_resp.headers['Content-Type'])
}
If you actually wanted to manipulate the image, you'll first have to save the image back to a file object:
img = Image.open(string_buffer)
# do stuff with `img`
output = io.BytesIO()
img.save(output, format='JPEG') # or another format
output.seek(0)
files = {
'image': ('somefilename.jpg', output, 'image/jpeg'),
}
The Image.save() method takes an arbitrary file object to write to, but because there is no filename in that case to take the format from, you'll have to manually specify the image format to write. Pick from the supported image formats.

Related

how to combine pdf files into one from a tempfile in Python

I have a python code that creates a temporary pdf files from tableau views and sends it to the slack channel separately.
I want to combine them together into one file but I can't figure out how to do it.
I am fairly new to python and would really appreciate some help in how to use PdfFileMerger in the code below.
i've tried to use
merger.append(f)
after f variable but it doesn't work giving me ar error ** AttributeError: 'dict' object has no attribute 'seek'
** what should I put in brackets?
for view_item in all_views :
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=True) as temp_file:
#server.views.populate_image(view_item, req_options=image_req_option)
server.views.populate_pdf(view_item, req_options = pdf_req_option)
print('got the image')
temp_file.write(view_item.pdf)
temp_file.file.seek(0)
print('in the beginnign again')
f = {'file': (temp_file.name,temp_file, 'pdf')}
merger.append(f)
response = requests.post(url='https://slack.com/api/files.upload', data=
{'token': bot_token, 'channels': slack_channels[0], 'media': f,'title': '{} {}'.format(view_item.name, yesterday), 'initial_comment' :''},
headers={'Accept': 'application/json'}, files=f)
print('the image is in the channel')
You'll need to feed PdfFileMerger the file objects, like so, not a dict.
Since PdfFileMerger will do things in-memory anyway, there's no need to write to tempfiles on the disk, a BytesIO in memory will do fine.
import io
merger = PdfFileMerger()
for view_item in all_views:
server.views.populate_pdf(view_item, req_options=pdf_req_option)
# Write retrieved data into memory file
tf = io.BytesIO()
tf.write(view_item.pdf)
tf.seek(0)
# Add it to the merger
merger.append(tf)
# Write merged data into memory file
temp_file = io.BytesIO()
merger.write(temp_file)
temp_file.seek(0)
f = {'file': ('merged.pdf', temp_file, 'pdf')}
# Slack stuff here...

PIL Image as Bytes with BytesIO to prevent hard disk saving

Problematic
I have a PIL Image and i want to convert it to a bytes array. I can't save the image on my hard disk so i can't use the default open(file_path, 'rb') function.
What i tried
To overturn this problem i'm trying to use the io library doing this :
buf = io.BytesIO()
image.save(buf, format='JPEG')
b_image = buf.getvalue()
Considering image as a functional PIL Image.
the "b_image" will be used as argument for the Microsoft Azure cognitives services function read_in_stream()
If we look in the documentation, we can see that this function image argument have to be :
image
xref:Generator
Required
An image stream.
Documentation available here
The issue
When i execute it i got the error :
File "C:...\envs\trainer\lib\site-packages\msrest\service_client.py", line 137, in stream_upload
chunk = data.read(self.config.connection.data_block_size)
AttributeError: 'bytes' object has no attribute 'read'
There is no error in the client authentification or at another point because when i give as parameter an image imported with this line :
image = open("./1.jpg", 'rb')
Everything is working correctly..
Sources
I also saw this post that explains exactly what i want to do but in my case it's not working. Any idea would be appreciated.
When we use the method read_in_stream, we need to provide a stream. But the code BytesIO.getvalue will return the content of the stream as string or bytes. So please update code as below
buf = io.BytesIO()
image.save(buf, format='JPEG')
computervision_client.read_in_stream(buf)
For more details, please refer to here
Update
Regarding the issue, I suggest you use rest API to implement your need.
import io
import requests
from PIL import Image
import time
url = "{your endpoint}/vision/v3.1/read/analyze"
key = ''
headers = {
'Ocp-Apim-Subscription-Key': key,
'Content-Type': 'application/octet-stream'
}
// process image
...
with io.BytesIO() as buf:
im.save(buf, 'jpeg')
response = requests.request(
"POST", url, headers=headers, data=buf.getvalue())
# get result
while True:
res = requests.request(
"GET", response.headers['Operation-Location'], headers=headers)
status = res.json()['status']
if status == 'succeeded':
print(res.json()['analyzeResult'])
break
time.sleep(1)

Python flask ajax save decoded base64 image to server temporarily

I am resizing images client side before sending them to my flask app.
The resized image, which is drawn into a canvas to be resized, is sent via a POST request.
In my app the image is decoded via base64:
def resize_image(item):
content = item.split(';')[1]
image_encoded = content.split(',')[1]
body = base64.decodestring(image_encoded.encode('utf-8'))
return body
The imagedata is stored as type String in the body variable. I can save the data to my local machine and it works:
filename = 'some_image.jpg'
with open(filename, 'wb') as f:
print "written"
f.write(body)
What I need is to upload the resized image to AWS3. On one point I need to read() the image contents, but until the image is saved somewhere as a file it is still a String, so it fails:
file_data = request.values['a']
imagedata = resize_image(file_data)
s3 = boto.connect_s3(app.config['MY_AWS_ID'], app.config['MY_AWS_SECRET'], host='s3.eu-central-1.amazonaws.com')
bucket_name = 'my_bucket'
bucket = s3.get_bucket(bucket_name)
k = Key(bucket)
# fails here
file_contents = imagedata.read()
k.key = "my_images/" + "test.png"
k.set_contents_from_string(file_contents)
Unless there is an other solution, I thought I save the image temporarily to my server (Heroku) and upload it and then delete it, how would this work? Deleting afterwards is important here!
set_contents_from_string takes a string as a parameter, you could probably just pass your image string data directly to it for upload to S3
Solution:
Delete this part:
file_contents = imagedata.read()
Use imagedata directly here:
k.set_contents_from_string(imagedata)
If you need to call .read() on your data, but don't need save file on disk use StringIO:
import StringIO
output = StringIO.StringIO()
output.write('decoded image')
output.seek(0)
output.read()
Out[1]: 'decoded image'

Correct way for uploading image bytes to cloudinary

I am using http://cloudinary.com/documentation/image_upload_api_reference as reference.
There are two cases in which I want to upload the files to cloudinary.
Upload image by directly giving url link.
Upload image bytes by taking them from different source.
I could solve case 1, but had trouble in 2nd. I am pasting my code flow below for reference.
import cloudinary
import cloudinary.uploader
from io import BytesIO
from StringIO import StringIO
def upload_image_to_cloudinary(img_tag):
logging.debug("Uploading Image to cloudinary : %s"%img_tag)
if 'src' not in img_tag.attrs:
del img_tag
return
img_src = img_tag['src']
if img_src.startswith('/blob'):
quip_client = pgquip.get_client()
blob_ids = img_src.split('/')
blob_response = quip_client.get_blob(blob_ids[2], blob_ids[3])
img_src_str = blob_response.read() # this returns str object.
# img_src = BytesIO(img_src_str)
img_src = StringIO(img_src_str)
cloudinary_response = cloudinary.uploader.upload_image(
img_src,
use_filename=True,
folder="/pagalguy/articles",
width=546,
crop="limit"
)
img_tag['src'] = cloudinary_response.metadata.get("url")
return img_tag
In case where img_src is a image blob str returned by another api, I passed it as file param mentioned in cloudinary doc in a very similar way as any external image url for eg: https://media.licdn.com/mpr/mpr/shrinknp_400_400/AAEAAQAAAAAAAAIkAAAAJGRhNzJiYjY1LTUxOTctNDI4NC1hOGIwLWQ1OTVlNmZlZmVmYw.jpg
And, for checking how generic upload flows work like boto for s3, I check below repo code.
Refered https://github.com/boto/boto/blob/develop/boto/vendored/six.py#L633 this too.
Error Log:
Invalid URL for upload
Traceback (most recent call last):
File "/base/data/home/apps/s~pagalguy-staging/namita:v1.397698162588746989/articleslib/article_util.py", line 68, in upload_images_n_publish
tag = image_util.upload_image_to_cloudinary(tag)
File "/base/data/home/apps/s~pagalguy-staging/namita:v1.397698162588746989/api/image_util.py", line 133, in upload_image_to_cloudinary
crop="limit"
File "/base/data/home/apps/s~pagalguy-staging/namita:v1.397698162588746989/libs/cloudinary/uploader.py", line 23, in upload_image
result = upload(file, **options)
File "/base/data/home/apps/s~pagalguy-staging/namita:v1.397698162588746989/libs/cloudinary/uploader.py", line 17, in upload
return call_api("upload", params, file = file, **options)
File "/base/data/home/apps/s~pagalguy-staging/namita:v1.397698162588746989/libs/cloudinary/uploader.py", line 226, in call_api
raise Error(result["error"]["message"])
Error: Invalid URL for upload
Finally I don't know which is the correct way to upload image bytes to cloudinary.
Your img_src parameter, which represents file, should be populated with either a byte array buffer (bytearray) or a Base64 URI. You can try something like:
with open(img_src_str, "rb") as imageFile:
f = imageFile.read()
img_src = bytearray(f)
cloudinary_response = cloudinary.uploader.upload(
img_src,
...
)
That API can upload bytes, so if you are uploading _io.BytesIO, you may just use .getvalue() method to your bytesIO object like uploader.upload(image_stream.getvalue(), public_id = filename)

PIL cannot identify image file for io.BytesIO object

I am using the Pillow fork of PIL and keep receiving the error
OSError: cannot identify image file <_io.BytesIO object at 0x103a47468>
when trying to open an image. I am using virtualenv with python 3.4 and no installation of PIL.
I have tried to find a solution to this based on others encountering the same problem, however, those solutions did not work for me. Here is my code:
from PIL import Image
import io
# This portion is part of my test code
byteImg = Image.open("some/location/to/a/file/in/my/directories.png").tobytes()
# Non test code
dataBytesIO = io.BytesIO(byteImg)
Image.open(dataBytesIO) # <- Error here
The image exists in the initial opening of the file and it gets converted to bytes. This appears to work for almost everyone else but I can't figure out why it fails for me.
EDIT:
dataBytesIO.seek(0)
does not work as a solution (tried it) since I'm not saving the image via a stream, I'm just instantiating the BytesIO with data, therefore (if I'm thinking of this correctly) seek should already be at 0.
(This solution is from the author himself. I have just moved it here.)
SOLUTION:
# This portion is part of my test code
byteImgIO = io.BytesIO()
byteImg = Image.open("some/location/to/a/file/in/my/directories.png")
byteImg.save(byteImgIO, "PNG")
byteImgIO.seek(0)
byteImg = byteImgIO.read()
# Non test code
dataBytesIO = io.BytesIO(byteImg)
Image.open(dataBytesIO)
The problem was with the way that Image.tobytes()was returning the byte object. It appeared to be invalid data and the 'encoding' couldn't be anything other than raw which still appeared to output wrong data since almost every byte appeared in the format \xff\. However, saving the bytes via BytesIO and using the .read() function to read the entire image gave the correct bytes that when needed later could actually be used.
image = Image.open(io.BytesIO(decoded))
# File "C:\Users\14088\anaconda3\envs\tensorflow\lib\site-packages\PIL\Image.py", line 2968, in open
# "cannot identify image file %r" % (filename if filename else fp)
# PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x000002B733BB11C8>
===
I fixed as worked:
message = request.get_json(force=True)
encoded = message['image']
# https://stackoverflow.com/questions/26070547/decoding-base64-from-post-to-use-in-pil
#image_data = re.sub('^data:image/.+;base64,', '', message['image'])
image_data = re.sub('^data:image/.+;base64,', '', encoded)
# Remove extra "data:image/...'base64" is Very important
# If "data:image/...'base64" is not remove, the following line generate an error message:
# File "C:\Work\SVU\950_SVU_DL_TF\sec07_TF_Flask06_09\32_KerasFlask06_VisualD3\32_predict_app.py", line 69, in predict
# image = Image.open(io.BytesIO(decoded))
# File "C:\Users\14088\anaconda3\envs\tensorflow\lib\site-packages\PIL\Image.py", line 2968, in open
# "cannot identify image file %r" % (filename if filename else fp)
# PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x000002B733BB11C8>
# image = Image.open(BytesIO(base64.b64decode(image_data)))
decoded = base64.b64decode(image_data)
image = Image.open(io.BytesIO(decoded))
# return json.dumps({'result': 'success'}), 200, {'ContentType': 'application/json'}
#print('#app.route => image:')
#print()
processed_image = preprocess_image(image, target_size=(224, 224))
prediction = model.predict(processed_image).tolist()
#print('prediction:', prediction)
response = {
'prediction': {
'dog': prediction[0][0],
'cat': prediction[0][1]
}
}
print('response:', response)
return jsonify(response)
On some cases the same error happens when you are dealing with a Raw Image file such CR2. Example: http://www.rawsamples.ch/raws/canon/g10/RAW_CANON_G10.CR2
when you try to run:
byteImg = Image.open("RAW_CANON_G10.CR2")
You will get this error:
OSError: cannot identify image file 'RAW_CANON_G10.CR2'
So you need to convert the image using rawkit first, here is an example how to do it:
from io import BytesIO
from PIL import Image, ImageFile
import numpy
from rawkit import raw
def convert_cr2_to_jpg(raw_image):
raw_image_process = raw.Raw(raw_image)
buffered_image = numpy.array(raw_image_process.to_buffer())
if raw_image_process.metadata.orientation == 0:
jpg_image_height = raw_image_process.metadata.height
jpg_image_width = raw_image_process.metadata.width
else:
jpg_image_height = raw_image_process.metadata.width
jpg_image_width = raw_image_process.metadata.height
jpg_image = Image.frombytes('RGB', (jpg_image_width, jpg_image_height), buffered_image)
return jpg_image
byteImg = convert_cr2_to_jpg("RAW_CANON_G10.CR2")
Code credit if for mateusz-michalik on GitHub (https://github.com/mateusz-michalik/cr2-to-jpg/blob/master/cr2-to-jpg.py)
While reading Dicom files the problem might be caused due to Dicom compression.
Make sure both gdcm and pydicom are installed.
GDCM is usually the one that's more difficult to install. The latest way to easily install the same is
conda install -U conda-forge gdcm
When dealing with url, this error can arise from a wrong extension of the downloaded
file or just a corrupted file.
So to avoid that use a try/except bloc so you app doesn't crash and will continue its job.
In the except part, you can retrieve the file in question for analysis:
A snippet here:
for url in urls:
with closing(urllib.request.urlopen(url)) as f:
try:
img = Image(f, 30*mm, 30*mm)
d_img.append(img)
except Exception as e:
print(url) #here you get the file causing the exception
print(e)
Here a related answer
The image file itself might be corrupted. So if you were to process a considerable amount of image files, then simply enclose the line that processes each image file with a try catch statement.

Categories

Resources