I have a function in which I'm trying to resize a photo twice from request.FILES['image']. I'm using the image.thumbnail() with the Parser as well. This works fine when I create one thumbnail, but in my view if I repeat the exact same thing again, it fails in the parser via IOError cannot parse image. I'm very confused. I've created StringIO files in memory instead of using Django's UploadedFile object as-is and it still does the same thing. Any help is much appreciated.
Suppose I wanted to do the following twice (with two different thumbnailing sizes) all without retrieving the URL twice:
import urllib2
from PIL import Image, ImageFile, ImageEnhance
# create Image instance
file = urllib2.urlopen(r'http://animals.nationalgeographic.com/staticfiles/NGS/Shared/StaticFiles/animals/images/primary/kemps-ridley-sea-turtle.jpg')
parser = ImageFile.Parser()
while True:
s = file.read(1024)
if not s:
break
parser.feed(s)
image = parser.close()
# make thumbnail
size = (75, 75)
image.thumbnail(size, Image.ANTIALIAS)
background = Image.new('RGBA', size, (255, 255, 255, 0))
background.paste(
image,
((size[0] - image.size[0]) / 2, (size[1] - image.size[1]) / 2))
background.save('copy.jpg')
For instance:
image = parser.close()
image2 = parser.close() # Obviously this doens't work
image2 = image # Obviously this doesn't either but you get what I need to do here
# Do 2 thumbnails with only one original source.
... other code ommitted ...
image.save('copy.jpg')
image2.save('copy.jpg')
If this works once, as you say, the image you retrieved is just fine. There are at least two different ways to get multiple thumbnails out of single PIL images.
You can use PIL's resize method, which will return a resized copy of the original. You just have to calculate the dimensions you'll need if you want to keep the proportions intact.
Use Image.copy() to get a copy of the image.
Like this:
original = parser.close()
...
thumb1 = original.copy()
size = (75,75)
thumb1.thumbnail(size, Image.ANTIALIAS)
...
thumb2 = original.copy()
thumbnail2 = original.resize(size2, Image.ANTIALIAS)
...
This way, the original will not be altered and you can get as many copies as you need.
A simpler solution than copying the original image is to instead reset the file pointer between calls to thumbnail(...) like so:
original.seek(0)
I'm assuming it's failing on the image = parser.close() line with an IOError. so there's probably something wrong with the way ImageFile is getting the image data. Have you tried making reading from a local file instead?
If the parser managed to decode an image, it returns an Image object.
Otherwise, this method raises an IOError exception.
Source.
Related
I'm using PIL module to get some data out of some images. I do this:
img = Image.open("example.jpg")
img = img.convert('L')
img.resize((800, 800))
data_list.append(np.array(img).flatten()/255)
I modify the image and then save the data that I want in a list. Is it okay to then just leave the image like this and not save it? because I don't really care about the image after I got the thing I want, so I prefer keep the images as it was. Is there a problem with changing an image and not saving it or I should do something to reset it?
When using img = Image.open("example.jpg") PIL only loads a copy of the image into the memory. The actual image file stays untouched, so you don't need to add any additional code.
You can however delete the variable with del img, which can be useful, especially with bigger images to clear up the memory.
Is it possible to remove a particular rbg color from an image? For example, I want to create a function such that I pass an image and color as a parameter and it returns the same image but without that color.
For example, this function
fuction(image, "R")
should give me an image that has no R shades. How can I do so?
Currently, something like this works:
def exclusionWithPIL(image, channel):
out = None
image = Image.open(image)
image_data = image.load()
height,width = image.size
for loop1 in range(height):
for loop2 in range(width):
r,g,b = image_data[loop1,loop2]
image_data[loop1,loop2] = 0,g,b
return image
result = rgb_exclusion('./image.jpg', "G")
result.save('new.jpg')
but here, I'm reading the image like this Image.open(image) inside the function. Instead, I want to pass in image1 which is already read like this:
image1 = load(image1_path)
def load(image_path):
out = io.imread(image_path)
out = out.astype(np.float64) / 255
return out
How can I modify the function accordingly?
P.S I use io.imread from skimage to read images.
Edit:
if I pass the image loaded by io.imread directly into the function, something like this seems to work:
out = image.copy()
if (channel == "R"):
out[:, :, 0] = 0
But I don't quite quite understand the indexes [:, :, 0]
found a tutorial online which solves exactly your problem:
Tutorial Link
The main idea behind their solution is to use PIL in order to open the image, load it, and then iterate over every pixel in the image (using 2 loops) and setting the chosen color to zero.
EDIT: I think that your problem occurs because of the fact that you're trying to pass to the function as a parameter an image which was loaded using sicikit-image library (io.imread()), and then inside the function you're trying to use PILLOW library's features on the same image.
That's why it shouldn't work, you're merging between 2 different libraries.
As I see it, there are 2 possible solution:
Make the function's parameter the image's path instead of the image itself, and then save the image in the same directory and do not return anything.
Stay consistent, and before passing the function an image as a parameter, load it using the PILLOW library, and when returning it, refer to the returned image in terms of the PILLOW library only.
I'm making a web app using Flask, and I want to resize the images that are uploaded. I'm using PIL to do this, but an error is thrown.
The process to do it is like this, but it seems inefficient:
filename = secure_filename(form.image.data.filename)
form.image.data.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
img = Image.open(os.path.join(app.config['UPLOAD_FOLDER'],filename), 'r')
img = img.resize(300, 300)
img.save(filename, quality=100, optimize=True)
What I'm trying to do is save the image after the user uploaded it, open the new file, resize it, and save it again.
How can I fix my error?
Also is there a way to do this more efficiently (without saving the un-resized file), using a Python library?
img = img.resize(300, 300)
=> img = img.resize((300,300))
The most important thing to remember is not to pass the size as object but as a tuple in resize function new_image = image.resize((img_width,img_size))
and NOT AS
new_image = image.resize(img_width,img_size)
I am using a python notebook. In it, I have code that creates images, and am using image.show()
for debugging purposes. However, this has the disadvantage that I cannot name the image, since it has a temporary file name. I am then using image.save("name.png") to save the images with different names, and then using Image.open("name.png") to open it. However, when I do this, only the last image is actually opened. What do I need to do so that I can call Image.open on several images and they all open? For example if I do:
image = Image.fromarray( ... )
image.save("original.png")
Image.open("original.png")
image = Image.fromarray( ... )
image.save("reconstruction.png")
Image.open("reconstruction.png")
only "reconstruction.png" shows.
If I use Image.show() after each of them
image = Image.fromarray( ... )
image.show()
image = Image.fromarray( ... )
image.show()
it will work, but they will have a temporary name, which is meaningless, and if I end up with 7-8 open images I want an easy way to track what is what.
Image.show() is mainly for debugging use:
Displays this image. This method is mainly intended for debugging purposes.
On Unix platforms, this method saves the image to a temporary PPM file, and calls the xv utility.
On Windows, it saves the image to a temporary BMP file, and uses the standard BMP display utility to show it (usually Paint).
You can give a title parameter, but it doesn't show on Windows:
title – Optional title to use for the image window, where possible.
You can use different names for image variable to keep track of them inside Python:
image1 = Image.fromarray( ... )
image1.save("original.png")
Image1.open("original.png")
image2 = Image.fromarray( ... )
image2.save("reconstruction.png")
Image2.open("reconstruction.png")
image1.show("image1")
image2.show("image2")
Also another approach which I used within my IPython/Jupyter/Colab notebook was:
import requests
from IPython.display import Image
from IPython.display import display
img0 = Image("img0_path", width = 140)
img1 = Image("img1_path", width = 140)
img2 = Image("img2_path", width = 140)
img3 = Image("img3_path", width = 140)
display(img0,img1,img2,img3)
I have 2 transparent PNG images of the same size (142,43). I am trying to vertically stack them. This is one of them:
The end result should be like this (142,86):
It also should retain its transparancy.
I've tried the following code:
from PIL import Image
img_list = [Image.open("example.png"), Image.open("example.png")]
bg = Image.open("1x1_transparent.png")
bg = bg.resize(size=(142, 43*2))
img_list[0] = img_list[0].convert('RGBA')
bg.paste(img_list[0], (0, 0), img_list[0])
bg.save('final.png')
Which imports a 1x1 transparent image, resizes it to the final target size, then tries to put the first image on it. This does not work. The saved image 'final.png' shows an empty image.
Any thoughts what I would be doing wrong?
If your output doesn't seem properly sized, it's probably because of this line:
bg.resize(size=(142, 43*2))
resize returns a new version of the image, leaving the original one unmodified. Try assigning the returned value to something so you can do additional operations on it and ultimately save the output.
bg = bg.resize(size=(142, 43*2))