Stitch series of photos from two directories using PIL - python

I want to stitch a series of images together using the Python Imaging Library. However, the images I want to stitch together are contained in two separate directories. Is PIL able to stitch images under this condition?
I have two series of 10 images stored in two directories - let's call them D1 and D2. I would like to use PIL to stitch Image 1 from D1 with Image 1 from D2, Image 2 from D1 with Image 2 from D2, etc. I have a third directory, D3, in which I would like to save the stitched output images.
I thought the correct way to do this would be to use the code snipped provided by user d3ming in [this example] (Stitching Photos together) and use nested for loops to loop over D1 and D2 to provide input images.
Here is the code that I have currently:
list_im1 = sorted(glob.glob(in_dir_1+"*")) #make list of first set of images
list_im2 = sorted(glob.glob(in_dir_2+"*")) #make list of second set of images
def merge_images(file1, file2):
"""Merge two images into one, displayed side by side
:param file1: path to first image file
:param file2: path to second image file
:return: the merged Image object
"""
image1 = Image.open(file1)
image2 = Image.open(file2)
(width1, height1) = image1.size
(width2, height2) = image2.size
result_width = width1 + width2
result_height = max(height1, height2)
result = Image.new('RGB', (result_width, result_height))
result.paste(im=image1, box=(0, 0))
result.paste(im=image2, box=(width1, 0))
return result
merged = merge_images(file1, file2)
merged.save(out_dir)
for i in in_dir_1: #first loop through D1
for j in in_dir_2: #second loop through D2
merge_images(i, j)
I expected that this code snippet, combined with the nested loop, would run through in_dir_1 (D1), search through the image with the same position in in_dir_2 (D2), and return me a series of 10 stitched images in out_dir (D3). However, my code is not returning any output images at all.
Any help will be greatly appreciated.

Immediate solution is replacing the return statement with Image.save().
list_im1 = sorted(glob.glob(in_dir_1+"*")) #make list of first set of images
list_im2 = sorted(glob.glob(in_dir_2+"*")) #make list of second set of images
def merge_images(file1, file2):
"""Merge two images into one, displayed side by side
:param file1: path to first image file
:param file2: path to second image file
"""
global ctr
image1 = Image.open(file1)
image2 = Image.open(file2)
(width1, height1) = image1.size
(width2, height2) = image2.size
result_width = width1 + width2
result_height = max(height1, height2)
result = Image.new('RGB', (result_width, result_height))
result.paste(im=image1, box=(0, 0))
result.paste(im=image2, box=(width1, 0))
result.save(out_dir + str(ctr) + ".png")
ctr += 1
ctr = 1
for i in in_dir_1: #first loop through D1
for j in in_dir_2: #second loop through D2
merge_images(i, j)
I would suggest you to use a dynamic output image file path (out_dir), meaning the file path should change with different files. Otherwise same image file would be overwritten over and over as the program goes on.
EDIT:-
If you want each image to be saved distinctly, then you can use a counter (just a number to keep track of which image is being processed).
I will be using a global variable which will increment the value of the variable each time a new image has been processed.
Edited the code accordingly.
EDIT 2:-
Since, each image has it's pair, therefore the total number of images in both the folder will be same. So you can use eval() instead of using a pair of for loops in order to get through that issue.
Just replace:-
for i in in_dir_1:
for j in in_dir_2:
merge_images(i, j)
By:-
for i, x in eval(in_dir_1):
merge_images(in_dir_1[i], in_dir_2[i])

Related

How to merge images after spliting into grids?

I'm trying to split single image into multiple grids in order to reduce dimension and do some image processing on that and then combine back splitted image to single image.
currently Im splitting single image into 72 multiple grids as shown in below code
from imutils import paths
from PIL import Image
from itertools import product
import os
source_path = r'D:\Data\images'
dest_path = r'D:\Data\cropped_data\\'
all_images = list(paths.list_images(source_path))
for img_path in all_images:
img = Image.open(img_path)
w,h = img.size
d = 500
grid = product(range(0, h-h%d, d), range(0, w-w%d, d))
file_name = img_path.split('\\')[-1].split('.')[0]
print(file_name)
save_path = dest_path+file_name
print(save_path)
os.makedirs(save_path,exist_ok=True)
for i, j in grid:
box = (j, i, j+d, i+d)
out = save_path+'\\'+'cropped'+f'_{i}_{j}'+'.jpg'
print(out)
img.crop(box).save(out)
above code snippet crop image into 72 mulitple grids and saves it in folder, I'm doing some preprocessing and saving it in same folder, I want to merge these images again back to original size in same sequence , I'm trying below code to achieve this
import glob
from PIL import Image
from itertools import product
all_images = glob.glob(r'splitted_image_folder\*.png')
d = 500
w = 6016
h = 3376
new_im = Image.new('RGB',(w,h),(250,250,250))
grid = product(range(0,h-h%d,d),range(0,w-w%d,d))
for u,(i,j) in enumerate(grid):
img = Image.open(all_images[u])
new_im.paste(img,(i,j))
new_im.save("merged_img.png","PNG")
executing this code merge only half of images at the first half of image and leaving other half in white,instead of merging all grid images it only pasting half of the images , I'm not able to understand why its missing other half .
Any guide or suggestion to achieve this will be appreciated

