From e44bb5bd009206a2bb30fea79bbb442ac698f86d Mon Sep 17 00:00:00 2001 From: sccolbert Date: Fri, 23 Oct 2009 15:06:35 +0200 Subject: [PATCH] added camera calibration from Holger Rapp and fixed a bug in CvMat struct --- .../image/data/cvCalibrateCamera2TestData.pck | Bin 0 -> 23480 bytes scikits/image/opencv/opencv_backend.pyx | 32 ++- scikits/image/opencv/opencv_constants.py | 56 ++++ scikits/image/opencv/opencv_cv.pyx | 266 +++++++++++------- scikits/image/opencv/opencv_type.pxd | 11 +- scikits/image/opencv/tests/test_opencv_cv.py | 88 ++++-- 6 files changed, 310 insertions(+), 143 deletions(-) create mode 100644 scikits/image/data/cvCalibrateCamera2TestData.pck diff --git a/scikits/image/data/cvCalibrateCamera2TestData.pck b/scikits/image/data/cvCalibrateCamera2TestData.pck new file mode 100644 index 0000000000000000000000000000000000000000..f728ce439c36b42c9e5f4ab977a8235e57521289 GIT binary patch literal 23480 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 literal 0 HcmV?d00001 diff --git a/scikits/image/opencv/opencv_backend.pyx b/scikits/image/opencv/opencv_backend.pyx index d05ff84e..6c1bcecf 100644 --- a/scikits/image/opencv/opencv_backend.pyx +++ b/scikits/image/opencv/opencv_backend.pyx @@ -6,7 +6,7 @@ from opencv_constants import * from opencv_type cimport * from _libimport import cxcore - +# setup numpy tables for this module np.import_array() #----------------------------------------------------------------------------- @@ -45,8 +45,8 @@ _ipltypes = {UINT8: IPL_DEPTH_8U, INT8: IPL_DEPTH_8S, INT16: IPL_DEPTH_16S, cdef int IPLIMAGE_SIZE = sizeof(IplImage) -# a function to convert from IplImage to cvMat -# this eliminates the need for a second populate function +# a function to convert from IplImage to cvMat +# this eliminates the need for a second populate function # for CvMat ctypedef CvMat* (*cvGetMatPtr)(IplImage*, CvMat*, int*, int) cdef cvGetMatPtr c_cvGetMat @@ -67,25 +67,29 @@ cdef void populate_iplimage(np.ndarray arr, IplImage* img): img.maskROI = NULL img.imageId = NULL img.tileInfo = NULL - - cdef int channels + cdef int ndim = arr.ndim cdef np.npy_intp* shape = arr.shape cdef np.npy_intp* strides = arr.strides # nChannels is essentially the value of np.shape[2] of a 3D numpy array # for a 2D array, nChannels is 1 - if ndim == 2: - img.nChannels = 1 + if ndim == 1: + # Might happen for a 1D vector + img.nChannels = 1 + img.width = 1 else: - img.nChannels = shape[2] - + if ndim == 2: + img.nChannels = 1 + else: + img.nChannels = shape[2] + img.width = shape[1] + + img.height = shape[0] + img.widthStep = strides[0] img.depth = _ipltypes[arr.dtype] - img.width = shape[1] - img.height = shape[0] img.imageSize = arr.nbytes img.imageData = arr.data - img.widthStep = strides[0] # really doesn't matter what this is set to, because opencv only uses it to # deallocate images, but it will never attempt to deallocate images we @@ -95,14 +99,14 @@ cdef void populate_iplimage(np.ndarray arr, IplImage* img): cdef CvMat* cvmat_ptr_from_iplimage(IplImage* arr): # this functions takes an IplImage* and returns a CvMat* # it is designed so that we dont need a separate populate_cvmat - # function, or deal with OpenCV magic values. However, it needs to create a + # function, or deal with OpenCV magic values. However, it needs to create a # CvMat header to pass to the opencv conversion routine. # This means that you have to call PyMem_Free on the CvMat* when you're # done with it. cdef CvMat* mat_hdr = PyMem_Malloc(sizeof(CvMat)) mat_hdr = c_cvGetMat(arr, mat_hdr, NULL, 0) return mat_hdr - + cdef int validate_array(np.ndarray arr) except -1: if arr.ndim != 2 and arr.ndim != 3: raise ValueError('Arrays must have either 2 or 3 dimensions') diff --git a/scikits/image/opencv/opencv_constants.py b/scikits/image/opencv/opencv_constants.py index 11e91f4a..19c4881a 100644 --- a/scikits/image/opencv/opencv_constants.py +++ b/scikits/image/opencv/opencv_constants.py @@ -33,3 +33,59 @@ CV_CALIB_CB_ADAPTIVE_THRESH = 1 CV_CALIB_CB_NORMALIZE_IMAGE = 2 CV_CALIB_CB_FILTER_QUADS = 4 +#################### +# cvMat TypeValues # +#################### +CV_CN_MAX = 4 +CV_CN_SHIFT = 3 +CV_DEPTH_MAX = (1 << CV_CN_SHIFT) + +CV_8U = 0 +CV_8S = 1 +CV_16U = 2 +CV_16S = 3 +CV_32S = 4 +CV_32F = 5 +CV_64F = 6 +CV_USRTYPE1 = 7 + +def _CV_MAKETYPE(depth,cn): + return ((depth) + (((cn)-1) << CV_CN_SHIFT)) + +CV_8UC1 = _CV_MAKETYPE(CV_8U,1) +CV_8UC2 = _CV_MAKETYPE(CV_8U,2) +CV_8UC3 = _CV_MAKETYPE(CV_8U,3) +CV_8UC4 = _CV_MAKETYPE(CV_8U,4) + +CV_8SC1 = _CV_MAKETYPE(CV_8S,1) +CV_8SC2 = _CV_MAKETYPE(CV_8S,2) +CV_8SC3 = _CV_MAKETYPE(CV_8S,3) +CV_8SC4 = _CV_MAKETYPE(CV_8S,4) + +CV_16UC1 = _CV_MAKETYPE(CV_16U,1) +CV_16UC2 = _CV_MAKETYPE(CV_16U,2) +CV_16UC3 = _CV_MAKETYPE(CV_16U,3) +CV_16UC4 = _CV_MAKETYPE(CV_16U,4) + +CV_16SC1 = _CV_MAKETYPE(CV_16S,1) +CV_16SC2 = _CV_MAKETYPE(CV_16S,2) +CV_16SC3 = _CV_MAKETYPE(CV_16S,3) +CV_16SC4 = _CV_MAKETYPE(CV_16S,4) + +CV_32SC1 = _CV_MAKETYPE(CV_32S,1) +CV_32SC2 = _CV_MAKETYPE(CV_32S,2) +CV_32SC3 = _CV_MAKETYPE(CV_32S,3) +CV_32SC4 = _CV_MAKETYPE(CV_32S,4) + +CV_32FC1 = _CV_MAKETYPE(CV_32F,1) +CV_32FC2 = _CV_MAKETYPE(CV_32F,2) +CV_32FC3 = _CV_MAKETYPE(CV_32F,3) +CV_32FC4 = _CV_MAKETYPE(CV_32F,4) + +CV_64FC1 = _CV_MAKETYPE(CV_64F,1) +CV_64FC2 = _CV_MAKETYPE(CV_64F,2) +CV_64FC3 = _CV_MAKETYPE(CV_64F,3) +CV_64FC4 = _CV_MAKETYPE(CV_64F,4) + + + diff --git a/scikits/image/opencv/opencv_cv.pyx b/scikits/image/opencv/opencv_cv.pyx index 9de30edb..a6b81988 100644 --- a/scikits/image/opencv/opencv_cv.pyx +++ b/scikits/image/opencv/opencv_cv.pyx @@ -1,8 +1,9 @@ import ctypes import numpy as np + cimport numpy as np from python cimport * -#from stdlib cimport * +from stdlib cimport * from opencv_type cimport * from opencv_backend import * from opencv_backend cimport * @@ -13,6 +14,9 @@ from opencv_cv import * from _libimport import cv +# setup numpy tables for this module +np.import_array() + ################################### # opencv function declarations ################################### @@ -88,7 +92,7 @@ ctypedef void (*cvGetQuadrangleSubPixPtr)(IplImage*, IplImage*, CvMat*) cdef cvGetQuadrangleSubPixPtr c_cvGetQuadrangleSubPix c_cvGetQuadrangleSubPix = ( ctypes.addressof(cv.cvGetQuadrangleSubPix))[0] - + # cvResize ctypedef void (*cvResizePtr)(IplImage*, IplImage*, int) cdef cvResizePtr c_cvResize @@ -106,27 +110,31 @@ ctypedef void (*cvWarpPerspectivePtr)(IplImage*, IplImage*, CvMat*, int, cdef cvWarpPerspectivePtr c_cvWarpPerspective c_cvWarpPerspective = ( ctypes.addressof(cv.cvWarpPerspective))[0] - + +# cvCalibrateCamera2 +ctypedef void (*cvCalibrateCamera2Ptr)(CvMat*, CvMat*, CvMat*, + CvSize, CvMat*, CvMat*, CvMat*, CvMat*, int) +cdef cvCalibrateCamera2Ptr c_cvCalibrateCamera2 +c_cvCalibrateCamera2 = ( + ctypes.addressof(cv.cvCalibrateCamera2))[0] + # cvFindChessboardCorners -ctypedef void (*cvFindChessboardCornersPtr)(IplImage*, CvSize, CvPoint2D32f*, +ctypedef void (*cvFindChessboardCornersPtr)(IplImage*, CvSize, CvPoint2D32f*, int*, int) cdef cvFindChessboardCornersPtr c_cvFindChessboardCorners c_cvFindChessboardCorners = ( ctypes.addressof(cv.cvFindChessboardCorners))[0] # cvDrawChessboardCorners -ctypedef void (*cvDrawChessboardCornersPtr)(IplImage*, CvSize, CvPoint2D32f*, +ctypedef void (*cvDrawChessboardCornersPtr)(IplImage*, CvSize, CvPoint2D32f*, int, int) cdef cvDrawChessboardCornersPtr c_cvDrawChessboardCorners c_cvDrawChessboardCorners = ( ctypes.addressof(cv.cvDrawChessboardCorners))[0] - #################################### # Function Implementations #################################### - - def cvSobel(np.ndarray src, np.ndarray out=None, int xorder=1, int yorder=0, int aperture_size=3): @@ -511,10 +519,10 @@ def cvGoodFeaturesToTrack(np.ndarray src, int corner_count, cdef np.npy_intp cornershape[2] cornershape[0] = corner_count cornershape[1] = 2 - + cdef np.ndarray out = new_array(2, cornershape, FLOAT32) cdef CvPoint2D32f* cvcorners = array_as_cvPoint2D32f_ptr(out) - + cdef int ncorners_found ncorners_found = corner_count @@ -537,97 +545,97 @@ def cvGoodFeaturesToTrack(np.ndarray src, int corner_count, &ncorners_found, quality_level, min_distance, maskimg, block_size, use_harris, k) - - return out[:ncorners_found] + + return out[:ncorners_found] def cvGetRectSubPix(np.ndarray src, size, center): - ''' Retrieves the pixel rectangle from an image with + ''' Retrieves the pixel rectangle from an image with sub-pixel accuracy. - + Paramters: - src - source image. + src - source image. size - two tuple (height, width) of rectangle (ints) center - two tuple (x, y) of rectangle center (floats) - + the center must lie within the image, but the rectangle may extend beyond the bounds of the image, at which point the border is replicated. - + Returns: A new image of the extracted rectangle. The same dtype as the src image. ''' - + validate_array(src) - + cdef np.npy_intp* shape = clone_array_shape(src) shape[0] = size[0] shape[1] = size[1] - + cdef CvPoint2D32f cvcenter cvcenter.x = center[0] cvcenter.y = center[1] - + cdef np.ndarray out = new_array(src.ndim, shape, src.dtype) - + cdef IplImage srcimg cdef IplImage outimg populate_iplimage(src, &srcimg) populate_iplimage(out, &outimg) - + c_cvGetRectSubPix(&srcimg, &outimg, cvcenter) - + PyMem_Free(shape) - + return out def cvGetQuadrangleSubPix(np.ndarray src, np.ndarray warpmat, float_out=False): - ''' Retrieves the pixel quandrangle from an image with - sub-pixel accuracy. In english: apply and affine transform to an image. - + ''' Retrieves the pixel quandrangle from an image with + sub-pixel accuracy. In english: apply and affine transform to an image. + Parameters: src - input image warpmat - a 2x3 array which is an affine transform - float_out - return a float32 array. If true, input must be + float_out - return a float32 array. If true, input must be uint8. If false, output is same type as input. - + Return: - warped image of same size and dtype as src. Except when + warped image of same size and dtype as src. Except when float_out == True (see above) ''' validate_array(src) validate_array(warpmat) - + assert_nchannels(src, [1, 3]) - + assert_nchannels(warpmat, [1]) - + assert warpmat.shape[0] == 2, 'warpmat must be 2x3' assert warpmat.shape[1] == 3, 'warpmat must be 2x3' - + cdef np.ndarray out - + if float_out: assert_dtype(src, [UINT8]) out = new_array_like_diff_dtype(src, FLOAT32) else: out = new_array_like(src) - + cdef IplImage srcimg cdef IplImage outimg cdef IplImage cvmat cdef CvMat* cvmatptr - + populate_iplimage(src, &srcimg) populate_iplimage(out, &outimg) populate_iplimage(warpmat, &cvmat) cvmatptr = cvmat_ptr_from_iplimage(&cvmat) - + c_cvGetQuadrangleSubPix(&srcimg, &outimg, cvmatptr) - + PyMem_Free(cvmatptr) - + return out - + def cvResize(np.ndarray src, height=None, width=None, int method=CV_INTER_LINEAR): """ @@ -657,107 +665,169 @@ def cvResize(np.ndarray src, height=None, width=None, c_cvResize(&srcimg, &outimg, method) - return out - -def cvWarpAffine(np.ndarray src, np.ndarray warpmat, + return out + +def cvWarpAffine(np.ndarray src, np.ndarray warpmat, int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, fillval=(0., 0., 0., 0.)): - + ''' Applies an affine transformation to an image. - + Parameters: src - source image warpmat - 2x3 affine transformation flags - a combination of interpolation and method flags. see opencv documentation for more details fillval - a 4 tuple of a color to fill the background - defaults to black. - + defaults to black. + Returns: a warped image the same size and dtype as src ''' validate_array(src) - validate_array(warpmat) - assert len(fillval) == 4, 'fillval must be a 4-tuple' - assert_nchannels(src, [1, 3]) - assert_nchannels(warpmat, [1]) + validate_array(warpmat) + assert len(fillval) == 4, 'fillval must be a 4-tuple' + assert_nchannels(src, [1, 3]) + assert_nchannels(warpmat, [1]) assert warpmat.shape[0] == 2, 'warpmat must be 2x3' assert warpmat.shape[1] == 3, 'warpmat must be 2x3' - + cdef np.ndarray out out = new_array_like(src) - + cdef CvScalar cvfill cdef int i for i in range(4): cvfill.val[i] = fillval[i] - + cdef IplImage srcimg cdef IplImage outimg cdef IplImage cvmat cdef CvMat* cvmatptr - + populate_iplimage(src, &srcimg) populate_iplimage(out, &outimg) populate_iplimage(warpmat, &cvmat) cvmatptr = cvmat_ptr_from_iplimage(&cvmat) - + c_cvWarpAffine(&srcimg, &outimg, cvmatptr, flags, cvfill) - + PyMem_Free(cvmatptr) - + return out - -def cvWarpPerspective(np.ndarray src, np.ndarray warpmat, + +def cvWarpPerspective(np.ndarray src, np.ndarray warpmat, int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, fillval=(0., 0., 0., 0.)): - + ''' Applies a perspective transformation to an image. - + Parameters: src - source image warpmat - 3x3 perspective transformation flags - a combination of interpolation and method flags. see opencv documentation for more details fillval - a 4 tuple of a color to fill the background - defaults to black. - + defaults to black. + Returns: a warped image the same size and dtype as src ''' validate_array(src) - validate_array(warpmat) - assert len(fillval) == 4, 'fillval must be a 4-tuple' - assert_nchannels(src, [1, 3]) - assert_nchannels(warpmat, [1]) + validate_array(warpmat) + assert len(fillval) == 4, 'fillval must be a 4-tuple' + assert_nchannels(src, [1, 3]) + assert_nchannels(warpmat, [1]) assert warpmat.shape[0] == 3, 'warpmat must be 3x3' assert warpmat.shape[1] == 3, 'warpmat must be 3x3' - + cdef np.ndarray out out = new_array_like(src) - + cdef CvScalar cvfill cdef int i for i in range(4): cvfill.val[i] = fillval[i] - + cdef IplImage srcimg cdef IplImage outimg cdef IplImage cvmat - cdef CvMat* cvmatptr - + cdef CvMat* cvmatptr = NULL + populate_iplimage(src, &srcimg) populate_iplimage(out, &outimg) populate_iplimage(warpmat, &cvmat) cvmatptr = cvmat_ptr_from_iplimage(&cvmat) - c_cvWarpPerspective(&srcimg, &outimg, cvmatptr, flags, cvfill) - + PyMem_Free(cvmatptr) + + return out - return out +def cvCalibrateCamera2(np.ndarray object_points, np.ndarray image_points, + np.ndarray point_counts, image_size): -def cvFindChessboardCorners(np.ndarray src, pattern_size, + # Validate input + validate_array(object_points) + assert_ndims(object_points, [2]) + + validate_array(image_points) + assert_ndims(image_points, [2]) + + assert_dtype(point_counts, [INT32]) + assert_ndims(point_counts, [1]) + + # Allocate a new intrinsics array + cdef np.npy_intp intrinsics_shape[2] + intrinsics_shape[0] = 3 + intrinsics_shape[1] = 3 + cdef np.ndarray intrinsics = new_array(2, intrinsics_shape, FLOAT64) + cdef IplImage ipl_intrinsics + populate_iplimage(intrinsics, &ipl_intrinsics) + cdef CvMat* cvmat_intrinsics = cvmat_ptr_from_iplimage(&ipl_intrinsics) + + # Allocate a new distortion array + cdef np.npy_intp distortion_shape[2] + distortion_shape[0] = 1 + distortion_shape[1] = 5 + cdef np.ndarray distortion = new_array(2, distortion_shape, FLOAT64) + cdef IplImage ipl_distortion + populate_iplimage(distortion, &ipl_distortion) + cdef CvMat* cvmat_distortion = cvmat_ptr_from_iplimage(&ipl_distortion) + + # Make the object & image points & npoints accessible for OpenCV + cdef IplImage ipl_object_points, ipl_image_points, ipl_point_counts + cdef CvMat* cvmat_object_points, *cvmat_image_points, *cvmat_point_counts + populate_iplimage(object_points, &ipl_object_points) + populate_iplimage(image_points, &ipl_image_points) + populate_iplimage(point_counts, &ipl_point_counts) + + cvmat_object_points = cvmat_ptr_from_iplimage(&ipl_object_points) + cvmat_image_points = cvmat_ptr_from_iplimage(&ipl_image_points) + cvmat_point_counts = cvmat_ptr_from_iplimage(&ipl_point_counts) + + # Set image size + cdef CvSize cv_image_size + cv_image_size.height = image_size[0] + cv_image_size.width = image_size[1] + + # Call the function + c_cvCalibrateCamera2(cvmat_object_points, cvmat_image_points, + cvmat_point_counts, cv_image_size, cvmat_intrinsics, + cvmat_distortion, NULL, NULL, 0) + + # Convert distortion back into a vector + distortion = np.PyArray_Squeeze(distortion) + + PyMem_Free(cvmat_intrinsics) + PyMem_Free(cvmat_distortion) + PyMem_Free(cvmat_object_points) + PyMem_Free(cvmat_image_points) + PyMem_Free(cvmat_point_counts) + + return intrinsics, distortion + +def cvFindChessboardCorners(np.ndarray src, pattern_size, int flags = CV_CALIB_CB_ADAPTIVE_THRESH): """ Wrapper around the OpenCV cvFindChessboardCorners function. @@ -766,11 +836,11 @@ def cvFindChessboardCorners(np.ndarray src, pattern_size, pattern_size - Tuple of inner corners (h,w) flags - see appropriate flags in opencv docs http://opencv.willowgarage.com/documentation/cvreference.html - + returns - an nx2 array of the corners found. - + """ - + validate_array(src) assert_nchannels(src, [1, 3]) @@ -778,7 +848,7 @@ def cvFindChessboardCorners(np.ndarray src, pattern_size, cdef np.npy_intp outshape[2] outshape[0] = pattern_size[0] * pattern_size[1] - outshape[1] = 2 + outshape[1] = 2 out = new_array(2, outshape, FLOAT32) cdef CvPoint2D32f* cvpoints = array_as_cvPoint2D32f_ptr(out) @@ -791,11 +861,11 @@ def cvFindChessboardCorners(np.ndarray src, pattern_size, populate_iplimage(src, &srcimg) cdef int ncorners_found - c_cvFindChessboardCorners(&srcimg, cvpattern_size, cvpoints, + c_cvFindChessboardCorners(&srcimg, cvpattern_size, cvpoints, &ncorners_found, flags) - + return out[:ncorners_found] - + def cvDrawChessboardCorners(np.ndarray src, pattern_size, np.ndarray corners, in_place=True): """ @@ -806,28 +876,28 @@ def cvDrawChessboardCorners(np.ndarray src, pattern_size, np.ndarray corners, src : ndarray, dim 3, dtype: uint8 Image to draw into. pattern_size : array_like, shape (2,) - Number of inner corners (w,h) + Number of inner corners (h,w) corners : ndarray, shape (n,2), dtype: float32 Corners found in the image. See cvFindChessboardCorners and cvFindCornerSubPix in_place: True/False (default=True) perform the drawing on the submitted - image. If false, a copy of the image will be made and drawn to. + image. If false, a copy of the image will be made and drawn to. """ validate_array(src) assert_nchannels(src, [3]) assert_dtype(src, [UINT8]) - + assert_ndims(corners, [2]) assert_dtype(corners, [FLOAT32]) - + cdef np.ndarray out - + if not in_place: out = src.copy() else: out = src - + cdef CvSize cvpattern_size cvpattern_size.height = pattern_size[0] cvpattern_size.width = pattern_size[1] @@ -838,17 +908,17 @@ def cvDrawChessboardCorners(np.ndarray src, pattern_size, np.ndarray corners, cdef CvPoint2D32f* cvcorners = array_as_cvPoint2D32f_ptr(corners) cdef int ncount = pattern_size[0] * pattern_size[1] - + cdef int pattern_was_found - + if corners.shape[0] == ncount: pattern_was_found = 1 else: pattern_was_found = 0 - + c_cvDrawChessboardCorners(&outimg, cvpattern_size, cvcorners, ncount, pattern_was_found) - + return out - - + + diff --git a/scikits/image/opencv/opencv_type.pxd b/scikits/image/opencv/opencv_type.pxd index 1e110df6..bbfc83fe 100644 --- a/scikits/image/opencv/opencv_type.pxd +++ b/scikits/image/opencv/opencv_type.pxd @@ -31,8 +31,6 @@ cdef struct _IplImage: int BorderMode[4] # ignored by opencv int BorderConst[4] # ignored by opencv char* imageDataOrigin # pointer to origin of data. Used for deallocation, but python will handle this so we'll set it to void* - - ctypedef _IplImage IplImage @@ -43,14 +41,15 @@ cdef union CvMat_uProxy: int* i float* fl double* db - + cdef struct CvMat: int type - int step + int step int* refcount + int hdr_refcount CvMat_uProxy data int rows - int cols + int cols cdef struct CvPoint2D32f: float x @@ -64,7 +63,7 @@ cdef struct CvTermCriteria: int type int max_iter double epsilon - + cdef struct CvScalar: double val[4] diff --git a/scikits/image/opencv/tests/test_opencv_cv.py b/scikits/image/opencv/tests/test_opencv_cv.py index 0ef12394..e2bd2d2a 100644 --- a/scikits/image/opencv/tests/test_opencv_cv.py +++ b/scikits/image/opencv/tests/test_opencv_cv.py @@ -6,6 +6,7 @@ import numpy as np from numpy.testing import * from scikits.image import data_dir +import cPickle with warnings.catch_warnings(): warnings.simplefilter("ignore") @@ -17,7 +18,7 @@ opencv_skip = dec.skipif(cv is None, class OpenCVTest: lena_RGB_U8 = np.load(os.path.join(data_dir, 'lena_RGB_U8.npy')) lena_GRAY_U8 = np.load(os.path.join(data_dir, 'lena_GRAY_U8.npy')) - + class TestSobel(OpenCVTest): @opencv_skip @@ -67,7 +68,7 @@ class TestSmooth(OpenCVTest): for st in (CV_BLUR_NO_SCALE, CV_BLUR, CV_GAUSSIAN, CV_MEDIAN, CV_BILATERAL): cvSmooth(self.lena_GRAY_U8, None, st, 3, 0, 0, 0, False) - + class TestFindCornerSubPix: @opencv_skip @@ -92,14 +93,14 @@ class TestGoodFeaturesToTrack(OpenCVTest): @opencv_skip def test_cvGoodFeaturesToTrack(self): cvGoodFeaturesToTrack(self.lena_GRAY_U8, 100, 0.1, 3) - + class TestGetRectSubPix(OpenCVTest): @opencv_skip def test_cvGetRectSubPix(self): cvGetRectSubPix(self.lena_RGB_U8, (20, 20), (48.6, 48.6)) - - + + class TestGetQuadrangleSubPix(OpenCVTest): @opencv_skip def test_cvGetQuadrangleSubPix(self): @@ -107,22 +108,22 @@ class TestGetQuadrangleSubPix(OpenCVTest): [-.4, .23, 0.4]], dtype='float32') cvGetQuadrangleSubPix(self.lena_RGB_U8, warpmat) - + class TestResize(OpenCVTest): @opencv_skip def test_cvResize(self): cvResize(self.lena_RGB_U8, height=50, width=50, method=CV_INTER_LINEAR) cvResize(self.lena_RGB_U8, height=200, width=200, method=CV_INTER_CUBIC) - - + + class TestWarpAffine(OpenCVTest): @opencv_skip def test_cvWarpAffine(self): warpmat = np.array([[0.5, 0.3, 0.4], [-.4, .23, 0.4]], dtype='float32') cvWarpAffine(self.lena_RGB_U8, warpmat) - - + + class TestWarpPerspective(OpenCVTest): @opencv_skip def test_cvWarpPerspective(self): @@ -130,27 +131,64 @@ class TestWarpPerspective(OpenCVTest): [-.4, .23, 0.4], [0.0, 1.0, 1.0]], dtype='float32') cvWarpPerspective(self.lena_RGB_U8, warpmat) - - + + class TestFindChessboardCorners: @opencv_skip def test_cvFindChessboardCorners(self): - chessboard_GRAY_U8 = np.load(os.path.join(data_dir, - 'chessboard_GRAY_U8.npy')) - pts = cvFindChessboardCorners(chessboard_GRAY_U8, (7, 7)) - - + chessboard_GRAY_U8 = np.load(os.path.join(data_dir, + 'chessboard_GRAY_U8.npy')) + pts = cvFindChessboardCorners(chessboard_GRAY_U8, (7, 7)) + + class TestDrawChessboardCorners: @opencv_skip def test_cvDrawChessboardCorners(self): - chessboard_GRAY_U8 = np.load(os.path.join(data_dir, - 'chessboard_GRAY_U8.npy')) - chessboard_RGB_U8 = np.load(os.path.join(data_dir, - 'chessboard_RGB_U8.npy')) - corners = cvFindChessboardCorners(chessboard_GRAY_U8, (7, 7)) + chessboard_GRAY_U8 = np.load(os.path.join(data_dir, + 'chessboard_GRAY_U8.npy')) + chessboard_RGB_U8 = np.load(os.path.join(data_dir, + 'chessboard_RGB_U8.npy')) + corners = cvFindChessboardCorners(chessboard_GRAY_U8, (7, 7)) cvDrawChessboardCorners(chessboard_RGB_U8, (7, 7), corners) - - - + +class TestCvCalibrateCamera2(object): + def test_cvCalibrateCamear2_Identity(self): + ys = xs = range(4) + + image_points = np.array( [(4 * x, 4 * y) for x in xs for y in ys ], + dtype=np.float64) + object_points = np.array( [(x, y, 0) for x in xs for y in ys ], + dtype=np.float64) + + image_points = np.ascontiguousarray(np.vstack((image_points,) * 3)) + object_points = np.ascontiguousarray(np.vstack((object_points,) * 3)) + + intrinsics, distortions = cvCalibrateCamera2( + object_points, image_points, + np.array([16, 16, 16], dtype=np.int32), (4, 4) + ) + + assert_almost_equal(distortions, np.array([0., 0., 0., 0., 0.])) + # The intrinsics will be strange, but we can at least check + # for known zeros and ones + assert_almost_equal( intrinsics[0,1], 0) + assert_almost_equal( intrinsics[1,0], 0) + assert_almost_equal( intrinsics[2,0], 0) + assert_almost_equal( intrinsics[2,1], 0) + assert_almost_equal( intrinsics[2,2], 1) + + @dec.slow + def test_cvCalibrateCamear2_KnownData(self): + (object_points,points_count,image_points,intrinsics,distortions) =\ + cPickle.load(open(os.path.join( + data_dir, "cvCalibrateCamera2TestData.pck"), "rb") + ) + + intrinsics_test, distortion_test = cvCalibrateCamera2( + object_points, image_points, points_count, (1024,1280) + ) + + + if __name__ == '__main__': run_module_suite()