{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 代码实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来将编程实现椒盐噪声。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:50.212391Z",
     "iopub.status.busy": "2022-09-20T09:46:50.212002Z",
     "iopub.status.idle": "2022-09-20T09:46:51.821402Z",
     "shell.execute_reply": "2022-09-20T09:46:51.820134Z",
     "shell.execute_reply.started": "2022-09-20T09:46:50.212278Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 335704 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 第2章编写了utils函数包文件，本书编写的函数都会封装到utils函数包中。\n",
    "from utils import *\n",
    "\n",
    "# 添加椒盐噪声\n",
    "def add_Salt(img, pro):\n",
    "    # img为输入图像\n",
    "    # pro为椒盐噪声的比例\n",
    "    \n",
    "    # 添加黑色像素点\n",
    "    noise = np.random.uniform(0, 255, img[:, :, 0].shape)\n",
    "    # mask为添加噪声的掩模\n",
    "    mask = noise < pro * 255\n",
    "    # 扩展mask的维度\n",
    "    mask = np.expand_dims(mask, axis=2)\n",
    "    mask = np.repeat(mask, 3, axis=2)    \n",
    "    img = img * (1 - mask)\n",
    "     \n",
    "    # 添加白色像素点\n",
    "    mask = noise > 255 - pro * 255    \n",
    "    mask = np.expand_dims(mask, axis=2)\n",
    "    mask = np.repeat(mask, 3, axis=2)\n",
    "    noise_img = 255 * mask + img * (1 - mask)\n",
    "    \n",
    "    return noise_img\n",
    "\n",
    "\n",
    "img = cv2.imread('lenaface.jpg')\n",
    "# 添加椒盐噪声的图像：\n",
    "img_salt = add_Salt(img, 0.05)\n",
    "plt.imshow(img_salt[:,:,::-1])\n",
    "plt.axis('off')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面，我们将编程实现高斯噪声，首先按照二维高斯分布随机生成噪声样本，并将其添加到图像中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:51.825384Z",
     "iopub.status.busy": "2022-09-20T09:46:51.824539Z",
     "iopub.status.idle": "2022-09-20T09:46:52.178453Z",
     "shell.execute_reply": "2022-09-20T09:46:52.177125Z",
     "shell.execute_reply.started": "2022-09-20T09:46:51.825319Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 345124 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def add_Gaussian(img, sigma=20, mean=0):\n",
    "    lab = cv2.cvtColor(img, cv2.COLOR_BGR2Lab)\n",
    "    # 生成高斯噪声\n",
    "    noise = np.random.normal(mean, sigma, lab[:, :, 0].shape)\n",
    "    lab = lab.astype(float)\n",
    "    # 添加高斯噪声\n",
    "    lab[:, :, 0] = lab[:, :, 0] + noise\n",
    "    lab[:, :, 0] = np.clip(lab[:, :, 0], 0, 255)\n",
    "    lab = lab.astype(np.uint8)\n",
    "    noise_img = cv2.cvtColor(lab, cv2.COLOR_Lab2BGR)\n",
    "    return noise_img\n",
    "\n",
    "# 添加高斯噪声的图像\n",
    "img_gaussian = add_Gaussian(img)\n",
    "plt.imshow(img_gaussian[:,:,::-1])\n",
    "plt.axis('off')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "接下来，我们对3.2.2节生成的有高斯噪声的人脸图像进行均值滤波，并观察它的效果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:52.180703Z",
     "iopub.status.busy": "2022-09-20T09:46:52.179992Z",
     "iopub.status.idle": "2022-09-20T09:46:52.511347Z",
     "shell.execute_reply": "2022-09-20T09:46:52.510504Z",
     "shell.execute_reply.started": "2022-09-20T09:46:52.180656Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 350312 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 4976 characters]",
      "text/plain": [
       "<Figure size 200x150 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 223940 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 均值滤波器 size=3\n",
    "size = 3\n",
    "k3 = 1/size/size * np.ones((size, size))\n",
    "\n",
    "# 展示输入图像与滤波核\n",
    "conv_gaussian = conv_2d(img_gaussian, k3)\n",
    "conv_gaussian.plot()\n",
    "\n",
    "# 展示卷积结果\n",
    "conv_gaussian.plot_conv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看出图像变的平滑了，但噪声依旧存在。我们增加均值滤波器的大小，并再次测试。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:52.514034Z",
     "iopub.status.busy": "2022-09-20T09:46:52.513278Z",
     "iopub.status.idle": "2022-09-20T09:46:53.097319Z",
     "shell.execute_reply": "2022-09-20T09:46:53.096653Z",
     "shell.execute_reply.started": "2022-09-20T09:46:52.513996Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 180708 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 144284 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 均值滤波器 size=5\n",
    "size = 5\n",
    "k5 = 1/size/size *np.ones((size,size))\n",
    "\n",
    "conv = conv_2d(img_gaussian, k5)\n",
    "\n",
    "# 展示卷积结果\n",
    "conv.plot_conv()\n",
    "\n",
    "\n",
    "# 均值滤波器 size=9\n",
    "size = 9\n",
    "k9 = 1/size/size * np.ones((size, size))\n",
    "\n",
    "conv = conv_2d(img_gaussian, k9)\n",
    "# 展示卷积结果\n",
    "conv.plot_conv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这时，可以很明显的看出，当均值滤波器的大小为$5\\times5$时，噪声基本消失不见；而当滤波器大小为$9\\times9$时，不仅噪声消除了，图像也变得模糊了。因此在实际滤波过程中，需要为滤波器选择合适的大小，尽可能在保留图像特征的情况下消除噪声。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，将编程实现高斯滤波器，并测试其效果。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:53.099123Z",
     "iopub.status.busy": "2022-09-20T09:46:53.098342Z",
     "iopub.status.idle": "2022-09-20T09:46:53.459493Z",
     "shell.execute_reply": "2022-09-20T09:46:53.458835Z",
     "shell.execute_reply.started": "2022-09-20T09:46:53.099072Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 346704 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 5100 characters]",
      "text/plain": [
       "<Figure size 200x150 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 150364 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 定义高斯核\n",
    "\n",
    "def gauss(kernel_size, sigma):\n",
    "    \n",
    "    kernel = np.zeros((kernel_size, kernel_size))\n",
    "    # 定义中心点坐标\n",
    "    center = kernel_size // 2\n",
    "    s = sigma ** 2\n",
    "    sum_val =  0\n",
    "    # 计算每个位置的高斯核权重\n",
    "    for i in range(kernel_size):\n",
    "        for j in range(kernel_size):\n",
    "            x, y = i-center, j-center\n",
    "            \n",
    "            kernel[i, j] = np.exp(-(x**2 + y**2)/ 2 * s)\n",
    "            sum_val += kernel[i, j]\n",
    "    \n",
    "    kernel = kernel/sum_val\n",
    "    \n",
    "    return kernel\n",
    "\n",
    "\n",
    "k_gaussian = gauss(9, 0.5)\n",
    "# 展示输入图像与滤波核\n",
    "conv = conv_2d(img_gaussian, k_gaussian)\n",
    "conv.plot()\n",
    "\n",
    "# 展示卷积结果\n",
    "conv.plot_conv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们通过对比输入图像和输出图像，可以很明显的看出高斯噪声被有效地抑制，看来高斯噪声还得是高斯滤波器才能有效地去除。我们改变高斯核的大小，并再次测试。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:53.461538Z",
     "iopub.status.busy": "2022-09-20T09:46:53.460622Z",
     "iopub.status.idle": "2022-09-20T09:46:53.968281Z",
     "shell.execute_reply": "2022-09-20T09:46:53.967357Z",
     "shell.execute_reply.started": "2022-09-20T09:46:53.461497Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 178256 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 158300 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 148908 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 高斯核大小为5\n",
    "conv1 = conv_2d(img_gaussian, gauss(5, 0.5))\n",
    "conv1.plot_conv()\n",
    "\n",
    "# 高斯核大小为7\n",
    "conv2 = conv_2d(img_gaussian, gauss(7, 0.5))\n",
    "conv2.plot_conv()\n",
    "\n",
    "# 高斯核大小为11\n",
    "conv3 = conv_2d(img_gaussian, gauss(11, 0.5))\n",
    "conv3.plot_conv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看出，高斯滤波器在有效处理高斯噪声的同时，也避免了使用均值滤波时出现的“块状效应”。在之后的代码中，我们可以使用cv2.GaussianBlur()对图像进行高斯滤波。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们把双边滤波器的实现留给大家作为作业，我们将调用库函数cv2.bilateralFilter()对图像进行双边滤波。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:53.974022Z",
     "iopub.status.busy": "2022-09-20T09:46:53.969448Z",
     "iopub.status.idle": "2022-09-20T09:46:54.524983Z",
     "shell.execute_reply": "2022-09-20T09:46:54.523854Z",
     "shell.execute_reply.started": "2022-09-20T09:46:53.973965Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 236396 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 为了之后展示图像更加方便，我们将该功能封装成一个函数\n",
    "def plot_image(img, name, camp='gray'):\n",
    "    plt.imshow(img, cmap=camp)\n",
    "    plt.title(name)\n",
    "    plt.axis('off')\n",
    "    plt.show()\n",
    "\n",
    "# 展示双边滤波器过滤高斯噪声的效果\n",
    "plot_image(cv2.bilateralFilter(np.uint8(img_gaussian),10,110,5)[:, :, ::-1], 'output')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图所示，双边滤波器在去噪的同时也很好的保持图像内容的清晰度，例如眼睛部位灰色的色块明显减少了。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "祛痘也是美颜修图时的一个常用操作。人脸上的“痘”其实就是一种椒盐噪声。我们先试试高斯滤波器对椒盐噪声的效果，观察高斯滤波器能否有效“祛痘”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:54.526918Z",
     "iopub.status.busy": "2022-09-20T09:46:54.526482Z",
     "iopub.status.idle": "2022-09-20T09:46:54.800413Z",
     "shell.execute_reply": "2022-09-20T09:46:54.799469Z",
     "shell.execute_reply.started": "2022-09-20T09:46:54.526863Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 243576 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 使用高斯滤波器过滤椒盐噪声\n",
    "conv_salt = conv_2d(np.uint8(img_salt), gauss(5, 1))\n",
    "conv_salt.plot_conv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "看来高斯滤波器无法有效处理椒盐噪声，因为高斯滤波器本质上是将部分像素点加权求和，并不能够抵抗椒盐噪声的影响。那么接下来，我们将介绍一种有效处理椒盐噪声的滤波器：中值滤波器。\n",
    "\n",
    "\n",
    "我们把中值滤波器的实现留给大家作为作业，我们将调用库函数cv2.medianBlur()对图像进行中值滤波，观察其对椒盐噪声的抑制效果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:54.803017Z",
     "iopub.status.busy": "2022-09-20T09:46:54.802760Z",
     "iopub.status.idle": "2022-09-20T09:46:55.085711Z",
     "shell.execute_reply": "2022-09-20T09:46:55.084826Z",
     "shell.execute_reply.started": "2022-09-20T09:46:54.802989Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 162744 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "conv_salt = conv_2d(img_salt, k3)\n",
    "plot_image(cv2.medianBlur(np.uint8(img_salt),5)[:, :, ::-1], 'output')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图中的人脸是不是看上去非常清晰？经过中值滤波器滤波之后，图像中连一点椒盐噪声的影子都看不见了。看来中值滤波器对椒盐噪声确实非常有效。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "既然可以通过滤波的方式让图像变得平滑，那么也可以让图像变得清晰，这一过程被称作图像锐化。如前所述，图像平滑是滤掉图像中高频信息，保留低频信息，那么图像锐化是不是反其道而行之，去掉低频信息，保留高频信息就可以实现了？试想一下，当一张图像经过高斯滤波之后，它的高频信息被滤波器所去除，只保留了低频信息。如果我们用原图像减去滤波后的图像，是不是就可以得到图像中更多的高频信息？那我们先这样试验一下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:55.094112Z",
     "iopub.status.busy": "2022-09-20T09:46:55.091471Z",
     "iopub.status.idle": "2022-09-20T09:46:55.323782Z",
     "shell.execute_reply": "2022-09-20T09:46:55.322749Z",
     "shell.execute_reply.started": "2022-09-20T09:46:55.094054Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 10180 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 由于卷积具有分配律，我们可以直接计算叠加后的卷积核\n",
    "size = 3\n",
    "k0 = np.zeros((size, size))\n",
    "k0[int((size-1)/2)][int((size-1)/2)] = 1\n",
    "\n",
    "kg = gauss(size, 1)\n",
    "k_shapen = 1 * (k0 - kg)\n",
    "\n",
    "# 得到高斯滤波的图像\n",
    "img_gau = cv2.GaussianBlur(img, (9,9), 5)\n",
    "shapen = conv_2d(img_gau, k_shapen)\n",
    "# 展示锐化效果\n",
    "shapen.plot_conv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "好像和预计的结果不相符，大家知道是哪里出了问题吗？图像中的高频信息是很少的，低频信息才是主体，所以仅保留高频信息得到的就是这样一张“黑图”。修正我们的想法：图像锐化是增强高频信息，而不是完全去除低频信息。\n",
    "\n",
    "所以再进行以下尝试：再把原图加回到上面这张“黑图”，那就是在保留原来图像低频信息的同时，将高频信息增加了一倍。那如果把上面这张“黑图”多次叠加到原图上，图像是不是会越来越清晰？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:55.326940Z",
     "iopub.status.busy": "2022-09-20T09:46:55.326577Z",
     "iopub.status.idle": "2022-09-20T09:46:55.596555Z",
     "shell.execute_reply": "2022-09-20T09:46:55.595718Z",
     "shell.execute_reply.started": "2022-09-20T09:46:55.326895Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 198632 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 构建滤波核\n",
    "k_shapen = k0 + 1 * (k0 - kg)\n",
    "img_gau = cv2.GaussianBlur(img, (3,3), 1)\n",
    "shapen = conv_2d(img_gau, k_shapen)\n",
    "shapen.plot_conv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图像明显变得清晰了。将高频信息与原图像多次叠加，并测试其效果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-09-20T09:46:55.598513Z",
     "iopub.status.busy": "2022-09-20T09:46:55.598074Z",
     "iopub.status.idle": "2022-09-20T09:46:55.855701Z",
     "shell.execute_reply": "2022-09-20T09:46:55.854897Z",
     "shell.execute_reply.started": "2022-09-20T09:46:55.598473Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 308564 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "k_shapen = k0 + 10 * (k0 - kg)\n",
    "img_gau = cv2.GaussianBlur(img, (3,3), 1)\n",
    "shapen = conv_2d(img_gau, k_shapen)\n",
    "shapen.plot_conv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由此可见将原图减去高斯滤波后的图像，即图像的高频信息，反复叠加在原图上，可以不断增强锐化的效果。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.10.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
