{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 代码实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，我们编程实现Sobel算子与图像的卷积，首先先导入lena图像，并编写沿$X$轴方向和$Y$轴方向的Sobel算子。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:17:52.525249Z",
     "iopub.status.busy": "2022-09-20T09:17:52.524296Z",
     "iopub.status.idle": "2022-09-20T09:17:54.158634Z",
     "shell.execute_reply": "2022-09-20T09:17:54.157641Z",
     "shell.execute_reply.started": "2022-09-20T09:17:52.525122Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(-0.5, 788.5, 430.5, -0.5)"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 295300 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "# 输入图像\n",
    "img = cv2.imread('lena.jpeg')\n",
    "\n",
    "# X轴方向算子\n",
    "kx = np.array([\n",
    "    [1,0,-1],\n",
    "    [2,0,-2],\n",
    "    [1,0,-1]\n",
    "])\n",
    "\n",
    "# Y轴方向Sobel算子\n",
    "ky = np.array([\n",
    "    [1, 2, 1],\n",
    "    [0, 0, 0],\n",
    "    [-1,-2,-1]\n",
    "])\n",
    "\n",
    "# 展示输入图像\n",
    "plt.imshow(img[:, :, ::-1])\n",
    "plt.axis('off')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接着，先计算$X$轴方向的梯度。利用已经学习过的卷积的知识，可以很轻松的编写出相应的代码。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:17:54.161398Z",
     "iopub.status.busy": "2022-09-20T09:17:54.160807Z",
     "iopub.status.idle": "2022-09-20T09:17:54.593141Z",
     "shell.execute_reply": "2022-09-20T09:17:54.592392Z",
     "shell.execute_reply.started": "2022-09-20T09:17:54.161353Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(-0.5, 788.5, 430.5, -0.5)"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 327396 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# X轴方向Sobel算子与图像进行卷积\n",
    "conv_x = cv2.filter2D(img, -1, kx)\n",
    "plt.imshow(conv_x[:, :, ::-1])\n",
    "plt.axis('off')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以观察到，沿$X$轴方向的梯度$\\boldsymbol{I}_x$，也就是垂直方向上的边缘信息被有效检测出，如手臂的线条、帽子等。然后，再计算图像$Y$轴方向的梯度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:17:54.595076Z",
     "iopub.status.busy": "2022-09-20T09:17:54.594599Z",
     "iopub.status.idle": "2022-09-20T09:17:54.995872Z",
     "shell.execute_reply": "2022-09-20T09:17:54.995166Z",
     "shell.execute_reply.started": "2022-09-20T09:17:54.595039Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(-0.5, 788.5, 430.5, -0.5)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 324136 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Y轴方向Sobel算子与图像进行卷积\n",
    "conv_y = cv2.filter2D(img, -1, ky)\n",
    "plt.imshow(conv_y[:, :, ::-1])\n",
    "plt.axis('off')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:17:54.997687Z",
     "iopub.status.busy": "2022-09-20T09:17:54.997235Z",
     "iopub.status.idle": "2022-09-20T09:17:55.202498Z",
     "shell.execute_reply": "2022-09-20T09:17:55.201514Z",
     "shell.execute_reply.started": "2022-09-20T09:17:54.997653Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(-0.5, 788.5, 430.5, -0.5)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 409364 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "E = abs(conv_x) + abs(conv_y)\n",
    "plt.imshow(E[:, :, ::-1])\n",
    "plt.axis('off')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来进行Canny算子的编写。首先进行图像平滑。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:33:09.639798Z",
     "iopub.status.busy": "2022-09-20T09:33:09.638828Z",
     "iopub.status.idle": "2022-09-20T09:33:09.955227Z",
     "shell.execute_reply": "2022-09-20T09:33:09.954099Z",
     "shell.execute_reply.started": "2022-09-20T09:33:09.639752Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 175872 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 140896 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from utils import *\n",
    "# 设定高斯核的大小与方差\n",
    "kernel_size = 5\n",
    "sigma = 1.5\n",
    "\n",
    "# 导入一张图像\n",
    "img = Image.open('lena.jpeg')\n",
    "\n",
    "# 将其转为灰度图像\n",
    "img = img.convert('L')\n",
    "img = np.array(img)\n",
    "\n",
    "# 构造高斯卷积核\n",
    "kernel = gaussian_kernel(kernel_size, sigma)\n",
    "\n",
    "# 与图像进行卷积\n",
    "smoothed = cv2.filter2D(img, -1, kernel)\n",
    "\n",
    "# 展示图像\n",
    "plot_image(img, 'Original image')\n",
    "plot_image(smoothed, 'Smoothed image')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对图像梯度进行计算。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:34:23.260175Z",
     "iopub.status.busy": "2022-09-20T09:34:23.259063Z",
     "iopub.status.idle": "2022-09-20T09:34:23.564352Z",
     "shell.execute_reply": "2022-09-20T09:34:23.563493Z",
     "shell.execute_reply.started": "2022-09-20T09:34:23.260113Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 104156 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 100740 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 求X轴方向梯度\n",
    "def partial_x(img):\n",
    "    \n",
    "    # 获得图像的大小\n",
    "    Hi, Wi = img.shape  \n",
    "    out = np.zeros((Hi, Wi))\n",
    "    \n",
    "    # 这里将卷积核均值化\n",
    "    k = np.array([[0,0,0],[0.5,0,-0.5],[0,0,0]])\n",
    "    \n",
    "    # 对图像进行卷积\n",
    "    out = cv2.filter2D(img, -1, k)\n",
    "    \n",
    "    return out\n",
    "\n",
    "\n",
    "# 求Y轴方向梯度\n",
    "def partial_y(img):\n",
    "    Hi, Wi = img.shape  \n",
    "    out = np.zeros((Hi, Wi))\n",
    "    k = np.array([[0,0.5,0],[0,0,0],[0,-0.5,0]])\n",
    "    out = cv2.filter2D(img, -1, k)\n",
    "    return out\n",
    "\n",
    "\n",
    "# 获得在两个方向上图像的梯度\n",
    "Gx = partial_x(smoothed)\n",
    "Gy = partial_y(smoothed)\n",
    "\n",
    "# 绘制图像梯度\n",
    "plot_image(Gx, 'Derivative in x direction')\n",
    "plot_image(Gy, 'Derivative in y direction')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:34:43.403784Z",
     "iopub.status.busy": "2022-09-20T09:34:43.403451Z",
     "iopub.status.idle": "2022-09-20T09:34:43.576826Z",
     "shell.execute_reply": "2022-09-20T09:34:43.575893Z",
     "shell.execute_reply.started": "2022-09-20T09:34:43.403745Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 176616 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 计算梯度以及方向\n",
    "def gradient(img):\n",
    "    G = np.zeros(img.shape)\n",
    "    theta = np.zeros(img.shape)\n",
    "    dx = partial_x(img)\n",
    "    dy = partial_y(img)\n",
    "    \n",
    "    # 获得图像梯度\n",
    "    G = np.sqrt(dx**2 + dy**2)\n",
    "    \n",
    "    # 获得梯度方向\n",
    "    theta = np.rad2deg(np.arctan2(dy, dx))\n",
    "    \n",
    "    # 将梯度方向的大小调整为0-360之间\n",
    "    theta %= 360\n",
    "\n",
    "    return G, theta\n",
    "\n",
    "G, theta = gradient(smoothed)\n",
    "\n",
    "# 展示利用图像梯度得到的结果\n",
    "plot_image(np.uint8(G), 'Gradient magnitude')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用边缘强度非最大值抑制。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:35:19.921417Z",
     "iopub.status.busy": "2022-09-20T09:35:19.921099Z",
     "iopub.status.idle": "2022-09-20T09:35:20.814155Z",
     "shell.execute_reply": "2022-09-20T09:35:20.813262Z",
     "shell.execute_reply.started": "2022-09-20T09:35:19.921383Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 174708 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 非最大值抑制算法\n",
    "def non_maximum_suppression(G, theta):\n",
    "    \n",
    "    # 获得梯度图的大小\n",
    "    H, W = G.shape\n",
    "\n",
    "    # 将梯度方向投影到最近的45度角空间中\n",
    "    theta = np.floor((theta + 22.5) / 45) * 45\n",
    "    theta %= 360\n",
    "    \n",
    "    # 最后输出的图像梯度\n",
    "    out = G.copy()\n",
    "    \n",
    "    for i in range(1, H-1):\n",
    "        for j in range(1,W-1):\n",
    "            \n",
    "            # 当前像素点的角度大小，可以将其分为4类\n",
    "            angle = theta[i,j]\n",
    "            \n",
    "            if angle == 0 or angle == 180:\n",
    "                ma = max(G[i, j-1], G[i, j+1])\n",
    "                \n",
    "            elif angle == 45 or angle == 45 + 180:\n",
    "                ma = max(G[i-1, j-1], G[i+1, j+1])\n",
    "                \n",
    "            elif angle == 90 or angle == 90 + 180:\n",
    "                ma = max(G[i-1, j], G[i+1,j])\n",
    "                \n",
    "            elif angle == 135 or angle == 135 + 180:\n",
    "                ma = max(G[i-1, j+1], G[i+1, j-1])\n",
    "                \n",
    "            else:\n",
    "                print(angle)\n",
    "                raise\n",
    "            # ma是当前像素点相邻两个邻居点的像素梯度的最大值\n",
    "            \n",
    "            # 如果ma的值大于当前像素点的梯度值，则认为当前点非边缘\n",
    "            # 并将该点的梯度值设置为0\n",
    "            if ma > G[i,j]:\n",
    "                out[i,j]=0\n",
    "    return out\n",
    "\n",
    "\n",
    "nms = non_maximum_suppression(G, theta)\n",
    "plot_image(np.uint8(nms), 'Non-maximum suppressed')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "编程实现通过双阈值确定强弱边缘。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:36:20.026400Z",
     "iopub.status.busy": "2022-09-20T09:36:20.026093Z",
     "iopub.status.idle": "2022-09-20T09:36:21.063101Z",
     "shell.execute_reply": "2022-09-20T09:36:21.062198Z",
     "shell.execute_reply.started": "2022-09-20T09:36:20.026368Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 60056 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 107292 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 双阈值确定强弱边缘\n",
    "def double_thresholding(img, high, low):\n",
    "    # 初始化强弱边缘为布尔矩阵\n",
    "    strong_edges = np.zeros(img.shape, dtype=np.bool_)\n",
    "    weak_edges = np.zeros(img.shape, dtype=np.bool_)\n",
    "    \n",
    "    # 获得输入图像大小\n",
    "    a,b = img.shape\n",
    "    for i in range(a):\n",
    "        for j in range(b):\n",
    "            # 大于Th阈值则为强边缘\n",
    "            if img[i,j] > high:\n",
    "                strong_edges[i,j] = 1\n",
    "                \n",
    "            # 小于Th阈值大于Tl阈值则为弱边缘\n",
    "            elif img[i,j] <= high and img[i,j] > low:\n",
    "                weak_edges[i, j] = 1\n",
    "\n",
    "    return strong_edges, weak_edges\n",
    "\n",
    "\n",
    "low_threshold = 3\n",
    "high_threshold = 6\n",
    "\n",
    "strong_edges, weak_edges = double_thresholding(nms, \n",
    "                    high_threshold, low_threshold)\n",
    "\n",
    "# 返回强弱边缘叠加的边缘图\n",
    "edges = strong_edges * 1.0 + weak_edges * 0.5\n",
    "\n",
    "plot_image(strong_edges, 'Strong Edges')\n",
    "plot_image(edges, 'Strong+Weak Edges')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "实现对弱边缘点的确定过程。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:36:41.236410Z",
     "iopub.status.busy": "2022-09-20T09:36:41.235771Z",
     "iopub.status.idle": "2022-09-20T09:36:41.494605Z",
     "shell.execute_reply": "2022-09-20T09:36:41.493468Z",
     "shell.execute_reply.started": "2022-09-20T09:36:41.236364Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 84144 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 获得在(x，y)处点的邻居\n",
    "def get_neighbors(x, y, H, W):\n",
    "    neighbors = []\n",
    "    for i in (x - 1, x, x + 1):\n",
    "        for j in (y - 1, y, y + 1):\n",
    "            if i >= 0 and i < H and j >= 0 and j < W:\n",
    "                if (i == x and j == y):\n",
    "                    continue\n",
    "                neighbors.append((i, j))\n",
    "\n",
    "    return neighbors\n",
    "\n",
    "\n",
    "# 判断弱边缘点是否为边缘\n",
    "def link_edges(strong_edges, weak_edges):\n",
    "    # 获得图的大小\n",
    "    H, W = strong_edges.shape\n",
    "    \n",
    "    # 获得强边缘点的位置\n",
    "    indices = np.stack(np.nonzero(strong_edges)).T\n",
    "    \n",
    "    # 初始化最终的结果，为布尔矩阵\n",
    "    edges = np.zeros((H, W), dtype=np.bool_)\n",
    "\n",
    "    weak_edges = np.copy(weak_edges)\n",
    "    edges = np.copy(strong_edges)\n",
    "    \n",
    "    # 将所有强边缘点的坐标组合成一个列表\n",
    "    q = [(i,j) for i in range(H) for j in range(W) if strong_edges[i,j]]\n",
    "    \n",
    "    while q:\n",
    "        \n",
    "        # pop()函数返回列表中的末尾元素，并将其从列表中删除\n",
    "        i, j = q.pop()\n",
    "        \n",
    "        # (a, b)是(i, j)的邻居\n",
    "        for a, b in get_neighbors(i, j, H, W):\n",
    "            \n",
    "            # 如果当前点是弱边缘点且在强边缘点的邻域内，即可认为该点是边缘点\n",
    "            if weak_edges[a][b]:\n",
    "                # 为了避免对同一弱边缘点重复判断，便将其值设置为0\n",
    "                weak_edges[a][b] = 0\n",
    "                \n",
    "                # 在边缘点中添加该弱边缘点\n",
    "                edges[a][b] = 1\n",
    "                \n",
    "                # 由于该弱边缘点已经变成边缘点，因此需要将其加入q中\n",
    "                # 通过将该点放进q中，在下一次迭代中可判断该点周围是否存在弱边缘点\n",
    "                q.append((a,b))\n",
    "                \n",
    "    return edges\n",
    "\n",
    "\n",
    "# 获得最终边缘图\n",
    "edges = link_edges(strong_edges, weak_edges)\n",
    "plot_image(edges, 'Final image')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "综上，我们将Canny边缘检测器封装成如下函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:37:16.571919Z",
     "iopub.status.busy": "2022-09-20T09:37:16.571615Z",
     "iopub.status.idle": "2022-09-20T09:37:18.504669Z",
     "shell.execute_reply": "2022-09-20T09:37:18.503735Z",
     "shell.execute_reply.started": "2022-09-20T09:37:16.571885Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 175872 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 85344 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def canny(img, kernel_size=5, sigma=1.5, high=6, low=3):\n",
    "    \n",
    "    # 获取高斯滤波器\n",
    "    kernel = gaussian_kernel(kernel_size, sigma)\n",
    "    # 将图像进行高斯滤波\n",
    "    smoothed = cv2.filter2D(img, -1, kernel)    \n",
    "    # 得到图像的梯度图\n",
    "    G, theta = gradient(smoothed)\n",
    "    # 对梯度图进行nms\n",
    "    nms = non_maximum_suppression(G, theta)\n",
    "    # 获得强弱边缘信息\n",
    "    strong_edges, weak_edges = double_thresholding(nms, high, low)\n",
    "    # 对弱边缘进行分类，得到最终的边缘图\n",
    "    edge = link_edges(strong_edges, weak_edges)\n",
    "\n",
    "    return edge\n",
    "\n",
    "\n",
    "plot_image(img, 'Original image')\n",
    "canny_img = canny(img)\n",
    "plot_image(canny_img, 'Edges of image')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "调整高斯核的大小与标准差，观察比较不同的平滑程度对边缘检测结果的影响。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:38:56.105174Z",
     "iopub.status.busy": "2022-09-20T09:38:56.104861Z",
     "iopub.status.idle": "2022-09-20T09:39:01.469897Z",
     "shell.execute_reply": "2022-09-20T09:39:01.468997Z",
     "shell.execute_reply.started": "2022-09-20T09:38:56.105139Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 74884 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 64148 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 54412 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "canny2 = canny(img, kernel_size=7, sigma=2)\n",
    "plot_image(canny2, 'Kernel Size: 7, Sigma: 2')\n",
    "\n",
    "canny2 = canny(img, kernel_size=9, sigma=2.5)\n",
    "plot_image(canny2, 'Kernel Size: 9, Sigma: 2.5')\n",
    "\n",
    "canny2 = canny(img, kernel_size=9, sigma=3)\n",
    "plot_image(canny2, 'Kernel Size: 9, Sigma: 3')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.16"
  },
  "vscode": {
   "interpreter": {
    "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
