pyvips icon indicating copy to clipboard operation
pyvips copied to clipboard

dzsave: Can we generate map tiles with transparent background?

Open acycliq opened this issue 3 years ago • 4 comments

I create an image with transparent background and I want to do a pyramid of tiles out of this image. I do not know how to make the tiles transparent please. Is this possible?

In the code below I sample from the bivariate normal and save the scatter plot as a transparent png file. What I want to achieve is to keep this transparency when I generate the tiles. However I got a white background and black whenever padding is needed for the tile to be square, see attached 0

I understand that the black background is because I am setting to 0 but I cannot figure out how to set for a transparent background when padding is needed. It takes a vector of 1 or 5 elements

import numpy as np
import pyvips
import matplotlib.pyplot as plt

mu = [0, 0]
scale = 1
cov = scale ** 2 * np.eye(2)
x, y = np.random.multivariate_normal(mu, cov, 1000).T

plt.plot(x, y, 'ob')
plt.savefig(r'./demo.png', transparent=True)

zoom_levels = 4
dim = 256 * 2 ** zoom_levels
im = pyvips.Image.new_from_file('demo.png', access='sequential')
im = im.colourspace('srgb')
im = im.addalpha()
factor = dim / max(im.width, im.height)
im = im.resize(factor)
im.dzsave(r'./transparent_tiles', layout='google', suffix='.jpg', background=0, skip_blanks=0)

acycliq avatar Apr 04 '21 01:04 acycliq

Hello @acycliq,

Do you mean you'd like the tiles in the pyramid to have a transparent background so you can overlay them on something else later? You'll need to use PNG for the tiles since JPG does not support transparency. You don't need the srgb or addalpha.

Try:

zoom_levels = 4
dim = 256 * 2 ** zoom_levels
im = pyvips.Image.new_from_file('demo.png', access='sequential')
factor = dim / max(im.width, im.height)
im = im.resize(factor)
im.dzsave(r'./transparent_tiles', layout='google', suffix='.png', 
        background=0, skip_blanks=0)

I see eg.

0

You also don't need to use a PNG intermediate file, you can pass the plot directly into pyvips, but you probably know. It would also be better to set a dpi in savefig rather than resizing in pyvips.

jcupitt avatar Apr 04 '21 05:04 jcupitt

Oh yes, of course! It is the .jpg extension I got wrong. I thought It had to do with the alpha and I couldnt figure out how to set it. I did not understand your point about setting the dpi instead of resizing John. Can you please explain that?

Another question is about the background please. I thought that setting it as background=0 the extra space that is padded to the tile in order to be square would be black. It doesnt happen here, which is good! I want all the tiles to be fully transparent because I want to overlay them on another image as you correctly said in your reply. I just try to understand how it works please.

Thanks so much for all your help!

acycliq avatar Apr 04 '21 09:04 acycliq

You're making a 640 x 400 pixel PNG, then upsizing to 4096 x 4096 using pyvips. Your edges will be fuzzy and a bit ugly. Instead, you can get matplotlib to generate the plot at 4096x4096 directly and remove the resize.

You don't need to save as PNG and then reload. Instead, matplotlib can make a big memory array which you can then load directly into pyvips. It'll be a lot quicker.

Try:

#!/usr/bin/python3

import io
import numpy as np
import pyvips
import matplotlib.pyplot as plt

mu = [0, 0]
scale = 1
cov = scale ** 2 * np.eye(2)
x, y = np.random.multivariate_normal(mu, cov, 1000).T

zoom_levels = 4
dim = 256 * 2 ** zoom_levels
plt.figure(figsize=(dim / 100, dim / 100), dpi=100)
plt.plot(x, y, 'ob')
io_buf = io.BytesIO()
plt.savefig(io_buf, transparent=True, dpi=100, format='raw')

im = pyvips.Image.new_from_memory(io_buf.getvalue(), dim, dim, 4, "uchar")
im.dzsave(r'./transparent_tiles', layout='google', suffix='.png', 
        background=0, skip_blanks=0)

jcupitt avatar Apr 04 '21 17:04 jcupitt

Ah ok, I got it. Great! Many many thanks once again!

acycliq avatar Apr 04 '21 23:04 acycliq