I made an API call to convert an image into a thumbnail version of itself, and that returned a Generator object. But I don't know how to save that object as an image on my local machine.
I get from the documentation of the API that a "successful response contains the thumbnail image binary", but I don't know how to access it. I was thinking, so I need to convert the binary into a string or list and then convert that into an image by using the Image class from PIL?
I don't know the best way to do it. I know Generators are just iterators that save state, but that doesn't mean much when it comes to image data being in it and accessing the data so that I have a saved image in my local folder.
Here is my code:
computervision_client = ComputerVisionClient(endpoint, CognitiveServicesCredentials(subscription_key))
# Get a local image
local_image_path_thumb = "resources\\objects.jpg"
local_image_thumb = open(local_image_path_objects, "rb")
print("Generating thumbnail from a local image...")
# Call the API with a local image, set the width/height if desired (pixels)
# Returns a Generator object, a thumbnail image binary.
thumb_local = computervision_client.generate_thumbnail_in_stream(100, 100, local_image_thumb, True)
# Save the thumbnail to your local root folder of this project.
# Save to here, somehow: "\\resources\\thumb_local.jpg"
print("Thumbnail saved to local folder.")
Here is the API documentation for the function generate_thumbnail_in_stream.
with open("output_file.png", "wb") as fp:
for chunk in thumb_local:
fp.write(chunk)
Related
I have an image that I am uploading using Django Forms, and its available in the variable as InMemoryFile What I want to do is to make it progressive.
Code to make an image a progressive
img = Image.open(source)
img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)
Forms.py
my_file = pic.pic_url.file
photo = uploader.upload_picture_to_album(title=title, file_obj=my_file)
The issue is, I have to save the file in case I want to make it progressive, and open it again to send it to the server. (It seems a redundant actions to make it progressive)
I just want to know if there is anyway to make an image progressive which does not save the image physically on disk but in memory, which I can use the existing code to upload it?
Idea
Looking for something similar.
my_file=pic.pic_url.file
progressive_file = (my_file)
photo = picasa_api.upload_picture_to_album(title=title, file_obj=progressive_file)
If all you want is not saving the intermediate file to disk, you can save it to a StringIO. Both PIL.open() and PIL.save() accept file-like objects as well as filenames.
img = Image.open(source)
progressive_img = StringIO()
img.save(progressive_img, "JPEG", quality=80, optimize=True, progressive=True)
photo = uploader.upload_picture_to_album(title=title, file_obj=progressive_img)
The uploader needs to support working with the StringIO but that is hopefully the case.
It's probably possible to directly stream the result from save() using suitable coroutines, but that is a little more work.
I'm resizing images in python using Pillow
image = Image.open("image_file.jpg")
print(image.format) # Prints JPEG
resized_image = image.resize([100,200],PIL.Image.ANTIALIAS)
print(resized_image.format) # Prints None!!
Why does resized_image.format Hold a None Value?
And How can i retain the format when resizing using pillow?
Because Image.resize creates a new Image object (resized copy of the image) and for any images when creating by the library itself (via a factory function, or by running a method on an existing image), the "format" attribute is set to None.
If you need the format attribute you still can to do this:
image = Image.open("image_file.jpg") #old image object
resized_image = image.resize([100,200],PIL.Image.ANTIALIAS)
resized_image.format = image.format # original image extension
Read the docs
As stated in the documentation:
The file format of the source file. For images created by the library itself (via a factory function, or by running a method on an existing image), this attribute is set to None.
You can specify the format on save:
image.save(fp, 'JPEG')
You can save the resized images with save comments
resized_image.save("New_image.png")
It will save into your current directory.
If you want to see in the python console itself you have to run
resized_image.show()
I found an example on how to store png in datastore:
img = images.Image(img_data)
# Basically, we just want to make sure it's a PNG
# since we don't have a good way to determine image type
# through the API, but the API throws an exception
# if you don't do any transforms, so go ahead and use im_feeling_lucky.
img.im_feeling_lucky()
png_data = img.execute_transforms(images.PNG)
img.resize(60, 100)
thumbnail_data = img.execute_transforms(images.PNG)
Picture(data=png_data,
thumbnail_data=thumbnail_data).put()
This code is very confusing to me, but it works for png. However, what should I do to be able to store all most common formats (jpg, gif, tiff, etc) ?
The quick answer
You can store binary data of any file type by using db.BlobProperty() in your model.
If you use the Image API to manipulate the image data, you're limited to inputting .jpg, .png, .gif, .bmp, .tiff, and .ico types, and outputting to either .jpg or .png.
Storing images
To simply store the images in the data store, use db.BlobProperty() in your model, and have this store the binary data for the picture. This is how the data is stored in the example code you linked to (see Line 85).
Because the type db.BlobProperty type is not a picture per se, but can store any binary data, some discipline is needed; there's no easy way to programmatically enforce a pictures-only constraint. Luckily, this means that you can store data of any type you want, including .jpg, .gif, .tiff, etc. files in addition to the .png format, as in the example.
You'll probably want to, as they have in the example, create a new Class for the Model, and store certain metadata ("name", "filetype", etc.) needed for the files, in addition to the image's binary data. You can see an example of this at Line 65 in the example you linked to.
To store the image in the BlobProperty, you'll want to use the db.put() to save the data; this is the same as with any type. See the code starting on Line 215 in the example code you linked to.
Manipulating images
If you have to manipulate the image, you can use the Images API package. From the Overview of the Images API we can see the following:
The service accepts image data in the JPEG, PNG, GIF (including animated GIF), BMP, TIFF and ICO formats.
It can return transformed images in the JPEG and PNG formats. If the input format and the output format are different, the service converts the input data to the output format before performing the transformation.
So even though you can technically store any type in the datastore, the valid input and output typer are limited if you're using this API to manipulate the images.
models.py
class Profile(db.Model):
avatar=db.BlobProperty()
views.py
if(self.request.get):
image = self.request.get('MyFile')
if image:
mime=self.request.POST['MyFile'].type
mime=mime.split('/')
icon_image = db.Blob(images.resize(image,460,460))
prof.avatar = db.Blob(icon_image)
if mime[1]== 'jpeg' or 'jpg' or 'gif' or 'png':
prof.put()
display image
class disp_image(webapp.RequestHandler):
def get(self):
if profile.avatar is not None:
image = view_profile.avatar
self.response.headers['Content-Type'] = "image/png"
return self.response.out.write(image)
Templates
<img id="crop" src='/module/disp_image' alt="profile image" >
More specifically, I want to change the filetype of an image uploaded through a Django ImageField.
My current thinking is to created a custom ImageField and overwrite the save method to manipulate the file.
I've having trouble getting an in memory file to because a PIL Image instance.
Thanks for the help.
Have you tried StringIO ?
see the docs http://effbot.org/imagingbook/introduction.htm#more-on-reading-images
#Reading from a string
import StringIO
im = Image.open(StringIO.StringIO(buffer))
Note that Django's ImageField inherits the open method from FieldFile. This returns a stream object that can be passed to PIL's Image.open (the standard factory method for creating Image objects from an image stream):
stream = imagefield.open()
image = Image.open(stream)
stream.close()
# ... and then save image with: image.save(outfile, format, options)
See PIL Image documentation.
I've been looking for a way to download an image from a URL, preform some image manipulations (resize) actions on it, and then save it to a django ImageField. Using the two great posts (linked below), I have been able to download and save an image to an ImageField. However, I've been having some trouble manipulating the file once I have it.
Specifically, the model field save() method requires a File() object as the second parameter. So my data has to eventually be a File() object. The blog posts linked below show how to use urllib2 to save your an image URL into a File() object. This is great, however, I also want to manipulate the image using PIL as an Image() object. (or ImageFile object).
My preferred approach would be then to load the image URL directly into an Image() object, preform the resize, and convert it to a File() object and then save it in the model. However, my attempts to convert an Image() to a File() have failed. If at all possible, I want to limit the number of times I write to the disk, so I'd like to do this object transformation in Memory or using a NamedTemporaryFile(delete=True) object so I don't have to worry about extra files laying around. (Of course, I want the file to be written to disk once it is saved via the model).
import urllib2
from PIL import Image, ImageFile
from django.core.files import File
from django.core.files.temp import NamedTemporaryFile
inStream = urllib2.urlopen('http://www.google.com/intl/en_ALL/images/srpr/logo1w.png')
parser = ImageFile.Parser()
while True:
s = inStream.read(1024)
if not s:
break
parser.feed(s)
inImage = parser.close()
# convert to RGB to avoid error with png and tiffs
if inImage.mode != "RGB":
inImage = inImage.convert("RGB")
# resize could occur here
# START OF CODE THAT DOES NOT SEEM TO WORK
# I need to somehow convert an image .....
img_temp = NamedTemporaryFile(delete=True)
img_temp.write(inImage.tostring())
img_temp.flush()
file_object = File(img_temp)
# .... into a file that the Django object will accept.
# END OF CODE THAT DOES NOT SEEM TO WORK
my_model_instance.image.save(
'some_filename',
file_object, # this must be a File() object
save=True,
)
With this approach, the file appears corrupt whenever I view it as an image. Does anyone have any approach that takes a file file from a URL, allows one to manipulate it as an Image and then save it to a Django ImageField?
Any help is much appreciated.
Programmatically saving image to Django ImageField
Django: add image in an ImageField from image url
Update 08/11/2010: I did end up going with StringIO, however, I was stringIO was throwing an unusual Exception when I tried to save it in a Django ImageField. Specifically, the stack trace showed a name error:
"AttribueError exception "StringIO instance has no attribute 'name'"
After digging through the Django source, it looks like this error was caused when the model save tries to access the size attribute of the StringIO "File". (Though the error above indicates a problem with the name, the root cause of this error appears to be the lack of a size property on the StringIO image). As soon as I assigned a value to the size attribute of the image file, it worked fine.
In an attempt to kill 2 birds with 1 stone. Why not use a (c)StringIO object instead of a NamedTemporaryFile? You won't have to store it on disk anymore and I know for a fact that something like this works (I use similar code myself).
from cStringIO import StringIO
img_temp = StringIO()
inImage.save(img_temp, 'PNG')
img_temp.seek(0)
file_object = File(img_temp, filename)