基于OpenCV的STEM图像分析

x33g5p2x  于 2023-03-13  发布在  其他
关注(0)|答案(3)|浏览(179)

我正在分析TEM/STEM扫描图像。白色圆圈是原子,而黑色是背景。捕获的图像有噪声。圆圈边界不清楚。

有没有办法增强图像以显示圆的边界?
我运行下面的代码:

#Python code
import numpy as np
import cv2
import matplotlib.pyplot as plt
from skimage import feature
from scipy.optimize import curve_fit

img = cv2.imread('input_image.tif', cv2.IMREAD_GRAYSCALE)

img_noise_removed = cv2.medianBlur(img, 3)

mask = cv2.GaussianBlur(img_noise_removed, (101, 101), 0)

img_subtracted = cv2.absdiff(img_noise_removed, mask)

edges = feature.canny(img_subtracted, sigma=1)

cv2.imwrite('noise_removed_image.tif', img_noise_removed)

但它并没有解决原子边界问题。

代码的第二部分

import cv2
import numpy as np
from skimage.feature import peak_local_max
from skimage.filters import threshold_otsu

img = cv2.imread('test2.tif', cv2.IMREAD_GRAYSCALE)

# median filter and a Laplacian filter
img_median = cv2.medianBlur(img, 3)
img_laplacian = cv2.Laplacian(img_median, cv2.CV_64F, ksize=3)

# Threshold the image using Otsu's method
thresh = threshold_otsu(img_laplacian)
binary = img_laplacian > thresh

# Finding the coordinates of the local maxima in the image
coords = peak_local_max(binary, min_distance=5, threshold_abs=0.3)

# Writing the coordinates to a text file
with open('coordinates.txt', 'w') as f:
    for coord in coords:
        f.write('{} {}\n'.format(coord[1], coord[0]))
ua4mk5z4

ua4mk5z41#

其他两个答案试图提取每个原子的轮廓,然后找到这些轮廓的质心。我认为这是错误的方法,你想使用图像中的灰度值不仅仅是找到轮廓。通过计算灰度加权一阶矩(灰度斑点的质心,而不是轮廓的质心),你可以得到一个更精确的结果。而且,你可以得到这个结果,而不过滤图像第一。
我假设示例图像与您处理的实际图像相当。如果实际图像的噪声更大,您可能需要调整分水岭函数的一些参数,使其对噪声具有鲁棒性。
我使用的是DIPlib [免责声明:我是一名作家],因为我比OpenCV更熟悉它,而且因为DIPlib是用于精确测量的,这与OpenCV不同。

import diplib as dip

# Read in the image
img = dip.ImageRead("5V6nl.jpg", 'bioformats')
img = img(0)  # The JPEG has 3 channels, though it's a gray-scale image

# We want to measure the position of the atoms in pixels. If there is pixel
# size information in the input image, it will be attached to the image,
# and the measurement will be in physical units. To avoid this, we remove
# the pixel size information. But you can keep it if you need it!
img.SetPixelSize([])

# The watershed of the inverse image gives a label for each atom, we'll be
# measuring inside each label independently
# (the "high first" flag is like inverting the image)
mask = img > 20
labels = dip.Watershed(img, mask, flags={"labels", "high first"})

# The "Gravity" feature is the gray-weighted first order moment
msr = dip.MeasurementTool.Measure(labels, img, ["Gravity"])

# Iterate over the resulting centroids.
# Note that there is no specific order to them.
gravity = msr["Gravity"]
for o in msr.Objects():
   values = gravity[o]  # this is a list with two elements (x, y)
   print(f"Object {o}: ({values[0]:.4f}, {values[1]:.4f})")

这将打印出一个包含325个项目的列表:

Object 1: (137.8478, 151.9975)
Object 2: (24.9894, 89.0565)
Object 3: (49.9969, 89.0618)
Object 4: (25.0534, 151.9687)
Object 5: (275.0785, 130.1821)
Object 6: (125.3453, 152.0229)
...