Count number of classes in a semantic segmented image

I have an image that is the output of a semantic segmentation algorithm, for example this one
I looked online and tried many pieces of code but none worked for me so far.
It is clear to the human eye that there are 5 different colors in this image: blue, black, red, and white.
I am trying to write a script in python to analyze the image and return the number of colors present in the image but so far it is not working. There are many pixels in the image which contain values that are a mixture of the colors above.
The code I am using is the following but I would like to understand if there is an easier way in your opinion to achieve this goal.
I think that I need to implement some sort of thresholding that has the following logic:
Is there a similar color to this one? if yes, do not increase the count of colors
Is this color present for more than N pixels? If not, do not increase the count of colors.
from PIL import Image
imgPath = "image.jpg"
img = Image.open(imgPath)
uniqueColors = set()
w, h = img.size
for x in range(w):
for y in range(h):
pixel = img.getpixel((x, y))
uniqueColors.add(pixel)
totalUniqueColors = len(uniqueColors)
print(totalUniqueColors)
print(uniqueColors)
Thanks in advance!
I solved my issue and I am now able to count colors in images coming from a semantic segmentation dataset (the images must be in .png since it is a lossless format).
Below I try to explain what I have found in the process for a solution and the code I used which should be ready to use (you need to just change the path to the images you want to analyze).
I had two main problems.
The first problem of the color counting was the format of the image. I was using (for some of the tests) .jpeg images that compress the image.
Therefore from something like this
If I would zoom in the top left corner of the glass (marked in green) I was seeing something like this
Which obviously is not good since it will introduce many more colors than the ones "visible to the human eye"
Instead, for my annotated images I had something like the following
If I zoom in the saddle of the bike (marked in green) I had something like this
The second problem was that I did not convert my image into an RGB image.
This is taken care in the code from the line:
img = Image.open(filename).convert('RGB')
The code is below. For sure it is not the most efficient but for me it does the job. Any suggestion to improve its performance is appreciated
import numpy as np
from PIL import Image
import argparse
import os
debug = False
def main(data_dir):
print("This small script allows you to count the number of different colors in an image")
print("This code has been written to count the number of classes in images from a semantic segmentation dataset")
print("Therefore, it is highly recommended to run this code on lossless images (such as .png ones)")
print("Images are being loaded from: {}".format(data_dir))
directory = os.fsencode(data_dir)
interesting_image_format = ".png"
# I will put in the variable filenames all the paths to the images to be analyzed
filenames = []
for file in os.listdir(directory):
filename = os.fsdecode(file)
if filename.endswith(interesting_image_format):
if debug:
print(os.path.join(directory, filename))
print("Analyzing image: {}".format(filename))
filenames.append(os.path.join(data_dir, filename))
else:
if debug:
print("I am not doing much here...")
continue
# Sort the filenames in an alphabetical order
filenames.sort()
# Analyze the images (i.e., count the different number of colors in the images)
number_of_colors_in_images = []
for filename in filenames:
img = Image.open(filename).convert('RGB')
if debug:
print(img.format)
print(img.size)
print(img.mode)
data_img = np.asarray(img)
if debug:
print(data_img.shape)
uniques = np.unique(data_img.reshape(-1, data_img.shape[-1]), axis=0)
# uncomment the following line if you want information for each analyzed image
print("The number of different colors in image ({}) {} is: {}".format(interesting_image_format, filename, len(uniques)))
# print("uniques.shape[0] for image {} is: {}".format(filename, uniques.shape[0]))
# Put the number of colors of each image into an array
number_of_colors_in_images.append(len(uniques))
print(number_of_colors_in_images)
# Print the maximum number of colors (classes) of all the analyzed images
print(np.max(number_of_colors_in_images))
# Print the average number of colors (classes) of all the analyzed images
print(np.average(number_of_colors_in_images))
def args_preprocess():
# Command line arguments
parser = argparse.ArgumentParser()
parser.add_argument(
"--data_dir", default="default_path_to_images", type=str, help='Specify the directory path from where to take the images of which we want to count the classes')
args = parser.parse_args()
main(args.data_dir)
if __name__ == '__main__':
args_preprocess()
The thing mentioned above about the lossy compression in .jpeg images and lossless compression in .png seems to be a nice thing to point out. But you can use the following piece of code to get the number of classes from a mask.
This is only applicable on .png images. Not tested on .jpeg images.
import cv2 as cv
import numpy as np
img_path = r'C:\Users\Bhavya\Downloads\img.png'
img = cv.imread(img_path)
img = np.array(img, dtype='int32')
pixels = []
for i in range(img.shape[0]):
for j in range(img.shape[1]):
r, g, b = list(img[i, j, :])
pixels.append((r, g, b))
pixels = list(set(pixels))
print(len(pixels))
In this solution what I have done is appended pair of pixel values(RGB) in the input image to a list and converted the list to set and then back to list. The first conversion of list to set removes all the duplicate elements(here pixel values) and gives unique pixel values and the next conversion from set to list is optional and just to apply some future list operations on the pixels.
Something has gone wrong - your image has 1277 unique colours, rather than the 5 you suggest.
Have you maybe saved/shared a lossy JPEG rather than the lossless PNG you should prefer for classified images?
A fast method of counting the unique colours with Numpy is as follows:
def withNumpy(img):
# Ignore A channel
px = np.asarray(img)[...,:3]
# Merge RGB888 into single 24-bit integer
px24 = np.dot(np.array(px, np.uint32),[1,256,65536])
# Return number of unique colours
return len(np.unique(px24))

