3 """Force spectroscopy curves filtering of flat curves
5 Other plugin dependencies:
6 procplots.py (plot processing plugin)
14 from numpy import diff
19 import libpeakspot as lps
21 import hookecurve as lhc
24 wxversion.select(lh.WX_GOOD)
27 class flatfiltsCommands:
29 def do_flatfilt(self):
33 Filters out flat (featureless) curves of the current playlist,
34 creating a playlist containing only the curves with potential
38 flatfilt [min_npks min_deviation]
40 min_npks = minmum number of points over the deviation
43 min_deviation = minimum signal/noise ratio
46 If called without arguments, it uses default values, that
47 should work most of the times.
49 #TODO: should this be optional?
52 self.AppendToOutput('Processing playlist...')
53 self.AppendToOutput('(Please wait)')
55 playlist = self.GetActivePlaylist()
56 curves = playlist.curves
61 notflat = self.has_features(curve)
65 feature_string = str(notflat) + ' features'
67 feature_string = 'no features'
69 feature_string = '1 feature'
70 output_string = ''.join(['Curve ', curve.name, '(', str(curve_index), '/', str(len(curves)), '): ', feature_string])
73 output_string = ''.join(['Curve ', curve.name, '(', str(curve_index), '/', str(len(curves)), '): cannot be filtered. Probably unable to retrieve force data from corrupt file.'])
74 self.AppendToOutput(output_string)
76 curve.features = notflat
77 features.append(curve_index - 1)
79 self.AppendToOutput('Found nothing interesting. Check the playlist, could be a bug or criteria could be too stringent.')
81 if len(features) < playlist.count:
82 self.AppendToOutput(''.join(['Found ', str(len(features)), ' potentially interesting curves.']))
83 self.AppendToOutput('Regenerating playlist...')
84 playlist_filtered = playlist.filter_curves(features)
85 self.AddPlaylist(playlist_filtered, name='flatfilt')
87 self.AppendToOutput('No curves filtered. Try different filtering criteria.')
89 def has_features(self, curve):
91 decides if a curve is flat enough to be rejected from analysis: it sees if there
92 are at least min_npks points that are higher than min_deviation times the absolute value
95 Algorithm original idea by Francesco Musiani, with my tweaks and corrections.
98 mindeviation = self.GetIntFromConfig('flatfilts', 'flatfilt', 'min_deviation')
99 minpeaks = self.GetIntFromConfig('flatfilts', 'flatfilt', 'min_npks')
100 #medianfilter = self.GetIntFromConfig('flatfilt', 'median_filter')
101 #mindeviation = self.GetIntFromConfig('convfilt', 'mindeviation')
102 #minpeaks = self.GetIntFromConfig('convfilt', 'minpeaks')
106 #item.identify(self.drivers)
107 #we assume the first is the plot with the force curve
108 #do the median to better resolve features from noise
109 flat_plot = self.plotmanip_median(curve.driver.default_plots()[0], curve, customvalue=medianfilter)
110 flat_vects = flat_plot.vectors
111 curve.driver.close_all()
112 #needed to avoid *big* memory leaks!
116 #absolute value of derivate
117 yretdiff=diff(flat_vects[1][1])
118 yretdiff=[abs(value) for value in yretdiff]
119 #average of derivate values
120 diffmean=numpy.mean(yretdiff)
124 for value in yretdiff:
125 if value/diffmean > mindeviation:
130 if c_pks >= minpeaks:
133 del flat_plot, flat_vects, yretdiff
137 ################################################################
138 #-----CONVFILT-------------------------------------------------
139 #-----Convolution-based peak recognition and filtering.
140 #Requires the libpeakspot.py library
142 def has_peaks(self, plot, curve=None):
144 Finds peak position in a force curve.
145 FIXME: should be moved to libpeakspot.py
148 blindwindow = self.GetFloatFromConfig('flatfilts', 'convfilt', 'blindwindow')
149 #need to convert the string that contains the list into a list
150 convolution = eval(self.GetStringFromConfig('flatfilts', 'convfilt', 'convolution'))
151 maxcut = self.GetFloatFromConfig('flatfilts', 'convfilt', 'maxcut')
152 mindeviation = self.GetFloatFromConfig('flatfilts', 'convfilt', 'mindeviation')
153 positive = self.GetBoolFromConfig('flatfilts', 'convfilt', 'positive')
154 seedouble = self.GetIntFromConfig('flatfilts', 'convfilt', 'seedouble')
155 stable = self.GetFloatFromConfig('flatfilts', 'convfilt', 'stable')
157 xret = plot.vectors[1][0]
158 yret = plot.vectors[1][1]
159 #Calculate convolution
160 convoluted = lps.conv_dx(yret, convolution)
162 #surely cut everything before the contact point
163 cut_index = self.find_contact_point(plot, curve)
164 #cut even more, before the blind window
165 start_x = xret[cut_index]
167 for value in xret[cut_index:]:
168 if abs((value) - (start_x)) > blindwindow * (10 ** -9):
171 cut_index += blind_index
172 #do the dirty convolution-peak finding stuff
173 noise_level = lps.noise_absdev(convoluted[cut_index:], positive, maxcut, stable)
174 above = lps.abovenoise(convoluted, noise_level, cut_index, mindeviation)
175 peak_location, peak_size = lps.find_peaks(above, seedouble=seedouble)
177 for i in range(len(peak_location)):
178 peak = peak_location[i]
179 maxpk = min(yret[peak - 10:peak + 10])
180 index_maxpk = yret[peak - 10:peak + 10].index(maxpk) + (peak - 10)
181 peak_location[i] = index_maxpk
183 return peak_location, peak_size
185 def exec_has_peaks(self, curve):
187 encapsulates has_peaks for the purpose of correctly treating the curve objects in the convfilt loop,
188 to avoid memory leaks
190 #item.identify(self.drivers)
191 #we assume the first is the plot with the force curve
192 plot = curve.driver.default_plots()[0]
194 if self.HasPlotmanipulator('plotmanip_flatten'):
195 #If flatten is present, use it for better recognition of peaks...
196 #flatten = self._find_plotmanip('flatten') #extract flatten plot manipulator
197 #plot = flatten(plot, item, customvalue=1)
198 plot = self.plotmanip_flatten(plot, curve, customvalue=1)
200 peak_location, peak_size = self.has_peaks(plot, curve)
201 #close all open files
202 curve.driver.close_all()
203 #needed to avoid *big* memory leaks!
206 return peak_location, peak_size
208 #------------------------
209 #------commands----------
210 #------------------------
215 Test command for convolution filter / test.
217 Syntax: peaks [deviations]
218 absolute deviation = number of times the convolution signal is above the noise absolute deviation.
222 #TODO: check if the following line gives us what we need
223 curve = self.GetActiveCurve()
224 defplots = curve.driver.default_plots()[0] #we need the raw, uncorrected plots
226 #if 'flatten' in self.config['plotmanips']:
227 if self.HasPlotmanipulator('plotmanip_flatten'):
228 #flatten=self._find_plotmanip('flatten') #extract flatten plot manipulator
229 #defplots=flatten(defplots, self.current)
230 defplots = self.plotmanip_flatten(defplots, curve, customvalue=0)
232 self.AppendToOutput('The flatten plot manipulator is not loaded. Enabling it could give better results.')
234 peak_location, peak_size = self.has_peaks(defplots, curve)
235 self.AppendToOutput('Found ' + str(len(peak_location)) + ' peaks.')
236 self.AppendToOutput('peaks ' + curve.filename + ' ' + str(len(peak_location)))
237 #to_dump = 'peaks ' + current_curve.filename + ' ' + str(len(peak_location))
238 #self.outlet.push(to_dump)
240 #if no peaks, we have nothing to plot. exit.
242 #otherwise, we plot the peak locations.
243 xplotted_ret = curve.plots[0].vectors[1][0]
244 yplotted_ret = curve.plots[0].vectors[1][1]
245 xgood = [xplotted_ret[index] for index in peak_location]
246 ygood = [yplotted_ret[index] for index in peak_location]
248 curve.plots[0].add_set(xgood, ygood)
249 curve.plots[0].styles.append('scatter')
250 curve.plots[0].colors.append('indigo')
252 #recplot = self._get_displayed_plot()
253 #recplot.vectors.append([xgood,ygood])
254 #if recplot.styles == []:
255 #recplot.styles = [None, None, 'scatter']
256 #recplot.colors = [None, None, None]
258 #recplot.styles += ['scatter']
259 #recplot.colors += [None]
261 #self._send_plot([recplot])
264 def do_convfilt(self):
268 Filters out flat (featureless) curves of the current playlist,
269 creating a playlist containing only the curves with potential
273 convfilt [min_npks min_deviation]
275 min_npks = minmum number of peaks
276 (to set the default, see convfilt.conf file; CONVCONF and SETCONF commands)
278 min_deviation = minimum signal/noise ratio *in the convolution*
279 (to set the default, see convfilt.conf file; CONVCONF and SETCONF commands)
281 If called without arguments, it uses default values.
284 self.AppendToOutput('Processing playlist...')
285 self.AppendToOutput('(Please wait)')
286 minpeaks = self.GetIntFromConfig('flatfilts', 'convfilt', 'minpeaks')
288 playlist = self.GetActivePlaylist()
290 curves = self.GetActivePlaylist().curves
295 peak_location, peak_size = self.exec_has_peaks(curve)
296 number_of_peaks = len(peak_location)
297 if number_of_peaks != 1:
298 if number_of_peaks > 0:
299 feature_string = str(number_of_peaks) + ' features'
301 feature_string = 'no features'
303 feature_string = '1 feature'
304 if number_of_peaks >= minpeaks:
305 feature_string += '+'
306 output_string = ''.join(['Curve ', curve.name, '(', str(curve_index), '/', str(len(curves)), '): ', feature_string])
310 output_string = ''.join(['Curve ', curve.name, '(', str(curve_index), '/', str(len(curves)), '): cannot be filtered. Probably unable to retrieve force data from corrupt file.'])
311 self.AppendToOutput(output_string)
312 if number_of_peaks >= minpeaks:
313 curve.peak_location = peak_location
314 curve.peak_size = peak_size
315 features.append(curve_index - 1)
317 #TODO: do we need this? Flattening might not be necessary/desired
318 #Warn that no flattening had been done.
319 if not self.HasPlotmanipulator('plotmanip_flatten'):
320 self.AppendToOutput('Flatten manipulator was not found. Processing was done without flattening.')
321 self.AppendToOutput('Try to enable it in the configuration file for better results.')
323 self.AppendToOutput('Found nothing interesting. Check the playlist, could be a bug or criteria could be too stringent.')
325 if len(features) < playlist.count:
326 self.AppendToOutput(''.join(['Found ', str(len(features)), ' potentially interesting curves.']))
327 self.AppendToOutput('Regenerating playlist...')
328 playlist_filtered = playlist.filter_curves(features)
329 self.AddPlaylist(playlist_filtered, name='convfilt')
331 self.AppendToOutput('No curves filtered. Try different filtering criteria.')