insituTEM 0.1.6__tar.gz → 0.1.7__tar.gz

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.
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: insituTEM
3
+ Version: 0.1.7
4
+ Author: Meng Li
5
+ Requires-Python: >=3.9
6
+ Requires-Dist: numpy
7
+ Requires-Dist: scipy
8
+ Requires-Dist: matplotlib
9
+ Requires-Dist: pandas
10
+ Requires-Dist: tifffile
11
+ Requires-Dist: tqdm
12
+ Requires-Dist: opencv-python
13
+ Requires-Dist: pillow
14
+ Requires-Dist: scikit-image
15
+ Requires-Dist: moviepy
16
+ Requires-Dist: ncempy
17
+ Requires-Dist: easygui
18
+ Requires-Dist: pystackreg
19
+ Requires-Dist: pyside6
20
+ Dynamic: author
@@ -0,0 +1,14 @@
1
+ # __init__.py
2
+ __version__ = '1.0.0'
3
+ __author__ = 'Meng Li'
4
+
5
+ import insitu_alignment as AL
6
+ import insitu_DP as DP
7
+ import insitu_IO as IO
8
+ import insitu_Preprocess as PP
9
+ import insitu_Diff as Diff
10
+ import insitu_EMraw as EM
11
+ import insitu_DENS as DENS
12
+
13
+
14
+ __all__ = ['function1', 'Class2']
@@ -0,0 +1,318 @@
1
+ """
2
+ in-situ TEM Toolbox - DENS holder data process
3
+
4
+ Assembles of functions related to process raw data by DENS holders
5
+
6
+ Example:
7
+
8
+ from insitu_TEM import insitu_DENS as DENS
9
+ DENS.XXX
10
+
11
+ Created on Tue May 13 2025
12
+ @author: Meng Li mli4@bnl.gov
13
+
14
+ """
15
+
16
+ """
17
+ Codes for Stream data process
18
+ """
19
+ import pandas as pd
20
+
21
+ def read_pstrace_csv(filepath):
22
+ # Try several encodings until one works
23
+ for encoding in ['utf-16', 'utf-8', 'ISO-8859-1']:
24
+ try:
25
+ with open(filepath, 'r', encoding=encoding) as f:
26
+ lines = [line.strip() for line in f if line.strip()]
27
+ break
28
+ except UnicodeDecodeError:
29
+ continue
30
+ else:
31
+ raise UnicodeDecodeError("Failed to decode file using common encodings.")
32
+
33
+ # Detect measurement type (e.g., CV I vs E Scan 1)
34
+ measurement_type = None
35
+ for line in lines:
36
+ if "vs" in line and not line.lower().startswith("date"):
37
+ measurement_type = line
38
+ break
39
+
40
+ # Detect actual column header (like "V,µA" or "s,V")
41
+ header_index = None
42
+ for i, line in enumerate(lines):
43
+ if ',' in line and all(any(c.isalpha() for c in token) for token in line.split(',')):
44
+ header_index = i
45
+ break
46
+
47
+ if header_index is None:
48
+ raise ValueError("Could not find a valid data header (e.g., 'V,µA' or 's,V')")
49
+
50
+ headers = [h.strip() for h in lines[header_index].split(',')]
51
+ data_lines = lines[header_index + 1:]
52
+
53
+ # Read and convert numeric data only
54
+ data = []
55
+ for line in data_lines:
56
+ try:
57
+ row = [float(val) for val in line.split(',')]
58
+ data.append(row)
59
+ except ValueError:
60
+ continue # skip non-numeric lines
61
+
62
+ df = pd.DataFrame(data, columns=headers)
63
+ return measurement_type, df
64
+
65
+
66
+ import matplotlib.pyplot as plt
67
+
68
+ def plot_multiple_xy(datasets, labels=None, xlabel=None, ylabel=None, title=None):
69
+ """
70
+ Plot multiple datasets (each as a 2-column DataFrame) on one plot.
71
+
72
+ Args:
73
+ datasets: list of DataFrames with 2 columns
74
+ labels: list of labels for each line
75
+ xlabel, ylabel: optional axis labels
76
+ title: optional plot title
77
+ """
78
+ fig, ax = plt.subplots(figsize=(6, 4), dpi=300)
79
+
80
+ colors = ['blue', 'green', 'red', 'orange', 'purple']
81
+
82
+ for i, df in enumerate(datasets):
83
+ x = df.iloc[:, 0]
84
+ y = df.iloc[:, 1]
85
+ label = labels[i] if labels else None
86
+ color = colors[i % len(colors)]
87
+ ax.plot(x, y, label=label, color=color, linewidth=1)
88
+
89
+ # X and Y = 0 guide lines
90
+ ax.axhline(0, color='gray', linestyle='-', linewidth=0.5)
91
+ ax.axvline(0, color='gray', linestyle='-', linewidth=0.5)
92
+
93
+ # Axis style: boxed
94
+ for side in ['top', 'bottom', 'left', 'right']:
95
+ ax.spines[side].set_linewidth(1)
96
+ ax.spines[side].set_color('black')
97
+
98
+ # Axis labels
99
+ ax.set_xlabel(xlabel if xlabel else datasets[0].columns[0], fontsize=12, fontweight='bold', labelpad=10)
100
+ ax.set_ylabel(ylabel if ylabel else datasets[0].columns[1], fontsize=12, fontweight='bold', labelpad=10)
101
+
102
+ # Axis limits
103
+ # Extract x/y and lengths
104
+ x_list = [df.iloc[:, 0].values for df in datasets]
105
+ y_list = [df.iloc[:, 1].values for df in datasets]
106
+ all_x = np.concatenate(x_list)
107
+ all_y = np.concatenate(y_list)
108
+ # ax.set_xlim(all_x.min()*1.1, all_x.max()*1.1)
109
+ # ax.set_ylim(all_y.min()*1.2, all_y.max()*1.1)
110
+ # Add 10% margin to x and y limits
111
+ x_margin = (all_x.max() - all_x.min()) * 0.05
112
+ y_margin = (all_y.max() - all_y.min()) * 0.05
113
+
114
+ ax.set_xlim(all_x.min() - x_margin, all_x.max() + x_margin)
115
+ ax.set_ylim(all_y.min() - y_margin, all_y.max() + y_margin)
116
+
117
+
118
+ # Ticks and layout
119
+ ax.tick_params(direction='in', top=True, right=True)
120
+ ax.grid(False)
121
+
122
+ # Title and legend
123
+ if title:
124
+ ax.set_title(title, fontsize=12,fontweight='bold', pad=10)
125
+ if labels:
126
+ ax.legend()
127
+
128
+ plt.tight_layout()
129
+ plt.show()
130
+
131
+
132
+ mport matplotlib.pyplot as plt
133
+
134
+ def plot_xy(df, measurement_type=None):
135
+ """
136
+ Plot single xy curve
137
+ """
138
+ x_col = df.columns[0]
139
+ y_col = df.columns[1]
140
+ x = df[x_col]
141
+ y = df[y_col]
142
+
143
+ fig, ax = plt.subplots(figsize=(6,4),dpi=300) # square aspect ratio
144
+
145
+ # Plot data
146
+ ax.plot(x, y, linewidth=1, color='blue')
147
+
148
+ # Keep left and bottom axis
149
+ ax.spines['left'].set_visible(True)
150
+ ax.spines['bottom'].set_visible(True)
151
+
152
+ # Show top and right spine to make a box
153
+ ax.spines['top'].set_visible(True)
154
+ ax.spines['right'].set_visible(True)
155
+
156
+ # Draw all 4 spines in black (like a square)
157
+ for side in ['top', 'bottom', 'left', 'right']:
158
+ ax.spines[side].set_linewidth(1)
159
+ ax.spines[side].set_color('black')
160
+
161
+ # Add x=0 and y=0 guide lines
162
+ ax.axhline(0, color='orange', linestyle='-', linewidth=0.5)
163
+ ax.axvline(0, color='orange', linestyle='-', linewidth=0.5)
164
+
165
+ # Axis label styling
166
+ ax.set_xlabel('Voltage (V)', fontsize=12, fontweight='bold', labelpad=10)
167
+ ax.set_ylabel('Current (uA)' , fontsize=12, fontweight='bold', labelpad=10)
168
+
169
+ # Title
170
+ if measurement_type:
171
+ ax.set_title(measurement_type, fontsize=12, fontweight='bold', pad=10)
172
+
173
+ # Tick styling
174
+ ax.tick_params(direction='in', top=True, right=True)
175
+
176
+ # Turn off grid
177
+ ax.grid(False)
178
+
179
+ plt.tight_layout()
180
+ plt.show()
181
+
182
+
183
+ # Convert the plot to movie
184
+
185
+ import matplotlib.pyplot as plt
186
+ import matplotlib.animation as animation
187
+ import numpy as np
188
+ import cv2
189
+ import tifffile
190
+ from matplotlib.backends.backend_agg import FigureCanvasAgg
191
+ import tqdm
192
+
193
+ def animate_CVplots(datasets, labels=["Scan 1", "Scan 2", "Scan 3"], xlabel="Voltage (V)", ylabel="Current (µA)", title="CV Scan",
194
+ fps=60, save_basename="cv_animation"):
195
+ """
196
+ Function to animate the plot
197
+ datasets, # List of DataFrames, each with 2 columns (x, y) for a scan
198
+ labels=None, # Optional list of labels for each scan (used in legend)
199
+ xlabel=None, # Optional label for x-axis (default: first column name)
200
+ ylabel=None, # Optional label for y-axis (default: second column name)
201
+ title=None, # Optional plot title
202
+ fps=50, # fps of the input data. Original speed: 4 fps
203
+ save_basename="cv_animation" # Prefix for saved output files (AVI + TIFF stack)
204
+
205
+ """
206
+ # Setup plot and canvas
207
+ fig, ax = plt.subplots(figsize=(6, 4), dpi=150)
208
+ fig.subplots_adjust(left=0.15, right=0.95, top=0.9, bottom=0.15)
209
+ canvas = FigureCanvasAgg(fig)
210
+
211
+ # Extract x/y and lengths
212
+ x_list = [df.iloc[:, 0].values for df in datasets]
213
+ y_list = [df.iloc[:, 1].values for df in datasets]
214
+ lengths = [len(x) for x in x_list]
215
+ cum_lengths = np.cumsum(lengths)
216
+ total_frames = cum_lengths[-1]
217
+
218
+ # Axis formatting
219
+ for side in ['top', 'bottom', 'left', 'right']:
220
+ ax.spines[side].set_linewidth(1)
221
+ ax.spines[side].set_color('black')
222
+
223
+ ax.axhline(0, color='gray', linewidth=0.5)
224
+ ax.axvline(0, color='gray', linewidth=0.5)
225
+ ax.tick_params(direction='in', top=True, right=True)
226
+ ax.set_xlabel(xlabel or datasets[0].columns[0], fontsize=12, fontweight='bold', labelpad=10)
227
+ ax.set_ylabel(ylabel or datasets[0].columns[1], fontsize=12, fontweight='bold', labelpad=10)
228
+ if title:
229
+ ax.set_title(title, fontsize=10)
230
+
231
+ # Prepare plot elements
232
+ colors = ['blue', 'green', 'red', 'purple']
233
+ lines, tips = [], []
234
+ for i in range(len(datasets)):
235
+ line, = ax.plot([], [], color=colors[i % len(colors)], label=(labels[i] if labels else None), linewidth=1)
236
+ tip, = ax.plot([], [], color=colors[i % len(colors)], marker='o', markersize=4)
237
+ lines.append(line)
238
+ tips.append(tip)
239
+
240
+ # Add live current value in top-left
241
+ value_text = ax.text(0.02, 0.95, '', transform=ax.transAxes,
242
+ fontsize=10, color='black', ha='left', va='top')
243
+
244
+
245
+ if labels:
246
+ ax.legend()
247
+
248
+ # Axis limits
249
+ all_x = np.concatenate(x_list)
250
+ all_y = np.concatenate(y_list)
251
+
252
+ # Add 10% margin to x and y limits
253
+ x_margin = (all_x.max() - all_x.min()) * 0.05
254
+ y_margin = (all_y.max() - all_y.min()) * 0.05
255
+
256
+ ax.set_xlim(all_x.min() - x_margin, all_x.max() + x_margin)
257
+ ax.set_ylim(all_y.min() - y_margin, all_y.max() + y_margin)
258
+ # ax.set_xlim(all_x.min()*1.1, all_x.max()*1.1)
259
+ # ax.set_ylim(all_y.min()*1.2, all_y.max()*1.1)
260
+
261
+ # Video writer setup
262
+ canvas.draw()
263
+ width, height = canvas.get_width_height()
264
+ # fps=int(1000/interval)
265
+ aviout=save_basename+"_"+str(fps)+".avi"
266
+ out = cv2.VideoWriter(aviout, cv2.VideoWriter_fourcc(*'MJPG'), fps, (width, height))
267
+ # tiff_frames = []
268
+
269
+ # Setup TIFF writer with BigTIFF support
270
+ tif_writer = tifffile.TiffWriter(f"{save_basename}.tif", bigtiff=True)
271
+
272
+ # Render frames
273
+ print("Plotting the curves into movies~")
274
+ for frame in tqdm.tqdm(range(total_frames)):
275
+ ax.set_title(title, fontsize=12,fontweight='bold') # reset title in case of matplotlib bugs
276
+
277
+ for i, (x, y, line, tip) in enumerate(zip(x_list, y_list, lines, tips)):
278
+ start = cum_lengths[i - 1] if i > 0 else 0
279
+ end = cum_lengths[i]
280
+ if frame < start:
281
+ # Not started yet
282
+ line.set_data([], [])
283
+ tip.set_data([], [])
284
+ elif frame >= end:
285
+ # Completed
286
+ line.set_data(x, y)
287
+ tip.set_data([], [])
288
+ else:
289
+ # Active scan
290
+ idx = frame - start + 1
291
+ line.set_data(x[:idx], y[:idx])
292
+ tip.set_data([x[idx - 1]], [y[idx - 1]])
293
+ value_text.set_text(
294
+ f"{labels[i] if labels else f'Scan {i+1}'}\nV = {x[idx - 1]:.3f} V\nI = {y[idx - 1]:.3f} µA"
295
+ )
296
+
297
+ # Render and convert to image
298
+ canvas.draw()
299
+ buf = np.frombuffer(canvas.tostring_rgb(), dtype=np.uint8)
300
+ image = buf.reshape((height, width, 3))
301
+ image_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
302
+ out.write(image_bgr)
303
+ # Save TIFF frame (with compression)
304
+ tif_writer.write(image, photometric='rgb', compression='deflate')
305
+
306
+ out.release()
307
+ tif_writer.close()
308
+
309
+ # tifffile.imwrite(f"{save_basename}.tif", tiff_frames, photometric='rgb',compression='deflate')
310
+ print("Conversion done!")
311
+ print(f"Saved AVI: {save_basename}.avi")
312
+ print(f"Saved TIFF stack: {save_basename}.tif")
313
+
314
+ from tkinter import Tk, filedialog
315
+ def select_multiple_files(filetypes=(("csv files", "*.csv"), ("All files", "*.*"))):
316
+ Tk().withdraw() # Hide the root window
317
+ filepaths = filedialog.askopenfilenames(title="Select PSTrace .csv files", filetypes=filetypes)
318
+ return list(filepaths) # Convert from tuple to list