{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Day 4\n",
    "# Discrete Fourier Transform\n",
    "\n",
    "## Calculation of complex numbers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "import numpy as np\n",
    "x = 2.0 + 1.0j\n",
    "y = complex(1.0, - 2.0)\n",
    "print(\"x + y = {0}\".format(x + y))\n",
    "print(x.real)\n",
    "print(\"real = {0}, imaginary = {1}\", format((x.real, x.imag)))\n",
    "print(\"conjugate x = {0}\".format(x.conjugate()))\n",
    "print(\"exp(x) = {0} + {1}j\".format(math.exp(x.real) * math.cos(x.imag), math.exp(x.real) * math.sin(x.imag)))\n",
    "print(\"exp(x) = {0}\".format(np.exp(x)))\n",
    "#print(\"exp(x) = {0}\".format(math.exp(x)))       \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Practice\n",
    "- Try substraction, multiplication, and Division\n",
    "- Try cos function of complex number and check it by calculation of real values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Discrete Fourier Transform (DFT)\n",
    "### Fourier expansion\n",
    "- Transformation\n",
    "    - $g(t)$ is defined on $[0, T]$.\n",
    "    - $c_n$ is defined for $n=0,1,2,\\ldots$\n",
    "$$\n",
    "c_n = \\frac{1}{T}\\int_0^T g(\\tau) e^{-\\frac{2 \\pi i}{T}n \\tau} d \\tau\n",
    "$$\n",
    "- Inverse transformation\n",
    "$$\n",
    "g(t) = \\sum_{n= -\\infty}^{\\infty} c_n e^{\\frac{2 \\pi i}{T}n t}\n",
    "$$\n",
    "\n",
    "### Fourier transform\n",
    "\n",
    "- Transformation\n",
    "    - $g(t)$ is defined on $(\\infty, -\\infty)$.\n",
    "    - $G(f)$ is defined on $(\\infty, -\\infty)$.\n",
    "\n",
    "$$\n",
    "G(f) = \\int_{-\\infty}^{\\infty} g(\\tau) e^{-2 \\pi i f \\tau} d \\tau\n",
    "$$\n",
    "\n",
    "- Inverse transformation\n",
    "$$\n",
    "g(t) = \\int_{-\\infty}^{\\infty} G(f) e^{2 \\pi i f t} d f\n",
    "$$\n",
    "\n",
    "### Fourier discrete transform (DFT)\n",
    "- Transformation\n",
    "    - $g[n]$ is defined for $n=0,1,2,\\ldots,N-1$.\n",
    "    - $G[m]$ is defined for $m=0,1,2,\\ldots,N-1$.\n",
    "$$\n",
    "G[m] = \\sum_{n = 0}^{N - 1}  g[n] e^{-\\frac{2 \\pi i}{N}nm}\n",
    "$$\n",
    "- Inverse transformation\n",
    "$$\n",
    "g[n] = \\frac{1}{N} \\sum_{m = 0}^{N - 1} G[m] e^{\\frac{2 \\pi i}{N}n m}\n",
    "$$\n",
    "\n",
    "### Fast Fourier transform\n",
    "- Alogrithm for DFT\n",
    "- With a similarly concept of Mearge sort, it can be accelerate the calclation.\n",
    "- The computational complexity of a starndard DFT is $N^2$.\n",
    "- The computational complexity of FFT is $N \\log N$.\n",
    "\n",
    "### Pactice\n",
    "\n",
    "- Make a class `DFT` to calculate DFT of which data size is $N$.\n",
    "    - `__init__(self, N)` \n",
    "        - Store `N` to the attribute `self.N`.\n",
    "        - Make a table `expTbl[n]`$=e^{\\frac{2 \\pi i}{N}n}$ for $n=0,1,2,\\ldots,N-1$. \n",
    "        - This table is used to make calculation of complex exponential functions once\n",
    "    - `dft(self, data)`\n",
    "        - Calculate the DFT of he attribute `data` type of `ndarry of complex64` and return the result. \n",
    "    - `idft(self, data)`\n",
    "        - Calculate the inverse DFT of he attribute `data` type of `ndarry of complex64` and return the result. \n",
    "- Main program is provided.\n",
    "    - The followin function is given.\n",
    "$$\n",
    "{\\rm data}[n] = 4.0 \\cos \\left(\\frac{2 \\pi}{N} 2n \\right) +  \\sin \\left(\\frac{2 \\pi}{N} 5n \\right)\n",
    "$$\n",
    "    - A program to plot `out1[]` is provided.\n",
    "- Calculate DFT of `data` by your class.\n",
    "- Calculate of inverse DFT of the result by your class and check whether the result coincides with `data`.\n",
    "- A class of FFT is provided so that compare their calculation speeds by increasing `N`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Main program\n",
    "import numpy as np\n",
    "import math\n",
    "import matplotlib.pyplot as plt\n",
    "import time\n",
    "\n",
    "# Make data\n",
    "N = 32\n",
    "alpha = 2.0 * math.pi  / N\n",
    "\n",
    "data = np.empty([N], dtype='complex64')\n",
    "for n in range(0, N):\n",
    "    data[n] = (4.0 * math.cos(2.0 * alpha * n) + 1.0 * math.sin(5.0 * alpha * n)) + 0j\n",
    "\n",
    "# Construct the object to calculate\n",
    "#dft = DFT(N)\n",
    "dft = FFT(N)\n",
    "\n",
    "start = time.time()\n",
    "out1 = dft.dft(data)\n",
    "print(\"time = \", time.time() - start, \" sec\")\n",
    "out2 = dft.idft(out1)\n",
    "out3 = out2 - data\n",
    "out0 = data\n",
    "\n",
    "x  = np.empty([N])\n",
    "y1 = np.empty([N])\n",
    "y2 = np.empty([N])\n",
    "for n in range(0, N):\n",
    "    x[n]  = n\n",
    "    y1[n] = out1[n].real\n",
    "    y2[n] = out1[n].imag\n",
    "    \n",
    "plt.plot(x, y1, x, y2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Program for FFT\n",
    "import numpy as np\n",
    "import math\n",
    "\n",
    "class DFT:\n",
    "    def __init__(self, N):\n",
    "        self.N = N\n",
    "        # Make table\n",
    "        self.expTbl = np.empty([N], dtype='complex64')\n",
    "        \n",
    "        \n",
    "    def dft(self, data):\n",
    "        dataOut   = np.empty([self.N], dtype='complex64')\n",
    "        \n",
    "        return dataOut\n",
    "    \n",
    "    def idft(self, data):\n",
    "        dataOut   = np.empty([self.N], dtype='complex64')\n",
    "\n",
    "        \n",
    "        return dataOut"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Program for FFT\n",
    "import numpy as np\n",
    "import math\n",
    "\n",
    "class FFT:\n",
    "    def __init__(self, N):\n",
    "        self.N   = N\n",
    "        self.N2 = int(N / 2)\n",
    "\n",
    "        self.expTbl = np.empty([self.N2 + 1], dtype='complex64')\n",
    "        alpha                = 2.0 * math.pi / self.N\n",
    "        for n in range(0, self.N2):\n",
    "            ang                   = alpha * n\n",
    "            self.expTbl[n] = math.cos(ang) + math.sin(ang) * (1j)\n",
    "        self.expTbl[self.N2] = -1.0 + 0j\n",
    "        # Bit テーブル\n",
    "        self.bTbl = np.empty([32], dtype='int')\n",
    "        ndig = 0\n",
    "        bp    = 0\n",
    "        \n",
    "        while True:\n",
    "            self.bTbl[bp] = (1 <<  bp)\n",
    "            if ((self.N & self.bTbl[bp]) != 0):\n",
    "                ndig = bp - 1\n",
    "                break;\n",
    "            bp += 1\n",
    "        \n",
    "        # Bit 逆順テーブル\n",
    "        self.rbTbl = np.zeros([self.N], dtype='int')\n",
    "        for n in range(0, self.N):\n",
    "            for dig in range(0, ndig + 1):\n",
    "                if (n & self.bTbl[dig]) != 0:\n",
    "                    self.rbTbl[n] |= self.bTbl[ndig - dig]\n",
    "\n",
    "    def dft(self, data):\n",
    "        dataOut   = np.empty([self.N], dtype='complex64')\n",
    "            \n",
    "        #データの並び替え\n",
    "        for n in range(0, self.N):\n",
    "            m = self.rbTbl[n]\n",
    "            dataOut[m] = data[n]\n",
    "\n",
    "        # バタフライ演算の繰り返し\n",
    "        # k  = 1, 2, 4,  8,...\n",
    "        # k2 = 2, 4, 8, 16,...\n",
    "        k = 1\n",
    "        while k < N:\n",
    "            h   = 0\n",
    "            k2 = k * 2\n",
    "            d   = int(self.N / k2)\n",
    "            for j in range(0, k):\n",
    "                for i in range(j, self.N, k2):\n",
    "                    ik = i + k\n",
    "                    # iからのk個と，ikからのk個でバタフライ演算を実行する。\n",
    "                    tmp             = dataOut[ik] * self.expTbl[self.N2 - h]\n",
    "                    tmp2            = dataOut[i] - tmp\n",
    "                    dataOut[ik] = tmp + dataOut[i]\n",
    "                    dataOut[i]  = tmp2\n",
    "                h += d;\n",
    "            k = k2;\n",
    "        return dataOut\n",
    "    \n",
    "    def idft(self, data):\n",
    "        dataOut   = np.empty([self.N], dtype='complex64')\n",
    "            \n",
    "        #データの並び替え\n",
    "        for n in range(0, self.N):\n",
    "            m = self.rbTbl[n]\n",
    "            dataOut[m] = data[n] / N\n",
    "\n",
    "        # バタフライ演算の繰り返し\n",
    "        # k  = 1, 2, 4,  8,...\n",
    "        # k2 = 2, 4, 8, 16,...\n",
    "        k = 1\n",
    "        while k < N:\n",
    "            h   = 0\n",
    "            k2 = k * 2\n",
    "            d   = int(self.N / k2)\n",
    "            for j in range(0, k):\n",
    "                for i in range(j, self.N, k2):\n",
    "                    ik = i + k\n",
    "                    # iからのk個と，ikからのk個でバタフライ演算を実行する。\n",
    "                    tmp             = dataOut[ik] * self.expTbl[h]\n",
    "                    tmp2           = dataOut[i]  + tmp\n",
    "                    dataOut[ik] =  dataOut[i] - tmp\n",
    "                    dataOut[i]   = tmp2\n",
    "                h += d;\n",
    "            k = k2;\n",
    "        return dataOut\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Convolusion\n",
    "- We assume that all data are periodic : for $g[n]$ $(n=0,1,2,\\ldots,N-1)$\n",
    "$$\n",
    "g[n] = g[n+N]\n",
    "$$\n",
    "- For $h[n]$ and $g[n]$ $(n=0,1,2,\\ldots,N-1)$, the covolusion is defined as\n",
    "$$\n",
    "d[n] =  \\sum_{k = 0}^{N - 1}  h[n-k] g[k] = \\sum_{k = 0}^{N - 1} h[k] g[n-k] \n",
    "$$\n",
    "- The 2nd and 3rd expressions coincide.\n",
    "- Let $G[m]$, $H[m]$, and $D[m]$ be the DFTs of $g[n]$, $h[n]$, and $d[n]$, respectively. \n",
    "- We have for $m=0,1,2,\\ldots,N-1$\n",
    "$$\n",
    "D[m] = H[m] G[m].\n",
    "$$\n",
    "### Practice \n",
    "- Make a class `Convolusion` to calculate the convolustion for data size $N$.\n",
    "    - `__init__(self, N)` \n",
    "        - Store `N` to the attribute `self.N`.\n",
    "    - `direct(self, h, g)`\n",
    "        - Calculate the convlusion directly of attributes `h` and `g` type of `ndarry of float` and return the result. \n",
    "    - `byFFT(self, h, g)`\n",
    "        - Calculate the convlusion using DFT (FFT) of attributes `h` and `g` type of `ndarry of float` and return the result.\n",
    "- Main program is provided.\n",
    "    - The followin functions are given with $\\alpha = -8.0 n /N$:\n",
    "$$\n",
    "{\\rm h}[n] =  \\exp(\\alpha n),\n",
    "$$\n",
    "\n",
    "$$\n",
    "{\\rm g}[n] = \n",
    "\\left\\{\n",
    "\\begin{array}{ll} \n",
    "1 &  (n \\mod (N/4) = 0,)\\\\\n",
    "0 &  (\\mbox{else.})\\\\\n",
    "\\end{array}\n",
    "\\right.\n",
    "$$\n",
    "    - A program to plot `out1[]` is provided.\n",
    "- Calculate the convolusion of `h` and `g` by your class for both methods.\n",
    "- Compare thecalculation speeds of the two by increasing `N`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Main program for convolusion\n",
    "import numpy as np\n",
    "import math\n",
    "import matplotlib.pyplot as plt\n",
    "import time\n",
    "\n",
    "# Make data\n",
    "N = 64\n",
    "alpha = - 8.0  / N\n",
    "\n",
    "h = np.zeros(N)\n",
    "g = np.zeros(N)\n",
    "\n",
    "for n in range(0, N):\n",
    "    h[n] = math.exp(alpha * n)\n",
    "    if n % (N / 4) == 0:\n",
    "        g[n] = 1\n",
    "\n",
    "# Construct the object to calculate\n",
    "conv = Convolusion(N)\n",
    "\n",
    "start = time.time()\n",
    "out1 = conv.direct(h, g)\n",
    "#out1 = conv.byFFT(h, g)\n",
    "print(\"time = \", time.time() - start, \" sec\")\n",
    "\n",
    "x  = np.zeros([N])\n",
    "for n in range(0, N):\n",
    "    x[n]  = n\n",
    "    \n",
    "plt.plot(x, h, x, g, x, out1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Program to calculate the convoluusion\n",
    "import numpy as np\n",
    "import math\n",
    "\n",
    "class Convolusion:\n",
    "    def __init__(self, N):\n",
    "        self.N = N\n",
    "\n",
    "    def direct(self, h, g):\n",
    "        d = np.zeros(self.N)\n",
    "        \n",
    "        return d\n",
    "\n",
    "    def byFFT(self, h, g):\n",
    "\n",
    "        \n",
    "        d = D.real\n",
    "        return d\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2-dimensional DFT\n",
    "\n",
    "- Let $G[k,l]$ be the result of 2-dimensional DFT (2D-DFT) of $g[m,n]$ for  $m, k =0,1,2,\\ldots,M-1$ and $n, l =0,1,2,\\ldots,N-1$.\n",
    "$$\n",
    "G[k,l] = \\sum_{m = 0}^{M - 1} \\sum_{n = 0}^{N - 1}  g[m,n] e^{-\\frac{2 \\pi i}{M}km -\\frac{2 \\pi i}{N}ln}.\n",
    "$$\n",
    "- Inverse 2D-DFT can be also described as\n",
    "$$\n",
    "g[m,n] = \\frac{1}{MN} \\sum_{k = 0}^{M - 1} \\sum_{l = 0}^{N - 1}  g[m,n] e^{\\frac{2 \\pi i}{M}km +\\frac{2 \\pi i}{N}ln}.\n",
    "$$\n",
    "- Let $H[k,l]$ be the result of 2-dimensional DFT (2D-DFT) of $h[m,n]$ for  $m, k =0,1,2,\\ldots,M-1$ and $n, l =0,1,2,\\ldots,N-1$.\n",
    "- We are assuming that all functions here are periodic. The periods for the first and the second indeces are $M$ and $N$, respectively.\n",
    "- 2D-DFT can be calculated as follows.\n",
    "    0. Calculate 1D-DFT for each row of input data.\n",
    "    0. Calculate 1D-DFT for each column of the result.\n",
    "- 2D-convolusion is given by\n",
    "$$\n",
    "d[m,n]=\n",
    "\\sum_{k = 0}^{M - 1} \\sum_{l = 0}^{N - 1}  h[m-k, n-l] g[k,l]\n",
    "=\\sum_{k = 0}^{M - 1} \\sum_{l = 0}^{N - 1} h[k,l] g[m-k, n-l].\n",
    "$$\n",
    "- Let $D[k,l]$ be the result of 2-dimensional DFT (2D-DFT) of $d[m,n]$ for  $m, k =0,1,2,\\ldots,M-1$ and $n, l =0,1,2,\\ldots,N-1$.\n",
    "- We have\n",
    "$$\n",
    "D[k,l] = H[k,l]G[k,l].\n",
    "$$\n",
    "\n",
    "\n",
    "## Practice\n",
    "\n",
    "- Make a class `DFT2D` to calculate 2D-DFT of which data size is `(M, N)` . \n",
    "    - We assume that `M` and  `N` are 2 powered numbers.\n",
    "    - `__init__(self, M, N)`\n",
    "        - Store `M` and `N` to the attribute `self.M` and `self.N`.\n",
    "        - Construct objects of `FFT` for data of length `M` and `N`, respectively.\n",
    "    - `dft2d(self, data)`\n",
    "        - Calculate the 2D-DFT of the attribute data type of ndarry of 2D of complex64 and return the result by using FFT.\n",
    "    - `idft2d(self, data)`\n",
    "        - Calculate the inverse 2D-DFT of the attribute data type of ndarry of 2D of complex64 and return the result by using FFT.\n",
    "- For a degradation model $h[m,n]$, we consider a uniform circular blur described by\n",
    "$$\n",
    "h[m,n] = \\frac{h_{\\rm 0}[m,n]}{\\sum_{m=0}^{M-1}\\sum_{n=0}^{N-1} h_{\\rm 0}[m,n]}\n",
    "$$\n",
    "where\n",
    "$$\n",
    "h_{\\rm 0}[m,n]  = \n",
    "\\left\\{\n",
    "\\begin{array}{cc}\n",
    "1 & (\\sqrt{m^2 + n^2} \\leq r) \\\\\n",
    "r + 1 - \\sqrt{m^2 + n^2} & (r < \\sqrt{m^2 + n^2} \\leq r + 1) \\\\\n",
    "0 & \\mbox{(else)}\n",
    "\\end{array}\n",
    "\\right. .\n",
    "$$\n",
    "(They are periodic so that shorter distance should be used. For example, the distance between $(0, 0)$ and (M-1,0) is 1.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import math\n",
    "import cv2\n",
    "\n",
    "# Make h\n",
    "def makeh(M, N, r):\n",
    "    hC = np.empty([M, N], dtype='complex64')\n",
    "    Mhalf = int(M/2)\n",
    "    Nhalf = int(N/2)\n",
    "    for m in range(0,Mhalf):\n",
    "        for n in range(0,Nhalf):\n",
    "            l = math.sqrt(m * m + n * n)\n",
    "            hC[m,n] = valH(l, r)\n",
    "        for n in range(Nhalf,N):\n",
    "            l = math.sqrt(m * m + (N - n) * (N - n))\n",
    "            hC[m,n] = valH(l, r)\n",
    "    for m in range(Mhalf,M):\n",
    "        for n in range(0,Nhalf):\n",
    "            l = math.sqrt((M - m) * (N - m) + n * n)\n",
    "            hC[m,n] = valH(l, r)\n",
    "        for n in range(Nhalf,N):\n",
    "            l = math.sqrt((M - m) * (N - m) + (N - n) * (N - n))\n",
    "            hC[m,n] = valH(l, r)\n",
    "\n",
    "    sumVal = np.abs(sum(sum(hC)))\n",
    "    hC /= sumVal\n",
    "    return hC\n",
    "\n",
    "def valH(l, r):\n",
    "    if (l < r):\n",
    "        h = 1.0\n",
    "    elif(l < r + 1):\n",
    "        h = r + 1 - l\n",
    "    else:\n",
    "        h = 0\n",
    "    return h\n",
    "\n",
    "def boundImage(img):\n",
    "    img[img > 255] = 255\n",
    "    img[img < 0] = 0\n",
    "\n",
    "    \n",
    "# Blur size\n",
    "r = 3.0\n",
    "\n",
    "inImg = cv2.imread(\"./crop.tif\")\n",
    "gray  = cv2.cvtColor(inImg, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "cv2.startWindowThread()\n",
    "cv2.imshow('input', gray)\n",
    "\n",
    "M, N  = gray.shape\n",
    "hC    = makeh(M, N, r)\n",
    "\n",
    "grayC = gray.astype('complex64')\n",
    "\n",
    "dft2d = DFT2D(M,N)\n",
    "grayF = dft2d.dft2d(grayC)\n",
    "hF    = dft2d.dft2d(hC)\n",
    "\n",
    "# Convolusion by Fourier transform\n",
    "blurF = hF * grayF\n",
    "\n",
    "blurC = dft2d.idft2d(blurF)\n",
    "blurR = blurC.real\n",
    "boundImage(blurR)\n",
    "blur = blurR.astype('uint8')\n",
    "\n",
    "cv2.imwrite(\"./blur.tif\", blur)\n",
    "cv2.imwrite(\"./blur.jpg\", blur)\n",
    "cv2.imshow('blur', blur)\n",
    "\n",
    "print(\"End of program\")\n",
    "\n",
    "while True:\n",
    "    if cv2.waitKey(1) == 27: # Esp key\n",
    "        break\n",
    "# When everything done, destroy windows\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Program of 2D-DFT\n",
    "\n",
    "import numpy as np\n",
    "import math\n",
    "\n",
    "class DFT2D:\n",
    "    def __init__(self, M, N):\n",
    "        self.M = M\n",
    "        self.N = N\n",
    "        self.fftM = FFT(M)\n",
    "        self.fftN = FFT(N)\n",
    "        \n",
    "    def dft2d(self, data):\n",
    "        dataOut = np.empty([self.M, self.N], dtype='complex64')\n",
    "        rowVect = np.empty([self.M], dtype='complex64')\n",
    "        colVect = np.empty([self.N], dtype='complex64')\n",
    "\n",
    "        \n",
    "        return dataOut\n",
    "    \n",
    "    \n",
    "    def idft2d(self, data):\n",
    "        dataOut = np.empty([self.M, self.N], dtype='complex64')\n",
    "        rowVect = np.empty([self.M], dtype='complex64')\n",
    "        colVect = np.empty([self.N], dtype='complex64')\n",
    "\n",
    "        \n",
    "        return dataOut\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Image degradation\n",
    "\n",
    "- For image observation $h[k,l]$ expresses blur, etc.\n",
    "- However, noise may be added in observation so that we consider the following model.\n",
    "$$\n",
    "d[m,n] = \\sum_{k = 0}^{M-1} \\sum_{l = 0}^{N-1} h[k,l] g[m-k,n-l] + n[m,n],\n",
    "$$\n",
    "where $n[m,n]$ expresses noise.\n",
    "- Let $N[k,l]$ be the result of 2-dimensional DFT (2D-DFT) of $n[m,n]$, respectively for  $m, k =0,1,2,\\ldots,M-1$ and $n, l =0,1,2,\\ldots,N-1$.\n",
    "- We have\n",
    "$$\n",
    "D[k,l] = H[k,l]G[k,l] + N[k,l].\n",
    "$$\n",
    "- We consider a problem to estimate the original image $g[m,n]$ from the observed image $d[m,n]$.\n",
    "- Here, we assume that the estimation is done by convolusion between $p[m,n]$ and $d[m,n]$.\n",
    "- Let $\\hat{g}[m,n]$ be the estimated original image.\n",
    "$$\n",
    "\\hat{f}[m,n] =  \\sum_{k = 0}^{M-1} \\sum_{l = 0}^{N-1} p[k,l]d[m-k,n-l]\n",
    "$$\n",
    "- Let $P[k,l]$ and $\\hat{G}[m,n]$ be the results of 2-dimensional DFT (2D-DFT) of $p[m,n]$ and $\\hat{g}[m,n]$, respectively for  $m, k =0,1,2,\\ldots,M-1$ and $n, l =0,1,2,\\ldots,N-1$.\n",
    "- We have\n",
    "$$\n",
    "\\hat{G}[k,l] =  P[k,l]D[k,l].\n",
    "$$\n",
    "\n",
    "## Wiener filter\n",
    "\n",
    "- We have decide $p[m,n]$ or $P[k,l]$ for restoration.\n",
    "- Wiener filter is a classical method for restoration.\n",
    "- For a 2D-singal $x[m,n]$, $E_x$ denotes the ensemble expectation with respect to $x$.\n",
    "- The correlation matrix $u[k,l]$ of $x[k,l]$ is given by \n",
    "$$\n",
    "u[k,l] = E_{x} x[m+k,n+l] x[m,n]\n",
    "$$\n",
    "- We assume that the signal is statistically motion invariant.\n",
    "- Then, $u[k,l]$ does not depend on $m$ and $n$.\n",
    "- The power spectrum is given as the expectation of $|X[k,l]|^2$ for a 2D-DFT $X[k,l]$ of a signal $x[k,l]$.\n",
    "- The averaged power spectrum and the results of 2-dimensional DFT (2D-DFT) of correlation matrix coincide with each other.\n",
    "- We assume that the followings are known.\n",
    "    - $H[k,l]$ : The results of 2-dimensional DFT (2D-DFT) of the degradation $h[m,n]$.\n",
    "    - $U[k,l]$ : The averaged power spectrum for all images.\n",
    "    - $V[k,l]$ : The averaged power spectrum for all noises.\n",
    "- Wiener filter is defined as\n",
    "$$\n",
    "P[k,l] = \\frac{U[k,l]\\overline{H[k,l]}}{H[k,l]U[k,l]\\overline{H[k,l]} +V[k,l]}.\n",
    "$$\n",
    "\n",
    "## Practice\n",
    "- Make a program for Winer filter\n",
    "    - Use the previous  model of $h[m,n]$\n",
    "    - The correlation matrix $u[m,n]$ of all images is given by \n",
    "$$\n",
    "u[m,n] = 128.0^2 \\gamma^{m^2 + n^2}\n",
    "$$\n",
    "(They are periodic so that shorter distance to $(0,0)$ should be used.)\n",
    "    - The correlation matrix $v[m,n]$ of all noises is given by \n",
    "$$\n",
    "v[m,n]  = \n",
    "\\left\\{\n",
    "\\begin{array}{cc}\n",
    "1 & (m = 0, \\, n = 0) \\\\\n",
    "0 & \\mbox{(else)}\n",
    "\\end{array}\n",
    "\\right. .\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Wiener filter\n",
    "\n",
    "def makeu(M, N, gamma):\n",
    "    uC = np.empty([M, N], dtype='complex64')\n",
    "\n",
    "    return uC\n",
    "\n",
    "\n",
    "# Blur size\n",
    "r = 3.0\n",
    "gamma = 0.97\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Image capture\n",
    "## OpenCV library is used.\n",
    "\n",
    "### Install\n",
    "    pip install --user opencv-python\n",
    "    pip3 install --user opencv-python\n",
    "\n",
    "### To use in python\n",
    "    import cv2\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Image capture\n",
    "import cv2\n",
    "\n",
    "cap = cv2.VideoCapture(0)\n",
    "\n",
    "while(True):\n",
    "\n",
    "    # Capture frame-by-frame\n",
    "    ret, frame = cap.read()\n",
    "\n",
    "    # Display the resulting frame\n",
    "    cv2.imshow('frame', frame)\n",
    "    if cv2.waitKey(1) == 27: # Esp key\n",
    "        break\n",
    "\n",
    "# When everything done, release the capture\n",
    "cap.release()\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Image capture with crop\n",
    "import cv2\n",
    "\n",
    "cv2.startWindowThread()\n",
    "cap = cv2.VideoCapture(0)\n",
    "\n",
    "while(True):\n",
    "\n",
    "    # Capture frame-by-frame\n",
    "    ret, frame = cap.read()\n",
    "    # Crop\n",
    "    \n",
    "    crop = frame[120:120+512, 400:400+512, :]\n",
    "\n",
    "    # Display the resulting frame\n",
    "    cv2.imshow('crop', crop)\n",
    "    if cv2.waitKey(1) == 27: # Esp key\n",
    "        break\n",
    "        \n",
    "cap.release()\n",
    "\n",
    "cv2.imwrite(\"./crop.tif\", crop)\n",
    "cv2.imwrite(\"./crop.jpg\", crop)\n",
    "# When everything done, release the capture\n",
    "\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Image in markdown\n",
    "    ![Crop image](./crop.jpg)\n",
    "![Crop image](crop.jpg)"
   ]
  }
 ],
 "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.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
