我正试图让python
尽可能接近图像中最明显的聚类的中心,如下所示:
在我的previous question中,我问如何获得二维数组的全局最大值和局部最大值,并且给出的答案完美无缺。问题在于我可以通过平均使用不同的bin大小获得的全局最大值来获得的中心估计总是略微偏离我将通过eye 设置的那个,因为我只考虑了最大的< strong> bin 而不是组最大的垃圾箱(就像一个眼睛一样)。
我尝试将answer to this question调整到我的问题,但事实证明我的图像太嘈杂让该算法正常工作。这是我实现答案的代码:
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()
,这是结果(改变箱子大小):
显然我的背景噪音太大,无法运行,因此问题是:如何使该算法不那么敏感??如果存在替代解决方案,请告诉我。
在Bi Rico建议之后,我尝试平滑我的2d数组,然后将其传递给本地最大查找器,如下所示:
H, xedges, yedges = np.histogram2d(x, y, range=rang, bins=binsxy)
H1 = gaussian_filter(H, 2, mode='nearest')
paws.append(H1)
这些是sigma
为2,4和8的结果:
mode ='constant'
似乎比nearest
好得多。它收敛到正确的中心,最大的bin大小为sigma=2
:
那么,如何获得最后一张图片中显示的最大坐标?
答案 0 :(得分:4)
在回答问题的最后部分时,您总是在图像中有点,您可以通过按某种顺序搜索图像的局部最大值来找到它们的坐标。如果您的数据不是点源,您可以对每个峰值应用掩码,以避免峰值邻域在执行将来搜索时达到最大值。我提出以下代码:
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()
测试的图像ggd4可以从以下网址下载:
http://www.ipac.caltech.edu/2mass/gallery/spr99/ggd4.jpg
第一部分是获取有关图像中噪声的一些信息。我通过计算完整图像的标准偏差来做到这一点(实际上最好选择一个没有信号的小矩形)。这告诉我们图像中存在多少噪音。 获得峰值的想法是要求连续的最大值,其高于某个阈值(比如噪声的3,4,5,10或20倍)。这就是函数get_max实际上正在做的事情。它执行最大值搜索,直到其中一个低于噪声施加的阈值。为了避免多次找到相同的最大值,需要从图像中去除峰值。一般来说,这样做的面具形状很大程度上取决于人们想要解决的问题。对于恒星的情况,应该通过使用高斯函数或类似的东西去除恒星。为简单起见,我选择了一个平方函数,函数的大小(以像素为单位)是变量“size”。 我认为,从这个例子中,任何人都可以通过添加更多一般内容来改进代码。
编辑:
原始图片如下:
识别发光点后的图像如下所示:
答案 1 :(得分:4)
Stack Overflow上有太多的n00b来评论Alejandro在其他地方的答案。我会稍微改进他的代码以使用预分配的numpy数组进行输出:
def get_max(image,sigma,alpha=3,size=10):
from copy import deepcopy
import numpy as np
# preallocate a lot of peak storage
k_arr = np.zeros((10000,2))
image_temp = deepcopy(image)
peak_ct=0
while True:
k = np.argmax(image_temp)
j,i = np.unravel_index(k, image_temp.shape)
if(image_temp[j,i] >= alpha*sigma):
k_arr[peak_ct]=[j,i]
# this is the part that masks already-found peaks.
x = np.arange(i-size, i+size)
y = np.arange(j-size, j+size)
xv,yv = np.meshgrid(x,y)
# the clip here handles edge cases where the peak is near the
# image edge
image_temp[yv.clip(0,image_temp.shape[0]-1),
xv.clip(0,image_temp.shape[1]-1) ] = 0
peak_ct+=1
else:
break
# trim the output for only what we've actually found
return k_arr[:peak_ct]
在使用他的示例图像分析这个和Alejandro的代码时,这段代码快了大约33%(对于Alejandro的代码为0.03秒,对我来说为0.02秒。)我期待在峰值数量更多的图像上,它会更快 - 附加对于更多峰值,列表的输出会越来越慢。
答案 2 :(得分:2)
我认为这里需要的第一步是用字段的标准偏差表示H中的值:
import numpy as np
H = H / np.std(H)
现在你可以在这个H的值上设置一个阈值。如果假设噪声是高斯噪声,选择一个阈值3,你可以非常肯定(99.7%)这个像素可以与真实峰值相关联不是噪音。请参阅here。
现在可以开始进一步的选择。我不清楚你到底想要找到什么。您想要峰值的确切位置吗?或者您是否想要一个位于该簇中间的峰簇的位置? 无论如何,从这一点开始,所有像素值都以字段的标准偏差表示,你应该能够得到你想要的。如果要查找群集,可以在&gt; 3-sigma网格点上执行最近邻搜索,并在距离上设置阈值。即只有当它们彼此足够接近时才连接它们。如果连接了多个网格点,您可以将其定义为组/集群并计算集群的某些(sigma加权?)中心。 希望我对Stackoverflow的第一个贡献对你有用!
答案 3 :(得分:1)
我会这样做:
1)将H归一化为0到1之间。
2)选择一个阈值,正如tcaswell建议的那样。它可能介于.9和.99之间
3)使用蒙面数组仅保留x,y坐标,其中H高于阈值:
import numpy.ma as ma
x_masked=ma.masked_array(x, mask= H < thresold)
y_masked=ma.masked_array(y, mask= H < thresold)
4)现在你可以对蒙面坐标进行加权平均,重量类似于(H-threshold)^ 2,或任何其他大于或等于1的力量,具体取决于你的品味/测试。
注释: 1)由于您可能需要调整thresold,因此对于您所具有的峰值类型而言这并不强大。这是一个小问题; 2)这不能同时处理两个峰值,如果第二个峰值高于阈值,则会给出错误的结果。
尽管如此,它总会给你一个没有崩溃的答案(有利有弊的东西......)
答案 4 :(得分:1)
我正在添加这个答案,因为这是我最终使用的解决方案。这是Bi Rico在这里(5月30日18:54)的评论和这个问题给出的答案的组合:Find peak of 2d histogram。
事实证明,使用此问题Peak detection in a 2D array中的峰值检测算法只会使问题复杂化。在对图像应用高斯滤波器之后,所有需要做的就是要求最大的bin(如Bi Rico指出的那样),然后获得最大坐标。
所以我没有像上面那样使用 detect-peaks 函数,而是在获得高斯2D直方图后添加以下代码:
# 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])