Image processing 为什么可以';我不能使二维高斯收敛吗?
我想拟合图像中的多个点,在局部背景校正失败的拥挤区域找到它们的真实强度 我的方法只是挑选图像的一个局部区域并进行拟合。问题是,拟合不会产生任何有用的结果,只是默认为初始参数。添加边界以使拟合完全不收敛 我做错了什么 守则:Image processing 为什么可以';我不能使二维高斯收敛吗?,image-processing,scipy,curve-fitting,gaussian,Image Processing,Scipy,Curve Fitting,Gaussian,我想拟合图像中的多个点,在局部背景校正失败的拥挤区域找到它们的真实强度 我的方法只是挑选图像的一个局部区域并进行拟合。问题是,拟合不会产生任何有用的结果,只是默认为初始参数。添加边界以使拟合完全不收敛 我做错了什么 守则: import scipy.optimize as opt import numpy as np import matplotlib.pyplot as plt import skimage.feature from collections import namedtuple
import scipy.optimize as opt
import numpy as np
import matplotlib.pyplot as plt
import skimage.feature
from collections import namedtuple
import skimage.io
def gaussian_2d(
xy_array, amplitude, pos_x, pos_y, sigma_x, sigma_y, rotation, offset
):
"""
Expression for a 2D gaussian function with variance in both x and y
"""
x, y = xy_array
a = (np.cos(rotation) ** 2) / (2 * sigma_x ** 2) + (
np.sin(rotation) ** 2
) / (2 * sigma_y ** 2)
b = -(np.sin(2 * rotation)) / (4 * sigma_x ** 2) + (
np.sin(2 * rotation)
) / (4 * sigma_y ** 2)
c = (np.sin(rotation) ** 2) / (2 * sigma_x ** 2) + (
np.cos(rotation) ** 2
) / (2 * sigma_y ** 2)
g = amplitude * np.exp(
-(
a * ((x - pos_x) ** 2)
+ 2 * b * (x - pos_x) * (y - pos_y)
+ c * ((y - pos_y) ** 2)
)
)
g += offset
return g.ravel()
def fit_gaussian_spots(x_guess, y_guess, array):
Params = namedtuple(
"Parameters", "amp, x, y, sigma_x, sigma_y, rotation, offset"
)
eps = 1e-8
initial_guess = Params(
amp=1, x=x_guess, y=y_guess, sigma_x=1, sigma_y=1, rotation=0, offset=0
)
# Bounds makes it even harder to converge
min_bounds = Params(
amp=eps,
x=x_guess * 0.5,
y=y_guess * 0.5,
sigma_x=eps,
sigma_y=eps,
rotation=-np.inf,
offset=eps,
)
max_bounds = Params(
amp=np.max(array),
x=x_guess * 1.5,
y=y_guess * 1.5,
sigma_x=np.inf,
sigma_y=np.inf,
rotation=2 * np.pi,
offset=np.max(array),
)
try:
X, Y = create_grid(*array.shape)
popt, pcov = opt.curve_fit(
f=gaussian_2d,
xdata=(X, Y),
ydata=array.ravel(),
p0=initial_guess,
# bounds=(min_bounds, max_bounds),
)
popt = Params(*np.round(popt))
except ValueError:
print("fit didn't converge!")
popt, pcov = None, None
return popt, pcov
def create_grid(h, w):
"""
Creates a grid of x and y points to fit and evaluate over
"""
x = np.arange(0, w, 1)
y = np.arange(0, h, 1)
x, y = np.meshgrid(x, y)
return x, y
def evaluate_gaussian(x, y, popt):
"""
Evaluates gaussian in coordinate positions.
NOTE: this is not necessary for extracting intensity,
as the pure signal is fitted as the amplitude.
"""
z = gaussian_2d((x, y), *popt)
return z
if __name__ == "__main__":
# Create x and y indices
np.random.seed(4)
h, w = 200, 200
x, y = create_grid(h=h, w=w)
# create data
img = []
for _ in range(10):
randx = np.random.randint(10, w - 10)
randy = np.random.randint(10, h - 10)
amp = 100
d = gaussian_2d(
xy_array=(x, y),
amplitude=amp,
pos_x=randx,
pos_y=randy,
sigma_x=9,
sigma_y=3,
rotation=3,
offset=0,
)
# d = d + np.random.normal(0, 5, d.shape) # add noise
# d += 100 # add offset
img.append(d)
img = np.sum(img, axis=0)
img = img.reshape(h, w)
print("max intensity: {:.2f}".format(img.max()))
# Detect soem possible spots first
spots = skimage.feature.peak_local_max(img, num_peaks=20, min_distance=10)
fig, ax = plt.subplots(ncols=2)
h, w = img.shape
local_area = 20
fit = []
# skimage returns rows, columns (y,x) while matplotlib operates in (x,y)
for idx, (pre_y, pre_x) in enumerate(spots):
# Fit gaussian in local area
popt, pcov = fit_gaussian_spots(
x_guess=pre_x,
y_guess=pre_y,
# Avoid falling off the edge of the image
array=img[
max(pre_y - local_area, 0) : pre_y + local_area,
max(pre_x - local_area, 0) : pre_x + local_area,
],
)
if popt is None:
continue
print(popt)
ax[0].add_patch(
plt.Circle(
(pre_x, pre_y), 5, linewidth=0.5, fill=False, color="red"
)
)
ax[1].add_patch(
plt.Rectangle(
(pre_x - local_area, pre_y - local_area),
width=local_area * 2,
height=local_area * 2,
fill=False,
color="yellow",
)
)
fit.append(evaluate_gaussian(x, y, popt))
fit = np.sum(fit, axis=0)
ax[0].set_title("true")
ax[0].imshow(
img, origin="bottom", extent=(x.min(), x.max(), y.min(), y.max())
)
ax[1].set_title("predicted")
ax[1].imshow(
fit.reshape(img.shape),
origin="bottom",
extent=(x.min(), x.max(), y.min(), y.max()),
)
plt.show()
事实证明,我最大的错误是忘记了要在图像子集中拟合的坐标当然是相对的。事实上,只要使用中心就可以了。我最终使用了下面的代码,没有任何限制,因为我发现使用首字母进行匹配总体上只会快一点
import scipy.optimize as opt
import numpy as np
import matplotlib.pyplot as plt
import skimage.feature
from collections import namedtuple
import skimage.io
import matplotlib.patches
import skimage.filters
import warnings
from scipy.optimize import OptimizeWarning
def zoom_array(array, xy, square_radius):
"""
Return a zoomed array at location
"""
x, y = xy
minix = int(max(x - square_radius, 0))
miniy = int(max(y - square_radius, 0))
maxix = int(x + square_radius)
maxiy = int(y + square_radius)
return array[miniy:maxiy, minix:maxix]
def gaussian_2d(
xy_array, amplitude, pos_x, pos_y, sigma_x, sigma_y, angle, offset
):
"""
Expression for a 2D gaussian function with variance in both x and y
"""
x, y = xy_array
a = (np.cos(angle) ** 2) / (2 * sigma_x ** 2) + (np.sin(angle) ** 2) / (
2 * sigma_y ** 2
)
b = -(np.sin(2 * angle)) / (4 * sigma_x ** 2) + (np.sin(2 * angle)) / (
4 * sigma_y ** 2
)
c = (np.sin(angle) ** 2) / (2 * sigma_x ** 2) + (np.cos(angle) ** 2) / (
2 * sigma_y ** 2
)
g = offset + amplitude * np.exp(
-(
a * ((x - pos_x) ** 2)
+ 2 * b * (x - pos_x) * (y - pos_y)
+ c * ((y - pos_y) ** 2)
)
)
return g.ravel()
def fit_gaussian_spots(x_guess, y_guess, array):
Params = namedtuple(
"Parameters", "amp, x, y, sigma_x, sigma_y, angle, offset"
)
initial_guess = Params(
amp=np.max(array),
x=x_guess,
y=y_guess,
sigma_x=1,
sigma_y=1,
angle=0,
offset=np.abs(np.min(array)),
)
with warnings.catch_warnings():
warnings.simplefilter("error", OptimizeWarning)
try:
X, Y = create_grid(*array.shape)
popt, pcov = opt.curve_fit(
f=gaussian_2d,
xdata=(X, Y),
ydata=array.ravel(),
p0=initial_guess,
maxfev=200,
method="lm"
# constraints make it slower. Better to time out bad fits
# bounds=(min_bounds, max_bounds),
)
popt = Params(*np.round(popt))
except (OptimizeWarning, ValueError, RuntimeError):
popt, pcov = None, None
return popt, pcov
def create_grid(h, w):
"""
Creates a grid of x and y points to fit and evaluate over
"""
x = np.arange(0, w, 1)
y = np.arange(0, h, 1)
x, y = np.meshgrid(x, y)
return x, y
def evaluate_gaussian(x, y, popt):
"""
Evaluates gaussian in coordinate positions.
NOTE: this is not necessary for extracting intensity,
as the pure signal is fitted as the amplitude.
"""
z = gaussian_2d((x, y), *popt)
return z
def _simulate_data():
"""Create data"""
img = []
for _ in range(N_SPOTS):
POSX = np.random.randint(0, W)
POSY = np.random.randint(0, H)
AMP = 100
g = gaussian_2d(
xy_array=(Xi, Yi),
amplitude=AMP,
pos_x=POSX,
pos_y=POSY,
sigma_x=2,
sigma_y=2,
angle=0,
offset=0,
)
img.append(g)
img = np.sum(img, axis=0)
img = img.reshape(H, W)
img = img + np.random.normal(5, 5, len(img.ravel())).reshape(img.shape)
return img
if __name__ == "__main__":
# Create x and y indices
np.random.seed(4)
PLOT_ROWS = 3
PLOT_COLS = 3
H, W = 200, 400
N_SPOTS = 50
Xi, Yi = create_grid(h=H, w=W)
img = _simulate_data()
# Detect a generous amount of spots to be safe
spots = skimage.feature.peak_local_max(img, num_peaks=300, min_distance=5)
figimg, aximg = plt.subplots()
aximg.imshow(
img, origin="bottom", extent=(Xi.min(), Xi.max(), Yi.min(), Yi.max())
)
figzoom, axzoom = plt.subplots(nrows=PLOT_ROWS, ncols=PLOT_COLS)
axzoom = axzoom.ravel()
zoom_ctr = 6
# skimage returns rows, columns (y,x) while matplotlib operates in (x,y)
idx = 0
for guessy, guessx in spots:
# Plot on the full image
# Initial
aximg.add_patch(
plt.Circle(
(guessx, guessy),
3,
linewidth=0.5,
fill=False,
alpha=0.5,
color="yellow",
)
)
# Fit
local_arr = zoom_array(img, (guessx, guessy), square_radius=zoom_ctr)
popt, pcov = fit_gaussian_spots(
x_guess=zoom_ctr, y_guess=zoom_ctr, array=local_arr
)
# Throw away bad fits
if popt is None or popt.sigma_x < 1 or popt.sigma_y < 1:
continue
predx = guessx + popt.x - zoom_ctr
predy = guessy + popt.y - zoom_ctr
# Plot on each of zooms
# Predicted
try:
axzoom[idx].imshow(local_arr, origin="bottom")
axzoom[idx].add_patch(
matplotlib.patches.Ellipse(
(popt.x, popt.y),
width=popt.sigma_x * 3,
height=popt.sigma_y * 3,
angle=popt.angle,
color="red",
fill=False,
)
)
axzoom[idx].set_title(
"fit: {:.1f} + {:.1f}\n"
"est: {:.1f} + {:.1f}".format(
popt.amp, popt.offset, np.max(local_arr), np.min(local_arr)
)
)
except IndexError:
pass
# Predicted
aximg.add_patch(
plt.Circle((predx, predy), 4, linewidth=2, fill=False, color="red")
)
idx += 1
plt.tight_layout()
plt.show()
导入scipy.optimize作为选项
将numpy作为np导入
将matplotlib.pyplot作为plt导入
导入skimage.feature
从集合导入namedtuple
导入skimage.io
导入matplotlib.patches
导入撇渣过滤器
进口警告
从scipy.optimize导入优化警告
def缩放阵列(阵列、xy、方形半径):
"""
返回位置处的缩放数组
"""
x、 y=xy
minix=int(最大值(x-平方半径,0))
最小值=整数(最大值(y-平方半径,0))
maxix=int(x+平方半径)
maxiy=int(y+平方半径)
返回数组[miniy:maxiy,minix:maxix]
def gaussian_2d(
xy_阵列、振幅、位置x、位置y、西格玛_x、西格玛_y、角度、偏移
):
"""
具有x和y方差的二维高斯函数的表达式
"""
x、 y=xy_阵列
a=(np.cos(角度)**2)/(2*sigma_x**2)+(np.sin(角度)**2)/(
2*sigma_y**2
)
b=-(np.sin(2*角))/(4*σx**2)+(np.sin(2*角))/(
4*sigma_y**2
)
c=(np.sin(角度)**2)/(2*sigma_x**2)+(np.cos(角度)**2)/(
2*sigma_y**2
)
g=偏移量+振幅*np.exp(
-(
a*((x-位置x)**2)
+2*b*(x-位置x)*(y-位置y)
+c*((y-位置y)**2)
)
)
returng.ravel()
def拟合高斯点(x猜,y猜,阵列):
Params=namedtuple(
“参数”,“放大器,x,y,sigma_x,sigma_y,角度,偏移”
)
初始猜测=参数(
amp=np.最大值(阵列),
我猜,
你猜是吧,
sigma_x=1,
sigma_y=1,
角度=0,
偏移量=np.abs(np.min(数组)),
)
带有警告。捕获警告()
警告。simplefilter(“错误”,优化警告)
尝试:
十、 Y=创建网格(*array.shape)
popt,pcov=最佳曲线拟合(
f=高斯分布,
扩展数据=(X,Y),
ydata=array.ravel(),
p0=初始猜测,
maxfev=200,
method=“lm”
#约束会使它变慢。最好暂停不合适的配合
#界限=(最小界限,最大界限),
)
popt=参数(*np.圆形(popt))
除了(OptimizeWarning、ValueError、RuntimeError):
popt,pcov=无,无
返回波普
def创建网格(h,w):
"""
创建x点和y点的栅格,以便在其上进行拟合和计算
"""
x=np.arange(0,w,1)
y=np.arange(0,h,1)
x、 y=np.meshgrid(x,y)
返回x,y
def评估_高斯(x,y,popt):
"""
在坐标位置计算高斯分布。
注意:这对于提取强度不是必需的,
因为纯信号被拟合为振幅。
"""
z=高斯分布(x,y),*popt)
返回z
def_模拟_数据():
“”“创建数据”“”
img=[]
对于范围内的(N个点):
POSX=np.random.randint(0,W)
POSY=np.random.randint(0,H)
AMP=100
g=高斯分布(
xy_数组=(Xi,Yi),
振幅=安培,
pos_x=POSX,
pos_y=POSY,
sigma_x=2,
sigma_y=2,
角度=0,
偏移量=0,
)
img.append(g)
img=np.和(img,轴=0)
img=img.重塑(H,W)
img=img+np.random.normal(5,5,len(img.ravel())。重塑(img.shape)
返回img
如果名称=“\uuuuu main\uuuuuuuu”:
#创建x和y索引
np.随机种子(4)
绘图行数=3
PLOT_COLS=3
H、 W=200400
N_点=50
席,Y= =创建网格(H=H,W=W)
img=_模拟_数据()
#检测大量斑点以确保安全
斑点=撇毛。特征。峰值\局部\最大值(img,峰值数=300,最小距离=5)
figimg,aximg=plt.subplot()
aximg.imshow(
IMG,Orth=“底部”,Exp=(席)(席),X.Max(),YI. min(),I.
)
图空间,axzoom=plt.子图(nrows=PLOT\u行,ncols=PLOT\u列)
axzoom=axzoom.ravel()
缩放比例=6
#skimage返回行、列(y,x),而matplotlib在(x,y)中操作
idx=0
对于猜测,在点中猜测X:
#在完整图像上绘图
#首字母
aximg.add_补丁(
小圆圈(
(猜,猜),
3.
线宽=0.5,
填充=假,
α=0.5,
color=“黄色”,
)
)
#合身
局部arr=缩放数组(img,(猜X,猜Y),正方形半径=缩放中心)
popt,pcov=拟合高斯点(
x_guess=zoom_ctr,y_guess=zoom_ctr,array=local_arr
)
#扔掉不合身的衣服
如果popt为None或popt.sigma_x<1或popt.sigma_y<1:
持续
predx=猜测x+弹出x-缩放
predy=猜测+弹出y-缩放
#在每个缩放上绘图
#预测
尝试:
axzoom[idx].imshow(local\u arr,origin=“bottom”)
axzoom[idx]。添加修补程序(
matplotlib.patches.Ellipse(
(popt.x,popt.y),
宽度=popt.sigma_x*3,
高度=popt.sigma_y*3,
角度=popt.angle,
color=“红色”,