Python 计算径向轮廓的最有效方法

Python 计算径向轮廓的最有效方法,python,numpy,image-processing,optimization,scipy,Python,Numpy,Image Processing,Optimization,Scipy,我需要优化图像处理应用程序的这一部分。 它基本上是像素与中心点的距离的总和 def radial_profile(data, center): y,x = np.indices((data.shape)) # first determine radii of all pixels r = np.sqrt((x-center[0])**2+(y-center[1])**2) ind = np.argsort(r.flat) # get sorted indices

我需要优化图像处理应用程序的这一部分。
它基本上是像素与中心点的距离的总和

def radial_profile(data, center):
    y,x = np.indices((data.shape)) # first determine radii of all pixels
    r = np.sqrt((x-center[0])**2+(y-center[1])**2)
    ind = np.argsort(r.flat) # get sorted indices
    sr = r.flat[ind] # sorted radii
    sim = data.flat[ind] # image values sorted by radii
    ri = sr.astype(np.int32) # integer part of radii (bin size = 1)
    # determining distance between changes
    deltar = ri[1:] - ri[:-1] # assume all radii represented
    rind = np.where(deltar)[0] # location of changed radius
    nr = rind[1:] - rind[:-1] # number in radius bin
    csim = np.cumsum(sim, dtype=np.float64) # cumulative sum to figure out sums for each radii bin
    tbin = csim[rind[1:]] - csim[rind[:-1]] # sum for image values in radius bins
    radialprofile = tbin/nr # the answer
    return radialprofile

img = plt.imread('crop.tif', 0)
# center, radi = find_centroid(img)
center, radi = (509, 546), 55
rad = radial_profile(img, center)

plt.plot(rad[radi:])
plt.show()
输入图像:

径向剖面图:

通过提取结果图的峰值,我可以准确地找到外圈的半径,这是本文的最终目标

编辑:为了进一步参考,我将发布我的最终解决方案。使用cython我得到了比公认答案快15-20倍的速度

import numpy as np
cimport numpy as np
cimport cython
from cython.parallel import prange
from libc.math cimport sqrt, ceil


DTYPE_IMG = np.uint8
ctypedef np.uint8_t DTYPE_IMG_t
DTYPE = np.int
ctypedef np.int_t DTYPE_t


@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
cdef void cython_radial_profile(DTYPE_IMG_t [:, :] img_view, DTYPE_t [:] r_profile_view, int xs, int ys, int x0, int y0) nogil:

    cdef int x, y, r, tmp

    for x in prange(xs):
        for y in range(ys):
            r =<int>(sqrt((x - x0)**2 + (y - y0)**2))
            tmp = img_view[x, y]
            r_profile_view[r] +=  tmp 


@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def radial_profile(np.ndarray img, int centerX, int centerY):


    cdef int xs, ys, r_max
    xs, ys = img.shape[0], img.shape[1]

    cdef int topLeft, topRight, botLeft, botRight

    topLeft = <int> ceil(sqrt(centerX**2 + centerY**2))
    topRight = <int> ceil(sqrt((xs - centerX)**2 + (centerY)**2))
    botLeft = <int> ceil(sqrt(centerX**2 + (ys-centerY)**2))
    botRight = <int> ceil(sqrt((xs-centerX)**2 + (ys-centerY)**2))

    r_max = max(topLeft, topRight, botLeft, botRight)

    cdef np.ndarray[DTYPE_t, ndim=1] r_profile = np.zeros([r_max], dtype=DTYPE)
    cdef DTYPE_t [:] r_profile_view = r_profile
    cdef DTYPE_IMG_t [:, :] img_view = img

    with nogil:
        cython_radial_profile(img_view, r_profile_view, xs, ys, centerX, centerY)
    return r_profile
将numpy导入为np
cimport numpy作为np
西姆波特赛顿酒店
来自cython.平行进口prange
来自libc.math cimport sqrt,ceil
DTYPE_IMG=np.uint8
ctypedef np.uint8\u t数据类型\u IMG\t
DTYPE=np.int
ctypedef np.int\u t数据类型
@cython.boundscheck(错误)
@cython.wrapparound(假)
@cython.nonecheck(错误)
cdef void cython_radial_profile(DTYPE_IMG_t[:,:]IMG_view,DTYPE_t[:]r_profile_view,int xs,int ys,int x0,int y0)图:
cdef int x,y,r,tmp
对于prange中的x(xs):
对于范围内的y(ys):
r=(sqrt((x-x0)**2+(y-y0)**2))
tmp=img_视图[x,y]
r_纵断面图[r]+=tmp
@cython.boundscheck(错误)
@cython.wrapparound(假)
@cython.nonecheck(错误)
def径向剖面图(np.ndarray img、int centerX、int centerY):
cdef int xs,ys,r_max
xs,ys=img.shape[0],img.shape[1]
cdef int左上、右上、左下、右下
左上=天花板(sqrt(中心X**2+中心Y**2))
topRight=ceil(sqrt((xs-centerX)**2+(centerY)**2))
底部左侧=天花板(sqrt(中心X**2+(中心Y)**2))
botRight=ceil(sqrt((xs centerX)**2+(ys centerY)**2))
r_max=max(左上、右上、左下、右下)
cdef np.ndarray[DTYPE_t,ndim=1]r_profile=np.zeros([r_max],DTYPE=DTYPE)
cdef数据类型\u t[:]r\u纵断面图\u视图=r\u纵断面图
cdef DTYPE_IMG_t[:,:]IMG_view=IMG
诺吉尔:
cython_径向_剖面(img_视图、r_剖面视图、xs、ys、centerX、centerY)
返回r_配置文件

看起来您可以在这里使用:


可以使用numpy.histogram将给定“环”(距离原点的r值范围)中显示的所有像素相加。每个环都是一个直方图箱。您可以根据环的宽度选择存储箱的数量。(在这里,我发现3个像素宽的环可以很好地使绘图不太嘈杂。)


(顺便说一句,如果这真的需要提高效率,并且有很多相同大小的图像,那么调用np.histogram之前的所有内容都可以预先计算。)

摘自我正在处理的numpy增强方案:

pp.plot(*group_by(np.round(R, 5).flatten()).mean(data.flatten()))
调用mean返回R中的唯一值,以及数据中对应值相对于R中相同值的平均值

因此与基于直方图的解决方案不太一样;您不必重新映射到新栅格,如果您希望拟合径向轮廓,而不丢失信息,这很好。性能应该比原始解决方案略好。此外,标准偏差可以用同样的效率计算

这是我的最新草稿;这不是一个非常简洁的答案,而是一个非常笼统的答案。我希望我们都能同意numpy需要类似np.group_by(键)、reduce(值);如果您有任何反馈,我们将非常欢迎。

中有一个函数可以实现以下功能:。您可以以类似于OP的
径向轮廓
功能的方式使用它:

import PyDIP as dip

img = dip.ImageReadTIFF('crop.tif')
# center, radi = find_centroid(img)
center, radi = (509, 546), 55
rad = dip.RadialMean(img, binSize=1, center=center)

rad[radi:].Show()

免责声明:我是PyDIP和DIPlib库的作者。

很好的解决方案,大约快了3倍。不过,在我接受这一点之前,我会稍等片刻,不知道人们能想出什么。我在这个实现中使用了bincount和histogram:
pp.plot(*group_by(np.round(R, 5).flatten()).mean(data.flatten()))
import PyDIP as dip

img = dip.ImageReadTIFF('crop.tif')
# center, radi = find_centroid(img)
center, radi = (509, 546), 55
rad = dip.RadialMean(img, binSize=1, center=center)

rad[radi:].Show()