How to complete this python function to save in the same folder? - python

I am trying to write my first real python function that does something real. What i want to accomplish is searching a given folder, and then open all images and merging them together so they make a filmstrip image. Imagine 5 images stacked on top of eachother in one image.
I have this code now, which should be pretty much ok, but propably needs some modification:
import os
import Image
def filmstripOfImages():
imgpath = '/path/here/'
files = glob.glob(imgpath + '*.jpg')
imgwidth = files[0].size[0]
imgheight = files[0].size[1]
totalheight = imgheight * len(files)
filename = 'filmstrip.jpg'
filmstrip_url = imgpath + filename
# Create the new image. The background doesn't have to be white
white = (255,255,255)
filmtripimage = Image.new('RGB',(imgwidth, totalheight),white)
row = 0
for file in files:
img = Image.open(file)
left = 0
right = left + imgwidth
upper = row*imgheight
lower = upper + imgheight
box = (left,upper,right,lower)
row += 1
filmstripimage.paste(img, box)
try:
filmstripimage.save(filename, 'jpg', quality=90, optimize=1)
except:
filmstripimage.save(miniature_filename, 'jpg', quality=90)")
How do i modify this so that it saves the new filmstrip.jpg in the same directory as I loaded the images from? And it probably has some things that are missing or wrong, anybody got a clue?
Related question: How to generate a filmstrip image in python from a folder of images?

It is not an answer to your question, but It might be helpful:
#!/usr/bin/env python
from PIL import Image
def makefilmstrip(images, mode='RGB', color='white'):
"""Return a combined (filmstripped, each on top of the other) image of the images.
"""
width = max(img.size[0] for img in images)
height = sum(img.size[1] for img in images)
image = Image.new(mode, (width, height), color)
left, upper = 0, 0
for img in images:
image.paste(img, (left, upper))
upper += img.size[1]
return image
if __name__=='__main__':
# Here's how it could be used:
from glob import glob
from optparse import OptionParser
# process command-line args
parser = OptionParser()
parser.add_option("-o", "--output", dest="file",
help="write combined image to OUTPUT")
options, filepatterns = parser.parse_args()
outfilename = options.file
filenames = []
for files in map(glob, filepatterns):
if files:
filenames += files
# construct image
images = map(Image.open, filenames)
img = makefilmstrip(images)
img.save(outfilename)
Example:
$ python filmstrip.py -o output.jpg *.jpg

I think if you change your try section to this:
filmstripimage.save(filmstrip_url, 'jpg', quality=90, optimize=1)

In the case you are not joking there are several problems with your script e.g. glob.glob() returns list of filenames (string objects, not Image objects) therefore files[0].size[0] will not work.

as J. F. Sebastian mentioned, glob does not return image objects... but also:
As it is right now, the script assumes the images in the folder are all the same size and shape. This is not often a safe assumption to make.
So for both of those reasons, you'll need to open the images before you can determine their size. Once you open it you should set the width, and scale the images to that width so there is no empty space.
Also, you didn't set miniature_filename anywhere in the script.

Related

How to find possible permutation of a list that contains images?

