How to create transparent waveform in python

49 views Asked by At

I can’t figure out how to make the graph transparent. I have a waveform graph from audio and fill the space with a background, I’m trying, but the background still appears on the graph.

def generate_waveform_image(save_dir, audio_file, output_filename, color='white', alpha=0, figsize=(12, 2), dpi=75):
        fig, ax = plt.subplots(figsize=figsize, dpi=dpi, frameon=False)
        plt.subplots_adjust(left=0, right=1, bottom=0, top=1)
        samples, sr = librosa.load(audio_file, sr=None, mono=True)
        times = np.arange(len(samples)) / sr
        ax.plot(times, samples, color=color, alpha=alpha, linewidth=0.1)
        ax.margins(0, 0)
        ax.axis('off')
        plt.savefig(os.path.join(save_dir, f'{output_filename}.png'), transparent=True, bbox_inches='tight',
                    pad_inches=0)

I tried everything and still_between, the background fills the entire image, but I need it to be like this:

enter image description here

ax.fill_between(times, -1, 1, color='gray', alpha=1)
1

There are 1 answers

1
JohanC On BEST ANSWER

plt.savefig(..., transparent=True) makes the background transparent. But you seem to want to have the foreground transparent.

Here is an approach:

  • optionally sets the 'agg' backend, so nothing is shown on-screen
  • plots the wavefront, and sets margins to zero
  • makes the figure's background transparent
  • fig.canvas.draw() is needed to fill in the pixels
  • the pixels are copied to a numpy array
  • the alpha values are inverted (the transparent background becomes opaque, the opaque curve becomes transparent, and the semi-transparent border of the curve gets its transparency inverted)
  • all pixels are colored black while keeping the new transparency (the default color seems to be white, while the curve borders have the curve color, with semi-transparency)
  • the numpy array is saved to file via PIL (Python's standard image library)
import matplotlib; matplotlib.use('agg')

import matplotlib.pyplot as plt
from PIL import Image
import numpy as np

fig, ax = plt.subplots(figsize=(12, 2))

values = np.random.randn(5000) * np.random.randn(5000).cumsum()
ax.plot(values, color='black', lw=1)
ax.margins(x=0)  # no margins left and right
ax.axis('off')  # hide the axes
fig.patch.set_alpha(0)  # transparent
plt.subplots_adjust(bottom=0, left=0, top=1, right=1)  # remove padding around the main plot
fig.canvas.draw()  # "draw" is needed to fill in the pixels

buffer, (width, height) = fig.canvas.print_to_buffer()
data = np.array(np.frombuffer(buffer, np.uint8).reshape((height, width, 4)))

data[:, :, 3] = 255 - data[:, :, 3]  # invert the transparency of all pixels
data[:, :, :3] = 0  # make all pixels black while keeping the new transparency

img = Image.fromarray(data)
img.save("transparent_waveform.png")

This is how the image looks inside Gimp:

matplotlib save image with transparent foreground