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)