Source code for MLV_toolbox.applyCircularAperture
from .VecLD import VecLD
import numpy as np
[docs]def applyCircularAperture(vecLD: VecLD, radius = None):
"""
Apply circular aperture to the vector line-drawing.
Args:
radius (float): Radius of the circular aperture.
"""
if not isinstance(vecLD, VecLD):
raise TypeError('The second argument must be of type VecLD.')
if radius is None:
radius = np.min(vecLD.imsize) / 2
maskedLD = VecLD(
originalImage = vecLD.originalImage,
imsize = vecLD.imsize,
lineMethod = vecLD.lineMethod,
numContours = 0,
contours = np.empty((0, 4))
)
center = vecLD.imsize / 2
for c in range(vecLD.numContours):
A = vecLD.contours[c][:, :2]
B = vecLD.contours[c][:, 2:4]
rA = np.sqrt(np.sum((A - center) ** 2, axis=1))
rB = np.sqrt(np.sum((B - center) ** 2, axis=1))
prevInside = (rA[0] <= radius)
currContour = np.empty((0, 4))
for s in range(vecLD.contours[c].shape[0]):
currInside = (rB[s] <= radius)
# if end points are on different sides, compute the intersection point with the circle
if (currInside ^ prevInside): # currInside XOR prevInside
# length of this segment
d = np.sqrt(np.sum((B[s,:] - A[s,:])**2))
# solve the quadratic equation
p = -d - (rA[s]**2 - rB[s]**2) / d
q = rA[s]**2 - radius**2
QQ = np.sqrt((p/2)**2 - q)
dA1 = -p/2 + QQ
dA2 = -p/2 - QQ
# make sure we pick the right solution
dA1valid = (0 <= dA1) & (dA1 <= d)
dA2valid = (0 <= dA2) & (dA2 <= d)
if dA1valid:
dA = dA1
if dA2valid:
raise ValueError("Two valid solution - don't know which one to pick.")
elif dA2valid:
dA = dA2
else:
raise ValueError("No valid solution - don't know what to do.")
C = A[s,:] + dA/d * (B[s,:]-A[s,:])
# consider all 4 cases
if prevInside:
if currInside:
# we are completely inside the circle - just keep the segment
currContour = np.vstack((currContour, vecLD.contours[c][s,:]))
else:
# going from inside to outside the circle
# break the segment and terminate this contour
currContour = np.vstack((currContour, np.hstack((A[s,:], C))))
maskedLD.numContours += 1
maskedLD.contours = np.concatenate((maskedLD.contours, [currContour]))
currContour = np.empty((0, 4))
else:
if currInside:
# going from outside to inside
# break the segment and start a new contour
currContour = np.vstack((C, B[s,:]))
maskedLD.numContours += 1
maskedLD.contours = np.concatenate((maskedLD.contours, [currContour]))
currContour = np.empty((0, 4))
else:
# completely outside - do nothing
pass
prevInside = currInside
# save the contour if it is non-empty
if currContour.size != 0:
maskedLD.numContours += 1
maskedLD.contours = np.concatenate((maskedLD.contours, [currContour]))
return maskedLD
setattr(VecLD, 'applyCircularAperture', applyCircularAperture)