请注意,图像边缘的原子会有错误的质心,我建议在这些测量中忽略它们,例如,通过丢弃离图像边界太近的质心。
labels图像如下所示:

你会注意到我们测量的区域非常松散。唯一的目标是包含每个原子的完整斑点,以便质心测量正确工作。我们不关心这些区域的确切范围,它们包含的任何较暗像素都不会对结果产生太大影响。
如果您检查中间的labels图像并注意到一个原子有多个区域,这意味着你的图像中的噪点比我们这里的例子图像中的噪点多。在这种情况下,你需要将maxDepth参数调整为dip.Watershed()。这个参数控制区域的合并。增加这个参数(默认值为1)将导致更少的区域。你必须调整它,直到你看到每个原子正好有一个区域。

labels = dip.Watershed(img, mask, maxDepth=10, flags={"labels", "high first"})
pprl5pva

pprl5pva2#

下面是在Python/OpenCV中找到原子质心的一种方法。

  • 读取输入
  • 转换为灰度
  • 阈值以便分离原子
  • 获取等值线
  • 对于每个轮廓,获得图像矩并计算质心。
  • 打印每个质心
  • 在输入副本的质心处绘制一个小圆
  • 保存结果

输入:

import cv2
import numpy as np

# read image
img = cv2.imread('STEM.jpg')

# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold
thresh = cv2.threshold(gray, 164, 255, cv2.THRESH_BINARY)[1]

# get contours
result = img.copy()
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
index=1
for cntr in contours:
    M = cv2.moments(cntr)
    cx = int(M["m10"] / M["m00"])
    cy = int(M["m01"] / M["m00"])
    print(index,cx,cy)
    cv2.circle(result, (cx,cy), 2, (0,0,255), -1)
    index = index + 1

# save results
cv2.imwrite('STEM_centroids.jpg', result)

# show results
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey(0)

阈值:

结果:

Index Centroid(x y)

