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).
Related
I'm trying to map an image to a velocity model, where the colors represent the velocities, so I read an image using OpenCV, take its dimensions, create an array of velocities within a certain range and try to recreate the image with these values, I saw an algoirthm very similar in matlab that works:
` vi=1000;
vf=4200;
M=imread('modelo_vr_2500x300.png');
Lx=size(M,1);
Ly=size(M,2);
N=M(1:Lx,1:Ly,1);
cor2=0:255;
vel2=cor2/256*(vf-vi)+vi;
V=zeros(size(N));
for i=1:length(cor2)
V=V+vel2(i)*(N==cor2(i));
end
imagesc(V)
colorbar`
So I tried to adapt it to Python, but it doesn't seem to work, all that I get is an image totally black, but if I print V, the new image, it has values, but they are quite high. I have no idea what I'm doing wrong, could anyone help?
import CV2
# read image
img = cv2.imread("figures/teste03-06B.png", cv2.IMREAD_UNCHANGED)
# get dimensions of image
dimensions = img.shape
# height = Ly, width = Lx, number of channels in image = Ch
Ly = img.shape[0]
Lx = img.shape[1]
Ch = img.shape[2]
N=img[0:Ly, 0:Lx, 0:Ch]
print('Image Dimension : ',dimensions)
print('Image Height : ',Ly)
print('Image Width : ',Lx)
print('Number of Channels : ',Ch)
cv2.imshow("Display window", img)
cv2.waitKey(0)
cv2.destroyWindow("Display window")
import numpy as np
vi=2000
vf=6000
color=np.array(range(256))
vel=((color/256)*(vf-vi))+vi
V = np.zeros_like(img)
for i in range(0,len(color)):
if N[i]==color[i]:
V=V+vel[i]
else:
V=V
print(V)
cv2.imshow("Display window", V)
cv2.waitKey(0)
cv2.destroyWindow("Display window")`
It doesn't give any error message, just doesn't work as it should, I have no idea why...
I think you're looking for this:
new_image = vel[img]
But you probably want to normalize vel first. Some variant of:
vel = vel/max(vel) * 255
vel = vel.astype(dtype=np.uint8)
I think that should work.
I wanted to change all the pixels in an image to a grey color (r = g = b = 128) if they are in a certain threshold (if the value is between 50 and 150 change it). I imported the image and when i try to process the image it gives me the following error : IndexError: index 3474 is out of bounds for axis 0 with size 3474 (the image is 3474x4632).
Here's the code:
from PIL import Image
import numpy as np
image = Image.open("texture.jpg")
w, h = image.size
print ("%d %d" % (w, h)) #to be sure what the width and height are
im = np.array(image)
for x in range(0, w):
for y in range(0, h):
if (im[x][y][0] <= 150 and im[x][y][0] >= 50):
im[x][y][0] = 128
im[x][y][1] = 128
im[x][y][2] = 128
cv2.imwrite("image2.jpg", im)
And here's the image i'm trying to convert: https://ibb.co/hnjq4p (too large to upload here). Any ideas about why it doesn't work ?
I believe that numpy reverses the axis order from PIL. Actually the first index is rows. So you should loop through w,h = im.shape or h,w = image.size instead. Maybe you can verify that this is correct by comparing image.size and im.shape?
That said, it will be much better if you do not loop. You can use masking and broadcasting to achieve the for loop task like this:
im[(im[...,0]<=150)&(im[...,0]>=50)] = 128 # will modify im in place
This will be much faster especially on large images like this.
Note that this only checks the first channel of the image to be between 150 and 50. This is what your for loop says so I guess it's what you want.
Please check im.shape: you should index your pixels as im[y,x] after converting to a numpy.array.
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 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)
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