How implement an array of images in Python-opencv?

I am programming a code that uses 30 images and I want to put those images in an array to resize them and then use them in other functions. I was trying some things but the second loop just shows me one unique image resized 30 times.
import cv2 as cv
import glob
import numpy as np
files = glob.glob ("C:/Users/Project/imgs/*.jpg")
images = np.empty(len(files))
#This loop reads images and show rgb images
#Works OK
for i in files:
#print(myFile)
images= cv.imread(i)
cv.imshow('myFile'+str(i),images)
new = []
for i in range(30):
new = cv.resize(images,(200,266))
cv.imshow('imagen', new)
cv.waitKey(0)
cv.destroyAllWindows()
If you want to keep many elements then first create empty list and next use apppend() to add element to list.
More or less
all_images = []
for name in files:
#print(name)
image = cv.imread(name)
cv.imshow('myFile '+name, image) # you don't need `str()`
all_images.append(image)
resized_images = []
for image in all_images:
new = cv.resize(image, (200,266))
cv.imshow('imagen', new)
resized_images.append(new)
If you want to resize only first 30 images
for image in all_images[:30]:

Program to Create an image grid dynamically from a folder with images in it

I have a folder with a set of images in it. I want to create any type of program that can take in the path of the folder, number of rows and columns and give me output with an image grid. I am not sure how to do that.
Primitive example.
It uses sys.argv to get arguments pattern instead of path (like "path/*.jpg"), rows, columns
python script 'images/dot-*.png' 2 3
It uses module PIL/pillow to read images, resize them and paste on output image.
import sys
import glob
from PIL import Image
# for test only
#sys.argv += ['images/dot-*.png', 2, 3]
# get arguments
pattern = sys.argv[1]
rows = int(sys.argv[2])
cols = int(sys.argv[3])
# get filenames
filenames = glob.glob(pattern)
# load images and resize to (100, 100)
images = [Image.open(name).resize((100, 100)) for name in filenames]
# create empty image to put thumbnails
new_image = Image.new('RGB', (cols*100, rows*100))
# put thumbnails
i = 0
for y in range(rows):
if i >= len(images):
break
y *= 100
for x in range(cols):
x *= 100
img = images[i]
new_image.paste(img, (x, y, x+100, y+100))
print('paste:', x, y)
i += 1
# save it
new_image.save('output.jpg')

Drawing multiple Images to a tiff file in python

I have same size of multiple images and I want to draw those images on a tiff file in such a way that there should be less than 5 elements in a row with some x distance (horizontally along the row) between its centres and y distance (vertically along the column) The images are stored in a folder, the program should read images and draw images on tiff file.
I found this as somewhat useful (and nearer to what i require) http://www.astrobetter.com/plotting-to-a-file-in-python/ But it is plotting a graph to file. I want to put images to my tiff file
How should I proceed?
This is what you describe, I think. Here is the image, you can have many of them as long as they're all the same size, configure the values on the images list in the code to change this.
and this is the output of the program:
and here is the code:
import Image
images = ['image.jpg','image.jpg','image.jpg','image.jpg','image.jpg','image.jpg','image.jpg']
hsize = min(5,len(images))
vsize = (len(images)/5) + 1
print hsize,vsize
vspace = 10
hspace = 10
(h,w) = Image.open(images[0]).size
im = Image.new('RGB',((hsize*(h+hspace)),(vsize*(w+vspace)) ))
for i,filename in enumerate(images):
imin = Image.open(filename).convert('RGB')
xpos = i % hsize
ypos = i / hsize
print xpos,ypos
im.paste(imin,(xpos*(h+hspace),ypos*(w+vspace)))
im.save('output.jpg')

Categories

Resources