I am trying to load an image using opencv and then splitting that image into 4 parts. I have saved all the images in a list and now i want to find out all the possible permutation combination.
This is my current code.
#Importing Libraries
import cv2 as cv
import numpy as np
import glob
import itertools
#Importing Image
path = 'cat.jpg'
img = cv.imread(path)
#Seperating the height and width from the image data
(h, w) = img.shape[:2]
#Finding the center
centerX, centerY = (w//2), (h//2)
#spliting the image
topleft = img[0:centerY, 0:centerX]
topright = img[0:centerY, centerX:w]
bottomleft = img[centerY:h, 0:centerX]
bottomright = img[centerY:h, centerX:w]
#creating a new directory
os.mkdir('splits')
#Saving split images
cv.imwrite('./splits/1.jpg', topleft)
cv.imwrite('./splits/2.jpg', topright)
cv.imwrite('./splits/3.jpg', bottomleft)
cv.imwrite('./splits/4.jpg', bottomright)
#Putting the images in a list
img_data = []
files = glob.glob ('./splits/*.jpg')
for myfile in files:
image = cv.imread(myfile)
img_data.append(image)
I have tried to use the permutation function from the itertools library. But i don't think so it is working properly.
I expaneded you code a little bit, ran this and it worked for me:
#Importing Libraries
import cv2 as cv
import glob
import os
from itertools import permutations
#Importing Image
path = 'cat.jpg'
img = cv.imread(path)
#Seperating the height and width from the image data
(h, w) = img.shape[:2]
#Finding the center
centerX, centerY = (w//2), (h//2)
#spliting the image
topleft = img[0:centerY, 0:centerX]
topright = img[0:centerY, centerX:w]
bottomleft = img[centerY:h, 0:centerX]
bottomright = img[centerY:h, centerX:w]
def concat_tile(im_list_2d):
return cv.vconcat([cv.hconcat(im_list_h) for im_list_h in im_list_2d])
#creating a new directory
try:
os.mkdir('splits')
os.mkdir("./permutations")
except:
pass
#Saving split images
cv.imwrite('./splits/1.jpg', topleft)
cv.imwrite('./splits/2.jpg', topright)
cv.imwrite('./splits/3.jpg', bottomleft)
cv.imwrite('./splits/4.jpg', bottomright)
#Putting the images in a list
img_data = []
files = glob.glob ('./splits/*.jpg')
print(files)
c = 0
for elem in permutations(files):
perm_file = None
im0 = cv.imread(elem[0])
im1 = cv.imread(elem[1])
im2 = cv.imread(elem[2])
im3 = cv.imread(elem[3])
im_tile = concat_tile([[im0, im1], [im2, im3]])
cv.imwrite(f"permutations/perm_{c}.jpg", im_tile)
c += 1
Ran into some trouble with creating the permutations folder locally, but you can work around that yourself and create it manually for now.
I have 24 files that are permutations like you described:
Also, here's how one of them looks:
Note the vconcat function and the user of permutations.
You can, of course, use a better description for you file names by splitting the path string, but that's easy.
Good luck!

cropping and slicing multiple images in a folder using python PIL or cv2, then saving in another folder

I am trying to crop all images in a folder and save them in another folder. Then split all the images I have cropped using the image_slicer library.
I know a similar question like this has been asked but the difference is that I don't want the original folder to contain the cropped photos.
Here is my code so far; it runs but does nothing:
from PIL import Image
import image_slicer
import os
path = 'BeforeAfter'
arr = os.listdir(path)
def crop():
for i in arr:
if os.path.isfile(i):
img = Image.open(i)
f, e = os.path.splitext(i)
left = 66.6
top = 37.4
right = 1212.4
bottom = 550.7
imCrop = img.crop((left, top, right, bottom))
imCrop.save("CroppedImages/", "PNG", quality= 100)
# tiles = image_slicer.slice(toslice, 12, save=False)
# image_slicer.save_tiles(tiles, directory='frames')
crop()

Stitching multiple pngs into a h5 image h5py

I created an model in blender. From here I took 2d slices through the y-plane of that model leading to the following.
600 png files each corresponding to a ylocation i.e y=0, y=0.1 etc
Each png file has a resolution of 500 x 600.
I am now trying to merge the 600 pngs into a h5 file using python before loading the .h5 into some software. I find that each individual png file is read fine and looks great. However when I look at the final 3d image there is some stretching of the image, and im not sure how this is being created.
The images are resized (from 600x600 to 500x600, but I have checked and this is not the cause of the stretching). I would like to know why I am introducing such stretching in other planes (not y-plane).
Here is my code, please note that there is some work in progress here, hence why I append the dataset to a list (this is to be used for later code)
from PIL import Image
import sys
import os
import h5py
import numpy as np
import cv2
from datetime import datetime
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(dir_path + '//..//..')
Xlen=500
Ylen=600
Zlen=600
directory=dir_path+"/LowPolyA21/"
for filename in os.listdir(directory):
if fnmatch.fnmatch(filename, '*.png'):
image = Image.open(directory+filename)
new_image = image.resize((Zlen, Xlen))
new_image.save(directory+filename)
dataset = np.zeros((Xlen, Zlen, Ylen), np.float)
# traverse all the pictures under the specified address
cnt_num = 0
img_list = sorted(os.listdir(directory))
os.chdir(directory)
for img in (img_list):
if img.endswith(".png"):
gray_img = cv2.imread(img, 0)
dataset[:, :, cnt_num] = gray_img
cnt_num += 1
dataset[dataset == 0] = -1
dataset=dataset.swapaxes(1,2)
datasetlist=[]
datasetlist.append(dataset)
dz_dy_dz = (float(0.001),float(0.001),float(0.001))
for j in range(Xlen):
for k in range(Ylen):
for l in range(Zlen):
if datasetlist[i][j,k,l]>1:
datasetlist[i][j,k,l]=1
now = datetime.now()
timestamp = now.strftime("%d%m%Y_%H%M%S%f")
out_h5_path='voxelA_'+timestamp+'_flipped'
out_h5_path2='voxelA_'+timestamp+'_flipped.h5'
with h5py.File(out_h5_path2, 'w') as f:
f.attrs['dx_dy_dz'] = dz_dy_dz
f['data'] = datasetlist[i] # Write data to the file's primary key data below
Example of image without stretching (in y-plane)
Example of image with stretching (in x-plane)

Is there a faster alternative to PIL's Image.paste for multiple images?

I am trying to use image.paste to paste many images onto one background.
My images contain their x,y offset values in their filenames (eg. Image_1000_2000.png is offset 1000,2000).
The code below works, but it's painfully slow. Here's what I've got:
import re
import glob
from PIL import Image
# Disable Decompression Bomb Protection
Image.MAX_IMAGE_PIXELS = None
# Set the dimensions of the blank canvas
newheight = 13000
newwidth = 13000
# Glob all the PNG images in the folder and create the blank canvas
photos = glob.glob("*.png")
blankbackground = Image.new('RGB', (newheight, newwidth), (0, 0, 0))
blankbackground.save(r'..\bg999.png', "PNG")
for photo in photos:
blankbackground = Image.open(r'..\bg999.png')
photog = Image.open(photo)
# Get the x , y offsets from the filenames using re
y_value = re.findall(r"0_(\d*).", photo)[0]
y = int(y_value)
x_value = re.findall(r'_(\d*).', photo)[0]
x = int(x_value)
# The actual paste operation, and save the image to be re-opened later
blankbackground.paste(photog,(x,y))
blankbackground.save(r"..\bg999.png")
print(photo)
Any suggestions on a speedier alternative?
EDIT: As per the comments below, it is not necessary to save / reload the image with every photo. This makes it considerably faster.
As pointed out by Siyuan and Dan, Image.save does not require you to save the image and re-load it every loop.
Move the Image.open to before the loop, and move the Image.save to after the loop, as shown:
import re
import glob
from PIL import Image
# Disable Decompression Bomb Protection
Image.MAX_IMAGE_PIXELS = None
# Set the dimensions of the blank canvas
newheight = 13000
newwidth = 13000
# Glob all the PNG images in the folder and create the blank canvas
photos = glob.glob("*.png")
blankbackground = Image.new('RGB', (newheight, newwidth), (0, 0, 0))
blankbackground.save(r'..\bg999.png', "PNG")
# MOVE THE IMAGE.OPEN BEFORE THE LOOP
blankbackground = Image.open(r'..\bg999.png')
for photo in photos:
photog = Image.open(photo)
# Get the x , y offsets from the filenames using re
y_value = re.findall(r"0_(\d*).", photo)[0]
y = int(y_value)
x_value = re.findall(r'_(\d*).', photo)[0]
x = int(x_value)
# The actual paste operation
blankbackground.paste(photog,(x,y))
print(photo)
# MOVE THE IMAGE.SAVE AFTER THE LOOP
blankbackground.save(r"..\bg999.png")
Thus, it goes from ten minutes to ten seconds.

Programmatically change image resolution

I have calculated that if I want my generated image to be A4 size # 600dpi for print purpose, it needs to be 7016x4961px # 72dpi. So, I generate it programmatically, then test it in Photoshop and it seems to be fine so if I resize it, it gets proper size and resolution
.
What I wonder about is if it's possible to make this resizing programmatically, preferably with PIL, but not necessarily with it. I need to make it higher DPI.
If you have generated your image 7016 x 4961 px, it is already A4 at 600 dpi. So you don't need to resize it, you just have to set resolution information in file.
You can do it with PIL:
from PIL import Image
im = Image.open("test.png")
im.save("test-600.png", dpi=(600,600))
This code will resize a PNG image into 7016x4961 with PIL:
size = 7016, 4961
im = Image.open("my_image.png")
im_resized = im.resize(size, Image.ANTIALIAS)
im_resized.save("my_image_resized.png", "PNG")
Perhaps a better approach would be to make your canvas x times bigger prior to printing, where x is a factor you have to figure out (7016x4961 in size for this particular image).
Here's how you can resize by batch (per folder) and skip other file types and Mac system files like .DS_Store
from PIL import Image
import os
Image.MAX_IMAGE_PIXELS = None
path = "./*your-source-folder*"
resize_ratio = 2 # where 0.5 is half size, 2 is double size
def resize_aspect_fit():
dirs = os.listdir(path)
for item in dirs:
print(item)
if item == '.DS_Store':
continue
if item == 'Icon\r':
continue
if item.endswith(".mp4"):
continue
if item.endswith(".txt"):
continue
if item.endswith(".db"):
continue
if os.path.isfile(path+item):
image = Image.open(path+item)
file_path, extension = os.path.splitext(path+item)
new_image_height = int(image.size[0] / (1/resize_ratio))
new_image_length = int(image.size[1] / (1/resize_ratio))
image = image.resize((new_image_height, new_image_length), Image.ANTIALIAS)
image.save("./*your-output-folder*/" + item)
resize_aspect_fit()

Categories

Resources