I have a small background image like this:
it's a smaller than my image size, so I need to draw it repeatedly. (think about background-repeat in css)
I searched a lot and can't find a solution to this....thanks a lot.
Based on the code linked to by Marcin, this will tile a background image on a larger one:
from PIL import Image
# Opens an image
bg = Image.open("NOAHB.png")
# The width and height of the background tile
bg_w, bg_h = bg.size
# Creates a new empty image, RGB mode, and size 1000 by 1000
new_im = Image.new('RGB', (1000,1000))
# The width and height of the new image
w, h = new_im.size
# Iterate through a grid, to place the background tile
for i in xrange(0, w, bg_w):
for j in xrange(0, h, bg_h):
# Change brightness of the images, just to emphasise they are unique copies
bg = Image.eval(bg, lambda x: x+(i+j)/1000)
#paste the image at location i, j:
new_im.paste(bg, (i, j))
new_im.show()
Produces this:
Or removing the Image.eval() line:
Related
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()
I'm trying to export out 1000x1000 thumbnails of images in Python PIL without distorting the original images.
This code works if the original image has dimensions exceeding 1000x1000.
(width, height) = img.size
left = int((width - 1000)/2)
right = left + 1000
new_img = img.crop((left, 0, right, height))
new_img = new_img.resize((1000,1000))
However, if the images have dimensions below this, such as 800 x 400, they become stretched out and distorted.
From what I understand from your question, whatever be the size of the image, it needs to be cropped to a 1000x1000 image.
One way to do this is by first cropping the image into a square and then resizing it to 1000x1000.
(width, height) = img.size
if width < height: # if width is smaller than height, crop height
h = int((height - width)/2)
new_img = img.crop((0, h, width, width+h))
else: # if height is smaller than width, crop width
w = int((width - height)/2)
new_img = img.crop((w, 0, height+w, height))
# resize to required size
new_img = new_img.resize((1000,1000))
It is more efficient to crop first and then enlarge than to enlarge first and then crop. This is because in the second case, you are doing image operations (i.e, cropping) on a larger image which uses more resources (CPU, RAM, etc) than cropping smaller images. If you're working on large number of images this could result considerable difference in processing time.
I want to remove the dark(black strips) and also the white curves in the image, and then align the remained parts connected in a new small-sized image, making the colored parts looks continuously. I hope can get some suggestions for solutions.
I have tried to use PIL to read the image.
I don't know how to set the right threshold and resize the image
I'm not an expert at all in image processing, but let me know if this is enough for you.
Looking at the brightness (sum of the RGB values) distribution, maybe one option is to just filter pixel based on their value:
It looks like the dark parts have a brightness below 100 (or something like that). I filtered it this way:
from PIL import Image
import numpy as np
def filter_image(img,threshold=100):
data = np.array(img.convert('RGB'))
brightness = np.sum(data,axis=2)
filtered_img = data.copy()*0
for i in range(data.shape[0]):
k = 0 # k index the columns that are bright enough
for j in range(data.shape[1]):
if brightness[i,j] > threshold:
filtered_img[i,k,:] = data[i,j,:]
k += 1 # we increment only if it's bright enough
# End of column iterator. The right side of the image is black
return Image.fromarray(filtered_img)
img = Image.open("test.png")
filtered = filter_image(img)
filtered.show()
I get the following result. I'm sure experts can do much better, but it's a start:
The following is only looking for black pixels, as can be seen by the first image, many of the pixels you want out are not black. You will need to find a way to scale up what you will take out.
Also, research will need to be done on collapsing an image, as can be seen by my collapse. Although, this image collapse may work if you are able to get rid of everything but the reddish colors. Or you can reduce by width, which is what the third picture shows.
from PIL import Image, ImageDraw
def main():
picture = Image.open('/Volumes/Flashdrive/Stack_OverFlow/imageprocessing.png', 'r')
# pix_val = list(im.getdata())
# print(pix_val)
# https://code-maven.com/create-images-with-python-pil-pillowimg = Image.new('RGB', (100, 30), color = (73, 109, 137))
blackcount = 0
pix = picture.convert('RGB') # https://stackoverflow.com/questions/11064786/get-pixels-rgb-using-pil
width, height = picture.size
img = Image.new('RGB', (width, height), color=(73, 109, 137))
newpic = []
for i in range(width):
newpictemp = []
for j in range(height):
# https://stackoverflow.com/questions/13167269/changing-pixel-color-python
r, g, b = pix.getpixel((i, j))
if r == 0 and g == 0 and b == 0:
blackcount += 1
else:
img.putpixel((i, j), (r, g, b))
newpictemp.append((r, g, b))
newpic.append(newpictemp)
img.save('pil_text.png')
newheight = int(((width * height) - blackcount) / width)
print(newheight)
img2 = Image.new('RGB', (width, newheight), color=(73, 109, 137))
for i in range(width):
for j in range(newheight):
try:
z = newpic[i][j]
img2.putpixel((i, j), newpic[i][j])
except:
continue
img2.save('pil_text2.png')
if __name__ == "__main__":
main()
No black pixels on left, removed black pixels on right, remove and resize by width (height resize shown in code)
I have a large number of images of a fixed size (say 500*500). I want to write a python script which will resize them to a fixed size (say 800*800) but will keep the original image at the center and fill the excess area with a fixed color (say black).
I am using PIL. I can resize the image using the resize function now, but that changes the aspect ratio. Is there any way to do this?
You can create a new image with the desired new size, and paste the old image in the center, then saving it. If you want, you can overwrite the original image (are you sure? ;o)
import Image
old_im = Image.open('someimage.jpg')
old_size = old_im.size
new_size = (800, 800)
new_im = Image.new("RGB", new_size) ## luckily, this is already black!
box = tuple((n - o) // 2 for n, o in zip(new_size, old_size))
new_im.paste(old_im, box)
new_im.show()
# new_im.save('someimage.jpg')
You can also set the color of the new border with a third argument of Image.new() (for example: Image.new("RGB", new_size, "White"))
Yes, there is.
Make something like this:
from PIL import Image, ImageOps
ImageOps.expand(Image.open('original-image.png'),border=300,fill='black').save('imaged-with-border.png')
You can write the same at several lines:
from PIL import Image, ImageOps
img = Image.open('original-image.png')
img_with_border = ImageOps.expand(img,border=300,fill='black')
img_with_border.save('imaged-with-border.png')
And you say that you have a list of images. Then you must use a cycle to process all of them:
from PIL import Image, ImageOps
for i in list-of-images:
img = Image.open(i)
img_with_border = ImageOps.expand(img,border=300,fill='black')
img_with_border.save('bordered-%s' % i)
Alternatively, if you are using OpenCV, they have a function called copyMakeBorder that allows you to add padding to any of the sides of an image. Beyond solid colors, they've also got some cool options for fancy borders like reflecting or extending the image.
import cv2
img = cv2.imread('image.jpg')
color = [101, 52, 152] # 'cause purple!
# border widths; I set them all to 150
top, bottom, left, right = [150]*4
img_with_border = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)
Sources: OpenCV border tutorial and
OpenCV 3.1.0 Docs for copyMakeBorder
PIL's crop method can actually handle this for you by using numbers that are outside the bounding box of the original image, though it's not explicitly stated in the documentation. Negative numbers for left and top will add black pixels to those edges, while numbers greater than the original width and height for right and bottom will add black pixels to those edges.
This code accounts for odd pixel sizes:
from PIL import Image
with Image.open('/path/to/image.gif') as im:
old_size = im.size
new_size = (800, 800)
if new_size > old_size:
# Set number of pixels to expand to the left, top, right,
# and bottom, making sure to account for even or odd numbers
if old_size[0] % 2 == 0:
add_left = add_right = (new_size[0] - old_size[0]) // 2
else:
add_left = (new_size[0] - old_size[0]) // 2
add_right = ((new_size[0] - old_size[0]) // 2) + 1
if old_size[1] % 2 == 0:
add_top = add_bottom = (new_size[1] - old_size[1]) // 2
else:
add_top = (new_size[1] - old_size[1]) // 2
add_bottom = ((new_size[1] - old_size[1]) // 2) + 1
left = 0 - add_left
top = 0 - add_top
right = old_size[0] + add_right
bottom = old_size[1] + add_bottom
# By default, the added pixels are black
im = im.crop((left, top, right, bottom))
Instead of the 4-tuple, you could instead use a 2-tuple to add the same number of pixels on the left/right and top/bottom, or a 1-tuple to add the same number of pixels to all sides.
It is important to consider old dimension, new dimension and their difference here. If the difference is odd (not even), you will need to specify slightly different values for left, top, right and bottom borders.
Assume the old dimension is ow,oh and new one is nw,nh.
So, this would be the answer:
import Image, ImageOps
img = Image.open('original-image.png')
deltaw=nw-ow
deltah=nh-oh
ltrb_border=(deltaw/2,deltah/2,deltaw-(deltaw/2),deltah-(deltah/2))
img_with_border = ImageOps.expand(img,border=ltrb_border,fill='black')
img_with_border.save('imaged-with-border.png')
You can load the image with scipy.misc.imread as a numpy array. Then create an array with the desired background with numpy.zeros((height, width, channels)) and paste the image at the desired location:
import numpy as np
import scipy.misc
im = scipy.misc.imread('foo.jpg', mode='RGB')
height, width, channels = im.shape
# make canvas
im_bg = np.zeros((height, width, channels))
im_bg = (im_bg + 1) * 255 # e.g., make it white
# Your work: Compute where it should be
pad_left = ...
pad_top = ...
im_bg[pad_top:pad_top + height,
pad_left:pad_left + width,
:] = im
# im_bg is now the image with the background.
ximg = Image.open(qpath)
xwid,xhgt = func_ResizeImage(ximg)
qpanel_3 = tk.Frame(Body,width=xwid+10,height=xhgt+10,bg='white',bd=5)
ximg = ximg.resize((xwid,xhgt),Image.ANTIALIAS)
ximg = ImageTk.PhotoImage(ximg)
panel = tk.Label(qpanel_3,image=ximg)
panel.image = ximg
panel.grid(row = 2)
from PIL import Image
from PIL import ImageOps
img = Image.open("dem.jpg").convert("RGB")
This part will add black borders at the sides (10% of width)
img_side = ImageOps.expand(img, border=(int(0.1*img.size[0]),0,int(0.1*img.size[0]),0), fill=(0,0,0))
img_side.save("sunset-sides.jpg")
This part will add black borders at the bottom & top (10% of height)
img_updown = ImageOps.expand(img, border=(0,int(0.1*img.size[1]),0,int(0.1*img.size[1])), fill=(0,0,0))
img_updown.save("sunset-top_bottom.jpg")
This part will add black borders at the bottom,top & sides (10% of height-width)
img_updown_side = ImageOps.expand(img, border=(int(0.1*img.size[0]),int(0.1*img.size[1]),int(0.1*img.size[0]),int(0.1*img.size[1])), fill=(0,0,0))
img_updown_side.save("sunset-all_sides.jpg")
img.close()
img_side.close()
img_updown.close()
img_updown_side.close()
I'm not familiar with PIL, but I know it's very easy to put a bunch of images into a grid in ImageMagick.
How do I, for example, put 16 images into a 4×4 grid where I can specify the gap between rows and columns?
This is easy to do in PIL too. Create an empty image and just paste in the images you want at whatever positions you need using paste. Here's a quick example:
import Image
#opens an image:
im = Image.open("1_tree.jpg")
#creates a new empty image, RGB mode, and size 400 by 400.
new_im = Image.new('RGB', (400,400))
#Here I resize my opened image, so it is no bigger than 100,100
im.thumbnail((100,100))
#Iterate through a 4 by 4 grid with 100 spacing, to place my image
for i in xrange(0,500,100):
for j in xrange(0,500,100):
#I change brightness of the images, just to emphasise they are unique copies.
im=Image.eval(im,lambda x: x+(i+j)/30)
#paste the image at location i,j:
new_im.paste(im, (i,j))
new_im.show()
Expanding on the great answer by fraxel, I wrote a program which takes in a folder of (.png) images, a number of pixels for the width of the collage, and the number of pictures per row, and does all the calculations for you.
#Evan Russenberger-Rosica
#Create a Grid/Matrix of Images
import PIL, os, glob
from PIL import Image
from math import ceil, floor
PATH = r"C:\Users\path\to\images"
frame_width = 1920
images_per_row = 5
padding = 2
os.chdir(PATH)
images = glob.glob("*.png")
images = images[:30] #get the first 30 images
img_width, img_height = Image.open(images[0]).size
sf = (frame_width-(images_per_row-1)*padding)/(images_per_row*img_width) #scaling factor
scaled_img_width = ceil(img_width*sf) #s
scaled_img_height = ceil(img_height*sf)
number_of_rows = ceil(len(images)/images_per_row)
frame_height = ceil(sf*img_height*number_of_rows)
new_im = Image.new('RGB', (frame_width, frame_height))
i,j=0,0
for num, im in enumerate(images):
if num%images_per_row==0:
i=0
im = Image.open(im)
#Here I resize my opened image, so it is no bigger than 100,100
im.thumbnail((scaled_img_width,scaled_img_height))
#Iterate through a 4 by 4 grid with 100 spacing, to place my image
y_cord = (j//images_per_row)*scaled_img_height
new_im.paste(im, (i,y_cord))
print(i, y_cord)
i=(i+scaled_img_width)+padding
j+=1
new_im.show()
new_im.save("out.jpg", "JPEG", quality=80, optimize=True, progressive=True)