1

SOLUTION

What works for me is to normalize my matrices between 0 and 1 rather than 0 and 255. I have no idea why this works, but if someone has an explanation, I'll accept the answer.


ORIGINAL QUESTION

I have a dataset of images that are black rectangles on white fields. For example, here are three such images (sorry, no borders):

enter image description here enter image description here enter image description here

I want to build a scatter plot in which each data point is represented as an image. I used the code from this SO, but got a plot that looked like this (plotted with borders for debugging):

enter image description here

As you can see, the images all look like squares. In fact, they should be various sized rectangles with different positions inside the frame. Is there a way to fix this?

Here is some code that generates some images and then plots them as scatter plot points. You can see that the images it saves (you need to create an images directory in the same directory from which you run the script) are different from the images that are plotted by matplotlib:

from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import matplotlib.patches as patches
import random
import numpy as np

# ------------------------------------------------------------------------------

def create_image():
    rx = random.random()
    ry = random.random()
    rw = random.random() / 2
    rh = random.random() / 2
    DPI = 72
    fig = plt.figure(frameon=False,
                     figsize=(32/DPI, 32/DPI),
                     dpi=DPI)
    ax = fig.add_axes([0, 0, 1, 1])
    ax.axis('off')
    rect = patches.Rectangle((rx, ry),
                             rw, rh,
                             linewidth=1,
                             edgecolor='none',
                             facecolor=(0, 0, 0))
    ax.add_patch(rect)
    fig.canvas.draw()
    img = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8)
    width, height = fig.get_size_inches() * fig.get_dpi()
    img = img.reshape((32, 32, 3))
    img = img.T
    fig.savefig('images/%s.png' % i)
    plt.clf()
    plt.close()
    return img

# ------------------------------------------------------------------------------

def imscatter(points, images, ax):
    for (x, y), image in zip(points, images):
        im = OffsetImage(image.T, zoom=1)
        ab = AnnotationBbox(im, (x, y), frameon=True, pad=0.2)
        ax.add_artist(ab)

# ------------------------------------------------------------------------------

fig, ax = plt.subplots()

# Create ten random points.
N_SAMPLES = 10
points = np.random.random((N_SAMPLES,2)) * 100
images = np.zeros((N_SAMPLES, 3, 32, 32))
for i in range(N_SAMPLES):
    images[i] = create_image()

Xp = points[:, 0]
Yp = points[:, 1]

ax.set_xlim([Xp.min().astype(int), Xp.max().astype(int)])
ax.set_ylim([Yp.min().astype(int), Yp.max().astype(int)])

imscatter(points, images, ax)

plt.show()

From the comments:

@ImportanceOfBeingErnest gets this image when running my script locally, while I get this image. My guess is that this is related to DPI or resolution somehow.

jds
  • 7,910
  • 11
  • 63
  • 101
  • Could you provide a [mcve]? It is not clear from the part of the code you show whether the output is expected or not. – ImportanceOfBeingErnest Feb 20 '18 at 23:56
  • 1
    It might be sometime to do with setting the parameter `zoom` to a value (in [this example](https://stackoverflow.com/questions/22566284/matplotlib-how-to-plot-images-instead-of-points) it was `zoom=0.1`). You'll be able to experiment with different values to change the size. The zoom value is also set on the OffsetImage object -> `im = OffsetImage(im, zoom=0.1)` – Max Collier Feb 21 '18 at 00:14
  • 1
    @MaxCollier, that was it, thanks. I had played with the zoom but did not change it significantly enough to notice a difference. Unfortunately, it makes the image smaller at the same time, which is frustrating. – jds Feb 21 '18 at 00:19
  • I imagine that if you use a bigger number than 1 it will start increasing the image size whereas numbers less than 1 will be decreasing the image size – Max Collier Feb 21 '18 at 00:24
  • There is no `zoom` in the code. For making this reproducible, do not "upload data", but create a [mcve], such that the issue is reproduced with some data created within the code. – ImportanceOfBeingErnest Feb 21 '18 at 00:36
  • @ImportanceOfBeingErnest, the zoom is irrelevant since I do not use it in my original code. I cannot create an MCV without the raw data. – jds Feb 21 '18 at 00:43
  • 1
    I mean at the moment some code with unknown input creates some undesired result, so be it. It is not even clear if there is some actual problem behind all of this. In order to solve a problem, one needs to see the problem. To create an image within the code, create an array `a = np.ones((rows, columns, 3))`. To change part of this to black, `a[starty:stopy, startx:stopx, :] = 0`. – ImportanceOfBeingErnest Feb 21 '18 at 00:50
  • @ImportanceOfBeingErnest, I see what you mean now. I've added such an example. – jds Feb 21 '18 at 01:08
  • Running your code creates [this image](https://i.stack.imgur.com/aGg6G.png) for me. – ImportanceOfBeingErnest Feb 21 '18 at 01:26
  • Strange. I get [this image](https://i.stack.imgur.com/avPko.png). Yours is what I want. – jds Feb 21 '18 at 02:44
  • If you got a resolution to this, can you self-answer? – smci Apr 29 '20 at 12:58

0 Answers0