pycistem 0.6.1__cp310-cp310-manylinux_2_28_x86_64.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 (84) hide show
  1. pycistem/__init__.py +9 -0
  2. pycistem/config.py +20 -0
  3. pycistem/core/__init__.py +1 -0
  4. pycistem/core/core.cpp +1070 -0
  5. pycistem/core/core.cpython-310-x86_64-linux-gnu.so +0 -0
  6. pycistem/core/database.cpp +408 -0
  7. pycistem/core/euler_search.cpp +72 -0
  8. pycistem/core/run_profiles.cpp +114 -0
  9. pycistem/database/__init__.py +301 -0
  10. pycistem/programs/__init__.py +10 -0
  11. pycistem/programs/_cistem_constants.py +23 -0
  12. pycistem/programs/apply_ctf.py +59 -0
  13. pycistem/programs/cistem_program.py +277 -0
  14. pycistem/programs/ctffind.py +254 -0
  15. pycistem/programs/estimate_beamtilt.py +60 -0
  16. pycistem/programs/match_template.py +325 -0
  17. pycistem/programs/reconstruct3d.py +79 -0
  18. pycistem/programs/refine_ctf.py +106 -0
  19. pycistem/programs/refine_template.py +157 -0
  20. pycistem/programs/refine_template_dev.py +35 -0
  21. pycistem/programs/refine_template_niko.py +124 -0
  22. pycistem/programs/resample.py +30 -0
  23. pycistem/programs/run_profile.py +17 -0
  24. pycistem/programs/unblur.py +247 -0
  25. pycistem/programs/unblur_patch.py +204 -0
  26. pycistem/utils/__init__.py +1 -0
  27. pycistem/utils/extract_particles.py +36 -0
  28. pycistem/utils/move_class_into_unbinned_particle_stack.py +34 -0
  29. pycistem/utils/order_by_class_occupancy.py +26 -0
  30. pycistem/utils/plot_class_occupancy.py +51 -0
  31. pycistem/utils/plot_classification_fsc_movie.py +90 -0
  32. pycistem/utils/plot_occupancy_by_condition.py +129 -0
  33. pycistem-0.6.1.dist-info/METADATA +258 -0
  34. pycistem-0.6.1.dist-info/RECORD +84 -0
  35. pycistem-0.6.1.dist-info/WHEEL +5 -0
  36. pycistem-0.6.1.dist-info/licenses/LICENSE +201 -0
  37. pycistem-0.6.1.dist-info/sboms/auditwheel.cdx.json +1 -0
  38. pycistem.libs/libXau-154567c4.so.6.0.0 +0 -0
  39. pycistem.libs/libXcomposite-9a78b2b5.so.1.0.0 +0 -0
  40. pycistem.libs/libXcursor-557eab0f.so.1.0.2 +0 -0
  41. pycistem.libs/libXdamage-45f20f14.so.1.1.0 +0 -0
  42. pycistem.libs/libXfixes-e3b7d94c.so.3.1.0 +0 -0
  43. pycistem.libs/libXi-9178a6bd.so.6.1.0 +0 -0
  44. pycistem.libs/libXinerama-6a3f4a3e.so.1.0.0 +0 -0
  45. pycistem.libs/libXrandr-9f75043e.so.2.2.0 +0 -0
  46. pycistem.libs/libatk-1-3e086f29.0.so.0.22810.1 +0 -0
  47. pycistem.libs/libblkid-a9167753.so.1.1.0 +0 -0
  48. pycistem.libs/libbz2-a1e77c99.so.1.0.6 +0 -0
  49. pycistem.libs/libcairo-dfbea965.so.2.11512.0 +0 -0
  50. pycistem.libs/libdatrie-584ecbbd.so.1.3.2 +0 -0
  51. pycistem.libs/libffi-3a37023a.so.6.0.2 +0 -0
  52. pycistem.libs/libfontconfig-dcb2ce6c.so.1.12.0 +0 -0
  53. pycistem.libs/libfreetype-2f3b32b6.so.6.16.1 +0 -0
  54. pycistem.libs/libfribidi-a2ddad26.so.0.4.0 +0 -0
  55. pycistem.libs/libgdk-x11-2-ae2f4865.0.so.0.2400.32 +0 -0
  56. pycistem.libs/libgdk_pixbuf-2-e875edac.0.so.0.3612.0 +0 -0
  57. pycistem.libs/libgio-2-54f4f0a9.0.so.0.5600.4 +0 -0
  58. pycistem.libs/libgmodule-2-a8eef785.0.so.0.5600.4 +0 -0
  59. pycistem.libs/libgmp-d944b113.so.10.3.2 +0 -0
  60. pycistem.libs/libgnutls-e5fc1c5f.so.30.28.2 +0 -0
  61. pycistem.libs/libgomp-e985bcbb.so.1.0.0 +0 -0
  62. pycistem.libs/libgraphite2-a2b39163.so.3.0.1 +0 -0
  63. pycistem.libs/libgtk-x11-2-76f42ab1.0.so.0.2400.32 +0 -0
  64. pycistem.libs/libharfbuzz-26b3d829.so.0.10705.0 +0 -0
  65. pycistem.libs/libhogweed-cd4c53be.so.4.5 +0 -0
  66. pycistem.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
  67. pycistem.libs/libmount-ec61bd71.so.1.1.0 +0 -0
  68. pycistem.libs/libnettle-37944285.so.6.5 +0 -0
  69. pycistem.libs/libp11-kit-ac9dcd7e.so.0.3.0 +0 -0
  70. pycistem.libs/libpango-1-e80d1584.0.so.0.4200.3 +0 -0
  71. pycistem.libs/libpangocairo-1-e0e0fbcf.0.so.0.4200.3 +0 -0
  72. pycistem.libs/libpangoft2-1-1fa1613e.0.so.0.4200.3 +0 -0
  73. pycistem.libs/libpcre-0dd207b5.so.1.2.10 +0 -0
  74. pycistem.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
  75. pycistem.libs/libpixman-1-06469c37.so.0.38.4 +0 -0
  76. pycistem.libs/libpng16-748299c7.so.16.34.0 +0 -0
  77. pycistem.libs/libselinux-d0805dcb.so.1 +0 -0
  78. pycistem.libs/libtasn1-564de53e.so.6.5.5 +0 -0
  79. pycistem.libs/libthai-cd935638.so.0.3.0 +0 -0
  80. pycistem.libs/libunistring-05abdd40.so.2.1.0 +0 -0
  81. pycistem.libs/libuuid-95b83d40.so.1.3.0 +0 -0
  82. pycistem.libs/libxcb-5ddf6756.so.1.1.0 +0 -0
  83. pycistem.libs/libxcb-render-161c0eb5.so.0.0.0 +0 -0
  84. pycistem.libs/libxcb-shm-0be6dfbf.so.0.0.0 +0 -0
