1 """
2 Sharpening of EM maps by non-negative blind deconvolution.
3 For details see:
4
5 Hirsch M, Schoelkopf B and Habeck M (2010)
6 A New Algorithm for Improving the Resolution of Cryo-EM Density Maps.
7 """
8
9 import os
10 import numpy
11 import csb.apps
12
13 from numpy import sum, sqrt
14
15 from csb.numeric import convolve, correlate, trim
16 from csb.bio.io.mrc import DensityMapReader, DensityMapWriter, DensityInfo, DensityMapFormatError
24
27
28 @property
31
33
34
35 cmd = csb.apps.ArgHandler(self.program, __doc__)
36
37 cmd.add_scalar_option('psf-size', 's', int, 'size of the point spread function', default=15)
38 cmd.add_scalar_option('output', 'o', str, 'output directory of the sharpened maps', default='.')
39 cmd.add_scalar_option('iterations', 'i', int, 'number of iterations', default=1000)
40 cmd.add_scalar_option('output-frequency', 'f', int, 'create a map file each f iterations', default=50)
41 cmd.add_boolean_option('verbose', 'v', 'verbose mode')
42
43 cmd.add_positional_argument('mapfile', str, 'Input Cryo EM file in CCP4 MRC format')
44
45 return cmd
46
49
51
52 if not os.path.isfile(self.args.mapfile):
53 DeconvolutionApp.exit('Input file not found.', code=ExitCodes.IO_ERROR)
54
55 if not os.path.isdir(self.args.output):
56 DeconvolutionApp.exit('Output directory does not exist.', code=ExitCodes.IO_ERROR)
57
58 if self.args.psf_size < 1:
59 DeconvolutionApp.exit('PSF size must be a positive number.', code=ExitCodes.ARGUMENT_ERROR)
60
61 if self.args.iterations < 1:
62 DeconvolutionApp.exit('Invalid number of iterations.', code=ExitCodes.ARGUMENT_ERROR)
63
64 if self.args.output_frequency < 1:
65 DeconvolutionApp.exit('Output frequency must be a positive number.', code=ExitCodes.ARGUMENT_ERROR)
66
67 if self.args.iterations < self.args.output_frequency:
68 DeconvolutionApp.exit('Output frequency is too low.', code=ExitCodes.ARGUMENT_ERROR)
69
70 self.args.output = os.path.abspath(self.args.output)
71
72 self.run()
73
75
76 writer = DensityMapWriter()
77
78 self.log('Reading input density map...')
79 try:
80 input = DensityMapReader(self.args.mapfile).read()
81 embd = Deconvolution(input.data, self.args.psf_size)
82
83 except DensityMapFormatError as e:
84 msg = 'Error reading input MRC file: {0}'.format(e)
85 DeconvolutionApp.exit(msg, code=ExitCodes.INVALID_DATA)
86
87 self.log('Running {0} iterations...'.format(self.args.iterations))
88 self.log(' Iteration Loss Correlation Output')
89
90 for i in range(1, self.args.iterations + 1):
91 embd.run_once()
92
93 if i % self.args.output_frequency == 0:
94 output = OutputPathBuilder(self.args, i)
95
96 density = DensityInfo(embd.data, None, None, header=input.header)
97 writer.write_file(output.fullpath, density)
98
99 self.log('{0:>9}. {1:15.2f} {2:10.4f} {3}'.format(
100 i, embd.loss, embd.correlation, output.filename))
101
102 self.log('Done: {0}.'.format(output.fullpath))
103
104 - def log(self, *a, **k):
108
111
113
114 basename = os.path.basename(args.mapfile)
115 file, extension = os.path.splitext(basename)
116
117 self._newfile = '{0}.{1}{2}'.format(file, i, extension)
118 self._path = os.path.join(args.output, self._newfile)
119
120 @property
123
124 @property
126 return os.path.basename(self._newfile)
127
129
130 @staticmethod
131 - def corr(x, y, center=False):
138
140 """
141 Blind deconvolution for n-dimensional images.
142
143 @param data: EM density map data (data field of L{csb.bio.io.mrc.DensityInfo})
144 @type data: array
145 @param psf_size: point spread function size
146 @type psf_size: ints
147 @param beta_x: hyperparameters of sparseness constraints
148 @type beta_x: float
149 @param beta_f: hyperparameters of sparseness constraints
150 @type beta_f: float
151 """
152
153 - def __init__(self, data, psf_size, beta_x=1e-10, beta_f=1e-10, cache=True):
154
155 self._f = []
156 self._x = []
157 self._y = numpy.array(data)
158 self._loss = []
159 self._corr = []
160
161 self._ycache = None
162 self._cache = bool(cache)
163
164 self._beta_x = float(beta_x)
165 self._beta_f = float(beta_f)
166
167 shape_psf = (psf_size, psf_size, psf_size)
168 self._initialize(shape_psf)
169
170 @property
173
174 @property
177
178 @property
180 """
181 Current loss value.
182 """
183 if len(self._loss) > 0:
184 return float(self._loss[-1])
185 else:
186 return None
187
188 @property
190 """
191 Current correlation value.
192 """
193 if len(self._corr) > 0:
194 return float(self._corr[-1])
195 else:
196 return None
197
198 @property
201
203 """
204 Initialize with flat image and psf.
205 """
206 self._f = numpy.ones(shape_psf)
207 self._x = numpy.ones(numpy.array(self._y.shape) + numpy.array(shape_psf) - 1)
208
209 self._normalize_psf()
210
212 self._f /= self._f.sum()
213
216
218
219 if cache and self._ycache is not None:
220 return self._ycache
221 else:
222 y = self._calculate_image()
223 if self._cache:
224 self._ycache = y
225 return y
226
235
237
238 y = self.calculate_image()
239
240 N = correlate(self._x, self._y) - self.beta_f
241 D = correlate(self._x, y)
242
243 self._f *= numpy.clip(N, 1e-300, 1e300) / numpy.clip(D, 1e-300, 1e300)
244 self._normalize_psf()
245
247
248 y = self.calculate_image(cache=cache)
249
250 return 0.5 * ((self._y - y) ** 2).sum() + \
251 + self.beta_f * self._f.sum() + self.beta_x * self._x.sum()
252
257
259 """
260 Run a single iteration.
261 """
262
263 self._loss.append(self.eval_loss(cache=True))
264 self._corr.append(self.eval_corr(cache=True))
265
266 self._update_map()
267 self._update_psf()
268
269 - def run(self, iterations):
270 """
271 Run multiple iterations.
272
273 @param iterations: number of iterations to run
274 @type iterations: int
275 """
276 for i in range(iterations):
277 self.run_once()
278
282
283
284 if __name__ == '__main__':
285 main()
286