当前位置:网站首页>[Thesis code] SML part code reading

[Thesis code] SML part code reading

2022-07-06 05:55:00 starbuling~

SML Boundary suppression and Gaussian smoothing in

Boundary smoothing suppression class

class BoundarySuppressionWithSmoothing(nn.Module):
    """ Apply boundary suppression and dilated smoothing  Boundary suppression , Expand and smooth  """

initialization

 def __init__(self, boundary_suppression=True, boundary_width=4, boundary_iteration=4,
                 dilated_smoothing=True, kernel_size=7, dilation=6):

Define some parameters

super(BoundarySuppressionWithSmoothing, self).__init__()
self.kernel_size = kernel_size						#  Convolution kernel size 
self.dilation = dilation                            #  expansion 
self.boundary_suppression = boundary_suppression    #  Boundary suppression 
self.boundary_width = boundary_width               	#  Boundary width 
self.boundary_iteration = boundary_iteration       	#  Boundary iteration 

Create Gaussian kernel

sigma = 1.0
size = 7
# function Is the probability density function of two-dimensional Gaussian distribution 
gaussian_kernel = np.fromfunction(lambda x, y: 
                           (1/(2*math.pi*sigma**2)) * math.e ** ((-1*((x-(size-1)/2)**2+(y-(size-1)/2)**2))/(2*sigma**2)), 
                           (size, size))	#  Construct Gaussian kernel  (7,7) 3 * sigma + 1
gaussian_kernel /= np.sum(gaussian_kernel)	#  Divide by the sum of all elements in the Gaussian kernel ( weighted mean , Avoid image pixel overflow )
gaussian_kernel = torch.Tensor(gaussian_kernel).unsqueeze(0).unsqueeze(0)
self.dilated_smoothing = dilated_smoothing	#  Expand and smooth 

l a m b d a ( x , y ) = 1 2 ∗ π ∗ σ 2 e x p ( − ( x − s i z e − 1 2 ) 2 + ( y − s i z e − 1 2 ) 2 2 ∗ σ 2 ) lambda(x,y) = \frac{1}{2 * \pi * \sigma^2} exp(-\frac{(x - \frac{size-1}{2})^2 + (y - \frac{size-1}{2})^2}{2 * \sigma^2}) lambda(x,y)=2πσ21exp(2σ2(x2size1)2+(y2size1)2)

numpy In the library fromfunction: Through custom functions fun, shape shape, data format dtype -> According to the array subscript (x,y) Generate values for each location , Make an array

  • Function parameter np.fromfunction(function, shape, dtype)

    • function: A function transformed into a specific value according to coordinates

      def  function(x,y):
        	 Internal function 
      (x,y)  Are the coordinates with the upper left corner as the origin ,x For row coordinates ,y Column coordinates , It means the first one x That's ok y Column .
      
    • shape(a,b): Represents an array array Size ,a That's ok b Column .

    • dtype: Represents the number type of the array

Define two-layer convolution (in_channel, out_channel, k, s)

  • (1, 1, 3, 1) The weight matrix is full 1 matrix
  • (1, 1, 7, 1) The weight matrix is Gaussian kernel
self.first_conv = nn.Conv2d(1, 1, kernel_size=3, stride=1, bias=False)
self.first_conv.weight = torch.nn.Parameter(torch.ones_like((self.first_conv.weight)))

self.second_conv = nn.Conv2d(
    1, 1, kernel_size=self.kernel_size, stride=1, dilation=self.dilation, bias=False)
self.second_conv.weight = torch.nn.Parameter(gaussian_kernel)

Forward propagation

def forward(self, x, prediction=None):
if len(x.shape) == 3:	
    x = x.unsqueeze(1)	#  If it is 3 dimension , expand 1 dimension 
x_size = x.size()
# B x 1 x H x W
assert len(x.shape) == 4	
out = x

Branch 1: Need boundary suppression

if self.boundary_suppression:
    # obtain the boundary map of width 2 by default  The default acquisition width is 2 Boundary graph of 
    # this can be calculated by the difference of dilation and erosion  This can be calculated by the difference between expansion and corrosion 
    boundaries = find_boundaries(prediction.unsqueeze(1))	#  Look for boundaries 
    expanded_boundaries = None	
    if self.boundary_iteration != 0:
        assert self.boundary_width % self.boundary_iteration == 0	#  The boundary width is divided by the number of iterations 
        diff = self.boundary_width // self.boundary_iteration		#  Width increased each time 

The main process of boundary suppression

