code: def filter_with_theta(image, theta, sigma=1., filter_size=9):
          
          x = np.arange(-filter_size // 2 + 1, filter_size // 2 + 1)
          # 1D Gaussian
          g = np.array([np.exp(-(x**2) / (2 * sigma**2))])
          # first-derivative of 1D Gaussian
          gp = np.array([-(x / sigma) * np.exp(-(x**2) / (2 * sigma**2))])
  
          ix = convolve2d(image, -gp, mode='same', boundary='fill', fillvalue=0)
          ix = convolve2d(ix, g.T, mode='same', boundary='fill', fillvalue=0)
  
          iy = convolve2d(image, g, mode='same', boundary='fill', fillvalue=0)
          iy = convolve2d(iy, -gp.T, mode='same', boundary='fill', fillvalue=0)
  
          output = np.cos(theta) * ix + np.sin(theta) * iy
  
          # np.cos(theta) * np.matmul(g.T, gp) + np.sin(theta) * np.matmul(gp.T, g)
          gt_filter = np.matmul(g.T, gp)
          gt_filter = np.cos(theta) * gt_filter + np.sin(theta) * gt_filter.T
  
          return output, gt_filter
docs: Implements a steerable Gaussian filter.
  
          This function can be used to evaluate the first
          directional derivative of an image, using the
          method outlined in
  
              W. T. Freeman and E. H. Adelson, "The Design
              and Use of Steerable Filters", IEEE PAMI, 1991.
  
          It evaluates the directional derivative of the input
          image I, oriented at THETA degrees with respect to the
          image rows. The standard deviation of the Gaussian kernel
          is given by SIGMA (assumed to be equal to unity by default).
  
          Args:
              image: any input image (only one channel)
              theta: orientation of filter [0, 2 * pi]
              sigma (float, optional): standard derivation of Gaussian
              filter_size (int, optional): filter support
  
          Returns:
              filtered image and the filter