Mercurial Hosting > traffic-intelligence
comparison python/cvutils.py @ 769:dfdb2a3722cc dev
moved import statements together
author | Nicolas Saunier <nicolas.saunier@polymtl.ca> |
---|---|
date | Wed, 20 Jan 2016 23:47:23 -0500 |
parents | 6022350f8173 |
children | e92a96f2bdd3 |
comparison
equal
deleted
inserted
replaced
768:f8e0a8ea8402 | 769:dfdb2a3722cc |
---|---|
1 #! /usr/bin/env python | 1 #! /usr/bin/env python |
2 '''Image/Video utilities''' | 2 '''Image/Video utilities''' |
3 | 3 |
4 import utils | 4 import utils, moving |
5 | 5 |
6 try: | 6 try: |
7 import cv2 | 7 import cv2 |
8 opencvAvailable = True | 8 opencvAvailable = True |
9 except ImportError: | 9 except ImportError: |
14 skimageAvailable = True | 14 skimageAvailable = True |
15 except ImportError: | 15 except ImportError: |
16 print('Scikit-image library could not be loaded (HoG-based classification methods will not be available)') | 16 print('Scikit-image library could not be loaded (HoG-based classification methods will not be available)') |
17 skimageAvailable = False | 17 skimageAvailable = False |
18 | 18 |
19 from sys import stdout | 19 from sys import stdout, maxint |
20 from numpy import dot, array, append, float32 | 20 from os import listdir |
21 from copy import deepcopy | |
22 from math import floor, log10, ceil | |
23 | |
24 from numpy import dot, array, append, float32, loadtxt, savetxt, append, zeros, ones, identity, abs as npabs, logical_and, unravel_index, sum as npsum, isnan, mgrid, median, floor as npfloor, ceil as npceil | |
25 from matplotlib.mlab import find | |
26 from matplotlib.pyplot import imread, imsave | |
27 | |
28 | |
21 | 29 |
22 #import aggdraw # agg on top of PIL (antialiased drawing) | 30 #import aggdraw # agg on top of PIL (antialiased drawing) |
23 | 31 |
24 | 32 |
25 cvRed = (0,0,255) | 33 cvRed = (0,0,255) |
75 return dot(rgb[...,:3], [0.299, 0.587, 0.144]) | 83 return dot(rgb[...,:3], [0.299, 0.587, 0.144]) |
76 | 84 |
77 def matlab2PointCorrespondences(filename): | 85 def matlab2PointCorrespondences(filename): |
78 '''Loads and converts the point correspondences saved | 86 '''Loads and converts the point correspondences saved |
79 by the matlab camera calibration tool''' | 87 by the matlab camera calibration tool''' |
80 from numpy.lib.io import loadtxt, savetxt | |
81 from numpy.lib.function_base import append | |
82 points = loadtxt(filename, delimiter=',') | 88 points = loadtxt(filename, delimiter=',') |
83 savetxt(utils.removeExtension(filename)+'-point-correspondences.txt',append(points[:,:2].T, points[:,3:].T, axis=0)) | 89 savetxt(utils.removeExtension(filename)+'-point-correspondences.txt',append(points[:,:2].T, points[:,3:].T, axis=0)) |
84 | 90 |
85 def loadPointCorrespondences(filename): | 91 def loadPointCorrespondences(filename): |
86 '''Loads and returns the corresponding points in world (first 2 lines) and image spaces (last 2 lines)''' | 92 '''Loads and returns the corresponding points in world (first 2 lines) and image spaces (last 2 lines)''' |
87 from numpy import loadtxt, float32 | |
88 points = loadtxt(filename, dtype=float32) | 93 points = loadtxt(filename, dtype=float32) |
89 return (points[:2,:].T, points[2:,:].T) # (world points, image points) | 94 return (points[:2,:].T, points[2:,:].T) # (world points, image points) |
90 | 95 |
91 def cvMatToArray(cvmat): | 96 def cvMatToArray(cvmat): |
92 '''Converts an OpenCV CvMat to numpy array.''' | 97 '''Converts an OpenCV CvMat to numpy array.''' |
93 print('Deprecated, use new interface') | 98 print('Deprecated, use new interface') |
94 from numpy import zeros | |
95 a = zeros((cvmat.rows, cvmat.cols))#array([[0.0]*cvmat.width]*cvmat.height) | 99 a = zeros((cvmat.rows, cvmat.cols))#array([[0.0]*cvmat.width]*cvmat.height) |
96 for i in xrange(cvmat.rows): | 100 for i in xrange(cvmat.rows): |
97 for j in xrange(cvmat.cols): | 101 for j in xrange(cvmat.cols): |
98 a[i,j] = cvmat[i,j] | 102 a[i,j] = cvmat[i,j] |
99 return a | 103 return a |
104 | |
105 def createWhiteImage(height, width, filename): | |
106 img = ones((height, width, 3), uint8)*255 | |
107 imsave(filename, img) | |
100 | 108 |
101 if opencvAvailable: | 109 if opencvAvailable: |
102 def computeHomography(srcPoints, dstPoints, method=0, ransacReprojThreshold=3.0): | 110 def computeHomography(srcPoints, dstPoints, method=0, ransacReprojThreshold=3.0): |
103 '''Returns the homography matrix mapping from srcPoints to dstPoints (dimension Nx2)''' | 111 '''Returns the homography matrix mapping from srcPoints to dstPoints (dimension Nx2)''' |
104 H, mask = cv2.findHomography(srcPoints, dstPoints, method, ransacReprojThreshold) | 112 H, mask = cv2.findHomography(srcPoints, dstPoints, method, ransacReprojThreshold) |
130 cv2.imshow(windowName, resizedImg) | 138 cv2.imshow(windowName, resizedImg) |
131 else: | 139 else: |
132 cv2.imshow(windowName, img) | 140 cv2.imshow(windowName, img) |
133 | 141 |
134 def computeUndistortMaps(width, height, undistortedImageMultiplication, intrinsicCameraMatrix, distortionCoefficients): | 142 def computeUndistortMaps(width, height, undistortedImageMultiplication, intrinsicCameraMatrix, distortionCoefficients): |
135 from copy import deepcopy | |
136 from numpy import identity | |
137 newImgSize = (int(round(width*undistortedImageMultiplication)), int(round(height*undistortedImageMultiplication))) | 143 newImgSize = (int(round(width*undistortedImageMultiplication)), int(round(height*undistortedImageMultiplication))) |
138 newCameraMatrix = deepcopy(intrinsicCameraMatrix) | 144 newCameraMatrix = deepcopy(intrinsicCameraMatrix) |
139 newCameraMatrix[0,2] = newImgSize[0]/2. | 145 newCameraMatrix[0,2] = newImgSize[0]/2. |
140 newCameraMatrix[1,2] = newImgSize[1]/2. | 146 newCameraMatrix[1,2] = newImgSize[1]/2. |
141 return cv2.initUndistortRectifyMap(intrinsicCameraMatrix, array(distortionCoefficients), identity(3), newCameraMatrix, newImgSize, cv2.CV_32FC1) | 147 return cv2.initUndistortRectifyMap(intrinsicCameraMatrix, array(distortionCoefficients), identity(3), newCameraMatrix, newImgSize, cv2.CV_32FC1) |
212 else: | 218 else: |
213 print('Video capture for {} failed'.format(filename)) | 219 print('Video capture for {} failed'.format(filename)) |
214 | 220 |
215 def getImagesFromVideo(videoFilename, firstFrameNum = 0, nFrames = 1, saveImage = False, outputPrefix = 'image'): | 221 def getImagesFromVideo(videoFilename, firstFrameNum = 0, nFrames = 1, saveImage = False, outputPrefix = 'image'): |
216 '''Returns nFrames images from the video sequence''' | 222 '''Returns nFrames images from the video sequence''' |
217 from math import floor, log10 | |
218 images = [] | 223 images = [] |
219 capture = cv2.VideoCapture(videoFilename) | 224 capture = cv2.VideoCapture(videoFilename) |
220 if capture.isOpened(): | 225 if capture.isOpened(): |
221 rawCount = capture.get(cv2.CAP_PROP_FRAME_COUNT) | 226 rawCount = capture.get(cv2.CAP_PROP_FRAME_COUNT) |
222 if rawCount < 0: | 227 if rawCount < 0: |
281 return croppedImg, yCropMin, yCropMax, xCropMin, xCropMax | 286 return croppedImg, yCropMin, yCropMax, xCropMin, xCropMax |
282 | 287 |
283 | 288 |
284 def displayTrajectories(videoFilename, objects, boundingBoxes = {}, homography = None, firstFrameNum = 0, lastFrameNumArg = None, printFrames = True, rescale = 1., nFramesStep = 1, saveAllImages = False, undistort = False, intrinsicCameraMatrix = None, distortionCoefficients = None, undistortedImageMultiplication = 1., annotations = [], gtMatches = {}, toMatches = {}): | 289 def displayTrajectories(videoFilename, objects, boundingBoxes = {}, homography = None, firstFrameNum = 0, lastFrameNumArg = None, printFrames = True, rescale = 1., nFramesStep = 1, saveAllImages = False, undistort = False, intrinsicCameraMatrix = None, distortionCoefficients = None, undistortedImageMultiplication = 1., annotations = [], gtMatches = {}, toMatches = {}): |
285 '''Displays the objects overlaid frame by frame over the video ''' | 290 '''Displays the objects overlaid frame by frame over the video ''' |
286 from moving import userTypeNames | |
287 from math import ceil, log10 | |
288 | |
289 capture = cv2.VideoCapture(videoFilename) | 291 capture = cv2.VideoCapture(videoFilename) |
290 width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)) | 292 width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)) |
291 height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) | 293 height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
292 | 294 |
293 windowName = 'frame' | 295 windowName = 'frame' |
300 key = -1 | 302 key = -1 |
301 ret = True | 303 ret = True |
302 frameNum = firstFrameNum | 304 frameNum = firstFrameNum |
303 capture.set(cv2.CAP_PROP_POS_FRAMES, firstFrameNum) | 305 capture.set(cv2.CAP_PROP_POS_FRAMES, firstFrameNum) |
304 if lastFrameNumArg is None: | 306 if lastFrameNumArg is None: |
305 from sys import maxint | |
306 lastFrameNum = maxint | 307 lastFrameNum = maxint |
307 else: | 308 else: |
308 lastFrameNum = lastFrameNumArg | 309 lastFrameNum = lastFrameNumArg |
309 nZerosFilename = int(ceil(log10(lastFrameNum))) | 310 nZerosFilename = int(ceil(log10(lastFrameNum))) |
310 objectToDeleteIds = [] | 311 objectToDeleteIds = [] |
331 cvPlot(img, obj.projectedPositions, cvColors[obj.getNum()], frameNum-obj.getFirstInstant()) | 332 cvPlot(img, obj.projectedPositions, cvColors[obj.getNum()], frameNum-obj.getFirstInstant()) |
332 if frameNum not in boundingBoxes.keys() and obj.hasFeatures(): | 333 if frameNum not in boundingBoxes.keys() and obj.hasFeatures(): |
333 imgcrop, yCropMin, yCropMax, xCropMin, xCropMax = imageBox(img, obj, frameNum, homography, width, height) | 334 imgcrop, yCropMin, yCropMax, xCropMin, xCropMax = imageBox(img, obj, frameNum, homography, width, height) |
334 cv2.rectangle(img, (xCropMin, yCropMin), (xCropMax, yCropMax), cvBlue, 1) | 335 cv2.rectangle(img, (xCropMin, yCropMin), (xCropMax, yCropMax), cvBlue, 1) |
335 objDescription = '{} '.format(obj.num) | 336 objDescription = '{} '.format(obj.num) |
336 if userTypeNames[obj.userType] != 'unknown': | 337 if moving.userTypeNames[obj.userType] != 'unknown': |
337 objDescription += userTypeNames[obj.userType][0].upper() | 338 objDescription += moving.userTypeNames[obj.userType][0].upper() |
338 if len(annotations) > 0: # if we loaded annotations, but there is no match | 339 if len(annotations) > 0: # if we loaded annotations, but there is no match |
339 if frameNum not in toMatches[obj.getNum()]: | 340 if frameNum not in toMatches[obj.getNum()]: |
340 objDescription += " FA" | 341 objDescription += " FA" |
341 cv2.putText(img, objDescription, obj.projectedPositions[frameNum-obj.getFirstInstant()].asint().astuple(), cv2.FONT_HERSHEY_PLAIN, 1, cvColors[obj.getNum()]) | 342 cv2.putText(img, objDescription, obj.projectedPositions[frameNum-obj.getFirstInstant()].asint().astuple(), cv2.FONT_HERSHEY_PLAIN, 1, cvColors[obj.getNum()]) |
342 # plot object bounding boxes | 343 # plot object bounding boxes |
381 def undistortedCoordinates(map1, map2, x, y, maxDistance = 1.): | 382 def undistortedCoordinates(map1, map2, x, y, maxDistance = 1.): |
382 '''Returns the coordinates of a point in undistorted image | 383 '''Returns the coordinates of a point in undistorted image |
383 map1 and map2 are the mapping functions from undistorted image | 384 map1 and map2 are the mapping functions from undistorted image |
384 to distorted (original image) | 385 to distorted (original image) |
385 map1(x,y) = originalx, originaly''' | 386 map1(x,y) = originalx, originaly''' |
386 from numpy import abs, logical_and, unravel_index, sum | 387 distx = npabs(map1-x) |
387 from matplotlib.mlab import find | 388 disty = npabs(map2-y) |
388 distx = abs(map1-x) | |
389 disty = abs(map2-y) | |
390 indices = logical_and(distx<maxDistance, disty<maxDistance) | 389 indices = logical_and(distx<maxDistance, disty<maxDistance) |
391 closeCoordinates = unravel_index(find(indices), distx.shape) # returns i,j, ie y,x | 390 closeCoordinates = unravel_index(find(indices), distx.shape) # returns i,j, ie y,x |
392 xWeights = 1-distx[indices] | 391 xWeights = 1-distx[indices] |
393 yWeights = 1-disty[indices] | 392 yWeights = 1-disty[indices] |
394 return dot(xWeights, closeCoordinates[1])/sum(xWeights), dot(yWeights, closeCoordinates[0])/sum(yWeights) | 393 return dot(xWeights, closeCoordinates[1])/npsum(xWeights), dot(yWeights, closeCoordinates[0])/npsum(yWeights) |
395 | 394 |
396 def undistortTrajectoryFromCVMapping(map1, map2, t): | 395 def undistortTrajectoryFromCVMapping(map1, map2, t): |
397 '''test 'perfect' inversion''' | 396 '''test 'perfect' inversion''' |
398 from moving import Trajectory | 397 undistortedTrajectory = moving.Trajectory() |
399 from numpy import isnan | |
400 undistortedTrajectory = Trajectory() | |
401 for i,p in enumerate(t): | 398 for i,p in enumerate(t): |
402 res = undistortedCoordinates(map1, map2, p.x,p.y) | 399 res = undistortedCoordinates(map1, map2, p.x,p.y) |
403 if not isnan(res).any(): | 400 if not isnan(res).any(): |
404 undistortedTrajectory.addPositionXY(res[0], res[1]) | 401 undistortedTrajectory.addPositionXY(res[0], res[1]) |
405 else: | 402 else: |
406 print i,p,res | 403 print i,p,res |
407 return undistortedTrajectory | 404 return undistortedTrajectory |
408 | 405 |
409 def computeInverseMapping(originalImageSize, map1, map2): | 406 def computeInverseMapping(originalImageSize, map1, map2): |
410 'Computes inverse mapping from maps provided by cv2.initUndistortRectifyMap' | 407 'Computes inverse mapping from maps provided by cv2.initUndistortRectifyMap' |
411 from numpy import ones, isnan | |
412 invMap1 = -ones(originalImageSize) | 408 invMap1 = -ones(originalImageSize) |
413 invMap2 = -ones(originalImageSize) | 409 invMap2 = -ones(originalImageSize) |
414 for x in range(0,originalImageSize[1]): | 410 for x in range(0,originalImageSize[1]): |
415 for y in range(0,originalImageSize[0]): | 411 for y in range(0,originalImageSize[0]): |
416 res = undistortedCoordinates(x,y, map1, map2) | 412 res = undistortedCoordinates(x,y, map1, map2) |
430 | 426 |
431 The code below is based off of: | 427 The code below is based off of: |
432 https://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html | 428 https://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html |
433 Modified by Paul St-Aubin | 429 Modified by Paul St-Aubin |
434 ''' | 430 ''' |
435 from numpy import zeros, mgrid, float32, savetxt | |
436 import glob, os | 431 import glob, os |
437 | 432 |
438 # termination criteria | 433 # termination criteria |
439 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) | 434 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) |
440 | 435 |
531 invH = inv(homography) | 526 invH = inv(homography) |
532 invH /= invH[2,2] | 527 invH /= invH[2,2] |
533 return invH | 528 return invH |
534 | 529 |
535 def undistortTrajectory(invMap1, invMap2, positions): | 530 def undistortTrajectory(invMap1, invMap2, positions): |
536 from numpy import floor, ceil | 531 floorPositions = npfloor(positions) |
537 floorPositions = floor(positions) | 532 #ceilPositions = npceil(positions) |
538 #ceilPositions = ceil(positions) | |
539 undistortedTrajectory = [[],[]] | 533 undistortedTrajectory = [[],[]] |
540 for i in xrange(len(positions[0])): | 534 for i in xrange(len(positions[0])): |
541 x,y = None, None | 535 x,y = None, None |
542 if positions[0][i]+1 < invMap1.shape[1] and positions[1][i]+1 < invMap1.shape[0]: | 536 if positions[0][i]+1 < invMap1.shape[1] and positions[1][i]+1 < invMap1.shape[0]: |
543 floorX = invMap1[floorPositions[1][i], floorPositions[0][i]] | 537 floorX = invMap1[floorPositions[1][i], floorPositions[0][i]] |
561 '''Computes the translation of img2 with respect to img1 | 555 '''Computes the translation of img2 with respect to img1 |
562 (loaded using OpenCV as numpy arrays) | 556 (loaded using OpenCV as numpy arrays) |
563 img1Points are used to compute the translation | 557 img1Points are used to compute the translation |
564 | 558 |
565 TODO add diagnostic if data is all over the place, and it most likely is not a translation (eg zoom, other non linear distortion)''' | 559 TODO add diagnostic if data is all over the place, and it most likely is not a translation (eg zoom, other non linear distortion)''' |
566 from numpy import median, sum | |
567 | 560 |
568 nextPoints = array([]) | 561 nextPoints = array([]) |
569 (img2Points, status, track_error) = cv2.calcOpticalFlowPyrLK(img1, img2, img1Points, nextPoints, winSize=windowSize, maxLevel=level, criteria=criteria) | 562 (img2Points, status, track_error) = cv2.calcOpticalFlowPyrLK(img1, img2, img1Points, nextPoints, winSize=windowSize, maxLevel=level, criteria=criteria) |
570 # calcOpticalFlowPyrLK(prevImg, nextImg, prevPts[, nextPts[, status[, err[, winSize[, maxLevel[, criteria[, derivLambda[, flags]]]]]]]]) -> nextPts, status, err | 563 # calcOpticalFlowPyrLK(prevImg, nextImg, prevPts[, nextPts[, status[, err[, winSize[, maxLevel[, criteria[, derivLambda[, flags]]]]]]]]) -> nextPts, status, err |
571 delta = [] | 564 delta = [] |
572 for (k, (p1,p2)) in enumerate(zip(img1Points, img2Points)): | 565 for (k, (p1,p2)) in enumerate(zip(img1Points, img2Points)): |
573 if status[k] == 1: | 566 if status[k] == 1: |
574 dp = p2-p1 | 567 dp = p2-p1 |
575 d = sum(dp**2) | 568 d = npsum(dp**2) |
576 if d < maxTranslation2: | 569 if d < maxTranslation2: |
577 delta.append(dp) | 570 delta.append(dp) |
578 if len(delta) >= minNMatches: | 571 if len(delta) >= minNMatches: |
579 return median(delta, axis=0) | 572 return median(delta, axis=0) |
580 else: | 573 else: |
599 subplot(1,2,2) | 592 subplot(1,2,2) |
600 imshow(hogViz) | 593 imshow(hogViz) |
601 return float32(features) | 594 return float32(features) |
602 | 595 |
603 def createHOGTrainingSet(imageDirectory, classLabel, rescaleSize = (64, 64), orientations=9, pixelsPerCell=(8, 8), cellsPerBlock=(2, 2), visualize=False, normalize=False): | 596 def createHOGTrainingSet(imageDirectory, classLabel, rescaleSize = (64, 64), orientations=9, pixelsPerCell=(8, 8), cellsPerBlock=(2, 2), visualize=False, normalize=False): |
604 from os import listdir | |
605 from matplotlib.pyplot import imread | |
606 | |
607 inputData = [] | 597 inputData = [] |
608 for filename in listdir(imageDirectory): | 598 for filename in listdir(imageDirectory): |
609 img = imread(imageDirectory+filename) | 599 img = imread(imageDirectory+filename) |
610 features = HOG(img, rescaleSize, orientations, pixelsPerCell, cellsPerBlock, visualize, normalize) | 600 features = HOG(img, rescaleSize, orientations, pixelsPerCell, cellsPerBlock, visualize, normalize) |
611 inputData.append(features) | 601 inputData.append(features) |