1 302 254
2 290 254
3 277 254
4 265 254
5 252 255
6 239 254
7 227 254
8 214 255
9 202 254
10 189 255
11 176 254
12 164 254
13 151 255
14 138 254
15 126 254
16 113 254
17 101 254
18 88 255
19 75 254
20 63 254
21 50 255
22 37 254
23 25 254
24 12 255
25 1 254
26 302 234
27 289 234
28 277 234
29 264 234
30 251 234
31 239 234
32 226 234
33 214 234
34 201 234
35 189 234
36 176 234
37 163 234
38 151 234
39 138 234
40 126 234
41 113 234
42 100 234
43 88 234
44 75 234
45 63 234
46 50 234
47 37 234
48 25 234
49 12 234
50 1 234
51 301 214
52 289 213
53 276 213
54 264 213
55 251 213
56 238 213
57 226 213
58 213 213
59 201 213
60 188 213
61 176 213
62 163 213
63 151 213
64 138 213
65 125 213
66 113 213
67 100 213
68 88 213
69 75 214
70 62 213
71 50 213
72 37 213
73 25 213
74 12 213
75 1 213
76 301 193
77 288 193
78 276 193
79 263 193
80 251 193
81 238 193
82 226 193
83 213 193
84 200 193
85 188 193
86 175 193
87 163 193
88 150 193
89 138 193
90 125 193
91 113 193
92 100 193
93 87 193
94 75 193
95 62 193
96 50 193
97 37 193
98 25 193
99 12 193
100 1 193
101 275 172
102 263 172
103 250 172
104 238 172
105 225 172
106 213 172
107 200 172
108 188 172
109 175 172
110 163 172
111 150 172
112 138 172
113 125 172
114 112 172
115 100 172
116 87 172
117 75 172
118 62 172
119 50 172
120 37 172
121 25 172
122 12 172
123 1 172
124 300 171
125 288 171
126 175 152
127 162 152
128 150 152
129 137 152
130 125 152
131 112 151
132 100 151
133 87 152
134 75 152
135 62 152
136 50 151
137 37 152
138 25 151
139 12 152
140 1 151
141 300 150
142 287 150
143 275 150
144 262 150
145 250 150
146 237 150
147 225 150
148 212 150
149 200 150
150 187 150
151 299 130
152 287 130
153 275 130
154 262 130
155 250 130
156 237 130
157 225 130
158 212 130
159 200 130
160 187 130
161 175 130
162 162 130
163 150 130
164 137 130
165 125 130
166 112 130
167 100 130
168 87 130
169 75 130
170 62 130
171 50 130
172 37 130
173 25 130
174 12 130
175 1 130
176 287 109
177 262 109
178 249 109
179 237 109
180 212 109
181 187 109
182 162 109
183 137 109
184 112 109
185 87 109
186 62 109
187 37 109
188 12 109
189 299 109
190 274 109
191 225 109
192 200 109
193 175 109
194 150 109
195 125 109
196 100 109
197 75 109
198 50 109
199 25 109
200 1 109
201 299 89
202 287 89
203 274 89
204 262 89
205 249 89
206 237 89
207 224 88
208 212 89
209 199 89
210 187 89
211 174 89
212 162 89
213 149 89
214 137 89
215 124 89
216 112 89
217 99 89
218 87 89
219 74 89
220 62 89
221 49 89
222 37 89
223 24 89
224 12 89
225 1 89
226 274 68
227 262 68
228 249 68
229 237 68
230 224 68
231 212 68
232 199 68
233 187 68
234 174 68
235 162 68
236 149 68
237 137 68
238 124 68
239 112 68
240 99 68
241 87 68
242 74 68
243 62 68
244 49 68
245 37 68
246 25 68
247 12 68
248 1 68
249 299 67
250 287 67
251 112 47
252 99 47
253 87 48
254 74 47
255 62 48
256 49 47
257 37 48
258 24 47
259 12 48
260 1 47
261 299 46
262 286 46
263 274 46
264 262 46
265 249 46
266 237 46
267 224 46
268 212 46
269 199 46
270 187 46
271 174 46
272 162 46
273 149 46
274 137 46
275 124 46
276 299 26
277 286 26
278 274 26
279 261 26
280 249 26
281 237 26
282 224 26
283 212 26
284 199 26
285 187 26
286 174 26
287 162 26
288 149 26
289 137 26
290 124 26
291 112 26
292 99 26
293 87 26
294 74 26
295 62 26
296 49 26
297 37 26
298 24 26
299 12 26
300 1 26
301 299 5
302 286 5
303 274 5
304 261 5
305 249 5
306 236 5
307 224 5
308 212 5
309 199 5
310 187 5
311 174 5
312 162 5
313 149 5
314 137 5
315 124 5
316 112 5
317 99 5
318 87 5
319 74 5
320 62 5
321 49 5
322 37 5
323 24 5
324 12 5
325 1 5
bf1o4zei

bf1o4zei3#

您的问题类似于https://stackoverflow.com/a/17116465/1510289,并且我相信类似的解决方案将适用于您的情况,即在模糊之后应用阈值,直到您生成满意的轮廓,即边界。
下面是代码(在链接中提到),但参数定制为您的特定输入图像,以产生准确的轮廓。

import cv2

image = cv2.imread('input.jpg')
image2 = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image2 = cv2.GaussianBlur(image2, ksize=(11,11), sigmaX=1, sigmaY=1)
cv2.imwrite('blurred.png', image2)
hello, image2 = cv2.threshold(image2, thresh=140, maxval=255, type=cv2.THRESH_BINARY)
cv2.imwrite('thresholded.png', image2)
contours, hier = cv2.findContours(
    image2,
    mode=cv2.RETR_EXTERNAL,
    method=cv2.CHAIN_APPROX_NONE)
print(f'Number of contours: {len(contours)}, hit any key to continue')
cv2.drawContours(
    image,
    contours=contours,
    contourIdx=-1,
    color=(0,255,0),
    thickness=1)
cv2.imwrite('augmented.png', image)
cv2.imshow('hello', image)
cv2.waitKey(-1)

模糊.png

阈值.png

扩充的.png

相关问题