|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "code", |
| 5 | + "execution_count": 1, |
| 6 | + "metadata": {}, |
| 7 | + "outputs": [], |
| 8 | + "source": [ |
| 9 | + "import cv2\n", |
| 10 | + "import numpy as np\n", |
| 11 | + "import matplotlib.pyplot as plt" |
| 12 | + ] |
| 13 | + }, |
| 14 | + { |
| 15 | + "cell_type": "markdown", |
| 16 | + "metadata": {}, |
| 17 | + "source": [ |
| 18 | + "# Optical Flow\n", |
| 19 | + "### Optical flow works on several assumptions:\n", |
| 20 | + "#### 1.The pixel intensities of an object do not change between consecutive frames.\n", |
| 21 | + "#### 2.Neighbouring pixels have similar motion." |
| 22 | + ] |
| 23 | + }, |
| 24 | + { |
| 25 | + "cell_type": "markdown", |
| 26 | + "metadata": {}, |
| 27 | + "source": [ |
| 28 | + "# Lucas-Kanade Optical Flow in OpenCV\n", |
| 29 | + "##### 1.Picking few 'corner' points in 1st frame & track them throught video\n", |
| 30 | + "### Parameters for Lucas Kanade Optical Flow\n", |
| 31 | + "Detect the motion of specific points or the aggregated motion of regions by modifying the winSize argument. This determines the integration window size. Small windows are more sensitive to noise and may miss larger motions. Large windows will “survive” an occlusion.\n", |
| 32 | + "\n", |
| 33 | + "The integration appears smoother with the larger window size.\n", |
| 34 | + "\n", |
| 35 | + "criteria has two here - the max number (10 above) of iterations and epsilon (0.03 above). More iterations means a more exhaustive search, and a smaller epsilon finishes earlier. These are primarily useful in exchanging speed vs accuracy, but mainly stay the same.\n", |
| 36 | + "\n", |
| 37 | + "When maxLevel is 0, it is the same algorithm without using pyramids (ie, calcOpticalFlowLK). Pyramids allow finding optical flow at various resolutions of the image." |
| 38 | + ] |
| 39 | + }, |
| 40 | + { |
| 41 | + "cell_type": "code", |
| 42 | + "execution_count": null, |
| 43 | + "metadata": {}, |
| 44 | + "outputs": [], |
| 45 | + "source": [ |
| 46 | + "# Parameters for ShiTomasi corner detection (good features to track paper)\n", |
| 47 | + "corner_track_params = dict(maxCorners = 10,\n", |
| 48 | + " qualityLevel = 0.3,\n", |
| 49 | + " minDistance = 7,\n", |
| 50 | + " blockSize = 7 )\n", |
| 51 | + "# Parameters for lucas kanade optical flow\n", |
| 52 | + "lk_params = dict( winSize = (200,200),\n", |
| 53 | + " maxLevel = 2,\n", |
| 54 | + " criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10,0.03))\n", |
| 55 | + "\n", |
| 56 | + "# Capture the video\n", |
| 57 | + "cap = cv2.VideoCapture(0)\n", |
| 58 | + "\n", |
| 59 | + "# Grab the very first frame of the stream\n", |
| 60 | + "ret, prev_frame = cap.read()\n", |
| 61 | + "\n", |
| 62 | + "# Grab a grayscale image (We will refer to this as the previous frame)\n", |
| 63 | + "prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)\n", |
| 64 | + "\n", |
| 65 | + "# Grabbing the corners\n", |
| 66 | + "prevPts = cv2.goodFeaturesToTrack(prev_gray, mask = None, **corner_track_params)\n", |
| 67 | + "\n", |
| 68 | + "# Create a matching mask of the previous frame for drawing on later\n", |
| 69 | + "mask = np.zeros_like(prev_frame)\n", |
| 70 | + "\n", |
| 71 | + "\n", |
| 72 | + "while True:\n", |
| 73 | + " \n", |
| 74 | + " # Grab current frame\n", |
| 75 | + " ret,frame = cap.read()\n", |
| 76 | + " \n", |
| 77 | + " # Grab gray scale\n", |
| 78 | + " frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n", |
| 79 | + " \n", |
| 80 | + " # Calculate the Optical Flow on the Gray Scale Frame\n", |
| 81 | + " nextPts, status, err = cv2.calcOpticalFlowPyrLK(prev_gray, frame_gray, prevPts, None, **lk_params)\n", |
| 82 | + " \n", |
| 83 | + " # Using the returned status array (the status output)\n", |
| 84 | + " # status output status vector (of unsigned chars); each element of the vector is set to 1 if\n", |
| 85 | + " # the flow for the corresponding features has been found, otherwise, it is set to 0.\n", |
| 86 | + " good_new = nextPts[status==1]\n", |
| 87 | + " good_prev = prevPts[status==1]\n", |
| 88 | + " '''\n", |
| 89 | + " good_new [[407. 341.]]\n", |
| 90 | + " ststus [[1]]\n", |
| 91 | + " nxtpts [[[407. 341.]]]\n", |
| 92 | + " good_new [[406.5915 340.9327]]\n", |
| 93 | + " ststus [[1]]\n", |
| 94 | + " nxtpts [[[406.5915 340.9327]]]\n", |
| 95 | + " good_new [[406.5915 340.9327]]\n", |
| 96 | + " ststus [[1]]\n", |
| 97 | + " nxtpts [[[406.5915 340.9327]]]\n", |
| 98 | + " good_new [[405.3383 340.77698]]\n", |
| 99 | + " ststus [[1]]\n", |
| 100 | + " '''\n", |
| 101 | + " \n", |
| 102 | + " # Use ravel to get points to draw lines and circles\n", |
| 103 | + " for i,(new,prev) in enumerate(zip(good_new,good_prev)):\n", |
| 104 | + " \n", |
| 105 | + " x_new,y_new = new.ravel()\n", |
| 106 | + " x_prev,y_prev = prev.ravel()\n", |
| 107 | + " \n", |
| 108 | + " # Lines will be drawn using the mask created from the first frame\n", |
| 109 | + " mask = cv2.line(mask, (x_new,y_new),(x_prev,y_prev), (0,255,0), 3)\n", |
| 110 | + " \n", |
| 111 | + " # Draw red circles at corner points\n", |
| 112 | + " frame = cv2.circle(frame,(x_new,y_new),8,(0,0,255),-1)\n", |
| 113 | + " \n", |
| 114 | + " # Display the image along with the mask we drew the line on.\n", |
| 115 | + " img = cv2.add(frame,mask)\n", |
| 116 | + " cv2.imshow('frame',img)\n", |
| 117 | + " \n", |
| 118 | + " k = cv2.waitKey(30) & 0xff\n", |
| 119 | + " if k == 27:\n", |
| 120 | + " break\n", |
| 121 | + " \n", |
| 122 | + " # Now update the previous frame and previous points\n", |
| 123 | + " prev_gray = frame_gray.copy()\n", |
| 124 | + " prevPts = good_new.reshape(-1,1,2)\n", |
| 125 | + " \n", |
| 126 | + " \n", |
| 127 | + "cv2.destroyAllWindows()\n", |
| 128 | + "cap.release()" |
| 129 | + ] |
| 130 | + }, |
| 131 | + { |
| 132 | + "cell_type": "markdown", |
| 133 | + "metadata": {}, |
| 134 | + "source": [ |
| 135 | + "##### 2.Picking a point 'Manually' & track them throught video" |
| 136 | + ] |
| 137 | + }, |
| 138 | + { |
| 139 | + "cell_type": "code", |
| 140 | + "execution_count": null, |
| 141 | + "metadata": {}, |
| 142 | + "outputs": [], |
| 143 | + "source": [ |
| 144 | + "import cv2\n", |
| 145 | + "import numpy as np\n", |
| 146 | + "import matplotlib.pyplot as plt\n", |
| 147 | + "\n", |
| 148 | + "clicked=False\n", |
| 149 | + "\n", |
| 150 | + "\n", |
| 151 | + "lk_params=dict(winSize=(200,200),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT,10,0.03))\n", |
| 152 | + "\n", |
| 153 | + "def track_pts(event,x,y,flags,params):\n", |
| 154 | + " global clicked,prev_pts\n", |
| 155 | + " if event==cv2.EVENT_LBUTTONDOWN:\n", |
| 156 | + " clicked=True\n", |
| 157 | + " #(x_prev,y_prev)=(x,y)\n", |
| 158 | + " \n", |
| 159 | + " prev_pts=np.array([x,y])\n", |
| 160 | + " prev_pts=np.float32(prev_pts.reshape((-1,1,2)))\n", |
| 161 | + " \n", |
| 162 | + "\n", |
| 163 | + "\n", |
| 164 | + "cv2.namedWindow('track')\n", |
| 165 | + "cv2.setMouseCallback('track',track_pts)\n", |
| 166 | + "\n", |
| 167 | + "cap=cv2.VideoCapture(0)\n", |
| 168 | + "t=0\n", |
| 169 | + "\n", |
| 170 | + "while True:\n", |
| 171 | + " ret,frame=cap.read()\n", |
| 172 | + " if clicked:\n", |
| 173 | + " frame_gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)\n", |
| 174 | + " nxtpts,status,error=cv2.calcOpticalFlowPyrLK(prev_gray,frame_gray,prev_pts,None,**lk_params)\n", |
| 175 | + " \n", |
| 176 | + " \n", |
| 177 | + " good_new=nxtpts[status==1]\n", |
| 178 | + " #print('good_new',good_new)\n", |
| 179 | + " #print('ststus',status)\n", |
| 180 | + " #print('nxtpts',nxtpts)\n", |
| 181 | + " good_prev=prev_pts[status==1]\n", |
| 182 | + " \n", |
| 183 | + " for i,(new,prev) in enumerate(zip(good_new,good_prev)):\n", |
| 184 | + " x_new,y_new=new.ravel()\n", |
| 185 | + " x_old,y_old=prev.ravel()\n", |
| 186 | + " frame=cv2.circle(frame,(x_new,y_new),5,(0,0,255),-1)\n", |
| 187 | + " mask=cv2.line(mask,(x_old,y_old),(x_new,y_new),(255,0,0),3)\n", |
| 188 | + " img=cv2.add(frame,mask)\n", |
| 189 | + "\n", |
| 190 | + " #frame==cv2.line(frame,(x_new,y_new),(x_old,y_old),(0,255,0),3)\n", |
| 191 | + " cv2.imshow('track',img)\n", |
| 192 | + " prev_pts=good_new.reshape(-1,1,2)\n", |
| 193 | + " \n", |
| 194 | + " else:\n", |
| 195 | + " frame_gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)\n", |
| 196 | + " cv2.imshow('track',frame)\n", |
| 197 | + " \n", |
| 198 | + " if t==0:\n", |
| 199 | + " mask=np.zeros_like(frame)\n", |
| 200 | + " t+=1 \n", |
| 201 | + " prev_gray=frame_gray.copy()\n", |
| 202 | + " \n", |
| 203 | + " \n", |
| 204 | + " \n", |
| 205 | + " k=cv2.waitKey(30)&0xFF\n", |
| 206 | + " if k==27:\n", |
| 207 | + " break\n", |
| 208 | + " \n", |
| 209 | + " \n", |
| 210 | + "cap.release() \n", |
| 211 | + "cv2.destroyAllWindows() " |
| 212 | + ] |
| 213 | + }, |
| 214 | + { |
| 215 | + "cell_type": "markdown", |
| 216 | + "metadata": {}, |
| 217 | + "source": [ |
| 218 | + "# Dense Optical Flow in OpenCV" |
| 219 | + ] |
| 220 | + }, |
| 221 | + { |
| 222 | + "cell_type": "markdown", |
| 223 | + "metadata": {}, |
| 224 | + "source": [ |
| 225 | + "# Dense Optical Flow in OpenCV\n", |
| 226 | + "\n", |
| 227 | + "calcOpticalFlowFarneback(prev, next, flow, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags) -> flow\n", |
| 228 | + "\n", |
| 229 | + "This function computes a dense optical flow using the Gunnar Farneback's algorithm.\n", |
| 230 | + "\n", |
| 231 | + "Here are the parameters for the function and what they represent:\n", |
| 232 | + " \n", |
| 233 | + "\n", |
| 234 | + "* prev first 8-bit single-channel input image.\n", |
| 235 | + "* next second input image of the same size and the same type as prev.\n", |
| 236 | + "* flow computed flow image that has the same size as prev and type CV_32FC2.\n", |
| 237 | + "* pyr_scale parameter, specifying the image scale (\\<1) to build pyramids for each image\n", |
| 238 | + " * pyr_scale=0.5 means a classical pyramid, where each next layer is twice smaller than the previous one.\n", |
| 239 | + " \n", |
| 240 | + "* levels number of pyramid layers including the initial image; levels=1 means that no extra layers are created and only the original images are used.\n", |
| 241 | + "* winsize averaging window size\n", |
| 242 | + " * larger values increase the algorithm robustness to image\n", |
| 243 | + "* noise and give more chances for fast motion detection, but yield more blurred motion field.\n", |
| 244 | + "* iterations number of iterations the algorithm does at each pyramid level.\n", |
| 245 | + "* poly_n size of the pixel neighborhood used to find polynomial expansion in each pixel\n", |
| 246 | + " * larger values mean that the image will be approximated with smoother surfaces, yielding more robust algorithm and more blurred motion field, typically poly_n =5 or 7.\n", |
| 247 | + "* poly_sigma standard deviation of the Gaussian that is used to smooth derivatives used as a basis for the polynomial expansion; for poly_n=5, you can set poly_sigma=1.1, for poly_n=7, a good value would be poly_sigma=1.5." |
| 248 | + ] |
| 249 | + }, |
| 250 | + { |
| 251 | + "cell_type": "code", |
| 252 | + "execution_count": 4, |
| 253 | + "metadata": {}, |
| 254 | + "outputs": [], |
| 255 | + "source": [ |
| 256 | + "cap=cv2.VideoCapture(0)\n", |
| 257 | + "ret,frame1=cap.read()\n", |
| 258 | + "\n", |
| 259 | + "prev_img=cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)\n", |
| 260 | + "hsv_mask=np.zeros_like(frame1)\n", |
| 261 | + "hsv_mask[:,:,1]=255\n", |
| 262 | + "\n", |
| 263 | + "while True:\n", |
| 264 | + " ret,frame2=cap.read()\n", |
| 265 | + " nxt_img=cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)\n", |
| 266 | + " \n", |
| 267 | + " flow=cv2.calcOpticalFlowFarneback(prev_img,nxt_img,None,0.5,3,15,3,5,1.2,0)\n", |
| 268 | + " \n", |
| 269 | + " #convet flow into polor co_ordinates\n", |
| 270 | + " mag,ang=cv2.cartToPolar(flow[:,:,0],flow[:,:,1],angleInDegrees=True)\n", |
| 271 | + " hsv_mask[:,:,0]=ang/2.0\n", |
| 272 | + " hsv_mask[:,:,2]=cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)\n", |
| 273 | + " \n", |
| 274 | + " bgr = cv2.cvtColor(hsv_mask,cv2.COLOR_HSV2BGR)\n", |
| 275 | + " cv2.imshow('frame2',bgr)\n", |
| 276 | + " \n", |
| 277 | + " k = cv2.waitKey(30) & 0xff\n", |
| 278 | + " if k == 27:\n", |
| 279 | + " break\n", |
| 280 | + " \n", |
| 281 | + " # Set the Previous image as the next iamge for the loop\n", |
| 282 | + " prev_img = nxt_img\n", |
| 283 | + "\n", |
| 284 | + " \n", |
| 285 | + "cap.release()\n", |
| 286 | + "cv2.destroyAllWindows() " |
| 287 | + ] |
| 288 | + }, |
| 289 | + { |
| 290 | + "cell_type": "code", |
| 291 | + "execution_count": null, |
| 292 | + "metadata": {}, |
| 293 | + "outputs": [], |
| 294 | + "source": [] |
| 295 | + } |
| 296 | + ], |
| 297 | + "metadata": { |
| 298 | + "kernelspec": { |
| 299 | + "display_name": "Python 3", |
| 300 | + "language": "python", |
| 301 | + "name": "python3" |
| 302 | + }, |
| 303 | + "language_info": { |
| 304 | + "codemirror_mode": { |
| 305 | + "name": "ipython", |
| 306 | + "version": 3 |
| 307 | + }, |
| 308 | + "file_extension": ".py", |
| 309 | + "mimetype": "text/x-python", |
| 310 | + "name": "python", |
| 311 | + "nbconvert_exporter": "python", |
| 312 | + "pygments_lexer": "ipython3", |
| 313 | + "version": "3.7.3" |
| 314 | + } |
| 315 | + }, |
| 316 | + "nbformat": 4, |
| 317 | + "nbformat_minor": 2 |
| 318 | +} |
0 commit comments