for iteration in range(self.boundary_iteration):
    if len(out.shape) != 4:
        out = out.unsqueeze(1)
    prev_out = out
  1. Get the boundary

    # if it is the last iteration or boundary width is zero  The last iteration or boundary width is 0 after , Stop expanding width 
    if self.boundary_width == 0 or iteration == self.boundary_iteration - 1:
        expansion_width = 0
    # reduce the expansion width for each iteration  Otherwise, the width of the freight station will be continuously increased in each iteration 
    else:
        expansion_width = self.boundary_width - diff * iteration - 1
        # expand the boundary obtained from the prediction (width of 2) by expansion rate
        expanded_boundaries = expand_boundaries(boundaries, r=expansion_width)	#  Expand the boundary according to the expansion width , The specific method is explained in detail in the function later 
    
  2. Reverse the boundary -> Get a non boundary mask

    # invert it so that we can obtain non-boundary mask
    non_boundary_mask = 1. * (expanded_boundaries == 0)	#  Reverse the boundary , Get the non boundary mask . The non boundary is 1, The boundary is 0
    
  3. Make the boundary area to 0

    f_size = 1
    num_pad = f_size
    
    # making boundary regions to 0
    x_masked = out * non_boundary_mask	#  The input image  *  Non boundary mask  ->  Get the non boundary region (1)
    x_padded = nn.ReplicationPad2d(num_pad)(x_masked)
    non_boundary_mask_padded = nn.ReplicationPad2d(num_pad)(non_boundary_mask)
    

    class torch.nn.ReplicationPad2d(padding)

    padding(int ,tuple) The size of the fill . If int , Then use the same fill in all boundaries .
    If it is 4 tuple , Then use (padding_left, padding_right, padding_top, padding_bottom)

  4. Sum the values in the receptive field

    # sum up the values in the receptive field
    y = self.first_conv(x_padded)
    # count non-boundary elements in the receptive field
    num_calced_elements = self.first_conv(non_boundary_mask_padded)
    num_calced_elements = num_calced_elements.long()
    
  5. Averaging

    # take an average by dividing y by count
    # if there is no non-boundary element in the receptive field,
    # keep the original value
    avg_y = torch.where((num_calced_elements == 0), prev_out, y / num_calced_elements)
    out = avg_y
    
  6. Update boundaries

    # update boundaries only
    out = torch.where((non_boundary_mask == 0), out, prev_out)
    del expanded_boundaries, non_boundary_mask
    

The second step : Expand and smooth

# second stage; apply dilated smoothing
if self.dilated_smoothing == True:
    out = nn.ReplicationPad2d(self.dilation * 3)(out)
    out = self.second_conv(out)

return out.squeeze(1)

Branch 1: No boundary suppression is required

else:
    if self.dilated_smoothing == True:	#  Expand and smooth 
        out = nn.ReplicationPad2d(self.dilation * 3)(out)
        out = self.second_conv(out)
    else:
        out = x

return out.squeeze(1)

find_boundaries

def find_boundaries(label):
    """ Calculate boundary mask by getting diff of dilated and eroded prediction maps """
    assert len(label.shape) == 4
    boundaries = (dilation(label.float(), selem_dilation) != erosion(label.float(), selem)).float()
    ### save_image(boundaries, f'boundaries_{boundaries.float().mean():.2f}.png', normalize=True)

    return boundaries
selem = torch.ones((3, 3)).cuda() #  It's a (3,3) Full size 1 Tensor , Corrosion coil nucleation 
selem_dilation = torch.FloatTensor(ndi.generate_binary_structure(2, 1)).cuda() #  Expanded convolution kernel 

corrosion :
image-20220620235535737

inflation :
image-20220620235446032

inflation (dilation) & corrosion (erosion)

These are two basic morphological operations , It is mainly used to find the maximum and minimum regions in the image .

  • Expansion is similar to ‘ Domain expansion ’ , Expand the highlighted area or white part of the image , The operation result graph is larger than the highlighted area of the original graph .
  • Corrosion similar ‘ The field is being eroded ’ , Reduce and refine the highlighted area or white part of the image , The operation result is smaller than the highlighted area of the original image .

The specific process : Define a convolution kernel , Convolute the picture . Expansion do “ or ” operation , expand 1 The scope of the ; Corrosion makes “ And ” operation , Reduce 1 The number of

dilation(image, kernel) # Images , Convolution kernel

erosion(image, kernel)

After expansion and corrosion of the label drawings , Different positions , It's the boundary . use 1 Express

expand_boundaries

def expand_boundaries(boundaries, r=0):
    """ Expand boundary maps with the rate of r """
    if r == 0:
        return boundaries
    expanded_boundaries = dilation(boundaries, d_ks[r])	#  Do the expansion operation 
    ### save_image(expanded_boundaries, f'expanded_boundaries_{r}_{boundaries.float().mean():.2f}.png', normalize=True)
    return expanded_boundaries

About d_ks[]:

d_k1 = torch.zeros((1, 1, 2 * 1 + 1, 2 * 1 + 1)).cuda()
d_k2 = torch.zeros((1, 1, 2 * 2 + 1, 2 * 2 + 1)).cuda()
d_k3 = torch.zeros((1, 1, 2 * 3 + 1, 2 * 3 + 1)).cuda()
d_k4 = torch.zeros((1, 1, 2 * 4 + 1, 2 * 4 + 1)).cuda()
d_k5 = torch.zeros((1, 1, 2 * 5 + 1, 2 * 5 + 1)).cuda()
d_k6 = torch.zeros((1, 1, 2 * 6 + 1, 2 * 6 + 1)).cuda()
d_k7 = torch.zeros((1, 1, 2 * 7 + 1, 2 * 7 + 1)).cuda()
d_k8 = torch.zeros((1, 1, 2 * 8 + 1, 2 * 8 + 1)).cuda()
d_k9 = torch.zeros((1, 1, 2 * 9 + 1, 2 * 9 + 1)).cuda()

d_ks = {
    1: d_k1, 2: d_k2, 3: d_k3, 4: d_k4,
        5: d_k5, 6: d_k6, 7: d_k7, 8: d_k8, 9: d_k9}


for k, v in d_ks.items():
    v[:, :, k, k] = 1
    for i in range(k):
        v = dilation(v, selem_dilation)
    d_ks[k] = v.squeeze(0).squeeze(0)

    print(f'dilation kernel at {
      k}:\n\n{
      d_ks[k]}')

The appearance of these convolution kernels is roughly as follows , And so on
image-20220621000259339

原网站

版权声明
本文为[starbuling~]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060545526936.html