You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

显微镜图像去晕影:无监督提取水平化剖面的技术求助

问题背景

我需要处理显微镜采集的带垂直合并(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 05:19:55