pytme 0.3b0.post1__cp311-cp311-macosx_15_0_arm64.whl → 0.3.1.dev20250731__cp311-cp311-macosx_15_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. pytme-0.3.1.dev20250731.data/scripts/estimate_ram_usage.py +97 -0
  2. {pytme-0.3b0.post1.data → pytme-0.3.1.dev20250731.data}/scripts/match_template.py +30 -41
  3. {pytme-0.3b0.post1.data → pytme-0.3.1.dev20250731.data}/scripts/postprocess.py +35 -21
  4. {pytme-0.3b0.post1.data → pytme-0.3.1.dev20250731.data}/scripts/preprocessor_gui.py +96 -24
  5. pytme-0.3.1.dev20250731.data/scripts/pytme_runner.py +1223 -0
  6. {pytme-0.3b0.post1.dist-info → pytme-0.3.1.dev20250731.dist-info}/METADATA +5 -7
  7. {pytme-0.3b0.post1.dist-info → pytme-0.3.1.dev20250731.dist-info}/RECORD +59 -49
  8. scripts/estimate_ram_usage.py +97 -0
  9. scripts/extract_candidates.py +118 -99
  10. scripts/match_template.py +30 -41
  11. scripts/match_template_devel.py +1339 -0
  12. scripts/postprocess.py +35 -21
  13. scripts/preprocessor_gui.py +96 -24
  14. scripts/pytme_runner.py +644 -190
  15. scripts/refine_matches.py +158 -390
  16. tests/data/.DS_Store +0 -0
  17. tests/data/Blurring/.DS_Store +0 -0
  18. tests/data/Maps/.DS_Store +0 -0
  19. tests/data/Raw/.DS_Store +0 -0
  20. tests/data/Structures/.DS_Store +0 -0
  21. tests/preprocessing/test_utils.py +18 -0
  22. tests/test_analyzer.py +2 -3
  23. tests/test_backends.py +3 -9
  24. tests/test_density.py +0 -1
  25. tests/test_extensions.py +0 -1
  26. tests/test_matching_utils.py +10 -60
  27. tests/test_orientations.py +0 -12
  28. tests/test_rotations.py +1 -1
  29. tme/__version__.py +1 -1
  30. tme/analyzer/_utils.py +4 -4
  31. tme/analyzer/aggregation.py +35 -15
  32. tme/analyzer/peaks.py +11 -10
  33. tme/backends/_jax_utils.py +64 -18
  34. tme/backends/_numpyfftw_utils.py +270 -0
  35. tme/backends/cupy_backend.py +16 -55
  36. tme/backends/jax_backend.py +79 -40
  37. tme/backends/matching_backend.py +17 -51
  38. tme/backends/mlx_backend.py +1 -27
  39. tme/backends/npfftw_backend.py +71 -65
  40. tme/backends/pytorch_backend.py +1 -26
  41. tme/density.py +58 -5
  42. tme/extensions.cpython-311-darwin.so +0 -0
  43. tme/filters/ctf.py +22 -21
  44. tme/filters/wedge.py +10 -7
  45. tme/mask.py +341 -0
  46. tme/matching_data.py +31 -19
  47. tme/matching_exhaustive.py +37 -47
  48. tme/matching_optimization.py +2 -1
  49. tme/matching_scores.py +229 -411
  50. tme/matching_utils.py +73 -422
  51. tme/memory.py +1 -1
  52. tme/orientations.py +24 -13
  53. tme/rotations.py +1 -1
  54. pytme-0.3b0.post1.data/scripts/pytme_runner.py +0 -769
  55. {pytme-0.3b0.post1.data → pytme-0.3.1.dev20250731.data}/scripts/estimate_memory_usage.py +0 -0
  56. {pytme-0.3b0.post1.data → pytme-0.3.1.dev20250731.data}/scripts/preprocess.py +0 -0
  57. {pytme-0.3b0.post1.dist-info → pytme-0.3.1.dev20250731.dist-info}/WHEEL +0 -0
  58. {pytme-0.3b0.post1.dist-info → pytme-0.3.1.dev20250731.dist-info}/entry_points.txt +0 -0
  59. {pytme-0.3b0.post1.dist-info → pytme-0.3.1.dev20250731.dist-info}/licenses/LICENSE +0 -0
  60. {pytme-0.3b0.post1.dist-info → pytme-0.3.1.dev20250731.dist-info}/top_level.txt +0 -0
