{ "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 }