Gab*_*iel 17 python numpy image-processing matplotlib
我试图python
尽可能接近返回图像中最明显聚类的中心,如下图所示:
在我之前的问题中,我问过如何获得二维数组的全局最大值和局部最大值,并且给出的答案完美无缺.问题在于我可以通过平均使用不同的bin大小获得的全局最大值得到的中心估计总是稍微偏离我用眼睛设置的那个,因为我只考虑最大的bin而不是一组最大的bin (就像一个人的眼睛).
我尝试将这个问题的答案适应我的问题,但事实证明我的图像太嘈杂,无法使算法工作.这是我实现该答案的代码:
import numpy as np
from scipy.ndimage.filters import maximum_filter
from scipy.ndimage.morphology import generate_binary_structure, binary_erosion
import matplotlib.pyplot as pp
from os import getcwd
from os.path import join, realpath, dirname
# Save path to dir where this code exists.
mypath = realpath(join(getcwd(), dirname(__file__)))
myfile = 'data_file.dat'
x, y = np.loadtxt(join(mypath,myfile), usecols=(1, 2), unpack=True)
xmin, xmax = min(x), max(x)
ymin, ymax = min(y), max(y)
rang = [[xmin, xmax], [ymin, ymax]]
paws = []
for d_b in range(25, 110, 25):
# Number of bins in x,y given the bin width 'd_b'
binsxy = [int((xmax - xmin) / d_b), int((ymax - ymin) / d_b)]
H, xedges, yedges = np.histogram2d(x, y, range=rang, bins=binsxy)
paws.append(H)
def detect_peaks(image):
"""
Takes an image and detect the peaks usingthe local maximum filter.
Returns a boolean mask of the peaks (i.e. 1 when
the pixel's value is the neighborhood maximum, 0 otherwise)
"""
# define an 8-connected neighborhood
neighborhood = generate_binary_structure(2,2)
#apply the local maximum filter; all pixel of maximal value
#in their neighborhood are set to 1
local_max = maximum_filter(image, footprint=neighborhood)==image
#local_max is a mask that contains the peaks we are
#looking for, but also the background.
#In order to isolate the peaks we must remove the background from the mask.
#we create the mask of the background
background = (image==0)
#a little technicality: we must erode the background in order to
#successfully subtract it form local_max, otherwise a line will
#appear along the background border (artifact of the local maximum filter)
eroded_background = binary_erosion(background, structure=neighborhood, border_value=1)
#we obtain the final mask, containing only peaks,
#by removing the background from the local_max mask
detected_peaks = local_max - eroded_background
return detected_peaks
#applying the detection and plotting results
for i, paw in enumerate(paws):
detected_peaks = detect_peaks(paw)
pp.subplot(4,2,(2*i+1))
pp.imshow(paw)
pp.subplot(4,2,(2*i+2) )
pp.imshow(detected_peaks)
pp.show()
Run Code Online (Sandbox Code Playgroud)
这是结果(改变箱子大小):
很明显,我的背景对于该算法来说太吵了,所以问题是:如何使该算法不那么敏感?如果存在替代解决方案,请告诉我.
在Bi Rico建议之后,我尝试平滑我的2d数组,然后将其传递给本地最大查找器,如下所示:
H, xedges, yedges = np.histogram2d(x, y, range=rang, bins=binsxy)
H1 = gaussian_filter(H, 2, mode='nearest')
paws.append(H1)
Run Code Online (Sandbox Code Playgroud)
这些是sigma
2,4和8的结果:
A mode ='constant'
似乎效果要好得多nearest
.它收敛到正确的中心,sigma=2
最大的bin大小:
那么,我如何获得最后一张图片中显示的最大坐标?
回答问题的最后一部分,图像中总是有点,您可以通过按某种顺序搜索图像的局部最大值来找到它们的坐标。如果您的数据不是点源,您可以对每个峰值应用掩模,以避免在执行未来搜索时峰值邻域达到最大值。我建议使用以下代码:
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import numpy as np
import copy
def get_std(image):
return np.std(image)
def get_max(image,sigma,alpha=20,size=10):
i_out = []
j_out = []
image_temp = copy.deepcopy(image)
while True:
k = np.argmax(image_temp)
j,i = np.unravel_index(k, image_temp.shape)
if(image_temp[j,i] >= alpha*sigma):
i_out.append(i)
j_out.append(j)
x = np.arange(i-size, i+size)
y = np.arange(j-size, j+size)
xv,yv = np.meshgrid(x,y)
image_temp[yv.clip(0,image_temp.shape[0]-1),
xv.clip(0,image_temp.shape[1]-1) ] = 0
print xv
else:
break
return i_out,j_out
#reading the image
image = mpimg.imread('ggd4.jpg')
#computing the standard deviation of the image
sigma = get_std(image)
#getting the peaks
i,j = get_max(image[:,:,0],sigma, alpha=10, size=10)
#let's see the results
plt.imshow(image, origin='lower')
plt.plot(i,j,'ro', markersize=10, alpha=0.5)
plt.show()
Run Code Online (Sandbox Code Playgroud)
用于测试的图像ggd4可以从以下位置下载:
http://www.ipac.caltech.edu/2mass/gallery/spr99/ggd4.jpg
第一部分是获取有关图像中噪声的一些信息。我通过计算整个图像的标准偏差来做到这一点(实际上最好选择一个没有信号的小矩形)。这告诉我们图像中存在多少噪声。获得峰值的想法是要求连续的最大值,这些最大值高于某个阈值(比方说,噪声的 3、4、5、10 或 20 倍)。这就是函数 get_max 实际所做的事情。它执行最大值搜索,直到其中之一低于噪声施加的阈值。为了避免多次找到相同的最大值,有必要从图像中删除峰值。一般来说,掩模的形状很大程度上取决于想要解决的问题。对于星星的情况,最好使用高斯函数或类似的函数来删除星星。为简单起见,我选择了平方函数,函数的大小(以像素为单位)是变量“大小”。我认为从这个例子来看,任何人都可以通过添加更通用的东西来改进代码。
编辑:
原始图像如下所示:
识别出发光点后的图像如下所示:
我添加这个答案是因为这是我最终使用的解决方案。这是 Bi Rico 的评论(5 月 30 日 18:54)和这个问题中给出的答案的组合:Findpeak of 2d histogram。
事实证明,使用此问题中的峰值检测算法2D 阵列中的峰值检测只会使问题变得复杂。将高斯滤波器应用于图像后,需要做的就是询问最大 bin(正如 Bi Rico 指出的那样),然后获得坐标中的最大值。
因此,我没有像上面那样使用检测峰值函数,而是在获得高斯二维直方图后添加以下代码:
# Get 2D histogram.
H, xedges, yedges = np.histogram2d(x, y, range=rang, bins=binsxy)
# Get Gaussian filtered 2D histogram.
H1 = gaussian_filter(H, 2, mode='nearest')
# Get center of maximum in bin coordinates.
x_cent_bin, y_cent_bin = np.unravel_index(H1.argmax(), H1.shape)
# Get center in x,y coordinates.
x_cent_coor , y_cent_coord = np.average(xedges[x_cent_bin:x_cent_bin + 2]), np.average(yedges[y_cent_g:y_cent_g + 2])
Run Code Online (Sandbox Code Playgroud)