Python PIL/Image make 3x3 Grid from sequence Images - python

I'm trying to make a 3x3 Grid by sequence images but can't seem to get it right. The images are in folder, named from 0 - 8 (total 9 images), the output of the final one image grid of 3x3 should as follow
image0 image1 image2
image3 image4 image5
image6 image7 image8
I was trying to follow How do you merge images into a canvas using PIL/Pillow? but couldn't get it work correctly.
There are no need to change anything in the image, just merge them and make a 3x3 Grid

To make a grid of arbitrary shape (cols*img_height, rows*img_width) out of rows*cols images:
def image_grid(imgs, rows, cols):
assert len(imgs) == rows*cols
w, h = imgs[0].size
grid = Image.new('RGB', size=(cols*w, rows*h))
grid_w, grid_h = grid.size
for i, img in enumerate(imgs):
grid.paste(img, box=(i%cols*w, i//cols*h))
return grid
In your case, assuming imgs is a list of PIL images:
grid = image_grid(imgs, rows=3, cols=3)

Here's an example how this can be done (consider image is one of your images):
img_w, img_h = image.size
background = Image.new('RGBA',(1300, 1300), (255, 255, 255, 255))
bg_w, bg_h = background.size
offset = (10,(((bg_h - img_h)) / 2)-370)
background.paste(image1,offset)
Adjust the offset, width and height to fit your requirements.

Related

How to iterate over multiple images of different dimensions and stack them into a single picture horizontally?

I have a multiple pictures with different dimensions. I have been trying to concat them horizontally using openCV.
The process is kind of following:
Iterate over all the images to find the max width and total height.
Create a black mask that is with the size of max width and total height got from all the images.
Stack all the images horizontally on that black mask.
I am not sure how to do this thing. Kindly help me!
Images are just 3D matrices, so you can do this very easily by creating a matrix of zeros (= black) of the desired size, then filling in your images.
I've created fake images here but you can use cv2 to read in your real images.
import numpy as np
import matplotlib.pyplot as plt
# create three images of different shapes and different shades of grey
img1 = np.ones((100, 200, 3), dtype=int)*50
img2 = np.ones((200, 400, 3), dtype=int)*100
img3 = np.ones((100, 300, 3), dtype=int)*150
imgs = [img1, img2, img3]
# get max width and total height
max_width = 0
total_height = 0
for img in imgs:
total_height += img.shape[0]
max_width = max(max_width, img.shape[1])
# make black canvas of appropriate shape
canvas = np.zeros((total_height, max_width, 3), dtype=int)
# stack images on canvas
start_height = 0
for img in imgs:
print(img.shape)
canvas[start_height:start_height+img.shape[0], 0:img.shape[1], :] = img
start_height+= img.shape[0]
# show results
plt.imshow(canvas)
This produces the following result:

How to merge multiple pictures diagonally into a single one using Python

I'm trying to merge multiple images diagonally into a single one using Python.
I checked a lot of questions but didn't find something similar to my need.
All I can do right now is a simple merge of files on top of each other:
from PIL import Image
import numpy as np
img = Image.open("1.png")
background = Image.open("2.png")
background.paste(img, (0, 0), img)
background.save('result.png',"PNG")
Here are the pictures to test :
image1, image2, image3
I need the pictures to be arranged diagonally to fit into a final 900 x 1200 px size picture with white Background. Probably they need to be sized down a bit and fit ? At least that's the process I am doing in Photoshop, manually (time consuming).
Sometimes there's 2 pictures to fit, sometimes could be 4 or 5.
This should do the job:
from PIL import Image
images = ['1.png', '2.png', '3.png']
# shift between images
offset = (200, 100)
target_size = (900, 1200)
images = [Image.open(fn) for fn in images]
no_img = len(images)
image_size = [s+no_img*o for s, o in zip(images[0].size, offset)]
#create empty background
combined_image = Image.new('RGBA', image_size)
# paste each image at a slightly shifted position, start at top right
for idx, image in enumerate(images):
combined_image.paste(image, ((no_img - idx - 1) * offset[0], idx * offset[1]), image)
# crop to non-empty area
combined_image = combined_image.crop(combined_image.getbbox())
# resizing and padding such that it fits 900 x 1200 px
scale = min(target_size[0] / combined_image.size[0], target_size[1] / combined_image.size[1])
combined_image = combined_image.resize((int(combined_image.size[0] * scale), int(combined_image.size[1] * scale)), Image.BICUBIC)
img_w, img_h = combined_image.size
finale_output = Image.new('RGB', target_size, (255, 255, 255))
offset = ((target_size[0] - img_w) // 2, (target_size[1] - img_h) // 2)
finale_output.paste(combined_image, offset, combined_image)
# display
finale_output.show()
EDIT: I added the code for resizing and padding such that the output is exactly of your wanted size (whilst maintaining the aspect ratio).

How can I randomly add 20 images(10x10) in an empty background image (200x200) in python?

I want the 10 small images to be placed in this circle
I'm working on a small project to randomly place or put several images of size (10 w x 10 h) in another image that will be used as background of size (200 w x 200 h) in python. The small images should be put at a random location in the background image.
I have 20 small images of size (10x10) and one empty image background of size (200x200). I want to put my 20 small images in the empty background image at a random location in the background.
Is there a way to do it in Python?
Code
# Depencies importation
import cv2
# Saving directory
saving_dir = "../Saved_Images/"
# Read the background image
bgimg = cv2.imread("../Images/background.jpg")
# Resizing the bacground image
bgimg_resized = cv2.resize(bgimg, (2050,2050))
# Read the image that will be put in the background image (exemple of 1)
small_img = cv2.imread("../Images/small.jpg")
# Convert the resized background image to gray
bgimg_gray = cv2.cvtColor(bgimg, cv2.COLOR_BGR2GRAY)
# Convert the grayscale image to a binary image
ret, thresh = cv2.threshold(bgimg_gray,127,255,0)
# Determine the moments of the binary image
M = cv2.moments(thresh)
# calculate x,y coordinate of center
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
# drawing the circle in the background image
circle = cv2.circle(bgimg, (cX, cY), 930, (0,0,255), 9)
print(circle)
# Saving the new image
cv2.imwrite(saving_dir+"bgimg"+".jpg", bgimg)
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.resizeWindow("Test", 1000, 1200)
# Showing the images
cv2.imshow("image", bgimg)
# Waiting for any key to stop the program execution
cv2.waitKey(0)
the above code is for one image, I want to do it for the 20 and to put them in a random location
Assuming you have that background image background.jpg (decreased to 200x200 px) and 10 images: image01.png, image02.png ... image10.png (10x10 px). Then:
import glob
import random
from PIL import Image
img_bg = Image.open('circle.jpg')
width, height = img_bg.size
images = glob.glob('*.png')
for img in images:
img = Image.open(img)
x = random.randint(40, width-40)
y = random.randint(40, height-40)
img_bg.paste(img, (x, y, x+10, y+10))
img_bg.save('result.png', 'PNG')
Output image:

python - Cropping an image of handwritten digit

I'm trying to predict handwritten digits using MNIST as the dataset & python. Right now, I have to give already cropped images as input to the program.
Further processing to make it to MNIST dataset format is done using the following function, but how to auto crop a random image given as input ?
def imageprepare(argv):
"""
This function returns the pixel values.
The imput is a png file location.
"""
im = Image.open(argv).convert('L')
width = float(im.size[0])
height = float(im.size[1])
newImage = Image.new('L', (28, 28), (255)) #creates white canvas of 28x28 pixels
if width > height: #check which dimension is bigger
#Width is bigger. Width becomes 20 pixels.
nheight = int(round((20.0/width*height),0)) #resize height according to ratio width
if (nheigth == 0): #rare case but minimum is 1 pixel
nheigth = 1
# resize and sharpen
img = im.resize((20,nheight), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wtop = int(round(((28 - nheight)/2),0)) #caculate horizontal pozition
newImage.paste(img, (4, wtop)) #paste resized image on white canvas
else:
#Height is bigger. Heigth becomes 20 pixels.
nwidth = int(round((20.0/height*width),0)) #resize width according to ratio height
if (nwidth == 0): #rare case but minimum is 1 pixel
nwidth = 1
# resize and sharpen
img = im.resize((nwidth,20), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wleft = int(round(((28 - nwidth)/2),0)) #caculate vertical pozition
newImage.paste(img, (wleft, 4)) #paste resized image on white canvas
#newImage.save("sample.png")
tv = list(newImage.getdata()) #get pixel values
#normalize pixels to 0 and 1. 0 is pure white, 1 is pure black.
tva = [ (255-x)*1.0/255.0 for x in tv]
return tva
You can use OpenCV contours to locate potential digits within your actual image, some of the techniques will depend on the actual data you are working from. There is an example of digit candidate location at http://www.pyimagesearch.com/2017/02/13/recognizing-digits-with-opencv-and-python/
that can give you some pointers.
However, you may get problems with some scripts as I believe that while in all European scripts every digit is supposed to be contiguous and distinct I am not sure that both points apply to all scripts.

how can I use the python imaging library to create a bitmap

I have a 2d list in python, and I want to make a graphical pic of the data. Maybe a n by m column grid where each square is a different color of grey depending on the value in my 2d list.
However, I can't seem to figure out how to create images using PIL. This is some of the stuff I've been messing with:
def createImage():
img = Image.new('L', (100,100), 'white')
img.save('test.bmp')
for i in range(0,15):
for j in range(0,15):
img.putpixel((i,j), (255,255,255))
However, I'm getting an error saying that an integer is required (problem on the line with the putpixel)
This is from http://en.wikibooks.org/wiki/Python_Imaging_Library/Editing_Pixels:
from PIL import Image
img = Image.new( 'RGB', (255,255), "black") # Create a new black image
pixels = img.load() # Create the pixel map
for i in range(img.size[0]): # For every pixel:
for j in range(img.size[1]):
pixels[i,j] = (i, j, 100) # Set the colour accordingly
img.show()

Categories