TL;DR; Use the below code to insert one image onto another at a fixed position (scaled down).
The code below draws an image onto another existing picture. The use case is straight forward: Insert a logo or similar at a fixed position or similar to enhance your figures. As always, check out the related gist on my GitHub which contains the most up-to-date version.
def overlayimage(overlaypicture,
baseimg,
outfile='same',
# position in relative coordinates for icon/other image
position=[0.1, 0.1],
# scaling of image
width=0.2,
# scaling of image
height=0.2,
# if the height/width do not fit the aspect
# of the image, adjust it automatically
keepaspect=True,
# the adjustment is by default for width, change to True
# to scale width instead
adjustheight=False,
# use the top corner (usual for images)
# set to False, as mainly we think from lower left as 0,0
# in science
fromtop=False,
# if outfile is not changed, input is required
# set force to change the original without checking
force=False,
):
import PIL
if outfile == 'same':
if not force:
print('Are you sure you wan\'t to overlay it on the same picture?',
'Set force to suppress this warning')
from time import sleep
try:
print("Press Ctrl+C within 10 seconds and then Y/N")
for i in range(0, 10): # waits 10 seconds
sleep(1)
except KeyboardInterrupt:
ans = input()
if (ans.lower()[0] in ['y', 'j'] and 'n' not in ans.lower()):
force = True
else:
print('There was an unrecognized character!',
'Are you sure you know how this works?',
'Let\'s try again...')
return overlayimage(overlaypicture,
baseimg,
position=position,
width=width,
height=height,
keepaspect=keepaspect,
adjustheight=adjustheight,
fromtop=fromtop,
force=force)
if force:
outfile = baseimg
else:
outfile = outfile.split('.')
outfile[0] += '_overlay'
outfile = '.'.join(outfile)
if width > 1:
width /= 100
if height > 1:
height /= 100
position = [p / 100 if p > 1 else p for p in position]
background = PIL.Image.open(baseimg)
foreground = PIL.Image.open(overlaypicture)
if keepaspect:
if adjustheight:
height *= foreground.height / foreground.width
else:
width *= foreground.width / foreground.height
foreground = foreground.resize((int(width*background.width),
int(height*background.height)),
PIL.Image.LANCZOS)
else:
foreground = foreground.resize((int(width*background.width),
int(height*background.height)),
PIL.Image.LANCZOS)
# 4-tuple defining the left, upper, right, and lower pixel corners
# since we usually define this from lower left but images are from upper
# left the proper position is 1-height-ypos
pos = [int(position[0]*background.width),
int((1-position[1]-height)*background.height)]
background.paste(foreground, pos, foreground)
background.save(outfile)
return outfile
if __name__ == '__main__':
print('*'*10, 'HELP for overlayimage', '*'*10)
print('Put another picture into one that already exists')
print('Outputfile is by default the same and position is 20%',
'and starts from lower left corner but can be adjusted as needed')
The idea is to enhance your graph with a logo or icon that portrays better for what is shown, e.g. insert an animal or similar instead of just text for you tickmarks. While this could be done manually, that feels too tedious if you have to do it many times. As this utility edits saved images, it uses PIL to handle the opening/closing/adding the image.
The usage is relatively straightforward, pass in the two filenames, first the one that is the image to be overlain and second the existing image. If you are certain the placement (whatever you enter for the position, default 10% from top and 10% from left as this is in image coordinates) is fine, you can overwrite the file in place (outfile=’same’). I recommend trying out first what you want to add where. Ideally, this utility is either combined with images that all have the same layout or you get the position directly from a script where you create your images.
Generally, the aspect of the image to be overlain is kept the same and the width is scaled accordingly but this can also be changed via keyword. Hope this helps someone out there.