insituTEM 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.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.
- insituTEM/__ init __.py +14 -0
- insituTEM/insitu_DENS.py +318 -0
- insituTEM/insitu_DP.py +228 -8
- insituTEM/insitu_Diff.py +359 -0
- insituTEM/insitu_EMraw.py +460 -0
- insituTEM/insitu_IO.py +514 -111
- insituTEM/insitu_Preprocess.py +229 -25
- insituTEM/insitu_alignment.py +297 -11
- insitutem-0.1.7.dist-info/METADATA +20 -0
- insitutem-0.1.7.dist-info/RECORD +12 -0
- {insituTEM-0.1.5.dist-info → insitutem-0.1.7.dist-info}/WHEEL +1 -1
- insituTEM-0.1.5.dist-info/METADATA +0 -13
- insituTEM-0.1.5.dist-info/RECORD +0 -9
- {insituTEM-0.1.5.dist-info → insitutem-0.1.7.dist-info}/top_level.txt +0 -0
insituTEM/insitu_Preprocess.py
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"""
|
|
3
3
|
|
|
4
4
|
in-situ TEM Toolbox - PreProcess
|
|
5
|
-
By: Meng and Michael
|
|
6
|
-
Assembles of functions related to movie preprocess
|
|
5
|
+
By: Meng Li and Michael
|
|
6
|
+
Assembles of functions related to movie preprocess
|
|
7
7
|
Example:
|
|
8
8
|
|
|
9
9
|
import insitu_Preprocess as PP
|
|
@@ -18,7 +18,8 @@ import cv2
|
|
|
18
18
|
from scipy import signal
|
|
19
19
|
from PIL import Image
|
|
20
20
|
import matplotlib.pyplot as plt
|
|
21
|
-
|
|
21
|
+
import tifffile
|
|
22
|
+
import tqdm
|
|
22
23
|
|
|
23
24
|
def estimate_dup(A,B,threshold=1):
|
|
24
25
|
"""
|
|
@@ -66,44 +67,76 @@ def median_blur (score_list, i,thrs=150,wid=21):
|
|
|
66
67
|
def detect_blur_fft(image, size=20, thresh=15,vis=False):
|
|
67
68
|
"""
|
|
68
69
|
Function to detect if one frame is blurred using FFT
|
|
70
|
+
Example:
|
|
71
|
+
score,is_blur = PP.detect_blur_fft(image, size=20, thresh=15,vis=False)
|
|
72
|
+
Input:
|
|
69
73
|
image: grayscale image, default size: 128*128 px
|
|
70
|
-
|
|
74
|
+
size: filter size to get the background of FFT
|
|
71
75
|
thresh: threshold value for blur scores, smaller score, more blurry
|
|
72
|
-
|
|
76
|
+
Output:
|
|
77
|
+
mean: mean value of the frame
|
|
78
|
+
is_blur: whether the frame is blur
|
|
73
79
|
"""
|
|
80
|
+
from insituTEM import insitu_DP as DP
|
|
74
81
|
# grab the dimensions of the image and use the dimensions to derive the center (x, y)-coordinates
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
# h, w = image.shape
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
magnitude=DP.getFFT(image)
|
|
86
|
+
h,w = magnitude.shape
|
|
87
|
+
cX, cY = w //2, h//2
|
|
88
|
+
bar=int(h/100)
|
|
89
|
+
center=bar*10
|
|
90
|
+
#use only the center of the FFT
|
|
91
|
+
crop_h,crop_w=(h-cX)//2, (w-cY)//2
|
|
92
|
+
|
|
93
|
+
|
|
84
94
|
# compute the background of FFT to make the diffraaction spots more obvious
|
|
85
95
|
bg = cv2.blur(magnitude,(size,size))
|
|
86
96
|
|
|
87
97
|
enhanced =cv2.subtract(magnitude, bg)
|
|
98
|
+
#remove noise by mean
|
|
99
|
+
# enhanced=enhanced[crop_h:(crop_h+cY),crop_w:(crop_w+cX)]
|
|
100
|
+
|
|
88
101
|
#remove white noise from the substracted image
|
|
89
|
-
enhanced[enhanced<
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
enhanced[enhanced<20]=0
|
|
103
|
+
|
|
104
|
+
# Check the data type and convert if necessary
|
|
105
|
+
if enhanced.dtype != np.uint8:
|
|
106
|
+
# Normalize the image to the range [0, 255] if it has a different range
|
|
107
|
+
enhanced = cv2.normalize(enhanced, None, 0, 255, cv2.NORM_MINMAX)
|
|
108
|
+
# Convert the image to uint8
|
|
109
|
+
enhanced = enhanced.astype(np.uint8)
|
|
110
|
+
|
|
111
|
+
# Apply median filter
|
|
112
|
+
enhanced = cv2.medianBlur(enhanced, 3)
|
|
113
|
+
|
|
114
|
+
# enhanced = cv2.medianBlur(enhanced, 3)
|
|
115
|
+
# #remove center spot
|
|
116
|
+
enhanced[(cY - center): (cY + center), cX - center:cX + center] =0
|
|
117
|
+
enhanced[(cY - bar): (cY + bar), :]=0
|
|
118
|
+
enhanced[:, (cX - bar): (cX + bar)]=0
|
|
119
|
+
# enhanced[
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
croppedFFT=enhanced[crop_h:(crop_h+cY),crop_w:(crop_w+cX)]
|
|
92
123
|
|
|
93
124
|
if vis:
|
|
94
|
-
(fig, ax) = plt.subplots(1, 2, )
|
|
125
|
+
(fig, ax) = plt.subplots(1, 2,figsize=(10, 5) )
|
|
95
126
|
ax[0].imshow(image, cmap="gray")
|
|
96
127
|
ax[0].set_title("Input")
|
|
97
|
-
ax[0].set_xticks([])
|
|
98
|
-
ax[0].set_yticks([])
|
|
128
|
+
# ax[0].set_xticks([])
|
|
129
|
+
# ax[0].set_yticks([])
|
|
99
130
|
# display the magnitude image
|
|
100
|
-
ax[1].imshow(
|
|
131
|
+
ax[1].imshow(croppedFFT,cmap="viridis")
|
|
101
132
|
ax[1].set_title("Filtered FFT")
|
|
102
|
-
ax[1].set_xticks([])
|
|
103
|
-
ax[1].set_yticks([])
|
|
133
|
+
# ax[1].set_xticks([])
|
|
134
|
+
# ax[1].set_yticks([])
|
|
135
|
+
|
|
104
136
|
# show our plots
|
|
105
137
|
plt.show()
|
|
106
|
-
mean = np.mean(enhanced)*1000
|
|
138
|
+
# mean = np.mean(enhanced)*1000
|
|
139
|
+
mean = np.mean(croppedFFT)*1000
|
|
107
140
|
# print(mean)
|
|
108
141
|
# the image will be considered "blurry" if the mean value of the magnitudes is less than the threshold value
|
|
109
142
|
return (mean,mean<thresh)
|
|
@@ -198,4 +231,175 @@ def removeDPs(img,DP):
|
|
|
198
231
|
else:
|
|
199
232
|
for j in range(nDP):
|
|
200
233
|
position=DP[j]
|
|
201
|
-
removeDP(img,position)
|
|
234
|
+
removeDP(img,position)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def BlurDetection(path,wid=31):
|
|
238
|
+
"""
|
|
239
|
+
Function to remove blurry frames based on the blur score
|
|
240
|
+
Input: tif file path
|
|
241
|
+
Output: deblurred tif stack
|
|
242
|
+
"""
|
|
243
|
+
from insituTEM import insitu_IO as IO
|
|
244
|
+
|
|
245
|
+
indexout=path[:-4]+'_DB-index.csv'
|
|
246
|
+
DBout=path[:-4]+'_DB.csv'
|
|
247
|
+
DBout2=path[:-4]+'_Blur.csv'
|
|
248
|
+
|
|
249
|
+
stack = tifffile.imread(path)
|
|
250
|
+
nFrames,h,w = stack.shape #make sure the order of h and w!
|
|
251
|
+
index = []#index of remaining frames
|
|
252
|
+
score = []
|
|
253
|
+
|
|
254
|
+
print("------------------------------------------")
|
|
255
|
+
print("Calculating blur scores!")
|
|
256
|
+
|
|
257
|
+
for i in tqdm.tqdm(range(nFrames)):
|
|
258
|
+
fr=stack[i]
|
|
259
|
+
s = blur_score(fr)
|
|
260
|
+
score.append(s)
|
|
261
|
+
index.append(i)
|
|
262
|
+
|
|
263
|
+
length = len(score)
|
|
264
|
+
Blur=np.zeros((length,3))#[i,blur_score,blur_th]
|
|
265
|
+
Blur[:,0]=index
|
|
266
|
+
Blur[:,1]=score
|
|
267
|
+
|
|
268
|
+
plt.plot(score,'gray')
|
|
269
|
+
plt.show()
|
|
270
|
+
|
|
271
|
+
print("------------------------------------------")
|
|
272
|
+
print("Check blur detection!")
|
|
273
|
+
|
|
274
|
+
thrs=int(input( 'Input blur threshold(default: 70): '))
|
|
275
|
+
while thrs!=0:
|
|
276
|
+
index2=[]
|
|
277
|
+
|
|
278
|
+
for i in tqdm.tqdm(range(len(score))):
|
|
279
|
+
medthres,is_blur=median_blur (score, i,thrs,wid=wid)#wid must be odd
|
|
280
|
+
# DB[index[i],3]=is_blur
|
|
281
|
+
Blur[i,2]=medthres
|
|
282
|
+
if is_blur == 0:
|
|
283
|
+
index2.append(index[i])
|
|
284
|
+
|
|
285
|
+
plt.plot(Blur[:,0],Blur[:,1],'gray',Blur[:,0],Blur[:,2]-thrs,'r',Blur[:,0],Blur[:,2]+thrs,'r')
|
|
286
|
+
plt.show()
|
|
287
|
+
thrs=int(input( 'Press 0 if you are satisfied, press corrected threshold if you want to change: '))
|
|
288
|
+
|
|
289
|
+
IO.writeCSV(DBout2,Blur)
|
|
290
|
+
|
|
291
|
+
result= np.zeros((len(index2),2))
|
|
292
|
+
for i in range(len(index2)):
|
|
293
|
+
result[i,0]=i
|
|
294
|
+
result[i,1]=index2[i]
|
|
295
|
+
|
|
296
|
+
IO.writeCSV(indexout,result)
|
|
297
|
+
|
|
298
|
+
print("------------------------------------------")
|
|
299
|
+
print("Save blur removed file")
|
|
300
|
+
pathout2=path[:-4]+'_DBlur.tif'
|
|
301
|
+
|
|
302
|
+
with tifffile.TiffWriter(pathout2,bigtiff=True) as tif:
|
|
303
|
+
for j in tqdm.tqdm(range(len(index2))):
|
|
304
|
+
i=int(result[j,1])
|
|
305
|
+
fr=stack[i]
|
|
306
|
+
|
|
307
|
+
tif.write(fr, contiguous=True)
|
|
308
|
+
print("------------------------------------------")
|
|
309
|
+
print("Blur removed!")
|
|
310
|
+
print("==========================================")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def movie_preprocess(path,remove_DP=True,DP=[[0, 0, 1, 1],[0,0,2,2]],Crop=True,cropboarder=[0,0,0,0],Trim=False,start_time_mmss="00:00",end_time_mmss="00:00",grayscale=True,exp_time=0):
|
|
314
|
+
"""
|
|
315
|
+
Function to preprocess the raw TEM movie to tiff
|
|
316
|
+
Input:
|
|
317
|
+
path: file path of the movie
|
|
318
|
+
remove DP: if there are constant dead point in the movie to remove,
|
|
319
|
+
if so, input the DP value.
|
|
320
|
+
Crop: if crop the movie to remove the boarders
|
|
321
|
+
if so, input the cropboarder: [left, top, right, bottom]
|
|
322
|
+
Trim: if trim the movie length
|
|
323
|
+
if so, input the start and end time in mm:ss
|
|
324
|
+
grayscale: if convert the movie to grayscale
|
|
325
|
+
exp_time: if remove the duplicated frame based on the real exposure time.
|
|
326
|
+
if no need to remove duplicates, put exp_time = 0
|
|
327
|
+
"""
|
|
328
|
+
# print("=========================================")
|
|
329
|
+
print("ETEM movie preprocessor start!")
|
|
330
|
+
|
|
331
|
+
import moviepy.editor as mp
|
|
332
|
+
import moviepy.video.fx.all as vfx
|
|
333
|
+
from insituTEM import insitu_IO as IO
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
video = mp.VideoFileClip(path)
|
|
337
|
+
if Trim == True:
|
|
338
|
+
# Convert mm:ss to seconds
|
|
339
|
+
start_time_seconds = sum(x * int(t) for x, t in zip([60, 1], start_time_mmss.split(":")))
|
|
340
|
+
end_time_seconds = sum(x * int(t) for x, t in zip([60, 1], end_time_mmss.split(":")))
|
|
341
|
+
|
|
342
|
+
video = video.subclip(start_time_seconds, end_time_seconds)# cut the clip between t=0 and 28 secs.
|
|
343
|
+
|
|
344
|
+
fps = int(video.fps)
|
|
345
|
+
# fps= 15
|
|
346
|
+
w = int(video.w)
|
|
347
|
+
h = int(video.h)
|
|
348
|
+
nFrames = int(fps*video.duration)
|
|
349
|
+
|
|
350
|
+
print("fps=", fps, " w =",w, " h=", h, "duration= ",video.duration)
|
|
351
|
+
print("Total number of Frames=", nFrames)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
#Preview first frame
|
|
356
|
+
print("------------------------------------------")
|
|
357
|
+
print("Preview the processed frame: press 0 if you want to continue: ")
|
|
358
|
+
|
|
359
|
+
if Crop == True:
|
|
360
|
+
video = video.fx(vfx.crop, x1=cropboarder[0], y1=cropboarder[1], x2=w-cropboarder[2], y2=h-cropboarder[3])#crop movie snapshot
|
|
361
|
+
|
|
362
|
+
fr = video.get_frame(mp.cvsecs(1/fps))
|
|
363
|
+
|
|
364
|
+
if grayscale==True:
|
|
365
|
+
fr = cv2.cvtColor(fr, cv2.COLOR_BGR2GRAY)
|
|
366
|
+
|
|
367
|
+
if remove_DP==True:
|
|
368
|
+
removeDPs(fr,DP)
|
|
369
|
+
|
|
370
|
+
IO.showFr(fr)
|
|
371
|
+
prompt = input("Input 0 if you want to continue, other keys to break :")
|
|
372
|
+
|
|
373
|
+
if prompt != "0":
|
|
374
|
+
print("Stopped by user. Modify the cropboarder values and restart.")
|
|
375
|
+
return
|
|
376
|
+
else:
|
|
377
|
+
#continue to apply to the full movie
|
|
378
|
+
#remove duplicate frames with known fps conversion
|
|
379
|
+
if exp_time==0:
|
|
380
|
+
dup=1
|
|
381
|
+
else:
|
|
382
|
+
dup= fps*exp_time
|
|
383
|
+
|
|
384
|
+
nFramesOut=int(nFrames/dup)
|
|
385
|
+
print ('nFramesOut= ', nFramesOut)
|
|
386
|
+
print('nFramesIn= ',nFrames)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
pathout=path[:-4]+'_CutDB.tif'
|
|
390
|
+
|
|
391
|
+
print("------------------------------------------")
|
|
392
|
+
print("Saving duplicate removed processed frames~")
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
with tifffile.TiffWriter(pathout,bigtiff=True) as tif:
|
|
396
|
+
for j in tqdm.tqdm(range(nFramesOut)):
|
|
397
|
+
i=j*dup
|
|
398
|
+
fr = video.get_frame(mp.cvsecs(i/fps))
|
|
399
|
+
if grayscale==True:
|
|
400
|
+
fr = cv2.cvtColor(fr, cv2.COLOR_BGR2GRAY)
|
|
401
|
+
if remove_DP==True:
|
|
402
|
+
removeDPs(fr,DP)
|
|
403
|
+
tif.write(fr, contiguous=True)
|
|
404
|
+
|
|
405
|
+
print("Conversion done!")
|
insituTEM/insitu_alignment.py
CHANGED
|
@@ -10,15 +10,20 @@ Example:
|
|
|
10
10
|
Al.get_matrix()
|
|
11
11
|
|
|
12
12
|
Created on Tue Jun 11 17:22:45 2019
|
|
13
|
-
By:
|
|
13
|
+
By: Meng Li and Michel
|
|
14
14
|
"""
|
|
15
|
+
|
|
16
|
+
|
|
15
17
|
import numpy as np
|
|
16
18
|
import csv
|
|
17
19
|
import cv2
|
|
18
20
|
from skimage import io #package for TIFF stack reading
|
|
19
21
|
import tifffile
|
|
20
22
|
from scipy import signal
|
|
21
|
-
|
|
23
|
+
import tqdm
|
|
24
|
+
import matplotlib.pyplot as plt
|
|
25
|
+
from insituTEM import insitu_IO as IO
|
|
26
|
+
|
|
22
27
|
|
|
23
28
|
def get_path():
|
|
24
29
|
from tkinter import Tk
|
|
@@ -54,6 +59,117 @@ Please choose the csv you want to use
|
|
|
54
59
|
return dx, dy, csvpath
|
|
55
60
|
|
|
56
61
|
|
|
62
|
+
def Adaptive_template_match(path, NOF = 50, csvout=True, TMout=True, TMdenoise=True, TMdenoise_window=5,extend=True, bg = 150):
|
|
63
|
+
'''
|
|
64
|
+
Function to produce adaptive template matching on tiff image stack based on ROI selection
|
|
65
|
+
input:
|
|
66
|
+
path: image path for grayscale Tiff stack
|
|
67
|
+
NOF: number of frames for template to grow
|
|
68
|
+
csvout: whether to write the translation matrix
|
|
69
|
+
TMout: whether to write the template match found feature
|
|
70
|
+
TMdenoise: whether to denoise the measured matrix to remove sudden change
|
|
71
|
+
TMdenoise_window: window size for TMdenoise
|
|
72
|
+
bg: background color (0-255)
|
|
73
|
+
extend: whether to extend the image for the translation (avoid crop)
|
|
74
|
+
|
|
75
|
+
output:
|
|
76
|
+
Translated image stack
|
|
77
|
+
Csv file of translation matrix
|
|
78
|
+
Captured ROI location
|
|
79
|
+
|
|
80
|
+
Write by Meng Li (mona.mengli@gmail.com) 2024.02.06
|
|
81
|
+
'''
|
|
82
|
+
import cv2
|
|
83
|
+
import numpy as np
|
|
84
|
+
from matplotlib import pyplot as plt
|
|
85
|
+
import tifffile
|
|
86
|
+
import easygui
|
|
87
|
+
import tqdm
|
|
88
|
+
from insituTEM import insitu_IO as IO
|
|
89
|
+
# from insituTEM import insitu_alignment as AL
|
|
90
|
+
|
|
91
|
+
###############################################################
|
|
92
|
+
## 1. Inital settings
|
|
93
|
+
|
|
94
|
+
stack = tifffile.imread(path)
|
|
95
|
+
nFrames, h,w = stack.shape
|
|
96
|
+
|
|
97
|
+
TM = np.zeros((nFrames,3))#frame, TX,TY,max_val
|
|
98
|
+
pathout3 = path[:-4]+'_TM.csv'
|
|
99
|
+
pathout2 = path[:-4]+'_TM.tif'
|
|
100
|
+
|
|
101
|
+
################################################################
|
|
102
|
+
#2. Open fr0 and select ROI for template matching
|
|
103
|
+
img = stack[0]
|
|
104
|
+
## Select ROI for template matching
|
|
105
|
+
r = cv2.selectROI("Select ROI, press ENTER to confirm",img,fromCenter=False,showCrosshair=False)
|
|
106
|
+
cv2.destroyWindow("Select ROI, press ENTER to confirm")
|
|
107
|
+
# Crop image
|
|
108
|
+
template = img[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]#opencv coordinate: Y,x , from upper left corner of the image
|
|
109
|
+
# Display cropped image
|
|
110
|
+
cv2.imshow("Template, press ENTER to confirm", template)
|
|
111
|
+
cv2.waitKey(0)
|
|
112
|
+
# cv2.destroyWindow("Template, press ENTER to confirm")
|
|
113
|
+
cv2.destroyAllWindows()
|
|
114
|
+
for _ in range(5):
|
|
115
|
+
cv2.waitKey(1)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# ################################################################
|
|
120
|
+
#3. Apply template to template matching
|
|
121
|
+
meth='cv2.TM_CCOEFF_NORMED'
|
|
122
|
+
method = eval(meth)
|
|
123
|
+
w, h = template.shape[::-1]
|
|
124
|
+
|
|
125
|
+
print("------------------------------------------")
|
|
126
|
+
print("Calculating drift~")
|
|
127
|
+
with tifffile.TiffWriter(pathout2,bigtiff=True) as tif:
|
|
128
|
+
for i in tqdm.tqdm(range(nFrames)):
|
|
129
|
+
fr=stack[i]
|
|
130
|
+
# Apply template Matching
|
|
131
|
+
res = cv2.matchTemplate(fr,template,method)
|
|
132
|
+
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
|
|
133
|
+
top_left = max_loc
|
|
134
|
+
TM[i,0]=i
|
|
135
|
+
TM[i,1]=top_left[0]
|
|
136
|
+
TM[i,2]=top_left[1]
|
|
137
|
+
bottom_right = (top_left[0] + w, top_left[1] + h)
|
|
138
|
+
cv2.rectangle(fr,top_left, bottom_right, 255, 2)
|
|
139
|
+
|
|
140
|
+
if i%NOF==NOF-1:
|
|
141
|
+
template = fr[int(top_left[1]):int(top_left[1] + h), int(top_left[0]):int(top_left[0] + w)]#opencv coordinate: Y,x , from upper left corner of the image
|
|
142
|
+
if TMout==True:
|
|
143
|
+
tif.write(fr, contiguous=True)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
TM[:,1]=TM[0,1]-TM[:,1]
|
|
147
|
+
TM[:,2]=TM[0,2]-TM[:,2]
|
|
148
|
+
|
|
149
|
+
# ################################################################
|
|
150
|
+
#4. Apply TM to movie
|
|
151
|
+
if TMdenoise==True:
|
|
152
|
+
#Get average to minimize total movement
|
|
153
|
+
TM=denoiseTM(TM,TMdenoise_window)
|
|
154
|
+
Mx=int(np.mean(TM[:,1]))
|
|
155
|
+
My=int(np.mean(TM[:,2]))
|
|
156
|
+
TM[:,1]=TM[:,1]-Mx
|
|
157
|
+
TM[:,2]=TM[:,2]-My
|
|
158
|
+
|
|
159
|
+
print("------------------------------------------")
|
|
160
|
+
print("Saving files~")
|
|
161
|
+
plt.plot(TM[:,0],TM[:,1],'b',TM[:,0],TM[:,2],'r')
|
|
162
|
+
|
|
163
|
+
if csvout==True:
|
|
164
|
+
IO.writeCSV(pathout3,TM,fmt='%1.1d')
|
|
165
|
+
|
|
166
|
+
TranslateStack(path,TM,bg=bg, extend = extend)
|
|
167
|
+
print("Adaptive template matching done!")
|
|
168
|
+
plt.show(block=False)
|
|
169
|
+
plt.pause(0.1)
|
|
170
|
+
plt.close()
|
|
171
|
+
|
|
172
|
+
|
|
57
173
|
|
|
58
174
|
|
|
59
175
|
def TranslateStack(movie_path,matrix,bg=150,extend=True):
|
|
@@ -90,7 +206,7 @@ def TranslateStack(movie_path,matrix,bg=150,extend=True):
|
|
|
90
206
|
|
|
91
207
|
#2d matrix flipped order of width and height to work with warpaffine method
|
|
92
208
|
print("\nApplying translation to stack\n")
|
|
93
|
-
with tifffile.TiffWriter(movie_pathout) as tif:
|
|
209
|
+
with tifffile.TiffWriter(movie_pathout,bigtiff=True) as tif:
|
|
94
210
|
|
|
95
211
|
for num in tqdm(range(nframes)):
|
|
96
212
|
temparray = img[num,:,:]
|
|
@@ -100,13 +216,6 @@ def TranslateStack(movie_path,matrix,bg=150,extend=True):
|
|
|
100
216
|
|
|
101
217
|
extend_img = cv2.copyMakeBorder(temparray, top, bottom, left, right, cv2.BORDER_CONSTANT, value=bg)
|
|
102
218
|
translated_img = cv2.warpAffine(extend_img,TM,(width,height),borderValue =bg)#fill with gray
|
|
103
|
-
#Generates an image translated by dx and dy
|
|
104
|
-
# if num == 0:
|
|
105
|
-
# tifffile.imwrite(movie_pathout, translated_img, append=False, bigtiff=True)
|
|
106
|
-
# #Overrides any previous files saved to the same path
|
|
107
|
-
# else:
|
|
108
|
-
# tifffile.imwrite(movie_pathout, translated_img, append=True, bigtiff=True)
|
|
109
|
-
# #appends onto the tiff stack
|
|
110
219
|
tif.write(translated_img, contiguous=True)
|
|
111
220
|
|
|
112
221
|
|
|
@@ -246,4 +355,181 @@ def template_match(imgpath, w=24):
|
|
|
246
355
|
for num in iterate:
|
|
247
356
|
TM[num,0] = dx[num]
|
|
248
357
|
TM[num,1] = dy[num]
|
|
249
|
-
IO.writeCSV(csvpathout,TM)
|
|
358
|
+
IO.writeCSV(csvpathout,TM)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def ATM_locglobal(path, NOF=50, csvout=True, TMout=True, TMdenoise=True, TMdenoise_window=5, extend=True, bg=150,search_margin_local=1, match_threshold=0.8):
|
|
362
|
+
'''
|
|
363
|
+
Function to produce adaptive template matching on TIFF image stack based on ROI selection with local and global selections
|
|
364
|
+
|
|
365
|
+
Input:
|
|
366
|
+
path: Image path for grayscale TIFF stack
|
|
367
|
+
NOF: Number of frames for template to grow
|
|
368
|
+
csvout: Whether to write the translation matrix
|
|
369
|
+
TMout: Whether to write the template match found feature
|
|
370
|
+
TMdenoise: Whether to denoise the measured matrix to remove sudden change
|
|
371
|
+
TMdenoise_window: Window size for TMdenoise
|
|
372
|
+
bg: Background color (0-255)
|
|
373
|
+
extend: Whether to extend the image for the translation (avoid crop)
|
|
374
|
+
match_threshold: Threshold for considering the new ROI position close to the previous one
|
|
375
|
+
|
|
376
|
+
Output:
|
|
377
|
+
Translated image stack
|
|
378
|
+
CSV file of translation matrix
|
|
379
|
+
Captured ROI location
|
|
380
|
+
|
|
381
|
+
Written by Meng Li (mona.mengli@gmail.com) 2024.07.06
|
|
382
|
+
'''
|
|
383
|
+
|
|
384
|
+
###############################################################
|
|
385
|
+
# 1. Initial settings
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
import sys
|
|
389
|
+
|
|
390
|
+
try:
|
|
391
|
+
stack = tifffile.imread(path)
|
|
392
|
+
except OSError as e:
|
|
393
|
+
print(f"Error reading the TIFF file: {e}")
|
|
394
|
+
sys.exit(1)
|
|
395
|
+
|
|
396
|
+
# stack = tifffile.imread(path)
|
|
397
|
+
nFrames, h, w = stack.shape
|
|
398
|
+
|
|
399
|
+
TM = np.zeros((nFrames, 3)) # frame, TX, TY, max_val
|
|
400
|
+
pathout3 = path[:-4] + '_TM.csv'
|
|
401
|
+
pathout2 = path[:-4] + '_TM.tif'
|
|
402
|
+
|
|
403
|
+
###############################################################
|
|
404
|
+
# 2. Open the first frame and select ROI for template matching
|
|
405
|
+
img = stack[0]
|
|
406
|
+
|
|
407
|
+
# Select ROI for template matching, returns x1,y1,w,h
|
|
408
|
+
r = cv2.selectROI("Select ROI, press ENTER to confirm", img, fromCenter=False, showCrosshair=False)
|
|
409
|
+
x1,y1,template_w,template_h=int(r[0]),int(r[1]),int(r[2]),int(r[3])
|
|
410
|
+
x2=x1+template_w
|
|
411
|
+
y2=y1+template_h
|
|
412
|
+
|
|
413
|
+
cv2.destroyWindow("Select ROI, press ENTER to confirm")
|
|
414
|
+
|
|
415
|
+
# Crop image to get the template
|
|
416
|
+
# template = img[int(r[1]):int(r[1] + r[3]), int(r[0]):int(r[0] + r[2])]
|
|
417
|
+
template=img[y1:y2,x1:x2]
|
|
418
|
+
# template_h, template_w = template.shape
|
|
419
|
+
|
|
420
|
+
# Display cropped image
|
|
421
|
+
cv2.imshow("Template, press ENTER to confirm", template)
|
|
422
|
+
cv2.waitKey(0)
|
|
423
|
+
cv2.destroyAllWindows()
|
|
424
|
+
cv2.waitKey(1)
|
|
425
|
+
|
|
426
|
+
###############################################################
|
|
427
|
+
# 3. Apply template matching
|
|
428
|
+
method = cv2.TM_CCOEFF_NORMED
|
|
429
|
+
|
|
430
|
+
print("------------------------------------------")
|
|
431
|
+
print("Calculating drift~")
|
|
432
|
+
|
|
433
|
+
last_top_left = (x1,y1)
|
|
434
|
+
|
|
435
|
+
last_max_val = None
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
with tifffile.TiffWriter(pathout2, bigtiff=True) as tif:
|
|
440
|
+
for i in tqdm.tqdm(range(nFrames)):
|
|
441
|
+
fr = stack[i]
|
|
442
|
+
|
|
443
|
+
search_area_global=fr
|
|
444
|
+
|
|
445
|
+
# Apply global template matching
|
|
446
|
+
res_global = cv2.matchTemplate(search_area_global, template, method)
|
|
447
|
+
min_val_global, max_val_global, min_loc_global, max_loc_global = cv2.minMaxLoc(res_global)
|
|
448
|
+
|
|
449
|
+
global_top_left = max_loc_global
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
# Define the local search area around the last known ROI position
|
|
453
|
+
# search_margin_local = 1 # Local search margin
|
|
454
|
+
x1_local=int(x1-search_margin_local)
|
|
455
|
+
y1_local=int(y1-search_margin_local)
|
|
456
|
+
x2_local=int(x1+template_w+search_margin_local)
|
|
457
|
+
y2_local=int(y1+template_h+search_margin_local)
|
|
458
|
+
search_area_local = fr[y1_local:y2_local, x1_local:x2_local]
|
|
459
|
+
# Apply local template matching
|
|
460
|
+
res_local = cv2.matchTemplate(search_area_local, template, method)
|
|
461
|
+
min_val_local, max_val_local, min_loc_local, max_loc_local = cv2.minMaxLoc(res_local)
|
|
462
|
+
|
|
463
|
+
local_top_left = (x1_local + max_loc_local[0], y1_local + max_loc_local[1])
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
# # start_x_local = max(last_top_left[0] - search_margin_local, 0)
|
|
468
|
+
# # start_y_local = max(last_top_left[1] - search_margin_local, 0)
|
|
469
|
+
# # end_x_local=last_top_left[0]+template_w+search_margin_local
|
|
470
|
+
# # end_y_local = last_top_left[1] +template_h + search_margin_local
|
|
471
|
+
# # # end_x_local = min(last_top_left[0]+template_w + search_margin_local, w)
|
|
472
|
+
# # # end_y_local = min(last_top_left[1] +template_h + search_margin_local, h])
|
|
473
|
+
|
|
474
|
+
# # search_area_local = fr[start_y_local:end_y_local, start_x_local:end_x_local]
|
|
475
|
+
|
|
476
|
+
# # Apply local template matching
|
|
477
|
+
# res_local = cv2.matchTemplate(search_area_local, template, method)
|
|
478
|
+
# min_val_local, max_val_local, min_loc_local, max_loc_local = cv2.minMaxLoc(res_local)
|
|
479
|
+
|
|
480
|
+
# local_top_left = (start_x_local + max_loc_local[0], start_y_local + max_loc_local[1])
|
|
481
|
+
|
|
482
|
+
# Use the local match if the match value is within the threshold of the previous match
|
|
483
|
+
if last_max_val is not None and max_val_local >= max_val_global * match_threshold:
|
|
484
|
+
top_left = (local_top_left[0] , local_top_left[1])
|
|
485
|
+
max_val = max_val_local
|
|
486
|
+
else:
|
|
487
|
+
top_left = (global_top_left[0], global_top_left[1])
|
|
488
|
+
max_val = max_val_global
|
|
489
|
+
|
|
490
|
+
TM[i, 0] = i
|
|
491
|
+
TM[i, 1] = top_left[0]
|
|
492
|
+
TM[i, 2] = top_left[1]
|
|
493
|
+
|
|
494
|
+
# Update the last known position and match value
|
|
495
|
+
last_top_left = top_left
|
|
496
|
+
last_max_val = max_val
|
|
497
|
+
|
|
498
|
+
# Draw rectangle for visualization
|
|
499
|
+
bottom_right = (top_left[0] + template_w, top_left[1] + template_h)
|
|
500
|
+
cv2.rectangle(fr, top_left, bottom_right, 255, 2)
|
|
501
|
+
|
|
502
|
+
# Periodically update the template
|
|
503
|
+
if i % NOF == NOF - 1:
|
|
504
|
+
template = fr[top_left[1]:bottom_right[1], top_left[0]:bottom_right[0]]
|
|
505
|
+
template_h, template_w = template.shape
|
|
506
|
+
|
|
507
|
+
if TMout:
|
|
508
|
+
tif.write(fr, contiguous=True)
|
|
509
|
+
|
|
510
|
+
TM[:, 1] = TM[0, 1] - TM[:, 1]
|
|
511
|
+
TM[:, 2] = TM[0, 2] - TM[:, 2]
|
|
512
|
+
|
|
513
|
+
###############################################################
|
|
514
|
+
# 4. Apply translation matrix to movie
|
|
515
|
+
if TMdenoise:
|
|
516
|
+
TM = denoiseTM(TM, TMdenoise_window)
|
|
517
|
+
Mx = int(np.mean(TM[:, 1]))
|
|
518
|
+
My = int(np.mean(TM[:, 2]))
|
|
519
|
+
TM[:, 1] -= Mx
|
|
520
|
+
TM[:, 2] -= My
|
|
521
|
+
|
|
522
|
+
print("------------------------------------------")
|
|
523
|
+
print("Saving files~")
|
|
524
|
+
|
|
525
|
+
plt.plot(TM[:, 0], TM[:, 1], 'b', TM[:, 0], TM[:, 2], 'r')
|
|
526
|
+
|
|
527
|
+
if csvout:
|
|
528
|
+
IO.writeCSV(pathout3, TM, fmt='%1.1d')
|
|
529
|
+
|
|
530
|
+
TranslateStack(path, TM, bg=bg, extend=extend)
|
|
531
|
+
|
|
532
|
+
print("Adaptive template matching done!")
|
|
533
|
+
plt.show()
|
|
534
|
+
|
|
535
|
+
|
|
@@ -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,12 @@
|
|
|
1
|
+
insituTEM/__ init __.py,sha256=L4AFZSmHZkQu6J-tlqBoTJaBFltRFZ5PArhk8pmDmco,283
|
|
2
|
+
insituTEM/insitu_DENS.py,sha256=q2ZGbAoPPfsWbG8XlQqTdJa5_VNUNvjR9FzG2J3KkP4,10865
|
|
3
|
+
insituTEM/insitu_DP.py,sha256=0HMGT7j9ndpYPAgXCNGHkZeg9yJ4KI4SMCim2kiG6C0,10930
|
|
4
|
+
insituTEM/insitu_Diff.py,sha256=EpCMuprigdJKXaFbg9kIGZi_msKRqIDmdqvA1yofTco,11951
|
|
5
|
+
insituTEM/insitu_EMraw.py,sha256=O7rwdQW4STkQlYr3yImnpJX_VqjIExHgFqFIoKblYYE,15347
|
|
6
|
+
insituTEM/insitu_IO.py,sha256=za4ow7DPnv_YgroD_3fH12neXwYjw717w8jPzCRQvqg,25554
|
|
7
|
+
insituTEM/insitu_Preprocess.py,sha256=xKnHnv8dc0gTHzzEPW_DivzzEJHNFSNGRD_r7UM2l6U,12779
|
|
8
|
+
insituTEM/insitu_alignment.py,sha256=duH383I-jSStKhy6l7mzy9yijSl7Elf4JDFfV8ilFzM,19595
|
|
9
|
+
insitutem-0.1.7.dist-info/METADATA,sha256=J88Jc0obYHWJp7-89Xab0ZdsSNkDRvWIc9v-RtKTgbs,438
|
|
10
|
+
insitutem-0.1.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
11
|
+
insitutem-0.1.7.dist-info/top_level.txt,sha256=DEgrwrdz6iV62_cukvjFXu4dnZYupwvS41QyvTfcqjs,10
|
|
12
|
+
insitutem-0.1.7.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: insituTEM
|
|
3
|
-
Version: 0.1.5
|
|
4
|
-
Summary: A package of tools for processing in situ TEM data
|
|
5
|
-
Author: Meng Li
|
|
6
|
-
Requires-Dist: tifffile
|
|
7
|
-
Requires-Dist: moviepy
|
|
8
|
-
Requires-Dist: tqdm
|
|
9
|
-
Requires-Dist: numpy
|
|
10
|
-
Requires-Dist: opencv-python
|
|
11
|
-
Requires-Dist: scipy
|
|
12
|
-
Requires-Dist: matplotlib
|
|
13
|
-
|