Author Topic: Reading SVL files with Python/Numpy  (Read 5284 times)

jean-louis

  • Newbie
  • *
  • Posts: 19
    • View Profile
    • durrieu.ch
Reading SVL files with Python/Numpy
« on: July 09, 2010, 10:18:35 »
Hi everyone,

Probably many people might need to use the annotation files in a "batch" way, using any program like Matlab or Python/Numpy to access and process the data.

To do so, I first thought the exported CSV files would do, but I found it difficult to load the generated CSV back into Sonic Visualiser: for instance, exporting a region layer into a CSV provides a format which looks like (one line per region):
<start time (s)>,<value>,<duration (s)>,<label>
I tried several things when importing such a format into SV, but nothing really worked...

Instead, the SVL format is easy to reload, and actually quite "human readable". Unfortunately, I have always found that XML format files were hard to load with Matlab or Python/Numpy... Well, I did a little program in Python/Numpy that does extract some pieces of information. I thought I might not be the only one needing such a tool, so here is the code:
Code: [Select]
# readSVLfiles.py

from xml.dom import minidom
import numpy as np

def extractSvlAnnotRegionFile(filename):
    """
    extractSvlAnnotRegionFile(filename)
   
    Extracts the SVL files (sonic visualiser)
    in this function, we assume annotation files
    for regions (generated by Sonic Visualiser 1.7.2,
    Regions Layer)
   
    Returns the following objects:
        parameters: copy-paste of the "header" of the SVL file,
        frames    : a numpy array containing the time
                    stamps of each frame, at the beginning
                    of the frame,
        durations : a numpy array containing the duration
                    of each frame,
        labels    : a dictionary with keys equal to the frame
                    number, containing the labels,
        values    : a dictionary with keys equal to the frame
                    number, containing the values.
   
    Note that this code does not parse the end of the xml file.
    The 'display' field is therefore discarded here.
   
    Numpy and xml.dom should be imported in order to use this
    function.
       
    Jean-Louis Durrieu, 2010
    firstname DOT lastname AT epfl DOT ch
    """
    ## Load the XML structure:
    dom = minidom.parse(filename)
   
    ## Keep only the data-tagged field:
    ##    note that you could also keep any other
    ##    field here.
    dataXML = dom.getElementsByTagName('data')[0]
   
    ## XML structure for the parameters:
    parametersXML = dataXML.getElementsByTagName('model')[0]
   
    ## XML structure for the dataset field, containing the points:
    datasetXML = dataXML.getElementsByTagName('dataset')[0]
    ## XML structure with all the points from datasetXML:
    pointsXML = datasetXML.getElementsByTagName('point')
   
    ## converting to a somewhat easier to manipulate format:
    ## Dictionary for the parameters:
    parameters = {}
    for key in parametersXML.attributes.keys():
        parameters[key] = parametersXML.getAttribute(key)
   
    ## number of points (or regions):
    nbPoints = len(pointsXML)
   
    ## number of attributes per point, not used here,
    ## but could be useful to check what type of SVL file it is?
    # nbAttributes = len(pointsXML[0].attributes.keys())
   
    ## Initialize the numpy arrays (frame time stamps and durations):
    frames = np.zeros([nbPoints], dtype=np.float)
    durations = np.zeros([nbPoints], dtype=np.float)
    ## Initialize the dictionaries (values and labels)
    values = {}
    labels = {}
   
    ## Iteration over the points:
    for node in range(nbPoints):
        ## converting sample to seconds for the time stamps and the durations:
        frames[node] = np.int(pointsXML[node].getAttribute('frame')) / np.double(parameters['sampleRate'])
        durations[node] = np.int(pointsXML[node].getAttribute('duration')) / np.double(parameters['sampleRate'])
        ## copy-paste for the values and the labels:
        values[node] = pointsXML[node].getAttribute('value')
        labels[node] = pointsXML[node].getAttribute('label')
       
    ## return the result:
    return parameters, frames, durations, labels, values


This is probably not much, but it can save time to some people... It could be a good idea to publish some kind of toolbox that allows to read these annotation files with third party software, in a more "user friendly" format than pure XML to load ?

Of course, any comment on that code is welcome, especially if it has to be corrected! Please let me know before I really start the big batch processing  :D Note also that the use of Numpy to read the XML file is a little bit superfluous. It might be possible to avoid it, if necessary: just spot the lines with "np." in them, and replace them with whatever suits your need.

Since there already exist some people involved in Python programming, I was wondering if such a tool was actually not already available somewhere... In any case, I would be interested to know whether this is useful to others!

Cheers,

Jean-Louis