{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import cv2\n", "import matplotlib.pyplot as plt\n", "import matplotlib\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# transform the image using the homography H \n", "# the size of the final image is decided using the bounds -> (left, top, right, bottom) \n", "# the left, top of the input image is translated to (0,0)\n", "def warp_image_with_bounds(im, H, bounds):\n", " t = [-bounds[0],-bounds[1]] # translation of left, top to (0,0)\n", " Ht = np.array([[1,0,t[0]],[0,1,t[1]],[0,0,1]]) # translation transform\n", " result = cv2.warpPerspective(im, Ht.dot(H), (bounds[2]-bounds[0], bounds[3]-bounds[1]))\n", " return result" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Reversable handler: allows images to be reversed and still work with the code\n", "def reverse_image(xmin, xmax, ymin, ymax, bounds):\n", " for x in bounds:\n", " if x[0][0] > xmax:\n", " xmax = x[0][0].astype(np.int32)\n", " if x[0][0] < xmin:\n", " xmin = x[0][0].astype(np.int32)\n", " if x[0][1] > ymax:\n", " ymax = x[0][1].astype(np.int32)\n", " if x[0][1] < ymin:\n", " ymin = x[0][1].astype(np.int32)\n", " return xmin, xmax, ymin, ymax" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# find the homography between the source and destination images using the specified points\n", "def find_homography(im_src, im_dst):\n", " # four corresponding points on source and destination image\n", " pts_src = np.array([[1649.15, 298], [2293.79, 236.60], [2243.19, 2168.90], [1770.98, 2133.52]])\n", " pts_dst = np.array([[495, 100], [1135, 131], [1021, 1986], [555,1995]])\n", " \n", " # display images and points\n", " fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(20,10))\n", " ax[0].imshow(im_src); ax[0].plot(pts_src[:,0], pts_src[:,1], 'ro', markersize=10); ax[0].axis('off')\n", " ax[1].imshow(im_dst); ax[1].plot(pts_dst[:,0], pts_dst[:,1], 'ro', markersize=10); ax[1].axis('off')\n", " plt.show()\n", " \n", " # compute homography between src and dst\n", " # YOUR CODE HERE\n", " print('Homography: ', H)\n", " return H" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def stitch_images(im_src, im_dst):\n", " # estimate homography\n", " H = find_homography(im_src, im_dst)\n", " \n", " # load images\n", " h1, w1, d1 = im_src.shape\n", " h2, w2, d2 = im_dst.shape \n", " src_corners = np.float32([[0,0],[0,h1],[w1,h1],[w1,0]]).reshape(-1,1,2) \n", " dst_corners = np.float32([[0,0],[0,h2],[w2,h2],[w2,0]]).reshape(-1,1,2)\n", " \n", " # FIND BOUNDS OF THE STITCHED IMAGE\n", " # YOUR CODE HERE\n", " # [xmin, xmax, ymin, ymax] = ...\n", "\n", " # warp the image with bounds\n", " result = warp_image_with_bounds(im_src, H, [xmin, ymin, xmax, ymax])\n", " \n", " # place the destination image into the result\n", " # YOUR CODE HERE\n", " \n", " # visualize the result\n", " fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(20,10))\n", " ax.imshow(result)\n", " ax.axis('off')\n", " \n", " return result" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def rectify(im_src): \n", " # four corresponding points on source and destination image\n", " pts_src = np.array([[464.48358, 727.9149], [2370.2458, 796.1351], [2310.8284, 2484.0332], [277.4284, 2525.8455]]).astype(np.float32)\n", " pts_dst = np.array([[0, 0], [2000, 0], [2000, 2000], [0, 2000]]).astype(np.float32)\n", " \n", " # compute homography between src and dst\n", " # YOUR CODE HERE\n", " \n", " # compute the bounds of the output rectified image (hint: cv2.perspectiveTransform)\n", " # YOUR CODE HERE\n", " # [xmin, xmax, ymin, ymax] = ...\n", " \n", " # warp the image using the bounds\n", " result = warp_image_with_bounds(im_src, H, [xmin, ymin, xmax, ymax])\n", "\n", " # display\n", " fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(20,10))\n", " ax.imshow(result)\n", " ax.axis('off')\n", " \n", " return result " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# image stitching and rectification\n", "\n", "im_src = plt.imread( 'a5-src.png' )\n", "im_dst = plt.imread( 'a5-dst.png' )\n", "stch = stitch_images( im_src, im_dst )\n", "rect = rectify(stitch)" ] } ], "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.9.6" }, "vscode": { "interpreter": { "hash": "994d27049b6d1b3a4dc8007fc39d9d11e995dbfa516d084782a5acb2c2c0d3bb" } } }, "nbformat": 4, "nbformat_minor": 2 }