I have many images of different sizes in my directory, however i want to resize them all by a certain ratio, let's say 0.25 or 0.2, it should be a variable i can control from my code and i want the resulting images to be an output in another directory.
I looked into this approach supplied by this previous question How to resize an image in python, while retaining aspect ratio, given a target size?
Here is my approach,
aspectRatio = currentWidth / currentHeight
heigth * width = area
So,
height * (height * aspectRatio) = area
height² = area / aspectRatio
height = sqrt(area / aspectRatio)
At that point we know the target height, and width = height * aspectRatio.
Ex:
area = 100 000
height = sqrt(100 000 / (700/979)) = 373.974
width = 373.974 * (700/979) = 267.397
but it lacks lots of details for example:how to transform these sizes back on the image which libraries to use and so on..
Edit: looking more into the docs img.resize looks ideal (although i also noticed .thumbnail) but i can't find a proper example on a case like mine.
from PIL import Image
ratio = 0.2
img = Image.open('/home/user/Desktop/test_pic/1-0.png')
hsize = int((float(img.size[1])*float(ratio)))
wsize = int((float(img.size[0])*float(ratio)))
img = img.resize((wsize,hsize), Image.ANTIALIAS)
img.save('/home/user/Desktop/test_pic/change.png')
You can create your own small routine to resize and resave pictures:
import cv2
def resize(oldPath,newPath,factor):
"""Resize image on 'oldPath' in both dimensions by the same 'factor'.
Store as 'newPath'."""
def r(image,f):
"""Resize 'image' by 'f' in both dimensions."""
newDim = (int(f*image.shape[0]),int(f*image.shape[1]))
return cv2.resize(image, newDim, interpolation = cv2.INTER_AREA)
cv2.imwrite(newPath, r(cv2.imread(oldPath), factor))
And test it like so:
# load and resize (local) pic, save as new file (adapt paths to your system)
resize(r'C:\Pictures\2015-08-05 10.58.36.jpg',r'C:\Pictures\mod.jpg',0.4)
# show openened modified image
cv2.imshow("...",cv2.imread(r'C:\Users\partner\Pictures\mod.jpg'))
# wait for keypress for diplay to close
cv2.waitKey(0)
You should add some error handling, f.e.:
no image at given path
image not readable (file path permissions)
image not writeable (file path permissions)
Related
I have been writing a paint program in tkinter, and one of my functions saves the final canvas output (my image is rendererd on the canvas at coord 0,0). I save the canvas to memory as a postscript file and use PIL to save the postscript to disk as a PNG (PIL uses ghostscript when saving a postscript file). The canvas is always saved at 60.15% of it's original size, however. I would like to save the image as 100% of it's original size, though i can't figure out how to do this in my code.
Here is my code below:
"""my image is 256 x 256"""
ps = self.canvas_image.canvas.postscript(colormode='color', x = 0, y = 0,
height = 256, width = 256)
im = Image.open(BytesIO(ps.encode('utf-8')))
im.save(filepath, "PNG")
And here are my images (top is original image, bottom is saved image):
Turns out that postscript is a vectorized image format, and the image needs to be scaled before being rasterized. Your vector scale may be different than mine (mine was 0.60): you can view the scale factor in an encapsulated postscript file if the DPI scale factors in this code don't work for you
The open EPS code was taken from this post: how to maintain canvas size when converting python turtle canvas to bitmap
I used this code snippet to solve my problem:
ps = self.canvas_image.canvas.postscript(colormode='color', x = 0, y = 0, height = 256, width = 256)
""" canvas postscripts seem to be saved at 0.60 scale, so we need to increase the default dpi (72) by 60 percent """
im = open_eps(ps, dpi=119.5)
#im = Image.open('test.ps')
im.save(filepath, dpi=(119.5, 119.5))
def open_eps(ps, dpi=300.0):
img = Image.open(BytesIO(ps.encode('utf-8')))
original = [float(d) for d in img.size]
scale = dpi/72.0
if dpi is not 0:
img.load(scale = math.ceil(scale))
if scale != 1:
img.thumbnail([round(scale * d) for d in original], Image.ANTIALIAS)
return img
I want to crop an image and save it, but the problem is that the image changes hue significantly after saving the same format. Why is this happening? I'm not even converting it in the process.
Here is my code:
def square_crop(resized_image):
print "square crop"
width, height = resized_image.size
print width,height
edge_length = 3600
if(width >= height):
print("width>=height")
left = (width - edge_length)/2
top = (height - edge_length)/2
right = (width + edge_length)/2
bottom = (height + edge_length)/2
squared_image = resized_image.crop((left, top, right, bottom))
squared_image.save('squared.png')
And the confusing part is that this code uses the same image and saves it without hue change, so the cropping function must have an issue:
def image_resize(image):
print "image resize"
width, height = image.size
print width,height
if(width > 3601 and height > 3601):
print width, height
if(width >= height):
ratio = float(width)/float(height)
w = 3601 * ratio
h = 3601.0
print ratio, w, h
resized_image = image.resize([int(w), int(h)])
resized_image.save("resized.png")
else:
ratio = float(height)/float(width)
print(ratio)
w = 3601.0
h = 3601 * ratio
print ratio, w, h
resized_image = image.resize([int(w), int(h)])
resized_image.save("heic1509a_resized.png")
*EDIT: When I import .jpg file and save to .jpg both functions have the same hue issue. Same with .tif.
**EDIT: Also I've noticed that for some images this red color loss does not happen. I truly don't have any idea what is going on. I will leave the before and after screenshots to see for yourself.
Before - After
***EDIT: The problem is from the color space as the images that have changed color when saved were encoded using ProPhoto RGB color space(ROMM RGB (Reference Output Medium Metric)).
I am using gimp2 to convert them first to RGB without losing color, but I would want to find a way to do that automatically from python.
I will post any new updates on this issue.
The problem was that when I was saving the file, the PIL library automatically switched the color space of the image(ROMM-RGB) to other color space(RGB or sRGB) and basically every color changed.
All you have to do is preserve the color space of the image and you're fine. If you want to convert to another color space you should look up OpenCV library.
I can't explain too much in detail because I am just breaking the ice on this. Here is the code that solved this issue:
resized_image.save('resized.jpg', #file name
format = 'JPEG', #format of the file
quality = 100, #compression quality
icc_profile = resized_image.info.get('icc_profile','')) #preserve the icc profile of the photo(this was the one that caused problems)
Here is a link to a more in-depth answer: LINK
I'm new to Pyglet (and stackoverflow) and cannot seem to find out how to resize images.
'pipe.png' is the image that I am trying to adjust the size of.
With this code, the image is not fully shown because the window size is too small.
I would like to adjust the size of the image so that it fits inside of the window.
The current size of 'pipe.png' is 100x576.
import pyglet
window = pyglet.window.Window()
pyglet.resource.path = ["C:\\"]
pipe = pyglet.resource.image('pipe.png')
pyglet.resource.reindex()
#window.event
def on_draw():
window.clear()
pipe.blit(0, 0)
pyglet.app.run()
EDIT:
I ended up finding out the answer here:
http://pyglet.org/doc-current/programming_guide/image.html#simple-image-blitting
The solution is:
imageWidth = 100
imageHeight = 100
imageName.width = imageWidth
imageName.height = imageHeight
This would adjust to image size to display as 100x100
Came across of this oldie, so for whoever lone ranger that ends up here juast as I did. Changing .width and .height won't do much in many cases (or at all these days?).
In order to successfully change a image resolution, you need to modify it's .scale attribute.
Here's a snippet of code that I use to resize a image:
from pyglet.gl import *
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
image = pyglet.image.load('test.png')
height, width = 800, 600 # Desired resolution
# the min() and max() mumbo jumbo is to honor the smallest requested resolution.
# this is because the smallest resolution given is the limit of say
# the window-size that the image will fit in, there for we can't honor
# the largest resolution or else the image will pop outside of the region.
image.scale = min(image.height, height)/max(image.height, height)), max(min(width, image.width)/max(width, image.width)
# Usually not needed, and should not be tampered with,
# but for a various bugs when using sprite-inheritance on a user-defined
# class, these values will need to be updated manually:
image.width = width
image.height = height
image.texture.width = width
image.texture.height = height
I'm having trouble getting PIL to enlarge an image. Large images get scaled down just fine, but small images won't get bigger.
# get the ratio of the change in height of this image using the
# by dividing the height of the first image
s = h / float(image.size[1])
# calculate the change in dimension of the new image
new_size = tuple([int(x*s) for x in image.size])
# if this image height is larger than the image we are sizing to
if image.size[1] > h:
# make a thumbnail of the image using the new image size
image.thumbnail(new_size)
by = "thumbnailed"
# add the image to the images list
new_images.append(image)
else:
# otherwise try to blow up the image - doesn't work
new_image = image.resize(new_size)
new_images.append(new_image)
by = "resized"
logging.debug("image %s from: %s to %s" % (by, str(image.size), str(new_size)))
Both resize and transform methods properly resize images.
size_tuple = im.size
x1 = y1 = 0
x2, y2 = size_tuple
# resize
im = im.resize(size_tuple)
# transform
im = im.transform(size_tuple, Image.EXTENT, (x1,y1,x2,y2))
If you are having the same problem I described - try it on another machine. There must be something wrong with the python installation on my server. It worked fine on my local machine.
Here is a working example how to resize an image in every direction with openCV and numpy:
import cv2, numpy
original_image = cv2.imread('original_image.jpg',0)
original_height, original_width = original_image.shape[:2]
factor = 2
resized_image = cv2.resize(original_image, (int(original_height*factor), int(original_width*factor)), interpolation=cv2.INTER_CUBIC )
cv2.imwrite('resized_image.jpg',resized_image)
#fixed var name
Simple as that. You wanna use "cv2.INTER_CUBIC" to enlarge (factor > 1) and "cv2.INTER_AREA" to make the images smaller (factor < 1).
I am probably looking for the wrong thing in the handbook, but I am looking to take an image object and expand it without resizing (stretching/squishing) the original image.
Toy example: imagine a blue rectangle, 200 x 100, then I perform some operation and I have a new image object, 400 x 300, consisting of a white background upon which a 200 x 100 blue rectangle rests. Bonus if I can control in which direction this expands, or the new background color, etc.
Essentially, I have an image to which I will be adding iteratively, and I do not know what size it will be at the outset.
I suppose it would be possible for me to grab the original object, make a new, slightly larger object, paste the original on there, draw a little more, then repeat. It seems like it might be computationally expensive. However, I thought there would be a function for this, as I assume it is a common operation. Perhaps I assumed wrong.
The ImageOps.expand function will expand the image, but it adds the same amount of pixels in each direction.
The best way is simply to make a new image and paste:
newImage = Image.new(mode, (newWidth,newHeight))
newImage.paste(srcImage, (x1,y1,x1+oldWidth,y1+oldHeight))
If performance is an issue, make your original image bigger than needed and crop it after the drawing is done.
Based on interjays answer:
#!/usr/bin/env python
from PIL import Image
import math
def resize_canvas(old_image_path="314.jpg", new_image_path="save.jpg",
canvas_width=500, canvas_height=500):
"""
Resize the canvas of old_image_path.
Store the new image in new_image_path. Center the image on the new canvas.
Parameters
----------
old_image_path : str
new_image_path : str
canvas_width : int
canvas_height : int
"""
im = Image.open(old_image_path)
old_width, old_height = im.size
# Center the image
x1 = int(math.floor((canvas_width - old_width) / 2))
y1 = int(math.floor((canvas_height - old_height) / 2))
mode = im.mode
if len(mode) == 1: # L, 1
new_background = (255)
if len(mode) == 3: # RGB
new_background = (255, 255, 255)
if len(mode) == 4: # RGBA, CMYK
new_background = (255, 255, 255, 255)
newImage = Image.new(mode, (canvas_width, canvas_height), new_background)
newImage.paste(im, (x1, y1, x1 + old_width, y1 + old_height))
newImage.save(new_image_path)
resize_canvas()
You might consider a rather different approach to your image... build it out of tiles of a fixed size. That way, as you need to expand, you just add new image tiles. When you have completed all of your computation, you can determine the final size of the image, create a blank image of that size, and paste the tiles into it. That should reduce the amount of copying you're looking at for completing the task.
(You'd likely want to encapsulate such a tiled image into an object that hid the tiling aspects from the other layers of code, of course.)
This code will enlarge a smaller image, preserving aspect ratio, then center it on a standard sized canvas. Also preserves transparency, or defaults to gray background.
Tested with P mode PNG files.
Coded debug final.show() and break for testing. Remove lines and hashtag on final.save(...) to loop and save.
Could parameterize canvas ratio and improve flexibility, but it served my purpose.
"""
Resize ... and reconfigures. images in a specified directory
Use case: Images of varying size, need to be enlarged to exaxtly 1200 x 1200
"""
import os
import glob
from PIL import Image
# Source directory plus Glob file reference (Windows)
source_path = os.path.join('C:', os.sep, 'path', 'to', 'source', '*.png')
# List of UNC Image File paths
images = glob.glob(source_path)
# Destination directory of modified image (Windows)
destination_path = os.path.join('C:', os.sep, 'path', 'to', 'destination')
for image in images:
original = Image.open(image)
# Retain original attributes (ancillary chunks)
info = original.info
# Retain original mode
mode = original.mode
# Retain original palette
if original.palette is not None:
palette = original.palette.getdata()[1]
else:
palette = False
# Match original aspect ratio
dimensions = original.getbbox()
# Identify destination image background color
if 'transparency' in info.keys():
background = original.info['transparency']
else:
# Image does not have transparency set
print(image)
background = (64)
# Get base filename and extension for destination
filename, extension = os.path.basename(image).split('.')
# Calculate matched aspect ratio
if dimensions[2] > dimensions[3]:
width = int(1200)
modifier = width / dimensions[2]
length = int(dimensions[3] * modifier)
elif dimensions[3] > dimensions[2]:
length = int(1200)
modifier = length / dimensions[3]
width = int(dimensions[2] * modifier)
else:
width, length = (1200, 1200)
size = (width, length)
# Set desired final image size
canvas = (1200, 1200)
# Calculate center position
position = (
int((1200 - width)/2),
int((1200 - length)/2),
int((1200 - width)/2) + width,
int((1200 - length)/2) + length
)
# Enlarge original image proportionally
resized = original.resize(size, Image.LANCZOS)
# Then create sized canvas
final = Image.new(mode, canvas, background)
# Replicate original properties
final.info = info
# Replicate original palatte
if palette:
final.putpalette(palette)
# Cemter paste resized image to final canvas
final.paste(resized, position)
# Save final image to destination directory
final.show()
#final.save("{}\\{}.{}".format(destination_path, filename, extension))
break