显微镜图像去晕影:无监督提取水平化剖面的技术求助
问题背景
我需要处理显微镜采集的带垂直合并(vertical binning)的图像,以获取明暗区域间的距离。初始图像如下:
我已定义threshold函数,完成图像旋转、圆形视场裁剪、垂直合并,并尝试减去抛物线剖面:
def threshold(image, X0Y0R): ''' crop the image along the circle of the ocular (step1) vertical binning of image, and subtract parabolic background (step2), resulting in sub_binn X0Y0R is the (X0,Y0,R) tuple, with circle center and radius returns: ((np.max(sub_binn)-np.min(sub_binn))/2)+np.min(sub_binn) -> sub_binn's avg gray level image -> the rotated, circle-cropped image binn -> 1D array vert binning, previous to parabolic subtraction sub_binn -> 1D array used by image_analisys() ''' image = rotate(image, tilt, reshape=True) ######## -->> step1 X0,Y0,R = X0Y0R[0],X0Y0R[1],X0Y0R[2] Y, X = np.ogrid[:image.shape[0], :image.shape[1]] outer_disk_mask = (X - X0)**2 + (Y - Y0)**2 > R**2 image[outer_disk_mask] = False ######## -->> step2 binn = [] for i in range(len(image[0])): binn = np.append(binn, np.sum(np.transpose(image)[i])) binn = binn[np.argwhere(binn>0)] x_n = np.arange(len(binn)) par = np.polyfit(range(len(binn)), binn, 2) sub_binn = [] for i in range(len(binn)): sub_binn = np.append(sub_binn, binn[[i]]-(x_n[i]*par[1]+par[0]*(x_n[i]*x_n[i]))) return ((np.max(sub_binn)-np.min(sub_binn))/2)+np.min(sub_binn),image, binn, sub_binn
运行以下代码查看结果:
image = img.imread('image.png').astype(np.int32) print(image.shape) X0,Y0,R = 950,700,550 it=threshold(image,X0Y0R=(X0,Y0,R)) fig,ax=plt.subplots(1,2,figsize=(12,4)) plt.subplot(121) plt.imshow(it[1], interpolation='none',cmap="gray") plt.subplot(122) plt.plot(it[2],color='gray',label='vertical binning') plt.plot(it[3],color='black',label='after parab subtract') plt.hlines(y=it[0], color='red',xmin=0,xmax=1000) print(it[1].shape, 2*R) plt.legend() plt.show()
但抛物线拟合始终不准确,比如本次结果中拟合值被高估:
由于需要适配不同图像,请问是否存在无监督的方法来提取近似水平的剖面?
无监督替代方案
针对抛物线拟合不准确的问题,以下几种无监督方法可以更鲁棒地提取背景剖面,适配不同图像:
1. 鲁棒多项式拟合(排除异常值)
普通polyfit对明暗区域的异常值敏感,改用鲁棒回归拟合抛物线,自动忽略偏离较大的点:
from sklearn.linear_model import RANSACRegressor from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline # 替换原代码中的polyfit部分 x = np.arange(len(binn)).reshape(-1,1) y = binn.reshape(-1,1) # 构建鲁棒二次拟合管道 model = make_pipeline(PolynomialFeatures(2), RANSACRegressor()) model.fit(x, y) par_fit = model.predict(x).flatten() sub_binn = binn - par_fit
2. 滚动窗口中位数背景估计
用滑动窗口的中位数作为背景,适合非严格抛物线的缓慢变化背景,对极值抗性强:
import scipy.ndimage as ndimage # 窗口大小根据图像宽度调整(比如100,需覆盖背景区域而非明暗条纹) window_size = 100 background = ndimage.median_filter(binn, size=window_size) sub_binn = binn - background
3. 形态学开运算提取背景
形态学开运算能保留缓慢变化的背景,去除亮/暗细节区域,无需假设背景的数学形态:
from skimage.morphology import opening, disk # 结构元素大小根据明暗条纹宽度调整 selem = disk(50) # 转换为2D数组适配skimage函数,再转回1D binn_2d = binn.reshape(1, -1) background = opening(binn_2d, selem).flatten() sub_binn = binn - background
4. 分位数回归拟合背景
用中位数分位数回归拟合二次曲线,聚焦数据中间趋势,不受极值干扰:
import pandas as pd import statsmodels.api as sm from statsmodels.formula.api import quantreg # 构造数据框 df = pd.DataFrame({'x': np.arange(len(binn)), 'y': binn}) # 中位数回归(分位数q=0.5) model = quantreg('y ~ x + I(x**2)', df) res = model.fit(q=0.5) par_fit = res.predict(df) sub_binn = binn - par_fit.values
方法选择建议
- 若背景接近抛物线但受极值干扰:选鲁棒多项式拟合或分位数回归
- 若背景无明确数学形态:选滚动中位数或形态学开运算,完全由数据驱动,适配复杂场景
内容的提问来源于stack exchange,提问作者alessandro
相关产品推荐
相关产品推荐

