annotate Example VamPy plugins/test/PyMFCC_legacy.py @ 120:a38d318c85a9 tip

MSVC fixes
author Chris Cannam
date Wed, 18 Dec 2019 16:51:20 +0000
parents d56f48aafb99
children
rev   line source
fazekasgy@52 1 '''PyMFCC_legacy.py - This example Vampy plugin demonstrates
fazekasgy@52 2 how to return sprectrogram-like features.
fazekasgy@52 3
fazekasgy@52 4 This plugin has frequency domain input and is using
fazekasgy@52 5 the legacy input interface: the frequency samples are
fazekasgy@52 6 passed as python list of complex numbers.
fazekasgy@52 7
fazekasgy@52 8 Note: This is not the adviced way of writing plugins,
fazekasgy@52 9 since the input interfaces provided for Numpy such as the
fazekasgy@52 10 Numpy Array interface (flag: vf_ARRAY) are much faster.
fazekasgy@52 11
fazekasgy@52 12 This plugin is using Numpy, but it does not rely on Vampy's
fazekasgy@52 13 capability of passing Numpy arrays to the process directly.
fazekasgy@52 14 However, it returns Numpy arrays from the process.
fazekasgy@52 15
fazekasgy@52 16 Centre for Digital Music, Queen Mary University of London.
fazekasgy@52 17 Copyright 2006 Gyorgy Fazekas, QMUL.
fazekasgy@52 18 (See Vamp API for licence information.)
fazekasgy@52 19
fazekasgy@52 20 Constants for Mel frequency conversion and filter
fazekasgy@52 21 centre calculation are taken from the GNU GPL licenced
fazekasgy@52 22 Freespeech library. Copyright (C) 1999 Jean-Marc Valin
fazekasgy@52 23 '''
fazekasgy@52 24
fazekasgy@52 25 import sys,numpy
fazekasgy@52 26 from numpy import log,exp,floor,sum
fazekasgy@52 27 from numpy import *
fazekasgy@52 28 from numpy.fft import *
fazekasgy@52 29 import vampy
fazekasgy@52 30 from vampy import *
fazekasgy@52 31
fazekasgy@52 32
fazekasgy@52 33 class melScaling(object):
fazekasgy@52 34
fazekasgy@52 35 def __init__(self,sampleRate,inputSize,numBands,minHz = 0,maxHz = None):
fazekasgy@52 36 '''Initialise frequency warping and DCT matrix.
fazekasgy@52 37 Parameters:
fazekasgy@52 38 sampleRate: audio sample rate
fazekasgy@52 39 inputSize: length of magnitude spectrum (half of FFT size assumed)
fazekasgy@52 40 numBands: number of mel Bands (MFCCs)
fazekasgy@52 41 minHz: lower bound of warping (default = DC)
fazekasgy@52 42 maxHz: higher bound of warping (default = Nyquist frequency)
fazekasgy@52 43 '''
fazekasgy@52 44 self.sampleRate = sampleRate
fazekasgy@52 45 self.NqHz = sampleRate / 2.0
fazekasgy@52 46 self.minHz = minHz
fazekasgy@52 47 if maxHz is None : maxHz = self.NqHz
fazekasgy@52 48 self.maxHz = maxHz
fazekasgy@52 49 self.inputSize = inputSize
fazekasgy@52 50 self.numBands = numBands
fazekasgy@52 51 self.valid = False
fazekasgy@52 52 self.updated = False
fazekasgy@52 53
fazekasgy@52 54
fazekasgy@52 55 def update(self):
fazekasgy@52 56 # make sure this will run only once if called from a vamp process
fazekasgy@52 57
fazekasgy@52 58 if self.updated: return self.valid
fazekasgy@52 59 self.updated = True
fazekasgy@52 60 self.valid = False
fazekasgy@52 61 print 'Updating parameters and recalculating filters: '
fazekasgy@52 62 print 'Nyquist: ',self.NqHz
fazekasgy@52 63
fazekasgy@52 64 if self.maxHz > self.NqHz :
fazekasgy@52 65 raise Exception('Maximum frequency must be smaller than the Nyquist frequency')
fazekasgy@52 66
fazekasgy@52 67 self.maxMel = 1000*log(1+self.maxHz/700.0)/log(1+1000.0/700.0)
fazekasgy@52 68 self.minMel = 1000*log(1+self.minHz/700.0)/log(1+1000.0/700.0)
fazekasgy@52 69 print 'minHz:%s\nmaxHz:%s\nminMel:%s\nmaxMel:%s\n' %(self.minHz,self.maxHz,self.minMel,self.maxMel)
fazekasgy@52 70 self.filterMatrix = self.getFilterMatrix(self.inputSize,self.numBands)
fazekasgy@52 71 self.DCTMatrix = self.getDCTMatrix(self.numBands)
fazekasgy@52 72 self.filterIter = self.filterMatrix.__iter__()
fazekasgy@52 73 self.valid = True
fazekasgy@52 74 return self.valid
fazekasgy@52 75
fazekasgy@52 76 # try :
fazekasgy@52 77 # self.maxMel = 1000*log(1+self.maxHz/700.0)/log(1+1000.0/700.0)
fazekasgy@52 78 # self.minMel = 1000*log(1+self.minHz/700.0)/log(1+1000.0/700.0)
fazekasgy@52 79 # self.filterMatrix = self.getFilterMatrix(self.inputSize,self.numBands)
fazekasgy@52 80 # self.DCTMatrix = self.getDCTMatrix(self.numBands)
fazekasgy@52 81 # self.filterIter = self.filterMatrix.__iter__()
fazekasgy@52 82 # self.valid = True
fazekasgy@52 83 # return True
fazekasgy@52 84 # except :
fazekasgy@52 85 # print "Invalid parameter setting encountered in MelScaling class."
fazekasgy@52 86 # return False
fazekasgy@52 87 # return True
fazekasgy@52 88
fazekasgy@52 89 def getFilterCentres(self,inputSize,numBands):
fazekasgy@52 90 '''Calculate Mel filter centres around FFT bins.
fazekasgy@52 91 This function calculates two extra bands at the edges for
fazekasgy@52 92 finding the starting and end point of the first and last
fazekasgy@52 93 actual filters.'''
fazekasgy@52 94 centresMel = numpy.array(xrange(numBands+2)) * (self.maxMel-self.minMel)/(numBands+1) + self.minMel
fazekasgy@52 95 centresBin = numpy.floor(0.5 + 700.0*inputSize*(exp(centresMel*log(1+1000.0/700.0)/1000.0)-1)/self.NqHz)
fazekasgy@52 96 return numpy.array(centresBin,int)
fazekasgy@52 97
fazekasgy@52 98 def getFilterMatrix(self,inputSize,numBands):
fazekasgy@52 99 '''Compose the Mel scaling matrix.'''
fazekasgy@52 100 filterMatrix = numpy.zeros((numBands,inputSize))
fazekasgy@52 101 self.filterCentres = self.getFilterCentres(inputSize,numBands)
fazekasgy@52 102 for i in xrange(numBands) :
fazekasgy@52 103 start,centre,end = self.filterCentres[i:i+3]
fazekasgy@52 104 self.setFilter(filterMatrix[i],start,centre,end)
fazekasgy@52 105 return filterMatrix.transpose()
fazekasgy@52 106
fazekasgy@52 107 def setFilter(self,filt,filterStart,filterCentre,filterEnd):
fazekasgy@52 108 '''Calculate a single Mel filter.'''
fazekasgy@52 109 k1 = numpy.float32(filterCentre-filterStart)
fazekasgy@52 110 k2 = numpy.float32(filterEnd-filterCentre)
fazekasgy@52 111 up = (numpy.array(xrange(filterStart,filterCentre))-filterStart)/k1
fazekasgy@52 112 dn = (filterEnd-numpy.array(xrange(filterCentre,filterEnd)))/k2
fazekasgy@52 113 filt[filterStart:filterCentre] = up
fazekasgy@52 114 filt[filterCentre:filterEnd] = dn
fazekasgy@52 115
fazekasgy@52 116 def warpSpectrum(self,magnitudeSpectrum):
fazekasgy@52 117 '''Compute the Mel scaled spectrum.'''
fazekasgy@52 118 return numpy.dot(magnitudeSpectrum,self.filterMatrix)
fazekasgy@52 119
fazekasgy@52 120 def getDCTMatrix(self,size):
fazekasgy@52 121 '''Calculate the square DCT transform matrix. Results are
fazekasgy@52 122 equivalent to Matlab dctmtx(n) but with 64 bit precision.'''
fazekasgy@52 123 DCTmx = numpy.array(xrange(size),numpy.float64).repeat(size).reshape(size,size)
fazekasgy@52 124 DCTmxT = numpy.pi * (DCTmx.transpose()+0.5) / size
fazekasgy@52 125 DCTmxT = (1.0/sqrt( size / 2.0)) * cos(DCTmx * DCTmxT)
fazekasgy@52 126 DCTmxT[0] = DCTmxT[0] * (sqrt(2.0)/2.0)
fazekasgy@52 127 return DCTmxT
fazekasgy@52 128
fazekasgy@52 129 def dct(self,data_matrix):
fazekasgy@52 130 '''Compute DCT of input matrix.'''
fazekasgy@52 131 return numpy.dot(self.DCTMatrix,data_matrix)
fazekasgy@52 132
fazekasgy@52 133 def getMFCCs(self,warpedSpectrum,cn=True):
fazekasgy@52 134 '''Compute MFCC coefficients from Mel warped magnitude spectrum.'''
fazekasgy@52 135 mfccs=self.dct(numpy.log(warpedSpectrum))
fazekasgy@52 136 if cn is False : mfccs[0] = 0.0
fazekasgy@52 137 return mfccs
fazekasgy@52 138
fazekasgy@52 139
fazekasgy@52 140 class PyMFCC_legacy(melScaling):
fazekasgy@52 141
fazekasgy@52 142 def __init__(self,inputSampleRate):
fazekasgy@52 143
fazekasgy@52 144 # flags for setting some Vampy options
fazekasgy@52 145 self.vampy_flags = vf_DEBUG | vf_REALTIME
fazekasgy@52 146
fazekasgy@52 147 self.m_inputSampleRate = int(inputSampleRate)
fazekasgy@52 148 self.m_stepSize = 512
fazekasgy@52 149 self.m_blockSize = 2048
fazekasgy@52 150 self.m_channels = 1
fazekasgy@52 151 self.numBands = 40
fazekasgy@52 152 self.cnull = 1
fazekasgy@52 153 self.two_ch = False
fazekasgy@52 154 melScaling.__init__(self,int(self.m_inputSampleRate),self.m_blockSize/2,self.numBands)
fazekasgy@52 155
fazekasgy@52 156 def initialise(self,channels,stepSize,blockSize):
fazekasgy@52 157 self.m_channels = channels
fazekasgy@52 158 self.m_stepSize = stepSize
fazekasgy@52 159 self.m_blockSize = blockSize
fazekasgy@52 160 self.window = numpy.hamming(blockSize)
fazekasgy@52 161 melScaling.__init__(self,int(self.m_inputSampleRate),self.m_blockSize/2,self.numBands)
fazekasgy@52 162 return True
fazekasgy@52 163
fazekasgy@52 164 def getMaker(self):
fazekasgy@52 165 return 'Vampy Test Plugins'
fazekasgy@52 166
fazekasgy@52 167 def getCopyright(self):
fazekasgy@52 168 return 'Plugin By George Fazekas'
fazekasgy@52 169
fazekasgy@52 170 def getName(self):
fazekasgy@52 171 return 'Vampy Legacy FrequencyDomain MFCC Plugin'
fazekasgy@52 172
fazekasgy@52 173 def getIdentifier(self):
fazekasgy@52 174 return 'vampy-mfcc-test-legacy'
fazekasgy@52 175
fazekasgy@52 176 def getDescription(self):
fazekasgy@52 177 return 'Vampy FrequencyDomain MFCC Plugin using the Legacy interface.'
fazekasgy@52 178
fazekasgy@52 179 def getMaxChannelCount(self):
fazekasgy@52 180 return 2
fazekasgy@52 181
fazekasgy@52 182 def getInputDomain(self):
fazekasgy@52 183 return FrequencyDomain
fazekasgy@52 184
fazekasgy@52 185 def getPreferredBlockSize(self):
fazekasgy@52 186 return 2048
fazekasgy@52 187
fazekasgy@52 188 def getPreferredStepSize(self):
fazekasgy@52 189 return 512
fazekasgy@52 190
fazekasgy@52 191 def getOutputDescriptors(self):
fazekasgy@52 192
fazekasgy@52 193 Generic = OutputDescriptor()
fazekasgy@52 194 Generic.hasFixedBinCount=True
fazekasgy@52 195 Generic.binCount=int(self.numBands)-self.cnull
fazekasgy@52 196 Generic.hasKnownExtents=False
fazekasgy@52 197 Generic.isQuantized=True
fazekasgy@52 198 Generic.sampleType = OneSamplePerStep
fazekasgy@52 199
fazekasgy@52 200 # note the inheritance of attributes (use is optional)
fazekasgy@52 201 MFCC = OutputDescriptor(Generic)
fazekasgy@52 202 MFCC.identifier = 'mfccs'
fazekasgy@52 203 MFCC.name = 'MFCCs'
fazekasgy@52 204 MFCC.description = 'MFCC Coefficients'
fazekasgy@52 205 MFCC.binNames=map(lambda x: 'C '+str(x),range(self.cnull,int(self.numBands)))
fazekasgy@52 206 MFCC.unit = None
fazekasgy@52 207 if self.two_ch and self.m_channels == 2 :
fazekasgy@52 208 MFCC.binCount = self.m_channels * (int(self.numBands)-self.cnull)
fazekasgy@52 209 else :
fazekasgy@52 210 MFCC.binCount = self.numBands-self.cnull
fazekasgy@52 211
fazekasgy@52 212 warpedSpectrum = OutputDescriptor(Generic)
fazekasgy@52 213 warpedSpectrum.identifier='warped-fft'
fazekasgy@52 214 warpedSpectrum.name='Mel Scaled Spectrum'
fazekasgy@52 215 warpedSpectrum.description='Mel Scaled Magnitide Spectrum'
fazekasgy@52 216 warpedSpectrum.unit='Mel'
fazekasgy@52 217 if self.two_ch and self.m_channels == 2 :
fazekasgy@52 218 warpedSpectrum.binCount = self.m_channels * int(self.numBands)
fazekasgy@52 219 else :
fazekasgy@52 220 warpedSpectrum.binCount = self.numBands
fazekasgy@52 221
fazekasgy@52 222 melFilter = OutputDescriptor(Generic)
fazekasgy@52 223 melFilter.identifier = 'mel-filter-matrix'
fazekasgy@52 224 melFilter.sampleType='FixedSampleRate'
fazekasgy@52 225 melFilter.sampleRate=self.m_inputSampleRate/self.m_stepSize
fazekasgy@52 226 melFilter.name='Mel Filter Matrix'
fazekasgy@52 227 melFilter.description='Returns the created filter matrix in getRemainingFeatures.'
fazekasgy@52 228 melFilter.unit = None
fazekasgy@52 229
fazekasgy@52 230 return OutputList(MFCC,warpedSpectrum,melFilter)
fazekasgy@52 231
fazekasgy@52 232
fazekasgy@52 233 def getParameterDescriptors(self):
fazekasgy@52 234
fazekasgy@52 235 melbands = ParameterDescriptor()
fazekasgy@52 236 melbands.identifier='melbands'
fazekasgy@52 237 melbands.name='Number of bands (coefficients)'
fazekasgy@52 238 melbands.description='Set the number of coefficients.'
fazekasgy@52 239 melbands.unit = ''
fazekasgy@52 240 melbands.minValue = 2
fazekasgy@52 241 melbands.maxValue = 128
fazekasgy@52 242 melbands.defaultValue = 40
fazekasgy@52 243 melbands.isQuantized = True
fazekasgy@52 244 melbands.quantizeStep = 1
fazekasgy@52 245
fazekasgy@52 246 cnull = ParameterDescriptor()
fazekasgy@52 247 cnull.identifier='cnull'
fazekasgy@52 248 cnull.name='Return C0'
fazekasgy@52 249 cnull.description='Select if the DC coefficient is required.'
fazekasgy@52 250 cnull.unit = None
fazekasgy@52 251 cnull.minValue = 0
fazekasgy@52 252 cnull.maxValue = 1
fazekasgy@52 253 cnull.defaultValue = 0
fazekasgy@52 254 cnull.isQuantized = True
fazekasgy@52 255 cnull.quantizeStep = 1
fazekasgy@52 256
fazekasgy@52 257 two_ch = ParameterDescriptor(cnull)
fazekasgy@52 258 two_ch.identifier='two_ch'
fazekasgy@52 259 two_ch.name='Process channels separately'
fazekasgy@52 260 two_ch.description='Process two channel files separately.'
fazekasgy@52 261 two_ch.defaultValue = False
fazekasgy@52 262
fazekasgy@52 263 minHz = ParameterDescriptor()
fazekasgy@52 264 minHz.identifier='minHz'
fazekasgy@52 265 minHz.name='minimum frequency'
fazekasgy@52 266 minHz.description='Set the lower frequency bound.'
fazekasgy@52 267 minHz.unit='Hz'
fazekasgy@52 268 minHz.minValue = 0
fazekasgy@52 269 minHz.maxValue = 24000
fazekasgy@52 270 minHz.defaultValue = 0
fazekasgy@52 271 minHz.isQuantized = True
fazekasgy@52 272 minHz.quantizeStep = 1.0
fazekasgy@52 273
fazekasgy@52 274 maxHz = ParameterDescriptor()
fazekasgy@52 275 maxHz.identifier='maxHz'
fazekasgy@52 276 maxHz.description='Set the upper frequency bound.'
fazekasgy@52 277 maxHz.name='maximum frequency'
fazekasgy@52 278 maxHz.unit='Hz'
fazekasgy@52 279 maxHz.minValue = 100
fazekasgy@52 280 maxHz.maxValue = 24000
fazekasgy@52 281 maxHz.defaultValue = 11025
fazekasgy@52 282 maxHz.isQuantized = True
fazekasgy@52 283 maxHz.quantizeStep = 100
fazekasgy@52 284
fazekasgy@52 285 return ParameterList(melbands,minHz,maxHz,cnull,two_ch)
fazekasgy@52 286
fazekasgy@52 287
fazekasgy@52 288 def setParameter(self,paramid,newval):
fazekasgy@52 289 self.valid = False
fazekasgy@52 290 if paramid == 'minHz' :
fazekasgy@52 291 if newval < self.maxHz and newval < self.NqHz :
fazekasgy@52 292 self.minHz = float(newval)
fazekasgy@52 293 print 'minHz: ', self.minHz
fazekasgy@52 294 if paramid == 'maxHz' :
fazekasgy@52 295 print 'trying to set maxHz to: ',newval
fazekasgy@52 296 if newval < self.NqHz and newval > self.minHz+1000 :
fazekasgy@52 297 self.maxHz = float(newval)
fazekasgy@52 298 else :
fazekasgy@52 299 self.maxHz = self.NqHz
fazekasgy@52 300 print 'set to: ',self.maxHz
fazekasgy@52 301 if paramid == 'cnull' :
fazekasgy@52 302 self.cnull = int(not int(newval))
fazekasgy@52 303 if paramid == 'melbands' :
fazekasgy@52 304 self.numBands = int(newval)
fazekasgy@52 305 if paramid == 'two_ch' :
fazekasgy@52 306 self.two_ch = bool(newval)
fazekasgy@52 307
fazekasgy@52 308 return
fazekasgy@52 309
fazekasgy@52 310 def getParameter(self,paramid):
fazekasgy@52 311 if paramid == 'minHz' :
fazekasgy@52 312 return float(self.minHz)
fazekasgy@52 313 if paramid == 'maxHz' :
fazekasgy@52 314 return float(self.maxHz)
fazekasgy@52 315 if paramid == 'cnull' :
fazekasgy@52 316 return float(not int(self.cnull))
fazekasgy@52 317 if paramid == 'melbands' :
fazekasgy@52 318 return float(self.numBands)
fazekasgy@52 319 if paramid == 'two_ch' :
fazekasgy@52 320 return float(self.two_ch)
fazekasgy@52 321 else:
fazekasgy@52 322 return 0.0
fazekasgy@52 323
fazekasgy@52 324 # set numpy process using the 'use_numpy_interface' flag
fazekasgy@52 325 def process(self,inputbuffers,timestamp):
fazekasgy@52 326
fazekasgy@52 327 if not self.update() : return None
fazekasgy@52 328
fazekasgy@52 329 if self.m_channels == 2 and self.two_ch :
fazekasgy@52 330 return self.process2ch(inputbuffers,timestamp)
fazekasgy@52 331
fazekasgy@52 332 fftsize = self.m_blockSize
fazekasgy@52 333
fazekasgy@52 334 if self.m_channels > 1 :
fazekasgy@52 335 # take the mean of the two magnitude spectra
fazekasgy@52 336 complexSpectrum0 = array(inputbuffers[0])
fazekasgy@52 337 complexSpectrum1 = array(inputbuffers[1])
fazekasgy@52 338 magnitudeSpectrum0 = abs(complexSpectrum0)[0:fftsize/2]
fazekasgy@52 339 magnitudeSpectrum1 = abs(complexSpectrum1)[0:fftsize/2]
fazekasgy@52 340 magnitudeSpectrum = (magnitudeSpectrum0 + magnitudeSpectrum1) / 2
fazekasgy@52 341 else :
fazekasgy@52 342 complexSpectrum = array(inputbuffers[0])
fazekasgy@52 343 magnitudeSpectrum = abs(complexSpectrum)[0:fftsize/2]
fazekasgy@52 344
fazekasgy@52 345 # do the computation
fazekasgy@52 346 melSpectrum = self.warpSpectrum(magnitudeSpectrum)
fazekasgy@52 347 melCepstrum = self.getMFCCs(melSpectrum,cn=True)
fazekasgy@52 348
fazekasgy@52 349 outputs = FeatureSet()
fazekasgy@52 350 outputs[0] = Feature(melCepstrum[self.cnull:])
fazekasgy@52 351 outputs[1] = Feature(melSpectrum)
fazekasgy@52 352 return outputs
fazekasgy@52 353
fazekasgy@52 354
fazekasgy@52 355 # process channels separately (stack the returned arrays)
fazekasgy@52 356 def process2ch(self,inputbuffers,timestamp):
fazekasgy@52 357
fazekasgy@52 358 fftsize = self.m_blockSize
fazekasgy@52 359
fazekasgy@52 360 complexSpectrum0 = array(inputbuffers[0])
fazekasgy@52 361 complexSpectrum1 = array(inputbuffers[1])
fazekasgy@52 362
fazekasgy@52 363 magnitudeSpectrum0 = abs(complexSpectrum0)[0:fftsize/2]
fazekasgy@52 364 magnitudeSpectrum1 = abs(complexSpectrum1)[0:fftsize/2]
fazekasgy@52 365
fazekasgy@52 366 # do the computations
fazekasgy@52 367 melSpectrum0 = self.warpSpectrum(magnitudeSpectrum0)
fazekasgy@52 368 melCepstrum0 = self.getMFCCs(melSpectrum0,cn=True)
fazekasgy@52 369 melSpectrum1 = self.warpSpectrum(magnitudeSpectrum1)
fazekasgy@52 370 melCepstrum1 = self.getMFCCs(melSpectrum1,cn=True)
fazekasgy@52 371
fazekasgy@52 372 outputs = FeatureSet()
fazekasgy@52 373
fazekasgy@52 374 outputs[0] = Feature(hstack((melCepstrum1[self.cnull:],melCepstrum0[self.cnull:])))
fazekasgy@52 375
fazekasgy@52 376 outputs[1] = Feature(hstack((melSpectrum1,melSpectrum0)))
fazekasgy@52 377
fazekasgy@52 378 return outputs
fazekasgy@52 379
fazekasgy@52 380
fazekasgy@52 381 def getRemainingFeatures(self):
fazekasgy@52 382 if not self.update() : return []
fazekasgy@52 383 frameSampleStart = 0
fazekasgy@52 384
fazekasgy@52 385 output_featureSet = FeatureSet()
fazekasgy@52 386
fazekasgy@52 387 # the filter is the third output (index starts from zero)
fazekasgy@52 388 output_featureSet[2] = flist = FeatureList()
fazekasgy@52 389
fazekasgy@52 390 while True:
fazekasgy@52 391 f = Feature()
fazekasgy@52 392 f.hasTimestamp = True
fazekasgy@52 393 f.timestamp = frame2RealTime(frameSampleStart,self.m_inputSampleRate)
fazekasgy@52 394 try :
fazekasgy@52 395 f.values = self.filterIter.next()
fazekasgy@52 396 except StopIteration :
fazekasgy@52 397 break
fazekasgy@52 398 flist.append(f)
fazekasgy@52 399 frameSampleStart += self.m_stepSize
fazekasgy@52 400
fazekasgy@52 401 return output_featureSet