@@ -0,0 +1,247 @@
1
+ import asyncio
2
+ import datetime
3
+ import sqlite3
4
+ import struct
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+ from typing import List, Union
8
+ import time
9
+
10
+ import mrcfile
11
+ import pandas as pd
12
+
13
+ from pycistem.database import datetime_to_msdos, get_movie_info_from_db
14
+ from pycistem.programs import cistem_program
15
+ from pycistem.programs._cistem_constants import socket_job_result, socket_send_next_job
16
+
17
+
18
+ @dataclass
19
+ class UnblurParameters:
20
+ input_filename: str
21
+ output_filename: str = "unblurred.mrc"
22
+ pixel_size: float = 1.0
23
+ minimum_shift_in_angstroms: float = 2.00
24
+ maximum_shift_in_angstroms: float = 40.0
25
+ should_dose_filter: bool = True
26
+ should_restore_power: bool = True
27
+ termination_threshold_in_angstroms: float = 1.0
28
+ max_iterations: int = 10
29
+ bfactor_in_angstroms: float = 1500
30
+ should_mask_central_cross: bool = True
31
+ horizontal_mask_size: int = 1
32
+ vertical_mask_size: int = 1
33
+ acceleration_voltage: float = 300.0
34
+ exposure_per_frame: float = 0.0
35
+ pre_exposure_amount: float = 0.0
36
+ movie_is_gain_corrected: bool = False
37
+ gain_filename: str = "gain.mrc"
38
+ movie_is_dark_corrected: bool = True
39
+ dark_filename: str = "dark.mrc"
40
+ output_binning_factor: float = 2.0
41
+ correct_mag_distortion: bool = False
42
+ mag_distortion_angle: float = 0.0
43
+ mag_distortion_major_scale: float = 1.0
44
+ mag_distortion_minor_scale: float = 1.0
45
+ write_out_amplitude_spectrum: bool = True
46
+ amplitude_spectrum_filename: str = "amplitude_spectrum.mrc"
47
+ write_out_small_sum_image: bool = True
48
+ small_sum_image_filename: str = "scaled_sum.mrc"
49
+ first_frame: int = 1
50
+ last_frame: int = 0
51
+ number_of_frames_for_running_average: int = 1
52
+ max_threads: int = 1
53
+ save_aligned_frames: bool = False
54
+ aligned_frames_filename: str = "aligned_frames.mrc"
55
+ output_shift_text_file: str = "shifts.txt"
56
+ eer_frames_per_image: int = 0
57
+ eer_super_res_factor: int = 1
58
+ align_on_cropped_area: bool = False
59
+ cropped_area_center_x : int = 0
60
+ cropped_area_center_y : int = 0
61
+ cropped_area_size_x : int = -1
62
+ cropped_area_size_y : int = -1
63
+ replace_dark_areas_with_gaussian_noise: bool = False
64
+ threshold_for_gaussian_noise: float = 0.1
65
+ measure_mean_and_variance_for_gaussian_noise : bool = False
66
+ mean_for_gaussian_noise : float = 0.0
67
+ variance_for_gaussian_noise : float = 0.0
68
+
69
+ def parameters_from_database(database, decolace=False, **kwargs):
70
+ movie_info = get_movie_info_from_db(database)
71
+ ProjectDirectory = Path(database).parent
72
+ par = [UnblurParameters(
73
+ input_filename = movie["FILENAME"],
74
+ output_filename= (ProjectDirectory / "Assets" / "Images" / f"{Path(movie['FILENAME']).stem}_{movie['MOVIE_ASSET_ID']}_auto_{i}.mrc").as_posix(),
75
+ pixel_size = movie["PIXEL_SIZE"],
76
+ gain_filename=movie["GAIN_FILENAME"],
77
+ output_binning_factor=movie["OUTPUT_BINNING_FACTOR"],
78
+ exposure_per_frame=movie["DOSE_PER_FRAME"],
79
+ amplitude_spectrum_filename=(ProjectDirectory / "Assets" / "Images" / "Spectra" / f"{Path(movie['FILENAME']).stem}_{movie['MOVIE_ASSET_ID']}_auto.mrc").as_posix(),
80
+ small_sum_image_filename=(ProjectDirectory / "Assets" / "Images" / "Scaled" / f"{Path(movie['FILENAME']).stem}_{movie['MOVIE_ASSET_ID']}_auto.mrc").as_posix(),
81
+ align_on_cropped_area=decolace,
82
+ replace_dark_areas_with_gaussian_noise=decolace
83
+ ) for i,movie in movie_info.iterrows()]
84
+ return(par)
85
+
86
+ def write_results_to_database(database, parameters, results, change_image_assets=True):
87
+ conn = sqlite3.connect(database)
88
+ cur = conn.cursor()
89
+ results = sorted(results, key=lambda x: x["parameter_index"])
90
+ MOVIE_ALIGNMENT_LIST = []
91
+
92
+ max_alignment_id= cur.execute("SELECT MAX(ALIGNMENT_ID) FROM MOVIE_ALIGNMENT_LIST").fetchone()[0]
93
+ if max_alignment_id is None:
94
+ max_alignment_id = 0
95
+ alignment_job_id= cur.execute("SELECT MAX(ALIGNMENT_JOB_ID) FROM MOVIE_ALIGNMENT_LIST").fetchone()[0]
96
+ if alignment_job_id is None:
97
+ alignment_job_id = 1
98
+ else:
99
+ alignment_job_id += 1
100
+
101
+ max_image_asset_id= cur.execute("SELECT MAX(IMAGE_ASSET_ID) FROM IMAGE_ASSETS").fetchone()[0]
102
+ if max_image_asset_id is None:
103
+ max_image_asset_id = 0
104
+ for result in results:
105
+ movie_info = cur.execute("SELECT X_SIZE, Y_SIZE, MOVIE_ASSET_ID, NAME, PROTEIN_IS_WHITE, SPHERICAL_ABERRATION FROM MOVIE_ASSETS WHERE FILENAME = ?", (parameters[result["parameter_index"]].input_filename,)).fetchone()
106
+ mrc = mrcfile.open(parameters[result["parameter_index"]].output_filename)
107
+ xsize = mrc.header.nx
108
+ ysize = mrc.header.ny
109
+ if result["orig_x"] > 0:
110
+ x_bin_factor = movie_info[0] / result["orig_x"]
111
+ y_bin_factor = movie_info[1] / result["orig_y"]
112
+ else:
113
+ x_bin_factor = movie_info[0] / xsize
114
+ y_bin_factor = movie_info[1] / ysize
115
+ average_bin_factor = (x_bin_factor + y_bin_factor) / 2.0
116
+ actual_pixel_size = parameters[result["parameter_index"]].pixel_size * average_bin_factor
117
+ MOVIE_ALIGNMENT_LIST.append({
118
+ "ALIGNMENT_ID" : max_alignment_id + 1,
119
+ "DATETIME_OF_RUN" : datetime_to_msdos(datetime.datetime.now()),
120
+ "ALIGNMENT_JOB_ID": alignment_job_id,
121
+ "MOVIE_ASSET_ID": movie_info[2],
122
+ "OUTPUT_FILE": parameters[result["parameter_index"]].output_filename,
123
+ "VOLTAGE": parameters[result["parameter_index"]].acceleration_voltage,
124
+ "PIXEL_SIZE": actual_pixel_size,
125
+ "EXPOSURE_PER_FRAME": parameters[result["parameter_index"]].exposure_per_frame,
126
+ "PRE_EXPOSURE_AMOUNT": parameters[result["parameter_index"]].pre_exposure_amount,
127
+ "MIN_SHIFT": parameters[result["parameter_index"]].minimum_shift_in_angstroms,
128
+ "MAX_SHIFT": parameters[result["parameter_index"]].maximum_shift_in_angstroms,
129
+ "SHOULD_DOSE_FILTER": parameters[result["parameter_index"]].should_dose_filter,
130
+ "SHOULD_RESTORE_POWER": parameters[result["parameter_index"]].should_restore_power,
131
+ "TERMINATION_THRESHOLD": parameters[result["parameter_index"]].termination_threshold_in_angstroms,
132
+ "MAX_ITERATIONS": parameters[result["parameter_index"]].max_iterations ,
133
+ "BFACTOR": parameters[result["parameter_index"]].bfactor_in_angstroms,
134
+ "SHOULD_MASK_CENTRAL_CROSS": parameters[result["parameter_index"]].should_mask_central_cross,
135
+ "HORIZONTAL_MASK": parameters[result["parameter_index"]].horizontal_mask_size,
136
+ "VERTICAL_MASK": parameters[result["parameter_index"]].vertical_mask_size,
137
+ "SHOULD_INCLUDE_ALL_FRAMES_IN_SUM": True,
138
+ "FIRST_FRAME_TO_SUM": parameters[result["parameter_index"]].first_frame,
139
+ "LAST_FRAME_TO_SUM": parameters[result["parameter_index"]].last_frame,
140
+ "ORIGINAL_X_SIZE": result["orig_x"],
141
+ "ORIGINAL_Y_SIZE": result["orig_y"],
142
+ "CROP_CENTER_X": result["crop_x"],
143
+ "CROP_CENTER_Y": result["crop_y"],
144
+ })
145
+ # Check if there is existing image asset
146
+
147
+ #existing_image_asset = cur.execute("SELECT IMAGE_ASSET_ID FROM IMAGE_ASSETS WHERE PARENT_MOVIE_ID = ?",(movie_info[2],)).fetchone()
148
+ #if existing_image_asset is None:
149
+ max_image_asset_id += 1
150
+ image_asset_id = max_image_asset_id
151
+ #else:
152
+ # image_asset_id = existing_image_asset[0]
153
+
154
+ if change_image_assets:
155
+ cur.execute("REPLACE INTO IMAGE_ASSETS (IMAGE_ASSET_ID, NAME, FILENAME, POSITION_IN_STACK, PARENT_MOVIE_ID, ALIGNMENT_ID, CTF_ESTIMATION_ID, X_SIZE, Y_SIZE, PIXEL_SIZE, VOLTAGE, SPHERICAL_ABERRATION, PROTEIN_IS_WHITE, ORIGINAL_X_SIZE, ORIGINAL_Y_SIZE, CROP_CENTER_X, CROP_CENTER_Y) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
156
+ (image_asset_id, movie_info[3], parameters[result["parameter_index"]].output_filename, 1,movie_info[2],max_alignment_id+1, -1, int(xsize), int(ysize), actual_pixel_size, parameters[result["parameter_index"]].acceleration_voltage, movie_info[5], movie_info[4],result["orig_x"],result["orig_y"],result["crop_x"],result["crop_y"]))
157
+ MOVIE_ALIGNMENT_PARAMETERS = {
158
+ "FRAME_NUMBER": range(1,len(result["x_shifts"])+1),
159
+ "X_SHIFT": result["x_shifts"],
160
+ "Y_SHIFT": result["y_shifts"],
161
+ }
162
+ conn.commit()
163
+ MOVIE_ALIGNMENT_PARAMETERS = pd.DataFrame(MOVIE_ALIGNMENT_PARAMETERS)
164
+ MOVIE_ALIGNMENT_PARAMETERS.to_sql(f"MOVIE_ALIGNMENT_PARAMETERS_{max_alignment_id+1}", conn, if_exists="fail", index=False)
165
+ max_alignment_id += 1
166
+ MOVIE_ALIGNMENT_LIST = pd.DataFrame(MOVIE_ALIGNMENT_LIST)
167
+ MOVIE_ALIGNMENT_LIST.to_sql("MOVIE_ALIGNMENT_LIST", conn, if_exists="append", index=False)
168
+ conn.close()
169
+
170
+
171
+
172
+
173
+
174
+ async def handle_results(reader, writer, logger):
175
+ #logger.info("Handling results")
176
+ await reader.read(4)
177
+ length = await reader.read(4)
178
+ number_of_bytes = int.from_bytes(length, byteorder="little")
179
+ results = await reader.read(number_of_bytes*4)
180
+ return(results)
181
+
182
+ signal_handlers = {
183
+ socket_send_next_job : handle_results
184
+ }
185
+
186
+ def run(parameters: Union[UnblurParameters,list[UnblurParameters]],**kwargs):
187
+
188
+ if not isinstance(parameters, list):
189
+ parameters = [parameters]
190
+
191
+ byte_results = asyncio.run(cistem_program.run("unblur", parameters, signal_handlers=signal_handlers,**kwargs))
192
+ result_shifts = []
193
+
194
+ for parameter_index,byte_result in byte_results:
195
+ number_of_images = int(((len(byte_result) /4 ) - 4 ) /2)
196
+ x_shifts = []
197
+ for offset in range(number_of_images):
198
+ x_shifts.append(struct.unpack_from("<f",byte_result,offset=offset*4)[0])
199
+ y_shifts = []
200
+ for offset in range(number_of_images):
201
+ y_shifts.append(struct.unpack_from("<f",byte_result,offset=offset*4+number_of_images*4)[0])
202
+ orig_x = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images)[0])
203
+ orig_y = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images+4)[0])
204
+ crop_x = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images+8)[0])
205
+ crop_y = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images+12)[0])
206
+ result_shifts.append({
207
+ "parameter_index": parameter_index,
208
+ "x_shifts": x_shifts,
209
+ "y_shifts": y_shifts,
210
+ "orig_x": orig_x,
211
+ "orig_y": orig_y,
212
+ "crop_x": crop_x,
213
+ "crop_y": crop_y
214
+ })
215
+
216
+ return(result_shifts)
217
+
218
+ async def run_async(parameters: Union[UnblurParameters,list[UnblurParameters]],**kwargs):
219
+
220
+ if not isinstance(parameters, list):
221
+ parameters = [parameters]
222
+ byte_results = await cistem_program.run("unblur", parameters, signal_handlers=signal_handlers,**kwargs)
223
+ result_shifts = []
224
+
225
+ for parameter_index,byte_result in byte_results:
226
+ number_of_images = int(((len(byte_result) /4 ) - 4 ) /2)
227
+ x_shifts = []
228
+ for offset in range(number_of_images):
229
+ x_shifts.append(struct.unpack_from("<f",byte_result,offset=offset*4)[0])
230
+ y_shifts = []
231
+ for offset in range(number_of_images):
232
+ y_shifts.append(struct.unpack_from("<f",byte_result,offset=offset*4+number_of_images*4)[0])
233
+ orig_x = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images)[0])
234
+ orig_y = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images+4)[0])
235
+ crop_x = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images+8)[0])
236
+ crop_y = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images+12)[0])
237
+ result_shifts.append({
238
+ "parameter_index": parameter_index,
239
+ "x_shifts": x_shifts,
240
+ "y_shifts": y_shifts,
241
+ "orig_x": orig_x,
242
+ "orig_y": orig_y,
243
+ "crop_x": crop_x,
244
+ "crop_y": crop_y
245
+ })
246
+
247
+ return(result_shifts)
@@ -0,0 +1,204 @@
1
+ import asyncio
2
+ import datetime
3
+ import sqlite3
4
+ import struct
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+ from typing import List, Union
8
+
9
+ import mrcfile
10
+ import pandas as pd
11
+
12
+ from pycistem.database import datetime_to_msdos, get_movie_info_from_db
13
+ from pycistem.programs import cistem_program
14
+ from pycistem.programs._cistem_constants import socket_job_result, socket_send_next_job
15
+
16
+
17
+ @dataclass
18
+ class UnblurPatchParameters:
19
+ input_filename: str
20
+ output_filename: str = "unblurred.mrc"
21
+ pixel_size: float = 1.0
22
+ minimum_shift_in_angstroms: float = 2.00
23
+ maximum_shift_in_angstroms: float = 40.0
24
+ should_dose_filter: bool = True
25
+ should_restore_power: bool = True
26
+ termination_threshold_in_angstroms: float = 1.0
27
+ max_iterations: int = 10
28
+ bfactor_in_angstroms: float = 1500
29
+ should_mask_central_cross: bool = True
30
+ horizontal_mask_size: int = 1
31
+ vertical_mask_size: int = 1
32
+ acceleration_voltage: float = 300.0
33
+ exposure_per_frame: float = 0.0
34
+ pre_exposure_amount: float = 0.0
35
+ movie_is_gain_corrected: bool = False
36
+ gain_filename: str = "gain.mrc"
37
+ movie_is_dark_corrected: bool = True
38
+ dark_filename: str = "dark.mrc"
39
+ output_binning_factor: float = 2.0
40
+ correct_mag_distortion: bool = False
41
+ mag_distortion_angle: float = 0.0
42
+ mag_distortion_major_scale: float = 1.0
43
+ mag_distortion_minor_scale: float = 1.0
44
+ write_out_amplitude_spectrum: bool = True
45
+ amplitude_spectrum_filename: str = "amplitude_spectrum.mrc"
46
+ write_out_small_sum_image: bool = True
47
+ small_sum_image_filename: str = "scaled_sum.mrc"
48
+ first_frame: int = 1
49
+ last_frame: int = 0
50
+ number_of_frames_for_running_average: int = 1
51
+ max_threads: int = 1
52
+ save_aligned_frames: bool = False
53
+ eer_frames_per_image: int = 0
54
+ eer_super_res_factor: int = 1
55
+ outputpath: str = "/tmp"
56
+ patchcorrection: bool = False
57
+ override_patchnum: bool = False
58
+ patch_num_x: int = 6
59
+ patch_num_y: int = 4
60
+ distortion_model: int = 3
61
+
62
+ def parameters_from_database(database, decolace=False, **kwargs):
63
+ movie_info = get_movie_info_from_db(database)
64
+ ProjectDirectory = Path(database).parent
65
+ par = [UnblurPatchParameters(
66
+ input_filename = movie["FILENAME"],
67
+ output_filename= (ProjectDirectory / "Assets" / "Images" / f"{Path(movie['FILENAME']).stem}_{movie['MOVIE_ASSET_ID']}_auto.mrc").as_posix(),
68
+ pixel_size = movie["PIXEL_SIZE"],
69
+ gain_filename=movie["GAIN_FILENAME"],
70
+ output_binning_factor=movie["OUTPUT_BINNING_FACTOR"],
71
+ exposure_per_frame=movie["DOSE_PER_FRAME"],
72
+ amplitude_spectrum_filename=(ProjectDirectory / "Assets" / "Images" / "Spectra" / f"{Path(movie['FILENAME']).stem}_{movie['MOVIE_ASSET_ID']}_auto.mrc").as_posix(),
73
+ small_sum_image_filename=(ProjectDirectory / "Assets" / "Images" / "Scaled" / f"{Path(movie['FILENAME']).stem}_{movie['MOVIE_ASSET_ID']}_auto.mrc").as_posix(),
74
+ ) for i,movie in movie_info.iterrows()]
75
+ return(par)
76
+
77
+ def write_results_to_database(database, parameters, results,change_image_assets):
78
+ conn = sqlite3.connect(database)
79
+ cur = conn.cursor()
80
+ results = sorted(results, key=lambda x: x["parameter_index"])
81
+ MOVIE_ALIGNMENT_LIST = []
82
+
83
+ max_alignment_id= cur.execute("SELECT MAX(ALIGNMENT_ID) FROM MOVIE_ALIGNMENT_LIST").fetchone()[0]
84
+ if max_alignment_id is None:
85
+ max_alignment_id = 0
86
+ alignment_job_id= cur.execute("SELECT MAX(ALIGNMENT_JOB_ID) FROM MOVIE_ALIGNMENT_LIST").fetchone()[0]
87
+ if alignment_job_id is None:
88
+ alignment_job_id = 1
89
+ else:
90
+ alignment_job_id += 1
91
+
92
+ max_image_asset_id= cur.execute("SELECT MAX(IMAGE_ASSET_ID) FROM IMAGE_ASSETS").fetchone()[0]
93
+ if max_image_asset_id is None:
94
+ max_image_asset_id = 0
95
+ for result in results:
96
+ movie_info = cur.execute("SELECT X_SIZE, Y_SIZE, MOVIE_ASSET_ID, NAME, PROTEIN_IS_WHITE, SPHERICAL_ABERRATION FROM MOVIE_ASSETS WHERE FILENAME = ?", (parameters[result["parameter_index"]].input_filename,)).fetchone()
97
+ x_bin_factor = movie_info[0] / result["orig_x"]
98
+ y_bin_factor = movie_info[1] / result["orig_y"]
99
+ average_bin_factor = (x_bin_factor + y_bin_factor) / 2.0
100
+ actual_pixel_size = parameters[result["parameter_index"]].pixel_size * average_bin_factor
101
+ MOVIE_ALIGNMENT_LIST.append({
102
+ "ALIGNMENT_ID" : max_alignment_id + 1,
103
+ "DATETIME_OF_RUN" : datetime_to_msdos(datetime.datetime.now()),
104
+ "ALIGNMENT_JOB_ID": alignment_job_id,
105
+ "MOVIE_ASSET_ID": movie_info[2],
106
+ "OUTPUT_FILE": parameters[result["parameter_index"]].output_filename,
107
+ "VOLTAGE": parameters[result["parameter_index"]].acceleration_voltage,
108
+ "PIXEL_SIZE": actual_pixel_size,
109
+ "EXPOSURE_PER_FRAME": parameters[result["parameter_index"]].exposure_per_frame,
110
+ "PRE_EXPOSURE_AMOUNT": parameters[result["parameter_index"]].pre_exposure_amount,
111
+ "MIN_SHIFT": parameters[result["parameter_index"]].minimum_shift_in_angstroms,
112
+ "MAX_SHIFT": parameters[result["parameter_index"]].maximum_shift_in_angstroms,
113
+ "SHOULD_DOSE_FILTER": parameters[result["parameter_index"]].should_dose_filter,
114
+ "SHOULD_RESTORE_POWER": parameters[result["parameter_index"]].should_restore_power,
115
+ "TERMINATION_THRESHOLD": parameters[result["parameter_index"]].termination_threshold_in_angstroms,
116
+ "MAX_ITERATIONS": parameters[result["parameter_index"]].max_iterations ,
117
+ "BFACTOR": parameters[result["parameter_index"]].bfactor_in_angstroms,
118
+ "SHOULD_MASK_CENTRAL_CROSS": parameters[result["parameter_index"]].should_mask_central_cross,
119
+ "HORIZONTAL_MASK": parameters[result["parameter_index"]].horizontal_mask_size,
120
+ "VERTICAL_MASK": parameters[result["parameter_index"]].vertical_mask_size,
121
+ "SHOULD_INCLUDE_ALL_FRAMES_IN_SUM": True,
122
+ "FIRST_FRAME_TO_SUM": parameters[result["parameter_index"]].first_frame,
123
+ "LAST_FRAME_TO_SUM": parameters[result["parameter_index"]].last_frame,
124
+ "ORIGINAL_X_SIZE": result["orig_x"],
125
+ "ORIGINAL_Y_SIZE": result["orig_y"],
126
+ "CROP_CENTER_X": result["crop_x"],
127
+ "CROP_CENTER_Y": result["crop_y"],
128
+ })
129
+ # Check if there is existing image asset
130
+
131
+ existing_image_asset = cur.execute("SELECT IMAGE_ASSET_ID FROM IMAGE_ASSETS WHERE PARENT_MOVIE_ID = ?",(movie_info[2],)).fetchone()
132
+ if existing_image_asset is None:
133
+ max_image_asset_id += 1
134
+ image_asset_id = max_image_asset_id
135
+ else:
136
+ image_asset_id = existing_image_asset[0]
137
+
138
+ mrc = mrcfile.open(parameters[result["parameter_index"]].output_filename)
139
+ xsize = mrc.header.nx
140
+ ysize = mrc.header.ny
141
+ cur.execute("REPLACE INTO IMAGE_ASSETS (IMAGE_ASSET_ID, NAME, FILENAME, POSITION_IN_STACK, PARENT_MOVIE_ID, ALIGNMENT_ID, CTF_ESTIMATION_ID, X_SIZE, Y_SIZE, PIXEL_SIZE, VOLTAGE, SPHERICAL_ABERRATION, PROTEIN_IS_WHITE, ORIGINAL_X_SIZE, ORIGINAL_Y_SIZE, CROP_CENTER_X, CROP_CENTER_Y) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
142
+ (image_asset_id, movie_info[3], parameters[result["parameter_index"]].output_filename, 1,movie_info[2],max_alignment_id+1, -1, int(xsize), int(ysize), actual_pixel_size, parameters[result["parameter_index"]].acceleration_voltage, movie_info[5], movie_info[4],result["orig_x"],result["orig_y"],result["crop_x"],result["crop_y"]))
143
+ MOVIE_ALIGNMENT_PARAMETERS = {
144
+ "FRAME_NUMBER": range(1,len(result["x_shifts"])+1),
145
+ "X_SHIFT": result["x_shifts"],
146
+ "Y_SHIFT": result["y_shifts"],
147
+ }
148
+ conn.commit()
149
+ MOVIE_ALIGNMENT_PARAMETERS = pd.DataFrame(MOVIE_ALIGNMENT_PARAMETERS)
150
+ MOVIE_ALIGNMENT_PARAMETERS.to_sql(f"MOVIE_ALIGNMENT_PARAMETERS_{max_alignment_id+1}", conn, if_exists="fail", index=False)
151
+ max_alignment_id += 1
152
+ MOVIE_ALIGNMENT_LIST = pd.DataFrame(MOVIE_ALIGNMENT_LIST)
153
+ MOVIE_ALIGNMENT_LIST.to_sql("MOVIE_ALIGNMENT_LIST", conn, if_exists="append", index=False)
154
+ conn.close()
155
+
156
+
157
+
158
+
159
+
160
+ async def handle_results(reader, writer, logger):
161
+ #logger.info("Handling results")
162
+ await reader.read(4)
163
+ length = await reader.read(4)
164
+ number_of_bytes = int.from_bytes(length, byteorder="little")
165
+ results = await reader.read(number_of_bytes*4)
166
+ print(struct.unpack_from("<"+''.join(["f" for a in range(number_of_bytes)]),results))
167
+ return(results)
168
+
169
+ signal_handlers = {
170
+ socket_send_next_job : handle_results
171
+ }
172
+
173
+ def run(parameters: Union[UnblurPatchParameters,list[UnblurPatchParameters]], unblur_command: str="unblur", **kwargs):
174
+
175
+ if not isinstance(parameters, list):
176
+ parameters = [parameters]
177
+
178
+ byte_results = asyncio.run(cistem_program.run(unblur_command, parameters, signal_handlers=signal_handlers,**kwargs))
179
+ result_shifts = []
180
+
181
+ for parameter_index,byte_result in byte_results:
182
+ number_of_images = int(((len(byte_result) /4 ) - 4 ) /2)
183
+ x_shifts = []
184
+ for offset in range(number_of_images):
185
+ x_shifts.append(struct.unpack_from("<f",byte_result,offset=offset*4)[0])
186
+ y_shifts = []
187
+ for offset in range(number_of_images):
188
+ y_shifts.append(struct.unpack_from("<f",byte_result,offset=offset*4+number_of_images*4)[0])
189
+ orig_x = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images)[0])
190
+ orig_y = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images+4)[0])
191
+ crop_x = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images+8)[0])
192
+ crop_y = int(struct.unpack_from("<f",byte_result,offset=2*4*number_of_images+12)[0])
193
+ result_shifts.append({
194
+ "parameter_index": parameter_index,
195
+ "x_shifts": x_shifts,
196
+ "y_shifts": y_shifts,
197
+ "orig_x": orig_x,
198
+ "orig_y": orig_y,
199
+ "crop_x": crop_x,
200
+ "crop_y": crop_y
201
+ })
202
+
203
+
204
+ return(result_shifts)
@@ -0,0 +1 @@
1
+ from .extract_particles import extract_particles
@@ -0,0 +1,36 @@
1
+ from pathlib import Path
2
+
3
+ def extract_particles(starfile_filename: Path, stack_filename: Path, box_size: int = 256):
4
+ """Extract particles from a star file and save them as individual images.
5
+
6
+ Args:
7
+ starfile_filename (str): The filename of the star file.
8
+ box_size (int, optional): The size of the extracted particles. Defaults to 256.
9
+ """
10
+ import starfile
11
+ import mrcfile
12
+ import numpy as np
13
+ from itertools import groupby
14
+ particle_info = starfile.read(starfile_filename)
15
+ mrc = mrcfile.new_mmap(stack_filename, (len(particle_info), box_size, box_size), mrc_mode=2, overwrite=True)
16
+ # Iterate over groupby cisTEMOriginalImageFilename
17
+
18
+ for micrograph_filename, subparticles in groupby(particle_info.itertuples(), lambda x: x.cisTEMOriginalImageFilename):
19
+ micrograph = mrcfile.open(micrograph_filename)
20
+ if micrograph.data.ndim == 3:
21
+ micrograph_data = micrograph.data[0].copy()
22
+ else:
23
+ micrograph_data = micrograph.data.copy()
24
+ for particle in subparticles:
25
+ x = round(particle.cisTEMOriginalXPosition/particle.cisTEMPixelSize)
26
+ y = round(particle.cisTEMOriginalYPosition/particle.cisTEMPixelSize)
27
+ particle_image = micrograph_data.take(range(y-box_size//2, y+box_size//2), mode='clip', axis=0).take(range(x-box_size//2, x+box_size//2), mode='clip', axis=1)
28
+ if particle_image.shape != (box_size, box_size):
29
+ raise ValueError(f"Particle at {x},{y} from micrograph {micrograph_filename} {micrograph_data.shape} is out of bounds {particle_image.shape}.")
30
+
31
+ particle_image -= particle_image.mean()
32
+ particle_image /= particle_image.std()
33
+ mrc.data[particle.cisTEMPositionInStack-1] = particle_image
34
+ yield
35
+ mrc.close()
36
+ return
@@ -0,0 +1,34 @@
1
+ import pandas as pd
2
+ from typing import Union
3
+ import sqlite3
4
+ import typer
5
+ from pathlib import Path
6
+ from typing_extensions import Annotated
7
+ import starfile
8
+
9
+ app = typer.Typer()
10
+
11
+
12
+ @app.command()
13
+ def plot(database: Annotated[Path, typer.Argument(...,help="The database file to use")],
14
+ refinement_id: Annotated[int, typer.Argument(...,help="The refinement to use")],
15
+ class_id: Annotated[int, typer.Argument(...,help="The class to cus")],
16
+ input_star_file: Annotated[Path, typer.Argument(...,help="The starfile to use")],
17
+ output_star_file: Annotated[Path, typer.Argument(...,help="The starfile to write to")],
18
+ ):
19
+ db = sqlite3.connect(database)
20
+ refinements = []
21
+ refinment_info = pd.read_sql_query(f"SELECT * FROM REFINEMENT_LIST WHERE REFINEMENT_ID = {refinement_id}", db).iloc[0]
22
+ refinment_result = pd.read_sql_query(f"SELECT * FROM REFINEMENT_RESULT_{refinement_id}_{class_id}", db)
23
+ refinment_package_info = pd.read_sql_query(f"SELECT * FROM REFINEMENT_PACKAGE_CONTAINED_PARTICLES_{refinment_info['REFINEMENT_PACKAGE_ASSET_ID']}", db)
24
+
25
+ starfile_info = starfile.read(input_star_file)
26
+
27
+ original_ids = refinment_package_info["ORIGINAL_PARTICLE_POSITION_ASSET_ID"].to_list()
28
+ subset_starfile_info = starfile_info.iloc[original_ids].copy()
29
+ subset_starfile_info["cisTEMOccupancy"] = refinment_result["OCCUPANCY"].to_list()
30
+ subset_starfile_info["cisTEMScore"] = refinment_result["SCORE"].to_list()
31
+ starfile.write(subset_starfile_info, output_star_file)
32
+
33
+ if __name__ == "__main__":
34
+ app()
@@ -0,0 +1,26 @@
1
+ import pandas as pd
2
+ from typing import Union
3
+ import sqlite3
4
+ import typer
5
+ from pathlib import Path
6
+ from typing_extensions import Annotated
7
+ import matplotlib.pyplot as plt
8
+
9
+ app = typer.Typer()
10
+
11
+
12
+ @app.command()
13
+ def plot(database: Annotated[Path, typer.Argument(...,help="The database file to use")],
14
+ start_refinement: Annotated[int, typer.Argument(...,help="The refinement to start from")]):
15
+ db = sqlite3.connect(database)
16
+ refinment_info = pd.read_sql_query(f"SELECT * FROM REFINEMENT_LIST WHERE REFINEMENT_ID = {start_refinement}", db)
17
+ class_info = pd.read_sql_query(f"SELECT * FROM REFINEMENT_DETAILS_{refinment_info['REFINEMENT_ID'].values[0]}", db)
18
+ class_info.sort_values(by="AVERAGE_OCCUPANCY", inplace=True)
19
+ # print as a table the CLASS_NUMBER and AVERAGE_OCCUPANCY
20
+ print(class_info[['CLASS_NUMBER', 'AVERAGE_OCCUPANCY']])
21
+
22
+
23
+
24
+
25
+ if __name__ == "__main__":
26
+ app()
@@ -0,0 +1,51 @@
1
+ import pandas as pd
2
+ from typing import Union
3
+ import sqlite3
4
+ import typer
5
+ from pathlib import Path
6
+ from typing_extensions import Annotated
7
+ import matplotlib.pyplot as plt
8
+
9
+ app = typer.Typer()
10
+
11
+ def return_class_occupancies(row, db):
12
+ class_info = pd.read_sql_query(f"SELECT * FROM REFINEMENT_DETAILS_{row['REFINEMENT_ID']}", db)
13
+ return [row['REFINEMENT_ID']] + class_info['AVERAGE_OCCUPANCY'].to_list()
14
+
15
+ def return_num_part_with_occ_higher_than(row, db, min_occ):
16
+ class_info = pd.read_sql_query(f"SELECT * FROM REFINEMENT_DETAILS_{row['REFINEMENT_ID']}", db)
17
+ result = [row['REFINEMENT_ID']]
18
+ for class_id in class_info['CLASS_NUMBER']:
19
+ particle_info = pd.read_sql_query(f"SELECT OCCUPANCY FROM REFINEMENT_RESULT_{row['REFINEMENT_ID']}_{class_id}", db)
20
+ result.append(len(particle_info[particle_info['OCCUPANCY'] > min_occ]))
21
+ print(result)
22
+ return result
23
+
24
+ @app.command()
25
+ def plot(database: Annotated[Path, typer.Argument(...,help="The database file to use")],
26
+ start_refinement: Annotated[int, typer.Argument(...,help="The refinement to start from")]):
27
+ db = sqlite3.connect(database)
28
+ refinements = []
29
+ refinment_info = pd.read_sql_query(f"SELECT * FROM REFINEMENT_LIST WHERE REFINEMENT_ID = {start_refinement}", db)
30
+ while len(refinment_info) == 1:
31
+ reference_refinement_id = refinment_info["REFINEMENT_ID"].values[0]
32
+ refinements.append(refinment_info)
33
+ refinment_info = pd.read_sql_query(f"SELECT * FROM REFINEMENT_LIST WHERE STARTING_REFINEMENT_ID = {reference_refinement_id}", db)
34
+ refinements = pd.concat(refinements)
35
+ av_occs = refinements.apply(return_class_occupancies, axis=1, result_type='expand', args=(db,))
36
+ av_occs.plot(x=0)
37
+ plt.savefig("average_occupancies.png")
38
+ plt.figure()
39
+ occ_0_5 = refinements.apply(return_num_part_with_occ_higher_than, axis=1, result_type='expand', args=(db, 50))
40
+ occ_0_5.plot(x=0)
41
+ plt.savefig("occupancies_0_5.png")
42
+ plt.figure()
43
+ occ_0_9 = refinements.apply(return_num_part_with_occ_higher_than, axis=1, result_type='expand', args=(db, 90))
44
+ occ_0_9.plot(x=0)
45
+ plt.savefig("occupancies_0_9.png")
46
+
47
+
48
+
49
+
50
+ if __name__ == "__main__":
51
+ app()