Is it possible to use an image (eg. jpg) as a 'marker' in plots?

I have a plot with a time series and another with a shapefile, and would like to plot some markers to highlight some parts of the figure. I would like those markers to be ‘images’ loaded from a file; eg png or jpg. Is that possible within ‘Plots.jl’ and if so how can it be done? Is there another package that can do this?

I had to do something like this to show the Fashion MNIST data recently (well, today). Here’s a simple code that does something close. This is far from finished and would benefit tremendously from someone who knows how to write good plot recipes to improve.

## Let's play around with this example
using Plots

## Now let's build this into an overall routine,
# The next thing to do would be to add a Plots.jl receipe.
function _make_centered_rect(i,j,m,n)
  #Note: The center is (0,0) and the size is expected to be rougly the area of the unit circle.
  #TODO: Add those offsets, we aren't using them yet!
  dx = 2/m
  dy = 2/n
  # make it in native coords, then
  x_coords = [(i-1)*dx-1,     i*dx-1, i*dx-1, (i-1)*dx-1, (i-1)*dx-1]
  y_coords = [(j-1)*dy-1, (j-1)*dy-1, j*dy-1,     j*dy-1, (j-1)*dy-1]
  return x_coords, y_coords
end
function _matrix_to_marker(X::AbstractArray{T,2},offset=(0,0),scale=1) where T
  shapes = Shape[]
  zvals = Float64[] # the
  m,n = size(X)
  for I in CartesianIndices(X)
    i,j = I[1],I[2]
    coords = _make_centered_rect(i,j,m,n)
    push!(shapes, Shape(scale.*coords[1].+offset[1],scale.*coords[2].+offset[2]))
    push!(zvals, X[i,j])
  end
  return shapes, zvals
end
function image_scatter!(x,y,X::AbstractArray{T,3},scale::Real=50;kwargs...) where T <: Real
  # check sizes
  @assert length(x)==length(y)
  @assert size(X,3) == length(x)
  xmin,xmax = extrema(x) # wish I could use the markersize for this...
  ymin,ymax = extrema(y)
  bigsize=max(xmax-xmin,ymax-ymin)

  for i = 1:length(x)
    shapes,zvals = _matrix_to_marker(@view(X[:,:,i]),(x[i],y[i]),scale*bigsize/500)
    plot!(shapes, fill_z = zvals, fillalpha=zvals, linecolor=:transparent; kwargs...)
  end
  plot!() # add this at the end to get plots
end
plot(legend=false)
image_scatter!(randn(100),randn(100), rand(28,28,100),15;c=:grays)

The procedure is fairly simple, we convert each image into a series of Shapes (one for each pixel). I believe this is what heatmap does internally in Plots.jl.

This won’t work for your case immediately. You’d have to load all the images into an equally sized array. You’d also have to get it to work with arrays of colorants (what Images are loaded as…)

1 Like

You can do that with GMT. Example

using GMT

plot(rand(5,2))
psimage!("@warning.png", D="g0.2/0.5+jCM+w2c", fmt=:png, show=1)

Sorry that this module still has not all the GMT options expanded to long verbose. That D="g0.2/0.5+jCM+w2c" means plot the image at the coordinates (0.2, 0.5) with a size of 2 cm and with the anchor point at the image center.
The result is below. There is another option where images are treated as symbols but it requires that the image has been converted to postscript. The Sun in this GMT (not the GMT.jl wrapper) example shows one example of that usage.

2 Likes