{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1 1. 2-D Transforms"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import cv2 as cv\n",
    "import string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "def saveto(filename):\n",
    "    plt.savefig('LaTeX Report/figures/'+ filename)\n",
    "\n",
    "def saveimg(filename, image):\n",
    "    cv.imwrite('LaTeX Report/figures/'+ filename,image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "# points a, b and, c\n",
    "a, b, c, d = (0, 0, 1), (0, 1, 1), (1, 1, 1), (1, 0, 1)\n",
    "# matrix with row vectors of points\n",
    "P = np.array([a, b, c, d]).T"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Translation\n",
    "t = 1 # translation along each axis\n",
    "H = [[1,0,t],[0,1,t],[0,0,1]]\n",
    "Pt = np.matmul(H, P)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Rotation \n",
    "theta = np.pi/4 # Anti clockwise pi/4 rad rotation\n",
    "H = [[np.cos(theta), -np.sin(theta), 0.], [np.sin(theta), np.cos(theta), 0.], [0., 0., 1.]]\n",
    "Pt = np.matmul(H, P)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Rotation + translation.(2D Euclidean transformation)\n",
    "H = [[np.cos(theta), -np.sin(theta), t], [np.sin(theta), np.cos(theta), t], [0., 0., 1.]]\n",
    "Pt = np.matmul(H, P)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Scaled rotation (similarity transform)\n",
    "s = 0.5\n",
    "H = [[s*np.cos(theta), -s*np.sin(theta), t], [s*np.sin(theta), s*np.cos(theta), t], [0., 0., 1.]]\n",
    "Pt = np.matmul(H, P)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Affine Parallel lines remain parallel under affine transformation\n",
    "a00 = 3 ; a01 = 2 ; a02 = 3\n",
    "a10 = 1 ; a11 = 3 ; a12 = 3\n",
    "H = [[a00, a01, a02],[a10, a11, a12],[0, 0, 1]]\n",
    "Pt = np.matmul(H, P)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Projective(perspective transform or homography)\n",
    "a20 = 1.5 ; a21 = 2; a22 = 1\n",
    "H = [[a00, a01, a02],[a10, a11, a12],[a20,a21,a22]]\n",
    "Pt = np.matmul(H, P)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.\n",
      "The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "P = P/P[-1, :]\n",
    "P = np.insert(P,4,P[:,0],axis=1)\n",
    "x = P[0, :]\n",
    "y = P[1, :]\n",
    "Pt = Pt/Pt[-1, :]\n",
    "Pt = np.insert(Pt,4,Pt[:,0],axis=1)\n",
    "xt = Pt[0, :]\n",
    "yt = Pt[1, :]\n",
    "fig, ax = plt.subplots(1,1, sharex=True, sharey=True)\n",
    "ax.plot(x, y, color='#6699cc', alpha=0.7,linewidth=3, solid_capstyle='round', zorder=2)\n",
    "ax.set_aspect('equal')\n",
    "ax.plot(xt, yt, color='#ff00cc', alpha=0.7,linewidth=3, solid_capstyle='round', zorder=2)\n",
    "ax.set_aspect('equal')\n",
    "saveto('Projective.eps')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2 2. Warping Using a Given Homography"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0.62544644, 0.057759174, 222.01217], [0.22240536, 1.1652147, -25.605611], [0.00049212545, -3.6542424e-05, 1.0]]\n"
     ]
    }
   ],
   "source": [
    "im1 = cv.imread('../images/graf/img1.ppm', cv.IMREAD_ANYCOLOR)\n",
    "saveimg('im1.png', im1)\n",
    "im5 = cv.imread('../images/graf/img5.ppm', cv.IMREAD_ANYCOLOR)\n",
    "saveimg('im5.png', im5)\n",
    "with open('../images/graf/H1to5p') as f:\n",
    "    H = [[float(x) for x in line.split()] for line in f]\n",
    "print(H)\n",
    "H = np.array(H)\n",
    "im5_warped = cv.warpPerspective(im5, np.linalg.inv(H), (900,900))\n",
    "saveimg('im5warped.png', im5_warped)\n",
    "im5_warped[0:im1.shape[0], 0:im1.shape[1]] = im1\n",
    "saveimg('im5warped2.png', im5_warped)\n",
    "\n",
    "# cv.namedWindow(\"Image 1\", cv.WINDOW_AUTOSIZE)\n",
    "# cv.imshow(\"Image 1\", im1)\n",
    "# cv.waitKey(0)\n",
    "# cv.namedWindow(\"Image 5\", cv.WINDOW_AUTOSIZE)\n",
    "# cv.imshow(\"Image 5\", im5)\n",
    "# cv.waitKey(0)\n",
    "# cv.namedWindow(\"Image 5 Warped\", cv.WINDOW_AUTOSIZE)\n",
    "# cv.imshow(\"Image 5 Warped\", im5_warped)\n",
    "# cv.waitKey(0)\n",
    "# cv.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Computing the Homogrpahy Using Mouse-Clicked Points and Warping"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "#https://learnopencv.com/homography-examples-using-opencv-python-c/\n",
    "import cv2 as cv\n",
    "import numpy as np\n",
    "N = 5\n",
    "global n\n",
    "n = 0\n",
    "p1 = np.empty((N,2))\n",
    "p2 = np.empty((N,2))\n",
    "# mouse callback function\n",
    "def draw_circle(event,x,y,flags,param):\n",
    "    global n\n",
    "    p = param[0]\n",
    "    if event == cv.EVENT_LBUTTONDOWN:\n",
    "        cv.circle(param[1],(x,y),5,(255,0,0),-1)\n",
    "        p[n] = (x,y)\n",
    "        n += 1\n",
    "im1 = cv.imread('../images/graf/img1.ppm', cv.IMREAD_ANYCOLOR)\n",
    "im4 = cv.imread('../images/graf/img4.ppm', cv.IMREAD_ANYCOLOR)\n",
    "\n",
    "im1copy = im1.copy()\n",
    "im4copy = im4.copy()\n",
    "cv.namedWindow('Image 1', cv.WINDOW_AUTOSIZE)\n",
    "param = [p1, im1copy]\n",
    "cv.setMouseCallback('Image 1',draw_circle, param)\n",
    "while(1):\n",
    "    cv.imshow(\"Image 1\", im1copy)\n",
    "    if n == N:\n",
    "        break\n",
    "    if cv.waitKey(20) & 0xFF == 27:\n",
    "        break\n",
    "        \n",
    "param = [p2, im4copy]\n",
    "n = 0\n",
    "cv.namedWindow(\"Image 4\", cv.WINDOW_AUTOSIZE)\n",
    "cv.setMouseCallback('Image 4',draw_circle, param)\n",
    "while(1):\n",
    "    cv.imshow(\"Image 4\", im4copy)\n",
    "    if n == N:\n",
    "        break\n",
    "    if cv.waitKey(20) & 0xFF == 27:\n",
    "        break\n",
    "cv.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "H, status = cv.findHomography(p1,p2)\n",
    "H = np.array(H)\n",
    "im4_warped = cv.warpPerspective(im4, np.linalg.inv(H), (900,900))\n",
    "saveimg('im4warped.png', im4_warped)\n",
    "im4_warped[0:im1.shape[0], 0:im1.shape[1]] = im1\n",
    "saveimg('im4warped2.png', im4_warped)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 6.59894211e-01  6.86220378e-01 -3.13000247e+01]\n",
      " [-1.50977725e-01  9.56863356e-01  1.52906596e+02]\n",
      " [ 4.12755346e-04 -1.98886486e-05  1.00000000e+00]]\n",
      "[[157. 560.]\n",
      " [141. 130.]\n",
      " [544. 542.]\n",
      " [405. 209.]\n",
      " [682.  56.]] [[434. 631.]\n",
      " [143. 243.]\n",
      " [576. 486.]\n",
      " [325. 250.]\n",
      " [358.  81.]]\n"
     ]
    }
   ],
   "source": [
    "print(H)\n",
    "print(p1,p2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Marking the Selected points on the images\n",
    "im1 = cv.imread('../images/graf/img1.ppm', cv.IMREAD_ANYCOLOR)\n",
    "im4 = cv.imread('../images/graf/img4.ppm', cv.IMREAD_ANYCOLOR)\n",
    "\n",
    "for point in p1.astype(np.int32):\n",
    "    cv.circle(im1, tuple(point), radius=8, color=(255, 255, 0), thickness=-1)\n",
    "saveimg('im1points.png', im1)\n",
    "for point in p2.astype(np.int32):\n",
    "    cv.circle(im4, tuple(point), radius=8, color=(255, 255, 0), thickness=-1)\n",
    "saveimg('im4points.png', im4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Computing the Homogrpahy Using Mouse-Clicked Points without OpenCV"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "#============================ Normalization ======================\n",
    "def normalizePoints(points):\n",
    "    \"\"\"\n",
    "       Normalizing the point cloud(pre-conditioning) \n",
    "    \"\"\"\n",
    "    # Calculating the centroid of the points.\n",
    "    centroid = sum(points)/len(points)\n",
    "    # Calculating the scaling factor.\n",
    "    total_dist = sum(np.sqrt(np.sum(((points - centroid)**2),axis = 1)))\n",
    "    avg_dist  = total_dist/len(points)\n",
    "    # For average distance from the origin to be sqrt(2) after scaling\n",
    "    scale = np.sqrt(2)/avg_dist    \n",
    "    # Defining Similarity transformation: translation and scaling\n",
    "    xt, yt = centroid\n",
    "    transform = np.array([[scale, 0, -xt*scale],\n",
    "                          [0, scale, -yt*scale],\n",
    "                          [0, 0,1]])\n",
    "    # Making the points homogeneous by adding 1 at the end\n",
    "    points = np.concatenate((points, np.ones((len(points),1))), axis =1)\n",
    "    # Similarity transformation through matrix multiplication\n",
    "    normalized_points = transform.dot(points.T).T\n",
    "    return transform, normalized_points\n",
    "\n",
    "#===================== Calculating homography ====================\n",
    "def calcHomography(p1,p2):\n",
    "    \"\"\"\n",
    "        The normalized DLT for 2D homographies.Given in the \n",
    "        Multiple View Geometry in Computer Vision Second Edition \n",
    "        by Richard Hartley & Andrew Zisserman. Algorithm 4.2 \n",
    "    \"\"\"\n",
    "    # Normalizing the points using predefined function\n",
    "    T1,p1 = normalizePoints(p1)\n",
    "    T2,p2 = normalizePoints(p2)\n",
    "    #Initialising an array to keep the coefficint matrix\n",
    "    A = np.zeros((2*len(p1), 9))\n",
    "    row = 0\n",
    "    # Filling rows of the matrix according to the expressions\n",
    "    for point1, point2 in zip(p1,p2):\n",
    "        # Coefficients of the current row \n",
    "        A[row, 3:6] = -point2[2]*point1\n",
    "        A[row, 6:9] =  point2[1]*point1\n",
    "        # Coefficients of the next row \n",
    "        A[row+1, 0:3] =  point2[2]*point1\n",
    "        A[row+1, 6:9] = -point2[0]*point1    \n",
    "        row+=2    \n",
    "    # Singular Value decomposition of A\n",
    "    U, D, VT = np.linalg.svd(A)\n",
    "    # unit singular vector corresponding to the smallest \n",
    "    # singular value, is the solution h. That is last column of V.\n",
    "    # i.e. Last row of the V^T\n",
    "    h =   VT[-1]\n",
    "    # Reshaping to get 3x3 homography\n",
    "    H = h.reshape((3,3))\n",
    "    # Denormalization\n",
    "    H = np.linalg.inv(T2).dot(H).dot(T1)\n",
    "    H = H/H[-1,-1]\n",
    "    return H"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "myH = calcHomography(p1,p2)\n",
    "myH = np.array(myH)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "im1 = cv.imread('../images/graf/img1.ppm', cv.IMREAD_ANYCOLOR)\n",
    "im4 = cv.imread('../images/graf/img4.ppm', cv.IMREAD_ANYCOLOR)\n",
    "\n",
    "im4_warped = cv.warpPerspective(im4, np.linalg.inv(myH), (900,900))\n",
    "saveimg('myim4warped.png', im4_warped)\n",
    "im4_warped[0:im1.shape[0], 0:im1.shape[1]] = im1\n",
    "saveimg('myim4warped2.png', im4_warped)\n",
    "\n",
    "for point in p1.astype(np.int32):\n",
    "    cv.circle(im1, tuple(point), radius=8, color=(0, 255, 0), thickness=-1)\n",
    "saveimg('myim1points.png', im1)\n",
    "for point in p2.astype(np.int32):\n",
    "    cv.circle(im4, tuple(point), radius=8, color=(0, 255, 0), thickness=-1)\n",
    "saveimg('myim4points.png', im4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 6.60355290e-01  6.85735356e-01 -3.13168167e+01]\n",
      " [-1.50699933e-01  9.56965356e-01  1.52763838e+02]\n",
      " [ 4.13108386e-04 -2.02092511e-05  1.00000000e+00]]\n",
      "[[157. 560.]\n",
      " [141. 130.]\n",
      " [544. 542.]\n",
      " [405. 209.]\n",
      " [682.  56.]] [[434. 631.]\n",
      " [143. 243.]\n",
      " [576. 486.]\n",
      " [325. 250.]\n",
      " [358.  81.]]\n"
     ]
    }
   ],
   "source": [
    "print(myH)\n",
    "print(p1,p2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "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.9.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}