TomNorway
TomNorway

Reputation: 3182

Aligning a text box edge with an image corner

I'm looking for a way of exactly aligning (overlaying) the corner edge of my image with corner and edge of a text box edge (bbox or other)

The code in question is:

import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1)
ax.imshow(np.random.random((256,256)), cmap=plt.get_cmap("viridis"))
ax.axis("off")

ax.annotate(
    s = 'image title', 
    xy=(0, 0), 
    xytext=(0, 0), 
    va='top',
    ha='left',
    fontsize = 15,
    bbox=dict(facecolor='white', alpha=1),
    )
plt.show()

Single image

As you can see, the edges of the text box is outside the image. For the life of me, I cannot find a consistent way of aligning the corner of the text box with the corner of the image. Ideally, I'd like the alignment to be independent of font size and image pixel size, but that might be asking a bit too much.

Finally, I'd like to achieve this with a grid of images, like the second example, below.

import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8, 8))
images = 4*[np.random.random((256,256))]

gs = gridspec.GridSpec(
    nrows=2, 
    ncols=2,
    top=1., 
    bottom=0., 
    right=1., 
    left=0., 
    hspace=0.,
    wspace=0.,
)
for g, i in zip(gs, range(len(images))):
    ax = plt.subplot(g)
    im = ax.imshow(
        images[i],
        cmap=plt.get_cmap("viridis")
    )
    ax.set_xticks([])
    ax.set_yticks([])
    ax.annotate(
        s = 'image title', 
        xy=(0, 0), 
        xytext=(0, 0), 
        va='top',
        ha='left',
        fontsize = 15,
        bbox=dict(facecolor='white', alpha=1),
    )

enter image description here

Upvotes: 5

Views: 1698

Answers (2)

TomNorway
TomNorway

Reputation: 3182

Thanks to P-robot for the solution. The key part of the solution is that the annotation text edge is offset x and y by one pixel from the xy coordinate. Any extra padding used increases the necessary amount to compensate for this offset. The second grouping of arguments given to ax.annotate, below, are the relevant ones.

fig, ax = plt.subplots(1)
ax.imshow(np.random.random((256,256)), cmap=plt.get_cmap("viridis"))
ax.axis("off")

padding = 5
ax.annotate(
    s = 'image title', 
    fontsize = 12,

    xy=(0, 0), 
    xytext=(padding-1, -(padding-1)), 
    textcoords = 'offset pixels',
    bbox=dict(facecolor='white', alpha=1, pad=padding),
    va='top',
    ha='left',
    )
plt.show()

Aligned image

Oddly, for the grid of four images, the offset in the x-direction did not require the subtraction of one pixel, which changes xytext to xytext=(padding, -(padding-1)).

Aligned grid of four images

Upvotes: 5

p-robot
p-robot

Reputation: 4904

The issue is caused from the padding of the bounding box. You can change the padding by passing the pad argument to the bounding box dictionary (for instance pad = 0 will keep the box inside the axes). I'm assuming you want some padding so it's probably best to set a padding argument and remove this from the position of the annotation (in units of pixels).

Upvotes: 3

Related Questions