Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
96 views
in Technique[技术] by (71.8m points)

python - Efficient adaptative (row dependant) dilation of a numpy array

Let's say I have a 2D array reprezenting equally-spaced (in °) lat/lon points, the "top" of the array being the North, the left being the West, etc. For instance with North = 55.4°, South = 37.5°, West = -12°, East = 16°, and resolution = 0.025°, the shape of the 2D array will be (717, 1121).

Now, considering some points of this 2D array, I would like to expend/dilate their value through a circular neighborhood in km, but since the distance in km between two lat/lon points depends on the latitude, the "array reprezentation" of the neighborhood will more be an ellipse than a disk, and beside will depend on the row of the 2D array. Indeed, the more we get closer to latitudes +/-90°, the more we'll need points to cross an East - West distance in km.

To perform that, I start by "binarizing" my 2D array, affecting the value "1" to the points where I want to apply the neighborhood. Once done I loop over the different rows (eg the latitudes), compute the "array reprezentation" of the circular neighborhood, then use it with function binary_dilation() from scipy.ndimage through argument 'structure' to perform the dilation. Here is my code:

import numpy as np
from geopy.distance import distance
from scipy import ndimage

def circular_mask(r):
    mask = np.zeros((2*r+1, 2*r+1))
    y,x = np.ogrid[-r:r+1, -r:r+1]
    disk = x**2 + y**2 <= r**2
    mask[disk] = 1
    return(mask)
def elliptic_mask(a,b):
    #NB: 'a' is the semi-minor axis, 'b' the semi-major axis
    mask = np.zeros((2*a+1, 2*b+1))
    y,x = np.ogrid[-a:a+1, -b:b+1]
    ellipse = (x/b)**2 + (y/a)**2 <= 1
    mask[ellipse] = 1
    return(mask)

North = 55.4
South = 37.5
West = -12.
East = 16.
Resol = 0.025
lon = West  #random longitude (the distance in km between tow lat/lon points depends only on the lat)
radius = 30.  #(radius in km for circular neighborhood)

latitudes = np.arange(South, North + Resol/2., Resol)  #list of different latitudes
array = np.random.randint(20, size = (717,1121))  #((North-South)/Resol)+1, ((East-West)/Resol)+1
array_01 = np.where(array >= 15, 1, 0)  #[binarization] dilate points >= 15
array_01_dilated = array_01.copy()  #will be filled during the loop

idx_row = array.shape[0]
for lat in latitudes:
    idx_row = idx_row + 1  #first latitude corresponds to South = the bottom of the array

    EW_resol_to_km = distance((lat, lon), (lat, lon + lon_resol)).km #number of km between two points side by side on latitude 'lat'
    EW_nb_resol_to_reach_radius = radius/EW_resol_to_km #number of side by side points on latitude 'lat' needed to reach distance 'radius'
    EW_nb_points = int(round(EW_nb_resol_to_reach_radius)) #convert into integer
    NS_resol_to_km = distance((lat, lon), (lat + lat_resol, lon)).km #number of km between two points side by side on longitude 'lon'
    NS_nb_resol_to_reach_radius = radius/NS_resol_to_km #number of side by side points on longitude 'lon' needed to reach distance 'radius'
    NS_nb_points = int(round(NS_nb_resol_to_reach_radius))

    if (NS_nb_points != EW_nb_points):
        ellipse = elliptic_mask(a = min(NS_nb_points,EW_nb_points), b = max(NS_nb_points,EW_nb_points)) #array reprezentation of circular neighborhood (perfectible)
    else:
        ellipse = circular_mask(radius = NS_nb_points)

    neighborhood_band = np.zeros((ellipse.shape[0], array.shape[1]))
    neighborhood_band[NS_nb_points + 1, :] = array_01[idx_row, :]
    neighborhood_band = ndimage.binary_dilation(neighborhood_band, structure = ellipse) #corresponds to the latitude 'lat' and the nearest ones modified due to dilation

    # fill 'array_01_dilated', taking into account possible borderies overtakings #
    idx_row_top_band = idx_row - NS_nb_points
    idx_row_bottom_band = idx_row + NS_nb_points
    if (idx_row_top_band < 0):
        neighborhood_band = neighborhood_band[(-idx_row_top_band):,:]
        idx_row_top_band = 0
    if (idx_row_bottom_band > array.shape[0]-1):
        neighborhood_band = neighborhood_band[:-(idx_row_bottom_band - (array.shape[0]-1)),:]
        idx_row_bottom_band = array.shape[0]-1
    array_01_dilated[idx_row_top_band:(idx_row_bottom_band + 1),:] = array_01_dilated[idx_row_top_band:(idx_row_bottom_band + 1),:] + neighborhood_band

array_binarized_neighborhood[array_binarized_neighborhood >= 1] = 1.  #for possible "dilation coverings"

This method being not very fast, I would like to know if there is another way to solve this problem (using scipy.ndimage.binary_dilation() with a row depending structure? a vectorized approach? ...).

Thank you very much !

question from:https://stackoverflow.com/questions/66045372/efficient-adaptative-row-dependant-dilation-of-a-numpy-array

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...