6 Force spectroscopy curves filtering of flat curves
7 Licensed under the GNU LGPL version 2
9 Other plugin dependencies:
10 procplots.py (plot processing plugin)
13 import xml.dom.minidom
18 from numpy import diff
23 import libpeakspot as lps
24 #import libhookecurve as lhc
25 import hookecurve as lhc
28 wxversion.select(lh.WX_GOOD)
31 class flatfiltsCommands:
33 def do_flatfilt(self):
37 Filters out flat (featureless) curves of the current playlist,
38 creating a playlist containing only the curves with potential
42 flatfilt [min_npks min_deviation]
44 min_npks = minmum number of points over the deviation
47 min_deviation = minimum signal/noise ratio
50 If called without arguments, it uses default values, that
51 should work most of the times.
53 #TODO: should this be optional?
56 self.AppendToOutput('Processing playlist...')
57 self.AppendToOutput('(Please wait)')
59 playlist = self.GetActivePlaylist()
60 curves = playlist.curves
65 notflat = self.has_features(curve)
69 feature_string = str(notflat) + ' features'
71 feature_string = 'no features'
73 feature_string = '1 feature'
74 output_string = ''.join(['Curve ', curve.name, '(', str(curve_index), '/', str(len(curves)), '): ', feature_string])
77 output_string = ''.join(['Curve ', curve.name, '(', str(curve_index), '/', str(len(curves)), '): cannot be filtered. Probably unable to retrieve force data from corrupt file.'])
78 self.AppendToOutput(output_string)
80 curve.features = notflat
81 features.append(curve_index - 1)
83 self.AppendToOutput('Found nothing interesting. Check the playlist, could be a bug or criteria could be too stringent.')
85 if len(features) < playlist.count:
86 self.AppendToOutput(''.join(['Found ', str(len(features)), ' potentially interesting curves.']))
87 self.AppendToOutput('Regenerating playlist...')
88 playlist_filtered = playlist.filter_curves(features)
89 self.AddPlaylist(playlist_filtered, name='flatfilt')
91 self.AppendToOutput('No curves filtered. Try different filtering criteria.')
93 def has_features(self, curve):
95 decides if a curve is flat enough to be rejected from analysis: it sees if there
96 are at least min_npks points that are higher than min_deviation times the absolute value
99 Algorithm original idea by Francesco Musiani, with my tweaks and corrections.
102 mindeviation = self.GetIntFromConfig('flatfilts', 'flatfilt', 'min_deviation')
103 minpeaks = self.GetIntFromConfig('flatfilts', 'flatfilt', 'min_npks')
104 #medianfilter = self.GetIntFromConfig('flatfilt', 'median_filter')
105 #mindeviation = self.GetIntFromConfig('convfilt', 'mindeviation')
106 #minpeaks = self.GetIntFromConfig('convfilt', 'minpeaks')
110 #item.identify(self.drivers)
111 #we assume the first is the plot with the force curve
112 #do the median to better resolve features from noise
113 flat_plot = self.plotmanip_median(curve.driver.default_plots()[0], curve, customvalue=medianfilter)
114 flat_vects = flat_plot.vectors
115 curve.driver.close_all()
116 #needed to avoid *big* memory leaks!
120 #absolute value of derivate
121 yretdiff=diff(flat_vects[1][1])
122 yretdiff=[abs(value) for value in yretdiff]
123 #average of derivate values
124 diffmean=numpy.mean(yretdiff)
128 for value in yretdiff:
129 if value/diffmean > mindeviation:
134 if c_pks >= minpeaks:
137 del flat_plot, flat_vects, yretdiff
141 ################################################################
142 #-----CONVFILT-------------------------------------------------
143 #-----Convolution-based peak recognition and filtering.
144 #Requires the libpeakspot.py library
146 def has_peaks(self, plot, curve=None):
148 Finds peak position in a force curve.
149 FIXME: should be moved to libpeakspot.py
152 blindwindow = self.GetFloatFromConfig('flatfilts', 'convfilt', 'blindwindow')
153 #need to convert the string that contains the list into a list
154 convolution = eval(self.GetStringFromConfig('flatfilts', 'convfilt', 'convolution'))
155 maxcut = self.GetFloatFromConfig('flatfilts', 'convfilt', 'maxcut')
156 mindeviation = self.GetFloatFromConfig('flatfilts', 'convfilt', 'mindeviation')
157 positive = self.GetBoolFromConfig('flatfilts', 'convfilt', 'positive')
158 seedouble = self.GetIntFromConfig('flatfilts', 'convfilt', 'seedouble')
159 stable = self.GetFloatFromConfig('flatfilts', 'convfilt', 'stable')
161 xret = plot.vectors[1][0]
162 yret = plot.vectors[1][1]
163 #Calculate convolution
164 convoluted = lps.conv_dx(yret, convolution)
166 #surely cut everything before the contact point
167 cut_index = self.find_contact_point(plot, curve)
168 #cut even more, before the blind window
169 start_x = xret[cut_index]
171 for value in xret[cut_index:]:
172 if abs((value) - (start_x)) > blindwindow * (10 ** -9):
175 cut_index += blind_index
176 #do the dirty convolution-peak finding stuff
177 noise_level = lps.noise_absdev(convoluted[cut_index:], positive, maxcut, stable)
178 above = lps.abovenoise(convoluted, noise_level, cut_index, mindeviation)
179 peak_location, peak_size = lps.find_peaks(above, seedouble=seedouble)
181 for i in range(len(peak_location)):
182 peak = peak_location[i]
183 maxpk = min(yret[peak - 10:peak + 10])
184 index_maxpk = yret[peak - 10:peak + 10].index(maxpk) + (peak - 10)
185 peak_location[i] = index_maxpk
187 return peak_location, peak_size
189 def exec_has_peaks(self, curve):
191 encapsulates has_peaks for the purpose of correctly treating the curve objects in the convfilt loop,
192 to avoid memory leaks
194 #item.identify(self.drivers)
195 #we assume the first is the plot with the force curve
196 plot = curve.driver.default_plots()[0]
198 if self.HasPlotmanipulator('plotmanip_flatten'):
199 #If flatten is present, use it for better recognition of peaks...
200 #flatten = self._find_plotmanip('flatten') #extract flatten plot manipulator
201 #plot = flatten(plot, item, customvalue=1)
202 plot = self.plotmanip_flatten(plot, curve, customvalue=1)
204 peak_location, peak_size = self.has_peaks(plot, curve)
205 #close all open files
206 curve.driver.close_all()
207 #needed to avoid *big* memory leaks!
210 return peak_location, peak_size
212 #------------------------
213 #------commands----------
214 #------------------------
219 Test command for convolution filter / test.
221 Syntax: peaks [deviations]
222 absolute deviation = number of times the convolution signal is above the noise absolute deviation.
226 #TODO: check if the following line gives us what we need
227 curve = self.GetActiveCurve()
228 defplots = curve.driver.default_plots()[0] #we need the raw, uncorrected plots
230 #if 'flatten' in self.config['plotmanips']:
231 if self.HasPlotmanipulator('plotmanip_flatten'):
232 #flatten=self._find_plotmanip('flatten') #extract flatten plot manipulator
233 #defplots=flatten(defplots, self.current)
234 defplots = self.plotmanip_flatten(defplots, curve, customvalue=0)
236 self.AppendToOutput('The flatten plot manipulator is not loaded. Enabling it could give better results.')
238 peak_location, peak_size = self.has_peaks(defplots, curve)
239 self.AppendToOutput('Found ' + str(len(peak_location)) + ' peaks.')
240 self.AppendToOutput('peaks ' + curve.filename + ' ' + str(len(peak_location)))
241 #to_dump = 'peaks ' + current_curve.filename + ' ' + str(len(peak_location))
242 #self.outlet.push(to_dump)
244 #if no peaks, we have nothing to plot. exit.
246 #otherwise, we plot the peak locations.
247 xplotted_ret = curve.plots[0].vectors[1][0]
248 yplotted_ret = curve.plots[0].vectors[1][1]
249 xgood = [xplotted_ret[index] for index in peak_location]
250 ygood = [yplotted_ret[index] for index in peak_location]
252 curve.plots[0].add_set(xgood, ygood)
253 curve.plots[0].styles.append('scatter')
254 curve.plots[0].colors.append('indigo')
256 #recplot = self._get_displayed_plot()
257 #recplot.vectors.append([xgood,ygood])
258 #if recplot.styles == []:
259 #recplot.styles = [None, None, 'scatter']
260 #recplot.colors = [None, None, None]
262 #recplot.styles += ['scatter']
263 #recplot.colors += [None]
265 #self._send_plot([recplot])
268 def do_convfilt(self):
272 Filters out flat (featureless) curves of the current playlist,
273 creating a playlist containing only the curves with potential
277 convfilt [min_npks min_deviation]
279 min_npks = minmum number of peaks
280 (to set the default, see convfilt.conf file; CONVCONF and SETCONF commands)
282 min_deviation = minimum signal/noise ratio *in the convolution*
283 (to set the default, see convfilt.conf file; CONVCONF and SETCONF commands)
285 If called without arguments, it uses default values.
288 self.AppendToOutput('Processing playlist...')
289 self.AppendToOutput('(Please wait)')
290 minpeaks = self.GetIntFromConfig('flatfilts', 'convfilt', 'minpeaks')
292 playlist = self.GetActivePlaylist()
294 curves = self.GetActivePlaylist().curves
299 peak_location, peak_size = self.exec_has_peaks(curve)
300 number_of_peaks = len(peak_location)
301 if number_of_peaks != 1:
302 if number_of_peaks > 0:
303 feature_string = str(number_of_peaks) + ' features'
305 feature_string = 'no features'
307 feature_string = '1 feature'
308 if number_of_peaks >= minpeaks:
309 feature_string += '+'
310 output_string = ''.join(['Curve ', curve.name, '(', str(curve_index), '/', str(len(curves)), '): ', feature_string])
314 output_string = ''.join(['Curve ', curve.name, '(', str(curve_index), '/', str(len(curves)), '): cannot be filtered. Probably unable to retrieve force data from corrupt file.'])
315 self.AppendToOutput(output_string)
316 if number_of_peaks >= minpeaks:
317 curve.peak_location = peak_location
318 curve.peak_size = peak_size
319 features.append(curve_index - 1)
321 #TODO: do we need this? Flattening might not be necessary/desired
322 #Warn that no flattening had been done.
323 if not self.HasPlotmanipulator('plotmanip_flatten'):
324 self.AppendToOutput('Flatten manipulator was not found. Processing was done without flattening.')
325 self.AppendToOutput('Try to enable it in the configuration file for better results.')
327 self.AppendToOutput('Found nothing interesting. Check the playlist, could be a bug or criteria could be too stringent.')
329 if len(features) < playlist.count:
330 self.AppendToOutput(''.join(['Found ', str(len(features)), ' potentially interesting curves.']))
331 self.AppendToOutput('Regenerating playlist...')
332 playlist_filtered = playlist.filter_curves(features)
333 self.AddPlaylist(playlist_filtered, name='convfilt')
335 self.AppendToOutput('No curves filtered. Try different filtering criteria.')