text: import numpy as np
  
  class ConvolutionNeuralNetwork:
  
      def __init__(self):
          self.input_layer = np.random.rand(512,512,3)
          self.convolute_weights = np.random.rand(3, 3)
      
      def feed_forward(self, input_image):
          preprocessed_image = self.preprocess_input(input_image)
          hidden_layer = self.convolute(preprocessed_image[:,:,0])
          print(hidden_layer.shape, preprocessed_image.shape)
  
      def preprocess_input(self, input_image):
          # Perform center crop
          image_width, image_height, _ = input_image.shape
          aspect_size = min(image_width, image_height)
          print(aspect_size)
          image_center_x = int(image_width/2)
          image_center_y = int(image_height/2)
          image_center_crop = input_image[image_center_y - int(aspect_size/2):image_center_y + int(aspect_size/2), (image_center_x - int(aspect_size/2)):(image_center_x + int(aspect_size/2))]
          print((image_center_x - int(aspect_size/2)), (image_center_x + int(aspect_size/2)))
          print(image_center_crop.shape)
          return image_center_crop
  
      def convolute(self, input_nodes):
          sub_shape = (self.convolute_weights.shape[0], self.convolute_weights.shape[0])
          view_shape = tuple(np.subtract(input_nodes.shape, sub_shape) + 1) + sub_shape
          strides = input_nodes.strides + input_nodes.strides
  
          sub_matrices = np.lib.stride_tricks.as_strided(input_nodes,view_shape,strides)
          return np.einsum('ij,ijkl->kl',self.convolute_weights, sub_matrices)
  
  
      def pooling(self, local_matrix):
          return np.amax(local_matrix)
  
      def activation_function_relu(self, x):
          if x < 0:
              return 0
          else:
              return x
      
  if __name__ == "__main__":
      cnn = ConvolutionNeuralNetwork()
      matrix = np.random.rand(1920, 1080, 3)
      print(cnn.feed_forward(matrix))