# A peak is a Dirac impulse, in the positive or negative. In other words, a peak is an isolated pixel with a wildly different value compared to its neighbours.
# The basic idea is to look at the 4-neighbourhood of each pixel, and if its value differs from the average of its (defined) neighbours by more than a given threshold diffth, then it's considered a peak and set at the neighbour's average.
# Note: I had to write a custom function because peaks can arise next to or completely around undefined pixels (tried setting undefined to constant value beforehand, tried setting to inf so that median will work, but always an edge case made those inappropriate).
defremovePeaks(I,diffth=10):
# Considering the image as binary (the background being the undefined pixels), start with handling the small CCs (connected components), of size 2 or less.
A=np.pad(I,1,constant_values=np.nan)
M=~np.isnan(A)
M1=remove_small_objects(M,2)
nbIslands=(M!=M1).sum()# Number of CCs of size 1
M2=remove_small_objects(M1,3)
M2_=(M2!=M1)# Only the CCs of size 2
nbCC2=M2_.sum()/2# Number of CCs of size 2
B=np.where(M==M1,A,np.nan)# Removing CCs of size 1. It could be a peak or not, but no way of being sure.
# The size 2 ones are handled in the main loop.
B_=B.copy()
allPeaks={}
nbPeaksCC2=0
forjinrange(1,B.shape[0]-1):
foriinrange(1,B.shape[1]-1):
ifnotnp.isnan(B[j,i]):
nb=np.full(4,np.nan)
nb[0]=B[j-1,i]
nb[1]=B[j+1,i]
nb[2]=B[j,i-1]
nb[3]=B[j,i+1]
ifabs(B[j,i]-np.nanmean(nb))>diffth:# Since there must be at least one defined neighbour (CCs of size 1 were removed), nanmean(nb) is always a number
ifM2_[j-1,i-1]:# It's part of a CC of size 2, and its value differs from its (one) neighbour by more than diffth, but no way of knowing which one is a peak, so remove both.
# With a very high peak, it and its neighbours are identified as peaks (the neighbour of a peak will see it's average neighbourhood value influenced by the peak, inducing a large difference).
# If you start with the one with the largest difference, i.e. the peak, then it will be flattened so when you recheck its neighbours, their respective difference from their average neighbourdhood also flattens out.
(j,i)=np.unravel_index(peak[0],B.shape)
nb=np.full(4,np.nan)
nb[0]=B[j-1,i]
nb[1]=B[j+1,i]
nb[2]=B[j,i-1]
nb[3]=B[j,i+1]
ifabs(B[j,i]-np.nanmean(nb))>diffth:
B[j,i]=np.nanmean(nb)
nbPeaks=nbPeaks+1
print("Removed "+str(int(nbPeaks+nbPeaksCC2))+" peaks, "+str(nbIslands)+" islands, and "+str(int(nbPeaksCC2))+" non-peaks.")
returnB[1:-1,1:-1]
# All undefined values are NaN, replace those with a specified method.
deffill_novals(fld,noval="inpaint",cval=0.0):
novals={"average","constant","inpaint","interpolate"}# Possible methods
noval_=noval.lower()
ifnoval_notinnovals:
print("Error: fill method \""+noval+"\" not in list.")
elifnoval_=="average":
np.nan_to_num(fld,copy=False,nan=np.nanmean(fld))
elifnoval_=="constant":
np.nan_to_num(fld,copy=False,nan=cval)
elifnoval_=="inpaint":
fld=inpaint.inpaint_biharmonic(fld,np.isnan(fld))
elifnoval=="interpolate":
x,y=np.indices(fld.shape)
# Will only interpolate within the convex hull of the known values. All other at the border are still nan