scripts/refine_matches.py CHANGED
@@ -1,34 +1,42 @@
1
1
  #!python3
2
- """ Iterative template matching parameter tuning.
2
+ """Iterative template matching parameter tuning.
3
3
 
4
- Copyright (c) 2024 European Molecular Biology Laboratory
4
+ Copyright (c) 2024 European Molecular Biology Laboratory
5
5
 
6
- Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
6
+ Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
7
7
  """
8
8
  import argparse
9
9
  import subprocess
10
10
  from sys import exit
11
+ from os import unlink
11
12
  from time import time
12
- from shutil import copyfile
13
13
  from typing import Tuple, List, Dict
14
14
 
15
15
  import numpy as np
16
- from scipy import optimize
17
16
  from sklearn.metrics import roc_auc_score
18
17
 
19
18
  from tme import Orientations, Density
20
- from tme.matching_utils import generate_tempfile_name, load_pickle, write_pickle, create_mask
19
+ from tme.backends import backend as be
20
+ from tme.matching_utils import generate_tempfile_name, create_mask
21
21
  from tme.matching_exhaustive import MATCHING_EXHAUSTIVE_REGISTER
22
22
 
23
- def parse_range(x : str):
24
- start, stop,step = x.split(":")
23
+
24
+ def parse_range(x: str):
25
+ start, stop, step = x.split(":")
25
26
  return range(int(start), int(stop), int(step))
26
27
 
28
+
27
29
  def parse_args():
28
30
  parser = argparse.ArgumentParser(
29
31
  description="Refine template matching candidates using deep matching.",
30
32
  )
31
33
  io_group = parser.add_argument_group("Input / Output")
34
+ io_group.add_argument(
35
+ "--target",
36
+ required=True,
37
+ type=str,
38
+ help="Image stack created using extract_candidates.py.",
39
+ )
32
40
  io_group.add_argument(
33
41
  "--orientations",
34
42
  required=True,
@@ -38,72 +46,52 @@ def parse_args():
38
46
  " for available options.",
39
47
  )
40
48
  io_group.add_argument(
41
- "--output_prefix", required=True, type=str, help="Path to write output to."
49
+ "--output-prefix", required=True, type=str, help="Path to write output to."
42
50
  )
43
51
  io_group.add_argument(
44
- "--iterations",
45
- required=False,
46
- default=0,
47
- type=int,
48
- help="Number of refinement iterations to perform.",
52
+ "--save-pickles",
53
+ action="store_true",
54
+ default=False,
55
+ help="Save intermediate results as pickle files in output directory.",
49
56
  )
50
57
  io_group.add_argument(
51
- "--verbose",
58
+ "--save-orientations",
52
59
  action="store_true",
53
60
  default=False,
54
- help="More verbose and more files written to disk.",
61
+ help="Save orientation results in output directory.",
55
62
  )
56
63
  matching_group = parser.add_argument_group("Template Matching")
57
- matching_group.add_argument(
58
- "--input_file",
59
- required=False,
60
- type=str,
61
- help="Path to the output of match_template.py.",
62
- )
63
- matching_group.add_argument(
64
- "-m",
65
- "--target",
66
- dest="target",
67
- type=str,
68
- required=False,
69
- help="Path to a target in CCP4/MRC, EM, H5 or another format supported by "
70
- "tme.density.Density.from_file "
71
- "https://kosinskilab.github.io/pyTME/reference/api/tme.density.Density.from_file.html",
72
- )
73
- matching_group.add_argument(
74
- "--target_mask",
75
- dest="target_mask",
76
- type=str,
77
- required=False,
78
- help="Path to a mask for the target in a supported format (see target).",
79
- )
80
64
  matching_group.add_argument(
81
65
  "-i",
82
66
  "--template",
83
- dest="template",
84
67
  type=str,
85
- required=False,
68
+ required=True,
86
69
  help="Path to a template in PDB/MMCIF or other supported formats (see target).",
87
70
  )
88
71
  matching_group.add_argument(
89
- "--template_mask",
90
- dest="template_mask",
72
+ "--template-mask",
91
73
  type=str,
92
74
  required=False,
93
75
  help="Path to a mask for the template in a supported format (see target).",
94
76
  )
95
77
  matching_group.add_argument(
96
- "--invert_target_contrast",
97
- dest="invert_target_contrast",
78
+ "--ctf-file",
79
+ type=str,
80
+ required=False,
81
+ default=None,
82
+ help="Path to a file with CTF parameters. This can be a Warp/M XML file "
83
+ "a GCTF/Relion STAR file, an MDOC file, or the output of CTFFIND4. If the file "
84
+ " does not specify tilt angles, --tilt-angles are used.",
85
+ )
86
+ matching_group.add_argument(
87
+ "--invert-target-contrast",
98
88
  action="store_true",
99
89
  default=False,
100
- help="Invert the target's contrast and rescale linearly between zero and one. "
101
- "This option is intended for targets where templates to-be-matched have "
90
+ help="Invert the target's contrast for cases where templates to-be-matched have "
102
91
  "negative values, e.g. tomograms.",
103
92
  )
104
93
  matching_group.add_argument(
105
- "--angular_sampling",
106
- dest="angular_sampling",
94
+ "--angular-sampling",
107
95
  required=True,
108
96
  default=None,
109
97
  help="Angular sampling rate using optimized rotational sets."
@@ -111,13 +99,15 @@ def parse_args():
111
99
  )
112
100
  matching_group.add_argument(
113
101
  "-s",
114
- dest="score",
102
+ "--score",
115
103
  type=str,
116
- default="FLCSphericalMask",
104
+ default="batchFLCSphericalMask",
117
105
  choices=list(MATCHING_EXHAUSTIVE_REGISTER.keys()),
118
106
  help="Template matching scoring function.",
119
107
  )
120
- matching_group.add_argument(
108
+
109
+ computation_group = parser.add_argument_group("Computation")
110
+ computation_group.add_argument(
121
111
  "-n",
122
112
  dest="cores",
123
113
  required=False,
@@ -125,113 +115,44 @@ def parse_args():
125
115
  default=4,
126
116
  help="Number of cores used for template matching.",
127
117
  )
128
- matching_group.add_argument(
129
- "--use_gpu",
130
- dest="use_gpu",
131
- action="store_true",
132
- default=False,
133
- help="Whether to perform computations on the GPU.",
134
- )
135
- matching_group.add_argument(
136
- "--no_centering",
137
- dest="no_centering",
138
- action="store_true",
139
- help="Assumes the template is already centered and omits centering.",
140
- )
141
- matching_group.add_argument(
142
- "--no_edge_padding",
143
- dest="no_edge_padding",
144
- action="store_true",
145
- default=False,
146
- help="Whether to not pad the edges of the target. Can be set if the target"
147
- " has a well defined bounding box, e.g. a masked reconstruction.",
148
- )
149
- matching_group.add_argument(
150
- "--no_fourier_padding",
151
- dest="no_fourier_padding",
152
- action="store_true",
153
- default=False,
154
- help="Whether input arrays should not be zero-padded to full convolution shape "
155
- "for numerical stability. When working with very large targets, e.g. tomograms, "
156
- "it is safe to use this flag and benefit from the performance gain.",
157
- )
158
-
159
- peak_group = parser.add_argument_group("Peak Calling")
160
- peak_group.add_argument(
161
- "--number_of_peaks",
162
- type=int,
163
- default=100,
164
- required=False,
165
- help="Upper limit of peaks to call, subject to filtering parameters. Default 1000. "
166
- "If minimum_score is provided all peaks scoring higher will be reported.",
167
- )
168
- extraction_group = parser.add_argument_group("Extraction")
169
- extraction_group.add_argument(
170
- "--keep_out_of_box",
171
- action="store_true",
172
- required=False,
173
- help="Whether to keep orientations that fall outside the box. If the "
174
- "orientations are sensible, it is safe to pass this flag.",
118
+ computation_group.add_argument(
119
+ "--backend",
120
+ default=be._backend_name,
121
+ choices=be.available_backends(),
122
+ help="Set computation backend.",
175
123
  )
176
124
 
177
125
  optimization_group = parser.add_argument_group("Optimization")
178
- optimization_group.add_argument(
179
- "--lowpass",
180
- dest="lowpass",
181
- action="store_true",
182
- default=False,
183
- help="Optimize template matching lowpass filter cutoff.",
184
- )
185
- optimization_group.add_argument(
186
- "--highpass",
187
- dest="highpass",
188
- action="store_true",
189
- default=False,
190
- help="Optimize template matching highpass filter cutoff.",
191
- )
192
126
  optimization_group.add_argument(
193
127
  "--lowpass-range",
194
- dest="lowpass_range",
195
128
  type=str,
196
129
  default="0:50:5",
197
130
  help="Optimize template matching lowpass filter cutoff.",
198
131
  )
199
132
  optimization_group.add_argument(
200
133
  "--highpass-range",
201
- dest="highpass_range",
202
134
  type=str,
203
135
  default="0:50:5",
204
136
  help="Optimize template matching highpass filter cutoff.",
205
137
  )
206
138
  optimization_group.add_argument(
207
139
  "--translation-uncertainty",
208
- dest="translation_uncertainty",
209
140
  type=int,
210
- default=None,
211
- help="Optimize template matching highpass filter cutoff.",
141
+ default=8,
142
+ help="Translational uncertainty for masking, defaults to 8.",
212
143
  )
213
144
 
214
-
215
145
  args = parser.parse_args()
216
146
 
217
- data_present = args.target is not None and args.template is not None
218
- if args.input_file is None and not data_present:
219
- raise ValueError(
220
- "Either --input_file or --target and --template need to be specified."
221
- )
222
- elif args.input_file is not None and data_present:
223
- raise ValueError(
224
- "Please specific either --input_file or --target and --template."
225
- )
226
-
147
+ args.target_mask = None
227
148
  if args.lowpass_range != "None":
228
149
  args.lowpass_range = parse_range(args.lowpass_range)
229
150
  else:
230
- args.lowpass_range = (None, )
151
+ args.lowpass_range = (None,)
231
152
  if args.highpass_range != "None":
232
153
  args.highpass_range = parse_range(args.highpass_range)
233
154
  else:
234
- args.highpass_range = (None, )
155
+ args.highpass_range = (None,)
235
156
  return args
236
157
 
237
158
 
@@ -250,6 +171,7 @@ def argdict_to_command(input_args: Dict, executable: str) -> List:
250
171
  ret.insert(0, executable)
251
172
  return " ".join(ret)
252
173
 
174
+
253
175
  def run_command(command):
254
176
  ret = subprocess.run(command, capture_output=True, shell=True)
255
177
  if ret.returncode != 0:
@@ -260,52 +182,42 @@ def run_command(command):
260
182
 
261
183
  return None
262
184
 
263
- def create_stacking_argdict(args) -> Dict:
264
- arg_dict = {
265
- "--target": args.target,
266
- "--template": args.template,
267
- "--orientations": args.orientations,
268
- "--output_file": args.candidate_stack_path,
269
- "--keep_out_of_box": args.keep_out_of_box,
270
- }
271
- return arg_dict
272
-
273
185
 
274
186
  def create_matching_argdict(args) -> Dict:
275
187
  arg_dict = {
276
188
  "--target": args.target,
277
189
  "--template": args.template,
278
- "--template_mask": args.template_mask,
279
- "-o": args.match_template_path,
190
+ "--template-mask": args.template_mask,
191
+ "--output": args.match_template_path,
280
192
  "-a": args.angular_sampling,
281
193
  "-s": args.score,
282
- "--no_fourier_padding": True,
283
- "--no_edge_padding": True,
284
- "--no_centering": args.no_centering,
285
194
  "-n": args.cores,
286
- "--invert_target_contrast": args.invert_target_contrast,
287
- "--use_gpu": args.use_gpu,
195
+ "--ctf-file": args.ctf_file,
196
+ "--invert-target-contrast": args.invert_target_contrast,
197
+ "--backend" : args.backend,
288
198
  }
289
199
  return arg_dict
290
200
 
291
201
 
292
202
  def create_postprocessing_argdict(args) -> Dict:
293
203
  arg_dict = {
294
- "--input_file": args.match_template_path,
295
- "--target_mask": args.target_mask,
296
- "--output_prefix": args.new_orientations_path,
297
- "--peak_caller": "PeakCallerMaximumFilter",
298
- "--number_of_peaks": args.number_of_peaks,
299
- "--output_format": "orientations",
300
- "--mask_edges": True,
204
+ "--input-file": args.match_template_path,
205
+ "--target-mask": args.target_mask,
206
+ "--output-prefix": args.new_orientations_path,
207
+ "--peak-caller": "PeakCallerMaximumFilter",
208
+ "--num-peaks": 1,
209
+ "--output-format": "orientations",
210
+ "--mask-edges": True,
301
211
  }
302
212
  if args.target_mask is not None:
303
- arg_dict["--mask_edges"] = False
213
+ arg_dict["--mask-edges"] = False
304
214
  return arg_dict
305
215
 
306
216
 
307
- def update_orientations(old : Orientations, new : Orientations, args, **kwargs) -> Orientations:
308
- stack_shape = Density.from_file(args.candidate_stack_path, use_memmap=True).shape
217
+ def update_orientations(
218
+ old: Orientations, new: Orientations, args, **kwargs
219
+ ) -> Orientations:
220
+ stack_shape = Density.from_file(args.target, use_memmap=True).shape
309
221
  stack_center = np.add(np.divide(stack_shape, 2).astype(int), np.mod(stack_shape, 2))
310
222
 
311
223
  peak_number = new.translations[:, 0].astype(int)
@@ -315,34 +227,44 @@ def update_orientations(old : Orientations, new : Orientations, args, **kwargs)
315
227
  )
316
228
  ret = old.copy()
317
229
  ret.scores[:] = 0
230
+
318
231
  ret.scores[peak_number] = new.scores
319
232
  ret.translations[peak_number] = new_translations
320
233
 
321
- # The effect of --align_orientations should be handled herer
234
+ # The effect of --align_orientations should be handled here
235
+ ret.rotations[peak_number] = new.rotations
322
236
  return ret
323
237
 
324
238
 
325
239
  class DeepMatcher:
326
- def __init__(self, args, margin : float = 0.5):
240
+ def __init__(self, args, margin: float = 0.5):
327
241
  self.args = args
328
242
  self.margin = margin
329
243
  self.orientations = Orientations.from_file(args.orientations)
330
244
 
331
245
  match_template_args = create_matching_argdict(args)
332
- match_template_args["--target"] = args.candidate_stack_path
246
+ match_template_args["--target"] = args.target
333
247
  self.match_template_args = match_template_args
334
248
 
335
249
  self.filter_parameters = {}
336
- if args.lowpass:
250
+ if args.lowpass_range:
337
251
  self.filter_parameters["--lowpass"] = 0
338
- if args.highpass:
339
- self.filter_parameters["--highpass"] = 200
340
- # self.filter_parameters["--whiten"] = False
341
- self.filter_parameters["--no_filter_target"] = False
342
-
252
+ if args.highpass_range:
253
+ self.filter_parameters["--highpass"] = 0
343
254
 
344
255
  self.postprocess_args = create_postprocessing_argdict(args)
345
- self.postprocess_args["--number_of_peaks"] = 1
256
+ self.log_file = f"{args.output_prefix}_optimization_log.txt"
257
+
258
+ header = [
259
+ "mean_score_positive",
260
+ "mean_score_negative",
261
+ "lowpass",
262
+ "highpass",
263
+ "auc_score",
264
+ "duration",
265
+ ]
266
+ with open(self.log_file, mode="w", encoding="utf-8") as f:
267
+ _ = f.write(",".join(header) + "\n")
346
268
 
347
269
  def get_initial_values(self) -> Tuple[float]:
348
270
  ret = tuple(float(x) for x in self.filter_parameters.values())
@@ -356,269 +278,115 @@ class DeepMatcher:
356
278
  ret[key] = value > 0.5
357
279
  return ret
358
280
 
359
- def forward(self, x : Tuple[float]):
360
-
361
-
281
+ def forward(self, x: Tuple[float]):
362
282
  # Label 1 -> True positive, label 0 -> false positive
363
283
  orientations_new = self(x)
364
284
  label, score = orientations_new.details, orientations_new.scores
365
- # loss = np.add(
366
- # (1 - label) * np.square(score),
367
- # label * np.square(np.fmax(self.margin - score, 0.0))
368
- # )
369
- # loss = loss.mean()
370
-
371
-
372
-
373
- loss = roc_auc_score(label, score)
374
- # print(
375
- # np.mean(score[label == 1]), np.mean(score[label == 0]),
376
- # *x, loss, time()
377
- # )
378
-
285
+ loss = np.add(
286
+ (1 - label) * np.square(score),
287
+ label * np.square(np.fmax(self.margin - score, 0.0)),
288
+ )
289
+ loss = loss.mean()
379
290
  return loss
380
291
 
381
292
  def __call__(self, x: Tuple[float]):
293
+ start = time()
382
294
  filter_parameters = self.format_parameters(x)
383
295
  self.match_template_args.update(filter_parameters)
384
- match_template = argdict_to_command(
385
- self.match_template_args,
386
- executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
387
- )
388
- run_command(match_template)
389
296
 
390
- # Assume we get a new peak for each input in the same order
391
- postprocess = argdict_to_command(
392
- self.postprocess_args,
393
- executable="python3 $HOME/src/pytme/scripts/postprocess.py",
394
- )
395
- run_command(postprocess)
297
+ if self.args.save_pickles or self.args.save_orientations:
298
+ prefix = "_".join([str(y) for y in x])
396
299
 
397
- orientations_new = Orientations.from_file(
398
- f"{self.postprocess_args['--output_prefix']}.tsv"
399
- )
400
- orientations_new = update_orientations(
401
- new=orientations_new,
402
- old=self.orientations,
403
- args=self.args
404
- )
405
-
406
- label, score = orientations_new.details, orientations_new.scores
407
- loss = roc_auc_score(label, score)
408
- print(
409
- np.mean(score[label == 1]), np.mean(score[label == 0]),
410
- *x, 0, loss, time()
411
- )
300
+ if self.args.save_pickles:
301
+ pickle_path = f"{self.args.output_prefix}_{prefix}.pickle"
302
+ self.match_template_args["--output"] = pickle_path
303
+ self.postprocess_args["--input-file"] = pickle_path
412
304
 
305
+ if self.args.save_orientations:
306
+ orientation_path = f"{self.args.output_prefix}_{prefix}"
413
307
 
414
- # Rerun with noise correction
415
- temp_args = self.match_template_args.copy()
416
- background_file = generate_tempfile_name(".pickle")
417
- temp_args["--scramble_phases"] = True
418
- temp_args["-o"] = background_file
419
308
  match_template = argdict_to_command(
420
- temp_args,
421
- executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
309
+ self.match_template_args,
310
+ executable="match_template",
422
311
  )
423
312
  run_command(match_template)
424
- temp_args = self.match_template_args.copy()
425
- temp_args["--background_file"] = background_file
313
+
314
+ # Assume we get a new peak for each input in the same order
426
315
  postprocess = argdict_to_command(
427
316
  self.postprocess_args,
428
- executable="python3 $HOME/src/pytme/scripts/postprocess.py",
317
+ executable="postprocess",
429
318
  )
430
319
  run_command(postprocess)
431
320
 
432
321
  orientations_new = Orientations.from_file(
433
- f"{self.postprocess_args['--output_prefix']}.tsv"
322
+ f"{self.postprocess_args['--output-prefix']}.tsv"
434
323
  )
435
324
  orientations_new = update_orientations(
436
- new=orientations_new,
437
- old=self.orientations,
438
- args=self.args
325
+ new=orientations_new, old=self.orientations, args=self.args
439
326
  )
440
327
 
328
+ if self.args.save_orientations:
329
+ orientations_new.to_file(f"{orientation_path}.tsv")
330
+
441
331
  label, score = orientations_new.details, orientations_new.scores
442
332
  loss = roc_auc_score(label, score)
443
- print(
444
- np.mean(score[label == 1]), np.mean(score[label == 0]),
445
- *x, 1, loss, time()
446
- )
447
333
 
448
- return orientations_new
334
+ mean_true = np.mean(score[label == 1])
335
+ mean_false = np.mean(score[label == 0])
336
+ params = ",".join([str(y) for y in x])
449
337
 
450
- # def __call__(self, x: Tuple[float]):
451
- # filter_parameters = self.format_parameters(x)
452
- # # print(filter_parameters)
453
- # self.match_template_args.update(filter_parameters)
454
- # match_template = argdict_to_command(
455
- # self.match_template_args,
456
- # executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
457
- # )
458
- # run_command(match_template)
459
-
460
- # data = load_pickle(self.args.match_template_path)
461
- # temp_args = self.match_template_args.copy()
462
- # temp_args["--scramble_phases"] = True
463
- # # write_pickle(data, "/home/vmaurer/deep_matching/t.pickle")
464
-
465
- # match_template = argdict_to_command(
466
- # temp_args,
467
- # executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
468
- # )
469
- # run_command(match_template)
470
- # data_norm = load_pickle(self.args.match_template_path)
471
- # # write_pickle(data_norm, "/home/vmaurer/deep_matching/noise.pickle")
472
-
473
- # data[0] = (data[0] - data_norm[0]) / (1 - data_norm[0])
474
- # data[0] = np.fmax(data[0], 0)
475
- # write_pickle(data, self.args.match_template_path)
476
-
477
- # # Assume we get a new peak for each input in the same order
478
- # postprocess = argdict_to_command(
479
- # self.postprocess_args,
480
- # executable="python3 $HOME/src/pytme/scripts/postprocess.py",
481
- # )
482
- # run_command(postprocess)
483
-
484
- # orientations_new = Orientations.from_file(
485
- # f"{self.postprocess_args['--output_prefix']}.tsv"
486
- # )
487
- # orientations_new = update_orientations(
488
- # new=orientations_new,
489
- # old=self.orientations,
490
- # args=self.args
491
- # )
492
-
493
- # return orientations_new
338
+ with open(self.log_file, mode="a", encoding="utf-8") as f:
339
+ _ = f.write(f"{mean_true},{mean_false},{params},{loss},{time()-start}\n")
340
+ return orientations_new
494
341
 
495
342
 
496
343
  def main():
497
344
  args = parse_args()
498
345
 
499
- if args.input_file is not None:
500
- data = load_pickle(args.input_file)
501
- target_origin, _, sampling_rate, cli_args = data[-1]
502
- args.target, args.template = cli_args.target, cli_args.template
503
-
504
- args.candidate_stack_path = generate_tempfile_name(suffix=".h5")
505
346
  args.new_orientations_path = generate_tempfile_name()
506
347
  args.match_template_path = generate_tempfile_name()
507
348
 
508
- match_deep = DeepMatcher(args)
509
- initial_values = match_deep.get_initial_values()
510
-
511
- # Do a single pass over the data
512
- if len(initial_values) == 0:
513
- create_image_stack = create_stacking_argdict(args)
514
- create_image_stack = argdict_to_command(
515
- create_image_stack,
516
- executable="python3 $HOME/src/pytme/scripts/extract_candidates.py",
517
- )
518
- run_command(create_image_stack)
519
-
520
- print("Created image stack")
521
- if args.verbose:
522
- copyfile(args.candidate_stack_path, f"{args.output_prefix}_stack.h5")
523
-
524
- print("Starting matching")
525
- orientations = match_deep(x=())
526
-
527
- if args.verbose:
528
- copyfile(args.match_template_path, f"{args.output_prefix}_stack.pickle")
529
- print("Completed matching")
530
- orientations.to_file(f"{args.output_prefix}.tsv")
531
- exit(0)
349
+ args.box_size = np.max(Density.from_file(args.template, use_memmap=True).shape)
532
350
 
351
+ args.target_mask = None
533
352
  if args.translation_uncertainty is not None:
534
353
  args.target_mask = generate_tempfile_name(suffix=".h5")
535
-
536
- for current_iteration in range(args.iterations):
537
- create_image_stack = create_stacking_argdict(args)
538
- create_image_stack = argdict_to_command(
539
- create_image_stack,
540
- executable="python3 $HOME/src/pytme/scripts/extract_candidates.py",
354
+ dens = Density.from_file(args.target)
355
+ stack_center = np.add(
356
+ np.divide(dens.data.shape, 2).astype(int), np.mod(dens.data.shape, 2)
357
+ ).astype(int)[1:]
358
+
359
+ out = dens.empty
360
+ out.data[..., :] = create_mask(
361
+ mask_type="ellipse",
362
+ center=stack_center,
363
+ radius=args.translation_uncertainty,
364
+ shape=dens.data.shape[1:],
541
365
  )
542
- run_command(create_image_stack)
543
-
544
- if args.translation_uncertainty is not None:
545
- dens = Density.from_file(args.candidate_stack_path)
546
- stack_center = np.add(
547
- np.divide(dens.data.shape, 2).astype(int), np.mod(dens.data.shape, 2)
548
- ).astype(int)[1:]
549
-
550
- out = dens.empty
551
- out.data[:,...] = create_mask(
552
- mask_type = "ellipse",
553
- center = stack_center,
554
- radius = args.translation_uncertainty,
555
- shape = dens.data.shape[1:]
556
- )
557
- out.to_file(args.target_mask)
558
-
559
-
560
-
561
- # Perhaps we need a different optimizer here to use sensible steps for each parameter
562
- parameters, min_loss = (), None
563
- match_deep = DeepMatcher(args)
564
- # for lowpass in (0, 10, 20, 50):
565
- # for highpass in (50, 100, 150, 200):
566
- # for whiten in (False, True):
567
- # loss = match_deep.forward((lowpass, highpass, whiten))
568
- # # print((lowpass, highpass), loss)
569
- # if min_loss is None:
570
- # min_loss = loss
571
- # if loss < min_loss:
572
- # min_loss = loss
573
- # parameters = (lowpass, highpass, whiten),
574
-
575
- # for lowpass in (10, 50, 100, 200):
576
- # for highpass in (10, 50, 100, 200):
577
- for lowpass in args.lowpass_range:
578
- for highpass in args.highpass_range:
579
- if lowpass is not None and highpass is not None:
580
- if lowpass >= highpass:
581
- continue
582
- for no_filter_target in (True, False):
583
- loss = match_deep.forward((lowpass, highpass, no_filter_target))
584
- if min_loss is None:
585
- min_loss = loss
586
- if loss < min_loss:
587
- min_loss = loss
588
- parameters = (lowpass, highpass, no_filter_target)
589
-
590
- # print("Final output", min_loss, parameters)
591
- import sys
592
- sys.exit(0)
593
-
594
- # parameters = optimize.minimize(
595
- # x0=match_deep.get_initial_values(),
596
- # fun=match_deep.forward,
597
- # method="L-BFGS-B",
598
- # options={"maxiter": 100}
599
- # )
600
- parameter_dict = match_deep.format_parameters(parameters)
601
- print("Converged with parameters", parameters)
602
-
603
- match_template = create_matching_argdict(args)
604
- match_template.update(parameter_dict)
605
- match_template = argdict_to_command(
606
- match_template,
607
- executable="python3 $HOME/src/pytme/scripts/match_template_filters.py",
608
- )
609
- _ = subprocess.run(match_template, capture_output=True, shell=True)
610
-
611
- # Some form of labelling is necessary for these matches
612
- # 1. All of them are true positives
613
- # 2. All of them are true positives up to a certain threshold
614
- # 3. Kernel fitting
615
- # 4. Perhaps also sensible to include a certain percentage of low scores as true negatives
616
- postprocess = create_postprocessing_argdict(args)
617
- postprocess = argdict_to_command(postprocess, executable="postprocess.py")
618
- _ = subprocess.run(postprocess, capture_output=True, shell=True)
619
- args.orientations = f"{args.new_orientations_path}.tsv"
620
- orientations = Orientations.from_file(args.orientations)
621
- orientations.to_file(f"{args.output_prefix}_{current_iteration}.tsv")
366
+ out.to_file(args.target_mask)
367
+
368
+ # Perhaps we need a different optimizer here to use sensible steps for each parameter
369
+ parameters, min_loss = (), None
370
+ match_deep = DeepMatcher(args)
371
+ for lowpass in args.lowpass_range:
372
+ for highpass in args.highpass_range:
373
+ if lowpass is not None and highpass is not None:
374
+ if lowpass >= highpass:
375
+ continue
376
+ parameters = (lowpass, highpass)
377
+ loss = match_deep.forward(parameters)
378
+ if min_loss is None:
379
+ min_loss, best_params = loss, parameters
380
+
381
+ if loss < min_loss:
382
+ min_loss, best_params = loss, parameters
383
+
384
+ unlink(args.target_mask)
385
+ unlink(args.new_orientations_path)
386
+
387
+ if not args.save_pickles:
388
+ unlink(args.match_template_path)
389
+ print("Final output", min_loss, best_params)
622
390
 
623
391
 
624
392
  if __name__ == "__main__":