From: Guillaume Pellerin Date: Tue, 22 Oct 2013 16:54:48 +0000 (+0200) Subject: add grapher utils and color schemes X-Git-Tag: 0.5.1-0~26 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=6787f8489a59896d3cd7a0e446e90b64f354a956;p=timeside.git add grapher utils and color schemes --- diff --git a/timeside/grapher/color_schemes.py b/timeside/grapher/color_schemes.py new file mode 100644 index 0000000..8f41d0c --- /dev/null +++ b/timeside/grapher/color_schemes.py @@ -0,0 +1,24 @@ + + +default_color_schemes = { + 'default': { + 'waveform': [(50,0,200), (0,220,80), (255,224,0), (255,0,0)], + 'spectrogram': [(0, 0, 0), (58/4,68/4,65/4), (80/2,100/2,153/2), (90,180,100), + (224,224,44), (255,60,30), (255,255,255)] + }, + 'iso': { + 'waveform': [(0,0,255), (0,255,255), (255,255,0), (255,0,0)], + 'spectrogram': [(0, 0, 0), (58/4,68/4,65/4), (80/2,100/2,153/2), (90,180,100), + (224,224,44), (255,60,30), (255,255,255)] + }, + 'purple': { + 'waveform': [(173,173,173), (147,149,196), (77,80,138), (108,66,0)], + 'spectrogram': [(0, 0, 0), (58/4,68/4,65/4), (80/2,100/2,153/2), (90,180,100), + (224,224,44), (255,60,30), (255,255,255)] + }, + 'awdio': { + 'waveform': [(255,255,255), (255,255,255), (255,255,255), (255,255,255)], + 'spectrogram': [(0, 0, 0), (58/4,68/4,65/4), (80/2,100/2,153/2), (90,180,100), + (224,224,44), (255,60,30), (255,255,255)] + }, +} diff --git a/timeside/grapher/core.py b/timeside/grapher/core.py index ebc2c2e..354e60c 100644 --- a/timeside/grapher/core.py +++ b/timeside/grapher/core.py @@ -30,7 +30,7 @@ except ImportError: import ImageFilter, ImageChops, Image, ImageDraw, ImageColor, ImageEnhance from timeside.core import FixedSizeInputAdapter -from color_schemes import default_color_schemes +from timeside.grapher.color_schemes import default_color_schemes from utils import * class Spectrum(object): diff --git a/timeside/grapher/utils.py b/timeside/grapher/utils.py new file mode 100644 index 0000000..36b6973 --- /dev/null +++ b/timeside/grapher/utils.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009-2013 Parisson SARL +# Copyright (c) 2009-2012 Guillaume Pellerin +# Copyright (C) 2008 MUSIC TECHNOLOGY GROUP (MTG) +# UNIVERSITAT POMPEU FABRA + +# This file is part of TimeSide. + +# TimeSide is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. + +# TimeSide is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with TimeSide. If not, see . + + +# Authors: +# Bram de Jong +# Guillaume Pellerin + +try: + from PIL import ImageFilter, ImageChops, Image, ImageDraw, ImageColor, ImageEnhance +except ImportError: + import ImageFilter, ImageChops, Image, ImageDraw, ImageColor, ImageEnhance + +import numpy + + +def interpolate_colors(colors, flat=False, num_colors=256): + """ Given a list of colors, create a larger list of colors interpolating + the first one. If flatten is True a list of numers will be returned. If + False, a list of (r,g,b) tuples. num_colors is the number of colors wanted + in the final list """ + + palette = [] + + for i in range(num_colors): + index = (i * (len(colors) - 1))/(num_colors - 1.0) + index_int = int(index) + alpha = index - float(index_int) + + if alpha > 0: + r = (1.0 - alpha) * colors[index_int][0] + alpha * colors[index_int + 1][0] + g = (1.0 - alpha) * colors[index_int][1] + alpha * colors[index_int + 1][1] + b = (1.0 - alpha) * colors[index_int][2] + alpha * colors[index_int + 1][2] + else: + r = (1.0 - alpha) * colors[index_int][0] + g = (1.0 - alpha) * colors[index_int][1] + b = (1.0 - alpha) * colors[index_int][2] + + if flat: + palette.extend((int(r), int(g), int(b))) + else: + palette.append((int(r), int(g), int(b))) + + return palette + + +class Noise(object): + """A class that mimics audiolab.sndfile but generates noise instead of reading + a wave file. Additionally it can be told to have a "broken" header and thus crashing + in the middle of the file. Also useful for testing ultra-short files of 20 samples.""" + + def __init__(self, num_frames, has_broken_header=False): + self.seekpoint = 0 + self.num_frames = num_frames + self.has_broken_header = has_broken_header + + def seek(self, seekpoint): + self.seekpoint = seekpoint + + def get_nframes(self): + return self.num_frames + + def get_samplerate(self): + return 44100 + + def get_channels(self): + return 1 + + def read_frames(self, frames_to_read): + if self.has_broken_header and self.seekpoint + frames_to_read > self.num_frames / 2: + raise IOError() + + num_frames_left = self.num_frames - self.seekpoint + if num_frames_left < frames_to_read: + will_read = num_frames_left + else: + will_read = frames_to_read + self.seekpoint += will_read + return numpy.random.random(will_read)*2 - 1 + + +def downsample(vector, factor): + """ + downsample(vector, factor): + Downsample (by averaging) a vector by an integer factor. + """ + if (len(vector) % factor): + print "Length of 'vector' is not divisible by 'factor'=%d!" % factor + return 0 + vector.shape = (len(vector)/factor, factor) + return numpy.mean(vector, axis=1) + + +def smooth(x, window_len=10, window='hanning'): + """ + Smooth the data using a window with requested size. + + This method is based on the convolution of a scaled window with the signal. + The signal is prepared by introducing reflected copies of the signal + (with the window size) in both ends so that transient parts are minimized + in the begining and end part of the output signal. + + Parameters + ---------- + x : numpy.array + the input signal + window_len : int + the dimension of the smoothing window + window : str + the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman' + flat window will produce a moving average smoothing. + + Returns + ------- + The smoothed signal + + See Also + ------- + + numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve + scipy.signal.lfilter + + + Examples + -------- + + >>> import numpy as np + >>> from timeside.grapher import smooth + >>> t = np.arange(-2,2,0.1) + >>> x = np.sin(t)+np.random.randn(len(t))*0.1 + >>> y = smooth(x) + >>> import matplotlib.pyplot as plt + >>> plt.plot(x) # doctest: +ELLIPSIS + [] + >>> plt.plot(y) # doctest: +ELLIPSIS + [] + >>> plt.legend(['Source signal', 'Smoothed signal']) # doctest: +ELLIPSIS + + >>> plt.show() # doctest: +SKIP + """ + + # TODO: the window parameter could be the window itself if an array instead of a string + + + if x.ndim != 1: + raise ValueError, "smooth only accepts 1 dimension arrays." + + if x.size < window_len: + raise ValueError, "Input vector needs to be bigger than window size." + + if window_len < 3: + return x + + if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']: + raise ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'" + + s=numpy.r_[2*x[0]-x[window_len:1:-1], x, 2*x[-1]-x[-1:-window_len:-1]] + + if window == 'flat': #moving average + w = numpy.ones(window_len,'d') + else: + w = getattr(numpy, window)(window_len) + + y = numpy.convolve(w/w.sum(), s, mode='same') + return y[window_len-1:-window_len+1] + + +def reduce_opacity(im, opacity): + """Returns an image with reduced opacity.""" + assert opacity >= 0 and opacity <= 1 + if im.mode != 'RGBA': + im = im.convert('RGBA') + else: + im = im.copy() + alpha = im.split()[3] + alpha = ImageEnhance.Brightness(alpha).enhance(opacity) + im.putalpha(alpha) + return im + + +def im_watermark(im, inputtext, font=None, color=None, opacity=.6, margin=(30,30)): + """ + imprints a PIL image with the indicated text in lower-right corner + """ + if im.mode != "RGBA": + im = im.convert("RGBA") + textlayer = Image.new("RGBA", im.size, (0,0,0,0)) + textdraw = ImageDraw.Draw(textlayer) + textsize = textdraw.textsize(inputtext, font=font) + textpos = [im.size[i]-textsize[i]-margin[i] for i in [0,1]] + textdraw.text(textpos, inputtext, font=font, fill=color) + if opacity != 1: + textlayer = reduce_opacity(textlayer,opacity) + return Image.composite(textlayer, im, textlayer)