From e0f426f78bbf0fd6e161de4a778800b2fb4e7288 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Wed, 24 Feb 2016 14:18:51 +0300 Subject: [PATCH 1/4] Backport of new python tests from master branch(PR https://github.com/Itseez/opencv/pull/6025). At the moment tests requre samples/data copied to source location from master branch. --- modules/python/test/test.py | 48 ++--- modules/python/test/test_calibration.py | 71 +++++++ modules/python/test/test_camshift.py | 92 ++++++++ modules/python/test/test_dft.py | 46 ++++ modules/python/test/test_digits.py | 197 ++++++++++++++++++ modules/python/test/test_facedetect.py | 90 ++++++++ .../python/test/test_feature_homography.py | 160 ++++++++++++++ modules/python/test/test_fitline.py | 66 ++++++ modules/python/test/test_gaussian_mix.py | 58 ++++++ modules/python/test/test_grabcut.py | 67 ++++++ modules/python/test/test_houghcircles.py | 81 +++++++ modules/python/test/test_houghlines.py | 65 ++++++ modules/python/test/test_kmeans.py | 70 +++++++ modules/python/test/test_letter_recog.py | 161 ++++++++++++++ modules/python/test/test_lk_homography.py | 96 +++++++++ modules/python/test/test_lk_track.py | 111 ++++++++++ modules/python/test/test_morphology.py | 51 +++++ modules/python/test/test_mser.py | 65 ++++++ modules/python/test/test_peopledetect.py | 62 ++++++ modules/python/test/test_squares.py | 96 +++++++++ modules/python/test/test_texture_flow.py | 51 +++++ modules/python/test/test_watershed.py | 33 +++ modules/python/test/tests_common.py | 79 +++++++ modules/python/test/tst_scene_render.py | 119 +++++++++++ 24 files changed, 2005 insertions(+), 30 deletions(-) create mode 100644 modules/python/test/test_calibration.py create mode 100644 modules/python/test/test_camshift.py create mode 100644 modules/python/test/test_dft.py create mode 100644 modules/python/test/test_digits.py create mode 100644 modules/python/test/test_facedetect.py create mode 100644 modules/python/test/test_feature_homography.py create mode 100644 modules/python/test/test_fitline.py create mode 100644 modules/python/test/test_gaussian_mix.py create mode 100644 modules/python/test/test_grabcut.py create mode 100644 modules/python/test/test_houghcircles.py create mode 100644 modules/python/test/test_houghlines.py create mode 100644 modules/python/test/test_kmeans.py create mode 100644 modules/python/test/test_letter_recog.py create mode 100644 modules/python/test/test_lk_homography.py create mode 100644 modules/python/test/test_lk_track.py create mode 100644 modules/python/test/test_morphology.py create mode 100644 modules/python/test/test_mser.py create mode 100644 modules/python/test/test_peopledetect.py create mode 100644 modules/python/test/test_squares.py create mode 100644 modules/python/test/test_texture_flow.py create mode 100644 modules/python/test/test_watershed.py create mode 100644 modules/python/test/tests_common.py create mode 100644 modules/python/test/tst_scene_render.py diff --git a/modules/python/test/test.py b/modules/python/test/test.py index a4a3d759a9..e9f1a13506 100755 --- a/modules/python/test/test.py +++ b/modules/python/test/test.py @@ -122,6 +122,15 @@ class OpenCVTests(unittest.TestCase): """ Compute a hash for an image, useful for image comparisons """ return hashlib.md5(im.tostring()).digest() +#import new OpenCV tests(do we really need old ones in this case) +from tests_common import NewOpenCVTests + +basedir = os.path.abspath(os.path.dirname(__file__)) + +def load_tests(loader, tests, pattern): + tests.addTests(loader.discover(basedir, pattern='test_*.py')) + return tests + # Tests to run first; check the handful of basic operations that the later tests rely on class PreliminaryTests(OpenCVTests): @@ -2239,36 +2248,15 @@ if __name__ == '__main__': print "Local repo path:", args.repo print "Local data path:", args.data OpenCVTests.repoPath = args.repo - OpenCVTests.dataPath = args.data + NewOpenCVTests.repoPath = args.repo + try: + OpenCVTests.dataPath = os.environ['OPENCV_TEST_DATA_PATH'] + NewOpenCVTests.extraTestDataPath = OpenCVTests.dataPath + except KeyError: + OpenCVTests.dataPath = args.data + NewOpenCVTests.extraTestDataPath = args.data + if args.data is None: + print('Missing opencv extra repository. Some of tests may fail.') random.seed(0) unit_argv = [sys.argv[0]] + other; unittest.main(argv=unit_argv) -# optlist, args = getopt.getopt(sys.argv[1:], 'l:rd') -# loops = 1 -# shuffle = 0 -# doc_frags = False -# for o,a in optlist: -# if o == '-l': -# loops = int(a) -# if o == '-r': -# shuffle = 1 -# if o == '-d': -# doc_frags = True -# -# cases = [PreliminaryTests, FunctionTests, AreaTests] -# if doc_frags: -# cases += [DocumentFragmentTests] -# everything = [(tc, t) for tc in cases for t in unittest.TestLoader().getTestCaseNames(tc) ] -# if len(args) == 0: -# # cases = [NewTests] -# args = everything -# else: -# args = [(tc, t) for (tc, t) in everything if t in args] -# -# suite = unittest.TestSuite() -# for l in range(loops): -# if shuffle: -# random.shuffle(args) -# for tc,t in args: -# suite.addTest(tc(t)) -# unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/modules/python/test/test_calibration.py b/modules/python/test/test_calibration.py new file mode 100644 index 0000000000..665521862c --- /dev/null +++ b/modules/python/test/test_calibration.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +''' +camera calibration for distorted images with chess board samples +reads distorted images, calculates the calibration and write undistorted images +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +class calibration_test(NewOpenCVTests): + + def test_calibration(self): + + from glob import glob + img_names = [] + for i in range(1, 15): + if i < 10: + img_names.append('samples/data/left0{}.jpg'.format(str(i))) + elif i != 10: + img_names.append('samples/data/left{}.jpg'.format(str(i))) + + square_size = 1.0 + pattern_size = (9, 6) + pattern_points = np.zeros((np.prod(pattern_size), 3), np.float32) + pattern_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2) + pattern_points *= square_size + + obj_points = [] + img_points = [] + h, w = 0, 0 + img_names_undistort = [] + for fn in img_names: + img = self.get_sample(fn, 0) + if img is None: + continue + + h, w = img.shape[:2] + found, corners = cv2.findChessboardCorners(img, pattern_size) + if found: + term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.1) + cv2.cornerSubPix(img, corners, (5, 5), (-1, -1), term) + + if not found: + continue + + img_points.append(corners.reshape(-1, 2)) + obj_points.append(pattern_points) + + # calculate camera distortion + rms, camera_matrix, dist_coefs, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, (w, h), None, None, flags = 0) + + eps = 0.01 + normCamEps = 10.0 + normDistEps = 0.001 + + cameraMatrixTest = [[ 532.80992189, 0., 342.4952186 ], + [ 0., 532.93346422, 233.8879292 ], + [ 0., 0., 1. ]] + + distCoeffsTest = [ -2.81325576e-01, 2.91130406e-02, + 1.21234330e-03, -1.40825372e-04, 1.54865844e-01] + + self.assertLess(abs(rms - 0.196334638034), eps) + self.assertLess(cv2.norm(camera_matrix - cameraMatrixTest, cv2.NORM_L1), normCamEps) + self.assertLess(cv2.norm(dist_coefs - distCoeffsTest, cv2.NORM_L1), normDistEps) \ No newline at end of file diff --git a/modules/python/test/test_camshift.py b/modules/python/test/test_camshift.py new file mode 100644 index 0000000000..a824320eff --- /dev/null +++ b/modules/python/test/test_camshift.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +''' +Camshift tracker +================ + +This is a demo that shows mean-shift based tracking +You select a color objects such as your face and it tracks it. +This reads from video camera (0 by default, or the camera number the user enters) + +http://www.robinhewitt.com/research/track/camshift.html + +''' + +# Python 2/3 compatibility +from __future__ import print_function +import sys +PY3 = sys.version_info[0] == 3 + +if PY3: + xrange = range + +import numpy as np +import cv2 +from tst_scene_render import TestSceneRender + +from tests_common import NewOpenCVTests, intersectionRate + +class camshift_test(NewOpenCVTests): + + framesNum = 300 + frame = None + selection = None + drag_start = None + show_backproj = False + track_window = None + render = None + errors = 0 + + def prepareRender(self): + + self.render = TestSceneRender(self.get_sample('samples/data/pca_test1.jpg'), deformation = True) + + def runTracker(self): + + framesCounter = 0 + self.selection = True + + xmin, ymin, xmax, ymax = self.render.getCurrentRect() + + self.track_window = (xmin, ymin, xmax - xmin, ymax - ymin) + + while True: + framesCounter += 1 + self.frame = self.render.getNextFrame() + hsv = cv2.cvtColor(self.frame, cv2.COLOR_BGR2HSV) + mask = cv2.inRange(hsv, np.array((0., 60., 32.)), np.array((180., 255., 255.))) + + if self.selection: + x0, y0, x1, y1 = self.render.getCurrentRect() + 50 + x0 -= 100 + y0 -= 100 + + hsv_roi = hsv[y0:y1, x0:x1] + mask_roi = mask[y0:y1, x0:x1] + hist = cv2.calcHist( [hsv_roi], [0], mask_roi, [16], [0, 180] ) + cv2.normalize(hist, hist, 0, 255, cv2.NORM_MINMAX) + self.hist = hist.reshape(-1) + self.selection = False + + if self.track_window and self.track_window[2] > 0 and self.track_window[3] > 0: + self.selection = None + prob = cv2.calcBackProject([hsv], [0], self.hist, [0, 180], 1) + prob &= mask + term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 ) + track_box, self.track_window = cv2.CamShift(prob, self.track_window, term_crit) + + trackingRect = np.array(self.track_window) + trackingRect[2] += trackingRect[0] + trackingRect[3] += trackingRect[1] + + if intersectionRate(self.render.getCurrentRect(), trackingRect) < 0.4: + self.errors += 1 + + if framesCounter > self.framesNum: + break + + self.assertLess(float(self.errors) / self.framesNum, 0.4) + + def test_camshift(self): + self.prepareRender() + self.runTracker() \ No newline at end of file diff --git a/modules/python/test/test_dft.py b/modules/python/test/test_dft.py new file mode 100644 index 0000000000..f796939970 --- /dev/null +++ b/modules/python/test/test_dft.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +''' +Test for disctrete fourier transform (dft) +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import cv2 +import numpy as np +import sys + +from tests_common import NewOpenCVTests + +class dft_test(NewOpenCVTests): + def test_dft(self): + + img = self.get_sample('samples/data/rubberwhale1.png', 0) + eps = 0.001 + + #test direct transform + refDft = np.fft.fft2(img) + refDftShift = np.fft.fftshift(refDft) + refMagnitide = np.log(1.0 + np.abs(refDftShift)) + + testDft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT) + testDftShift = np.fft.fftshift(testDft) + testMagnitude = np.log(1.0 + cv2.magnitude(testDftShift[:,:,0], testDftShift[:,:,1])) + + refMagnitide = cv2.normalize(refMagnitide, 0.0, 1.0, cv2.NORM_MINMAX) + testMagnitude = cv2.normalize(testMagnitude, 0.0, 1.0, cv2.NORM_MINMAX) + + self.assertLess(cv2.norm(refMagnitide - testMagnitude), eps) + + #test inverse transform + img_back = np.fft.ifft2(refDft) + img_back = np.abs(img_back) + + img_backTest = cv2.idft(testDft) + img_backTest = cv2.magnitude(img_backTest[:,:,0], img_backTest[:,:,1]) + + img_backTest = cv2.normalize(img_backTest, 0.0, 1.0, cv2.NORM_MINMAX) + img_back = cv2.normalize(img_back, 0.0, 1.0, cv2.NORM_MINMAX) + + self.assertLess(cv2.norm(img_back - img_backTest), eps) \ No newline at end of file diff --git a/modules/python/test/test_digits.py b/modules/python/test/test_digits.py new file mode 100644 index 0000000000..587c355e37 --- /dev/null +++ b/modules/python/test/test_digits.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python + +''' +SVM and KNearest digit recognition. + +Sample loads a dataset of handwritten digits from '../data/digits.png'. +Then it trains a SVM and KNearest classifiers on it and evaluates +their accuracy. + +Following preprocessing is applied to the dataset: + - Moment-based image deskew (see deskew()) + - Digit images are split into 4 10x10 cells and 16-bin + histogram of oriented gradients is computed for each + cell + - Transform histograms to space with Hellinger metric (see [1] (RootSIFT)) + + +[1] R. Arandjelovic, A. Zisserman + "Three things everyone should know to improve object retrieval" + http://www.robots.ox.ac.uk/~vgg/publications/2012/Arandjelovic12/arandjelovic12.pdf + +''' + + +# Python 2/3 compatibility +from __future__ import print_function + +# built-in modules +from multiprocessing.pool import ThreadPool + +import cv2 + +import numpy as np +from numpy.linalg import norm + + +SZ = 20 # size of each digit is SZ x SZ +CLASS_N = 10 +DIGITS_FN = 'samples/data/digits.png' + +def split2d(img, cell_size, flatten=True): + h, w = img.shape[:2] + sx, sy = cell_size + cells = [np.hsplit(row, w//sx) for row in np.vsplit(img, h//sy)] + cells = np.array(cells) + if flatten: + cells = cells.reshape(-1, sy, sx) + return cells + +def deskew(img): + m = cv2.moments(img) + if abs(m['mu02']) < 1e-2: + return img.copy() + skew = m['mu11']/m['mu02'] + M = np.float32([[1, skew, -0.5*SZ*skew], [0, 1, 0]]) + img = cv2.warpAffine(img, M, (SZ, SZ), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR) + return img + +class StatModel(object): + def load(self, fn): + self.model.load(fn) # Known bug: https://github.com/Itseez/opencv/issues/4969 + def save(self, fn): + self.model.save(fn) + +class KNearest(StatModel): + def __init__(self, k = 3): + self.k = k + self.model = cv2.KNearest() + + def train(self, samples, responses): + self.model.train(samples, responses) + + def predict(self, samples): + retval, results, neigh_resp, dists = self.model.find_nearest(samples, self.k) + return results.ravel() + +class SVM(StatModel): + def __init__(self, C = 1, gamma = 0.5): + self.params = dict( kernel_type = cv2.SVM_RBF, + svm_type = cv2.SVM_C_SVC, + C = C, + gamma = gamma ) + self.model = cv2.SVM() + + def train(self, samples, responses): + self.model.train(samples, responses, params = self.params) + + def predict(self, samples): + return self.model.predict_all(samples).ravel() + + +def evaluate_model(model, digits, samples, labels): + resp = model.predict(samples) + err = (labels != resp).mean() + + confusion = np.zeros((10, 10), np.int32) + for i, j in zip(labels, resp): + confusion[int(i), int(j)] += 1 + + return err, confusion + +def preprocess_simple(digits): + return np.float32(digits).reshape(-1, SZ*SZ) / 255.0 + +def preprocess_hog(digits): + samples = [] + for img in digits: + gx = cv2.Sobel(img, cv2.CV_32F, 1, 0) + gy = cv2.Sobel(img, cv2.CV_32F, 0, 1) + mag, ang = cv2.cartToPolar(gx, gy) + bin_n = 16 + bin = np.int32(bin_n*ang/(2*np.pi)) + bin_cells = bin[:10,:10], bin[10:,:10], bin[:10,10:], bin[10:,10:] + mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:] + hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)] + hist = np.hstack(hists) + + # transform to Hellinger kernel + eps = 1e-7 + hist /= hist.sum() + eps + hist = np.sqrt(hist) + hist /= norm(hist) + eps + + samples.append(hist) + return np.float32(samples) + +from tests_common import NewOpenCVTests + +class digits_test(NewOpenCVTests): + + def load_digits(self, fn): + digits_img = self.get_sample(fn, 0) + digits = split2d(digits_img, (SZ, SZ)) + labels = np.repeat(np.arange(CLASS_N), len(digits)/CLASS_N) + return digits, labels + + def test_digits(self): + + digits, labels = self.load_digits(DIGITS_FN) + + # shuffle digits + rand = np.random.RandomState(321) + shuffle = rand.permutation(len(digits)) + digits, labels = digits[shuffle], labels[shuffle] + + digits2 = list(map(deskew, digits)) + samples = preprocess_hog(digits2) + + train_n = int(0.9*len(samples)) + digits_train, digits_test = np.split(digits2, [train_n]) + samples_train, samples_test = np.split(samples, [train_n]) + labels_train, labels_test = np.split(labels, [train_n]) + errors = list() + confusionMatrixes = list() + + model = KNearest(k=4) + model.train(samples_train, labels_train) + error, confusion = evaluate_model(model, digits_test, samples_test, labels_test) + errors.append(error) + confusionMatrixes.append(confusion) + + model = SVM(C=2.67, gamma=5.383) + model.train(samples_train, labels_train) + error, confusion = evaluate_model(model, digits_test, samples_test, labels_test) + errors.append(error) + confusionMatrixes.append(confusion) + + eps = 0.001 + normEps = len(samples_test) * 0.02 + + confusionKNN = [[45, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 57, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 0, 59, 1, 0, 0, 0, 0, 1, 0], + [ 0, 0, 0, 43, 0, 0, 0, 1, 0, 0], + [ 0, 0, 0, 0, 38, 0, 2, 0, 0, 0], + [ 0, 0, 0, 2, 0, 48, 0, 0, 1, 0], + [ 0, 1, 0, 0, 0, 0, 51, 0, 0, 0], + [ 0, 0, 1, 0, 0, 0, 0, 54, 0, 0], + [ 0, 0, 0, 0, 0, 1, 0, 0, 46, 0], + [ 1, 1, 0, 1, 1, 0, 0, 0, 2, 42]] + + confusionSVM = [[45, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 57, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 0, 59, 2, 0, 0, 0, 0, 0, 0], + [ 0, 0, 0, 43, 0, 0, 0, 1, 0, 0], + [ 0, 0, 0, 0, 40, 0, 0, 0, 0, 0], + [ 0, 0, 0, 1, 0, 50, 0, 0, 0, 0], + [ 0, 0, 0, 0, 1, 0, 51, 0, 0, 0], + [ 0, 0, 1, 0, 0, 0, 0, 54, 0, 0], + [ 0, 0, 0, 0, 0, 0, 0, 0, 47, 0], + [ 0, 1, 0, 1, 0, 0, 0, 0, 1, 45]] + + self.assertLess(cv2.norm(confusionMatrixes[0] - confusionKNN, cv2.NORM_L1), normEps) + self.assertLess(cv2.norm(confusionMatrixes[1] - confusionSVM, cv2.NORM_L1), normEps) + + self.assertLess(errors[0] - 0.034, eps) + self.assertLess(errors[1] - 0.018, eps) \ No newline at end of file diff --git a/modules/python/test/test_facedetect.py b/modules/python/test/test_facedetect.py new file mode 100644 index 0000000000..8d64fde10f --- /dev/null +++ b/modules/python/test/test_facedetect.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +''' +face detection using haar cascades +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +def detect(img, cascade): + rects = cascade.detectMultiScale(img, scaleFactor=1.3, minNeighbors=4, minSize=(30, 30), + flags=cv2.CASCADE_SCALE_IMAGE) + if len(rects) == 0: + return [] + rects[:,2:] += rects[:,:2] + return rects + +from tests_common import NewOpenCVTests, intersectionRate + +class facedetect_test(NewOpenCVTests): + + def test_facedetect(self): + import sys, getopt + + cascade_fn = self.repoPath + '/data/haarcascades/haarcascade_frontalface_alt.xml' + nested_fn = self.repoPath + '/data/haarcascades/haarcascade_eye.xml' + + cascade = cv2.CascadeClassifier(cascade_fn) + nested = cv2.CascadeClassifier(nested_fn) + + samples = ['samples/data/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png'] + + faces = [] + eyes = [] + + testFaces = [ + #lena + [[218, 200, 389, 371], + [ 244, 240, 294, 290], + [ 309, 246, 352, 289]], + + #lisa + [[167, 119, 307, 259], + [188, 153, 229, 194], + [236, 153, 277, 194]] + ] + + for sample in samples: + + img = self.get_sample( sample) + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + gray = cv2.GaussianBlur(gray, (5, 5), 5.1) + + rects = detect(gray, cascade) + faces.append(rects) + + if not nested.empty(): + for x1, y1, x2, y2 in rects: + roi = gray[y1:y2, x1:x2] + subrects = detect(roi.copy(), nested) + + for rect in subrects: + rect[0] += x1 + rect[2] += x1 + rect[1] += y1 + rect[3] += y1 + + eyes.append(subrects) + + faces_matches = 0 + eyes_matches = 0 + + eps = 0.8 + + for i in range(len(faces)): + for j in range(len(testFaces)): + if intersectionRate(faces[i][0], testFaces[j][0]) > eps: + faces_matches += 1 + #check eyes + if len(eyes[i]) == 2: + if intersectionRate(eyes[i][0], testFaces[j][1]) > eps and intersectionRate(eyes[i][1] , testFaces[j][2]) > eps: + eyes_matches += 1 + elif intersectionRate(eyes[i][1], testFaces[j][1]) > eps and intersectionRate(eyes[i][0], testFaces[j][2]) > eps: + eyes_matches += 1 + + self.assertEqual(faces_matches, 2) + self.assertEqual(eyes_matches, 2) \ No newline at end of file diff --git a/modules/python/test/test_feature_homography.py b/modules/python/test/test_feature_homography.py new file mode 100644 index 0000000000..280945e98b --- /dev/null +++ b/modules/python/test/test_feature_homography.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +''' +Feature homography +================== + +Example of using features2d framework for interactive video homography matching. +ORB features and FLANN matcher are used. The actual tracking is implemented by +PlaneTracker class in plane_tracker.py +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 +import sys +PY3 = sys.version_info[0] == 3 + +if PY3: + xrange = range + +# local modules +from tst_scene_render import TestSceneRender + +def intersectionRate(s1, s2): + + x1, y1, x2, y2 = s1 + s1 = np.array([[x1, y1], [x2,y1], [x2, y2], [x1, y2]]) + + area, intersection = cv2.intersectConvexConvex(s1, np.array(s2)) + return 2 * area / (cv2.contourArea(s1) + cv2.contourArea(np.array(s2))) + +from tests_common import NewOpenCVTests + +class feature_homography_test(NewOpenCVTests): + + render = None + tracker = None + framesCounter = 0 + frame = None + + def test_feature_homography(self): + + self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), + self.get_sample('samples/data/box.png'), noise = 0.5, speed = 0.5) + self.frame = self.render.getNextFrame() + self.tracker = PlaneTracker() + self.tracker.clear() + self.tracker.add_target(self.frame, self.render.getCurrentRect()) + + while self.framesCounter < 100: + self.framesCounter += 1 + tracked = self.tracker.track(self.frame) + if len(tracked) > 0: + tracked = tracked[0] + self.assertGreater(intersectionRate(self.render.getCurrentRect(), np.int32(tracked.quad)), 0.6) + else: + self.assertEqual(0, 1, 'Tracking error') + self.frame = self.render.getNextFrame() + + +# built-in modules +from collections import namedtuple + +FLANN_INDEX_KDTREE = 1 +FLANN_INDEX_LSH = 6 +flann_params= dict(algorithm = FLANN_INDEX_LSH, + table_number = 6, # 12 + key_size = 12, # 20 + multi_probe_level = 1) #2 + +MIN_MATCH_COUNT = 10 + +''' + image - image to track + rect - tracked rectangle (x1, y1, x2, y2) + keypoints - keypoints detected inside rect + descrs - their descriptors + data - some user-provided data +''' +PlanarTarget = namedtuple('PlaneTarget', 'image, rect, keypoints, descrs, data') + +''' + target - reference to PlanarTarget + p0 - matched points coords in target image + p1 - matched points coords in input frame + H - homography matrix from p0 to p1 + quad - target bounary quad in input frame +''' +TrackedTarget = namedtuple('TrackedTarget', 'target, p0, p1, H, quad') + +class PlaneTracker: + def __init__(self): + self.detector = cv2.ORB( nfeatures = 1000 ) + self.matcher = cv2.FlannBasedMatcher(flann_params, {}) # bug : need to pass empty dict (#1329) + self.targets = [] + self.frame_points = [] + + def add_target(self, image, rect, data=None): + '''Add a new tracking target.''' + x0, y0, x1, y1 = rect + raw_points, raw_descrs = self.detect_features(image) + points, descs = [], [] + for kp, desc in zip(raw_points, raw_descrs): + x, y = kp.pt + if x0 <= x <= x1 and y0 <= y <= y1: + points.append(kp) + descs.append(desc) + descs = np.uint8(descs) + self.matcher.add([descs]) + target = PlanarTarget(image = image, rect=rect, keypoints = points, descrs=descs, data=data) + self.targets.append(target) + + def clear(self): + '''Remove all targets''' + self.targets = [] + self.matcher.clear() + + def track(self, frame): + '''Returns a list of detected TrackedTarget objects''' + self.frame_points, frame_descrs = self.detect_features(frame) + if len(self.frame_points) < MIN_MATCH_COUNT: + return [] + matches = self.matcher.knnMatch(frame_descrs, k = 2) + matches = [m[0] for m in matches if len(m) == 2 and m[0].distance < m[1].distance * 0.75] + if len(matches) < MIN_MATCH_COUNT: + return [] + matches_by_id = [[] for _ in xrange(len(self.targets))] + for m in matches: + matches_by_id[m.imgIdx].append(m) + tracked = [] + for imgIdx, matches in enumerate(matches_by_id): + if len(matches) < MIN_MATCH_COUNT: + continue + target = self.targets[imgIdx] + p0 = [target.keypoints[m.trainIdx].pt for m in matches] + p1 = [self.frame_points[m.queryIdx].pt for m in matches] + p0, p1 = np.float32((p0, p1)) + H, status = cv2.findHomography(p0, p1, cv2.RANSAC, 3.0) + status = status.ravel() != 0 + if status.sum() < MIN_MATCH_COUNT: + continue + p0, p1 = p0[status], p1[status] + + x0, y0, x1, y1 = target.rect + quad = np.float32([[x0, y0], [x1, y0], [x1, y1], [x0, y1]]) + quad = cv2.perspectiveTransform(quad.reshape(1, -1, 2), H).reshape(-1, 2) + + track = TrackedTarget(target=target, p0=p0, p1=p1, H=H, quad=quad) + tracked.append(track) + tracked.sort(key = lambda t: len(t.p0), reverse=True) + return tracked + + def detect_features(self, frame): + '''detect_features(self, frame) -> keypoints, descrs''' + keypoints, descrs = self.detector.detectAndCompute(frame, None) + if descrs is None: # detectAndCompute returns descs=None if not keypoints found + descrs = [] + return keypoints, descrs \ No newline at end of file diff --git a/modules/python/test/test_fitline.py b/modules/python/test/test_fitline.py new file mode 100644 index 0000000000..05719b74f1 --- /dev/null +++ b/modules/python/test/test_fitline.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +''' +Robust line fitting. +================== + +Example of using cv2.fitLine function for fitting line +to points in presence of outliers. + +Switch through different M-estimator functions and see, +how well the robust functions fit the line even +in case of ~50% of outliers. + +''' + +# Python 2/3 compatibility +from __future__ import print_function +import sys +PY3 = sys.version_info[0] == 3 + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +w, h = 512, 256 + +def toint(p): + return tuple(map(int, p)) + +def sample_line(p1, p2, n, noise=0.0): + np.random.seed(10) + p1 = np.float32(p1) + t = np.random.rand(n,1) + return p1 + (p2-p1)*t + np.random.normal(size=(n, 2))*noise + +dist_func_names = ['CV_DIST_L2', 'CV_DIST_L1', 'CV_DIST_L12', 'CV_DIST_FAIR', 'CV_DIST_WELSCH', 'CV_DIST_HUBER'] + +class fitline_test(NewOpenCVTests): + + def test_fitline(self): + + noise = 5 + n = 200 + r = 5 / 100.0 + outn = int(n*r) + + p0, p1 = (90, 80), (w-90, h-80) + line_points = sample_line(p0, p1, n-outn, noise) + outliers = np.random.rand(outn, 2) * (w, h) + points = np.vstack([line_points, outliers]) + + lines = [] + + for name in dist_func_names: + func = getattr(cv2.cv, name) + vx, vy, cx, cy = cv2.fitLine(np.float32(points), func, 0, 0.01, 0.01) + line = [float(vx), float(vy), float(cx), float(cy)] + lines.append(line) + + eps = 0.05 + + refVec = (np.float32(p1) - p0) / cv2.norm(np.float32(p1) - p0) + + for i in range(len(lines)): + self.assertLessEqual(cv2.norm(refVec - lines[i][0:2], cv2.NORM_L2), eps) \ No newline at end of file diff --git a/modules/python/test/test_gaussian_mix.py b/modules/python/test/test_gaussian_mix.py new file mode 100644 index 0000000000..498cf3862f --- /dev/null +++ b/modules/python/test/test_gaussian_mix.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# Python 2/3 compatibility +from __future__ import print_function +import sys +PY3 = sys.version_info[0] == 3 + +if PY3: + xrange = range + +import numpy as np +from numpy import random +import cv2 + +def make_gaussians(cluster_n, img_size): + points = [] + ref_distrs = [] + for i in xrange(cluster_n): + mean = (0.1 + 0.8*random.rand(2)) * img_size + a = (random.rand(2, 2)-0.5)*img_size*0.1 + cov = np.dot(a.T, a) + img_size*0.05*np.eye(2) + n = 100 + random.randint(900) + pts = random.multivariate_normal(mean, cov, n) + points.append( pts ) + ref_distrs.append( (mean, cov) ) + points = np.float32( np.vstack(points) ) + return points, ref_distrs + +from tests_common import NewOpenCVTests + +class gaussian_mix_test(NewOpenCVTests): + + def test_gaussian_mix(self): + + np.random.seed(10) + cluster_n = 5 + img_size = 512 + + points, ref_distrs = make_gaussians(cluster_n, img_size) + + em = cv2.EM(cluster_n,cv2.EM_COV_MAT_GENERIC) + em.train(points) + means = em.getMat("means") + covs = em.getMatVector("covs") # Known bug: https://github.com/Itseez/opencv/pull/4232 + found_distrs = zip(means, covs) + + matches_count = 0 + + meanEps = 0.05 + covEps = 0.1 + + for i in range(cluster_n): + for j in range(cluster_n): + if (cv2.norm(means[i] - ref_distrs[j][0], cv2.NORM_L2) / cv2.norm(ref_distrs[j][0], cv2.NORM_L2) < meanEps and + cv2.norm(covs[i] - ref_distrs[j][1], cv2.NORM_L2) / cv2.norm(ref_distrs[j][1], cv2.NORM_L2) < covEps): + matches_count += 1 + + self.assertEqual(matches_count, cluster_n) \ No newline at end of file diff --git a/modules/python/test/test_grabcut.py b/modules/python/test/test_grabcut.py new file mode 100644 index 0000000000..38211f7d89 --- /dev/null +++ b/modules/python/test/test_grabcut.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +''' +=============================================================================== +Interactive Image Segmentation using GrabCut algorithm. +=============================================================================== +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 +import sys + +from tests_common import NewOpenCVTests + +class grabcut_test(NewOpenCVTests): + + def verify(self, mask, exp): + + maxDiffRatio = 0.02 + expArea = np.count_nonzero(exp) + nonIntersectArea = np.count_nonzero(mask != exp) + curRatio = float(nonIntersectArea) / expArea + return curRatio < maxDiffRatio + + def scaleMask(self, mask): + + return np.where((mask==cv2.GC_FGD) + (mask==cv2.GC_PR_FGD),255,0).astype('uint8') + + def test_grabcut(self): + + img = self.get_sample('cv/shared/airplane.png') + mask_prob = self.get_sample("cv/grabcut/mask_probpy.png", 0) + exp_mask1 = self.get_sample("cv/grabcut/exp_mask1py.png", 0) + exp_mask2 = self.get_sample("cv/grabcut/exp_mask2py.png", 0) + + if img is None: + self.assertTrue(False, 'Missing test data') + + rect = (24, 126, 459, 168) + mask = np.zeros(img.shape[:2], dtype = np.uint8) + bgdModel = np.zeros((1,65),np.float64) + fgdModel = np.zeros((1,65),np.float64) + cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 0, cv2.GC_INIT_WITH_RECT) + cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 2, cv2.GC_EVAL) + + if mask_prob is None: + mask_prob = mask.copy() + cv2.imwrite(self.extraTestDataPath + '/cv/grabcut/mask_probpy.png', mask_prob) + if exp_mask1 is None: + exp_mask1 = self.scaleMask(mask) + cv2.imwrite(self.extraTestDataPath + '/cv/grabcut/exp_mask1py.png', exp_mask1) + + self.assertEqual(self.verify(self.scaleMask(mask), exp_mask1), True) + + mask = mask_prob + bgdModel = np.zeros((1,65),np.float64) + fgdModel = np.zeros((1,65),np.float64) + cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 0, cv2.GC_INIT_WITH_MASK) + cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 1, cv2.GC_EVAL) + + if exp_mask2 is None: + exp_mask2 = self.scaleMask(mask) + cv2.imwrite(self.extraTestDataPath + '/cv/grabcut/exp_mask2py.png', exp_mask2) + + self.assertEqual(self.verify(self.scaleMask(mask), exp_mask2), True) \ No newline at end of file diff --git a/modules/python/test/test_houghcircles.py b/modules/python/test/test_houghcircles.py new file mode 100644 index 0000000000..6cf7d74ebb --- /dev/null +++ b/modules/python/test/test_houghcircles.py @@ -0,0 +1,81 @@ +#!/usr/bin/python + +''' +This example illustrates how to use cv2.HoughCircles() function. +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import cv2 +import numpy as np +import sys +from numpy import pi, sin, cos + +from tests_common import NewOpenCVTests + +def circleApproximation(circle): + + nPoints = 30 + phi = 0 + dPhi = 2*pi / nPoints + contour = [] + for i in range(nPoints): + contour.append(([circle[0] + circle[2]*cos(i*dPhi), + circle[1] + circle[2]*sin(i*dPhi)])) + + return np.array(contour).astype(int) + +def convContoursIntersectiponRate(c1, c2): + + s1 = cv2.contourArea(c1) + s2 = cv2.contourArea(c2) + + s, _ = cv2.intersectConvexConvex(c1, c2) + + return 2*s/(s1+s2) + +class houghcircles_test(NewOpenCVTests): + + def test_houghcircles(self): + + fn = "samples/data/board.jpg" + + src = self.get_sample(fn, 1) + img = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) + img = cv2.medianBlur(img, 5) + + circles = cv2.HoughCircles(img, cv2.cv.CV_HOUGH_GRADIENT, 1, 10, np.array([]), 100, 30, 1, 30)[0] + + testCircles = [[38, 181, 17.6], + [99.7, 166, 13.12], + [142.7, 160, 13.52], + [223.6, 110, 8.62], + [79.1, 206.7, 8.62], + [47.5, 351.6, 11.64], + [189.5, 354.4, 11.64], + [189.8, 298.9, 10.64], + [189.5, 252.4, 14.62], + [252.5, 393.4, 15.62], + [602.9, 467.5, 11.42], + [222, 210.4, 9.12], + [263.1, 216.7, 9.12], + [359.8, 222.6, 9.12], + [518.9, 120.9, 9.12], + [413.8, 113.4, 9.12], + [489, 127.2, 9.12], + [448.4, 121.3, 9.12], + [384.6, 128.9, 8.62]] + + matches_counter = 0 + + for i in range(len(testCircles)): + for j in range(len(circles)): + + tstCircle = circleApproximation(testCircles[i]) + circle = circleApproximation(circles[j]) + if convContoursIntersectiponRate(tstCircle, circle) > 0.6: + matches_counter += 1 + + self.assertGreater(float(matches_counter) / len(testCircles), .5) + self.assertLess(float(len(circles) - matches_counter) / len(circles), .75) \ No newline at end of file diff --git a/modules/python/test/test_houghlines.py b/modules/python/test/test_houghlines.py new file mode 100644 index 0000000000..081c84984d --- /dev/null +++ b/modules/python/test/test_houghlines.py @@ -0,0 +1,65 @@ +#!/usr/bin/python + +''' +This example illustrates how to use Hough Transform to find lines +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import cv2 +import numpy as np +import sys +import math + +from tests_common import NewOpenCVTests + +def linesDiff(line1, line2): + + norm1 = cv2.norm(line1 - line2, cv2.NORM_L2) + line3 = line1[2:4] + line1[0:2] + norm2 = cv2.norm(line3 - line2, cv2.NORM_L2) + + return min(norm1, norm2) + +class houghlines_test(NewOpenCVTests): + + def test_houghlines(self): + + fn = "/samples/data/pic1.png" + + src = self.get_sample(fn) + dst = cv2.Canny(src, 50, 200) + + lines = cv2.HoughLinesP(dst, 1, math.pi/180.0, 40, np.array([]), 50, 10)[0,:,:] + + eps = 5 + testLines = [ + #rect1 + [ 232, 25, 43, 25], + [ 43, 129, 232, 129], + [ 43, 129, 43, 25], + [232, 129, 232, 25], + #rect2 + [251, 86, 314, 183], + [252, 86, 323, 40], + [315, 183, 386, 137], + [324, 40, 386, 136], + #triangle + [245, 205, 377, 205], + [244, 206, 305, 278], + [306, 279, 377, 205], + #rect3 + [153, 177, 196, 177], + [153, 277, 153, 179], + [153, 277, 196, 277], + [196, 177, 196, 277]] + + matches_counter = 0 + + for i in range(len(testLines)): + for j in range(len(lines)): + if linesDiff(testLines[i], lines[j]) < eps: + matches_counter += 1 + + self.assertGreater(float(matches_counter) / len(testLines), .7) \ No newline at end of file diff --git a/modules/python/test/test_kmeans.py b/modules/python/test/test_kmeans.py new file mode 100644 index 0000000000..8cee11c675 --- /dev/null +++ b/modules/python/test/test_kmeans.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +''' +K-means clusterization test +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 +from numpy import random +import sys +PY3 = sys.version_info[0] == 3 +if PY3: + xrange = range + +from tests_common import NewOpenCVTests + +def make_gaussians(cluster_n, img_size): + points = [] + ref_distrs = [] + sizes = [] + for i in xrange(cluster_n): + mean = (0.1 + 0.8*random.rand(2)) * img_size + a = (random.rand(2, 2)-0.5)*img_size*0.1 + cov = np.dot(a.T, a) + img_size*0.05*np.eye(2) + n = 100 + random.randint(900) + pts = random.multivariate_normal(mean, cov, n) + points.append( pts ) + ref_distrs.append( (mean, cov) ) + sizes.append(n) + points = np.float32( np.vstack(points) ) + return points, ref_distrs, sizes + +def getMainLabelConfidence(labels, nLabels): + + n = len(labels) + labelsDict = dict.fromkeys(range(nLabels), 0) + labelsConfDict = dict.fromkeys(range(nLabels)) + + for i in range(n): + labelsDict[labels[i][0]] += 1 + + for i in range(nLabels): + labelsConfDict[i] = float(labelsDict[i]) / n + + return max(labelsConfDict.values()) + +class kmeans_test(NewOpenCVTests): + + def test_kmeans(self): + + np.random.seed(10) + + cluster_n = 5 + img_size = 512 + + points, _, clusterSizes = make_gaussians(cluster_n, img_size) + + term_crit = (cv2.TERM_CRITERIA_EPS, 30, 0.1) + ret, labels, centers = cv2.kmeans(points, cluster_n, term_crit, 10, 0) + + self.assertEqual(len(centers), cluster_n) + + offset = 0 + for i in range(cluster_n): + confidence = getMainLabelConfidence(labels[offset : (offset + clusterSizes[i])], cluster_n) + offset += clusterSizes[i] + self.assertGreater(confidence, 0.9) \ No newline at end of file diff --git a/modules/python/test/test_letter_recog.py b/modules/python/test/test_letter_recog.py new file mode 100644 index 0000000000..05f879978e --- /dev/null +++ b/modules/python/test/test_letter_recog.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python + +''' +The sample demonstrates how to train Random Trees classifier +(or Boosting classifier, or MLP, or Knearest, or Support Vector Machines) using the provided dataset. + +We use the sample database letter-recognition.data +from UCI Repository, here is the link: + +Newman, D.J. & Hettich, S. & Blake, C.L. & Merz, C.J. (1998). +UCI Repository of machine learning databases +[http://www.ics.uci.edu/~mlearn/MLRepository.html]. +Irvine, CA: University of California, Department of Information and Computer Science. + +The dataset consists of 20000 feature vectors along with the +responses - capital latin letters A..Z. +The first 10000 samples are used for training +and the remaining 10000 - to test the classifier. +====================================================== + Models: RTrees, KNearest, Boost, SVM, MLP +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +def load_base(fn): + a = np.loadtxt(fn, np.float32, delimiter=',', converters={ 0 : lambda ch : ord(ch)-ord('A') }) + samples, responses = a[:,1:], a[:,0] + return samples, responses + +class LetterStatModel(object): + class_n = 26 + train_ratio = 0.5 + + def load(self, fn): + self.model.load(fn) + def save(self, fn): + self.model.save(fn) + + def unroll_samples(self, samples): + sample_n, var_n = samples.shape + new_samples = np.zeros((sample_n * self.class_n, var_n+1), np.float32) + new_samples[:,:-1] = np.repeat(samples, self.class_n, axis=0) + new_samples[:,-1] = np.tile(np.arange(self.class_n), sample_n) + return new_samples + + def unroll_responses(self, responses): + sample_n = len(responses) + new_responses = np.zeros(sample_n*self.class_n, np.int32) + resp_idx = np.int32( responses + np.arange(sample_n)*self.class_n ) + new_responses[resp_idx] = 1 + return new_responses + +class RTrees(LetterStatModel): + def __init__(self): + self.model = cv2.RTrees() + + def train(self, samples, responses): + sample_n, var_n = samples.shape + params = dict(max_depth=20 ) + self.model.train(samples, cv2.CV_ROW_SAMPLE, responses.astype(int), params = params) + + def predict(self, samples): + return np.float32( [self.model.predict(s) for s in samples] ) + + +class KNearest(LetterStatModel): + def __init__(self): + self.model = cv2.KNearest() + + def train(self, samples, responses): + self.model.train(samples, responses) + + def predict(self, samples): + retval, results, neigh_resp, dists = self.model.find_nearest(samples, k = 10) + return results.ravel() + + +class Boost(LetterStatModel): + def __init__(self): + self.model = cv2.Boost() + + def train(self, samples, responses): + sample_n, var_n = samples.shape + new_samples = self.unroll_samples(samples) + new_responses = self.unroll_responses(responses) + var_types = np.array([cv2.CV_VAR_NUMERICAL] * var_n + [cv2.CV_VAR_CATEGORICAL, cv2.CV_VAR_CATEGORICAL], np.uint8) + params = dict(max_depth=10, weak_count=15) + self.model.train(new_samples, cv2.CV_ROW_SAMPLE, new_responses.astype(int), varType = var_types, params=params) + + def predict(self, samples): + new_samples = self.unroll_samples(samples) + pred = np.array( [self.model.predict(s) for s in new_samples] ) + pred = pred.reshape(-1, self.class_n).argmax(1) + return pred + + +class SVM(LetterStatModel): + def __init__(self): + self.model = cv2.SVM() + + def train(self, samples, responses): + params = dict( kernel_type = cv2.SVM_RBF, + svm_type = cv2.SVM_C_SVC, + C = 1, + gamma = .1 ) + self.model.train(samples, responses.astype(int), params = params) + + def predict(self, samples): + return self.model.predict_all(samples).ravel() + + +class MLP(LetterStatModel): + def __init__(self): + self.model = cv2.ANN_MLP() + + def train(self, samples, responses): + sample_n, var_n = samples.shape + new_responses = self.unroll_responses(responses).reshape(-1, self.class_n) + layer_sizes = np.int32([var_n, 100, 100, self.class_n]) + + self.model.create(layer_sizes, cv2.ANN_MLP_SIGMOID_SYM, 2, 1) + params = dict( train_method = cv2.ANN_MLP_TRAIN_PARAMS_BACKPROP, + bp_moment_scale = 0.0, + bp_dw_scale = 0.001, + term_crit = (cv2.TERM_CRITERIA_COUNT, 20, 0.01) ) + self.model.train(samples, np.float32(new_responses), None, params = params) + + def predict(self, samples): + ret, resp = self.model.predict(samples) + return resp.argmax(-1) + +from tests_common import NewOpenCVTests + +class letter_recog_test(NewOpenCVTests): + + def test_letter_recog(self): + + eps = 0.01 + + models = [RTrees, KNearest, Boost, SVM, MLP] + models = dict( [(cls.__name__.lower(), cls) for cls in models] ) + testErrors = {RTrees: (98.930000, 92.390000), KNearest: (94.960000, 92.010000), + Boost: (85.970000, 74.920000), SVM: (99.780000, 95.680000), MLP: (90.060000, 87.410000)} + + for model in models: + Model = models[model] + classifier = Model() + + samples, responses = load_base(self.repoPath + '/samples/data/letter-recognition.data') + train_n = int(len(samples)*classifier.train_ratio) + + classifier.train(samples[:train_n], responses[:train_n]) + train_rate = np.mean(classifier.predict(samples[:train_n]) == responses[:train_n].astype(int)) + test_rate = np.mean(classifier.predict(samples[train_n:]) == responses[train_n:].astype(int)) + + self.assertLess(train_rate - testErrors[Model][0], eps) + self.assertLess(test_rate - testErrors[Model][1], eps) \ No newline at end of file diff --git a/modules/python/test/test_lk_homography.py b/modules/python/test/test_lk_homography.py new file mode 100644 index 0000000000..8e526d0de7 --- /dev/null +++ b/modules/python/test/test_lk_homography.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +''' +Lucas-Kanade homography tracker test +=============================== +Uses goodFeaturesToTrack for track initialization and back-tracking for match verification +between frames. Finds homography between reference and current views. +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +#local modules +from tst_scene_render import TestSceneRender +from tests_common import NewOpenCVTests, isPointInRect + +lk_params = dict( winSize = (19, 19), + maxLevel = 2, + criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) + +feature_params = dict( maxCorners = 1000, + qualityLevel = 0.01, + minDistance = 8, + blockSize = 19 ) + +def checkedTrace(img0, img1, p0, back_threshold = 1.0): + p1, st, err = cv2.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params) + p0r, st, err = cv2.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params) + d = abs(p0-p0r).reshape(-1, 2).max(-1) + status = d < back_threshold + return p1, status + +class lk_homography_test(NewOpenCVTests): + + render = None + framesCounter = 0 + frame = frame0 = None + p0 = None + p1 = None + gray0 = gray1 = None + numFeaturesInRectOnStart = 0 + + def test_lk_homography(self): + self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), + self.get_sample('samples/data/box.png'), noise = 0.1, speed = 1.0) + + frame = self.render.getNextFrame() + frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + self.frame0 = frame.copy() + self.p0 = cv2.goodFeaturesToTrack(frame_gray, **feature_params) + + isForegroundHomographyFound = False + + if self.p0 is not None: + self.p1 = self.p0 + self.gray0 = frame_gray + self.gray1 = frame_gray + currRect = self.render.getCurrentRect() + for (x,y) in self.p0[:,0]: + if isPointInRect((x,y), currRect): + self.numFeaturesInRectOnStart += 1 + + while self.framesCounter < 200: + self.framesCounter += 1 + frame = self.render.getNextFrame() + frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + if self.p0 is not None: + p2, trace_status = checkedTrace(self.gray1, frame_gray, self.p1) + + self.p1 = p2[trace_status].copy() + self.p0 = self.p0[trace_status].copy() + self.gray1 = frame_gray + + if len(self.p0) < 4: + self.p0 = None + continue + H, status = cv2.findHomography(self.p0, self.p1, cv2.RANSAC, 5.0) + + goodPointsInRect = 0 + goodPointsOutsideRect = 0 + for (x0, y0), (x1, y1), good in zip(self.p0[:,0], self.p1[:,0], status[:,0]): + if good: + if isPointInRect((x1,y1), self.render.getCurrentRect()): + goodPointsInRect += 1 + else: goodPointsOutsideRect += 1 + + if goodPointsOutsideRect < goodPointsInRect: + isForegroundHomographyFound = True + self.assertGreater(float(goodPointsInRect) / (self.numFeaturesInRectOnStart + 1), 0.6) + else: + p = cv2.goodFeaturesToTrack(frame_gray, **feature_params) + + self.assertEqual(isForegroundHomographyFound, True) \ No newline at end of file diff --git a/modules/python/test/test_lk_track.py b/modules/python/test/test_lk_track.py new file mode 100644 index 0000000000..ccc67a5128 --- /dev/null +++ b/modules/python/test/test_lk_track.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python + +''' +Lucas-Kanade tracker +==================== + +Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack +for track initialization and back-tracking for match verification +between frames. +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +#local modules +from tst_scene_render import TestSceneRender +from tests_common import NewOpenCVTests, intersectionRate, isPointInRect + +lk_params = dict( winSize = (15, 15), + maxLevel = 2, + criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) + +feature_params = dict( maxCorners = 500, + qualityLevel = 0.3, + minDistance = 7, + blockSize = 7 ) + +def getRectFromPoints(points): + + distances = [] + for point in points: + distances.append(cv2.norm(point, cv2.NORM_L2)) + + x0, y0 = points[np.argmin(distances)] + x1, y1 = points[np.argmax(distances)] + + return np.array([x0, y0, x1, y1]) + + +class lk_track_test(NewOpenCVTests): + + track_len = 10 + detect_interval = 5 + tracks = [] + frame_idx = 0 + render = None + + def test_lk_track(self): + + self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), self.get_sample('samples/data/box.png')) + self.runTracker() + + def runTracker(self): + foregroundPointsNum = 0 + + while True: + frame = self.render.getNextFrame() + frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + + if len(self.tracks) > 0: + img0, img1 = self.prev_gray, frame_gray + p0 = np.float32([tr[-1][0] for tr in self.tracks]).reshape(-1, 1, 2) + p1, st, err = cv2.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params) + p0r, st, err = cv2.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params) + d = abs(p0-p0r).reshape(-1, 2).max(-1) + good = d < 1 + new_tracks = [] + for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good): + if not good_flag: + continue + tr.append([(x, y), self.frame_idx]) + if len(tr) > self.track_len: + del tr[0] + new_tracks.append(tr) + self.tracks = new_tracks + + if self.frame_idx % self.detect_interval == 0: + goodTracksCount = 0 + for tr in self.tracks: + oldRect = self.render.getRectInTime(self.render.timeStep * tr[0][1]) + newRect = self.render.getRectInTime(self.render.timeStep * tr[-1][1]) + if isPointInRect(tr[0][0], oldRect) and isPointInRect(tr[-1][0], newRect): + goodTracksCount += 1 + + if self.frame_idx == self.detect_interval: + foregroundPointsNum = goodTracksCount + + fgIndex = float(foregroundPointsNum) / (foregroundPointsNum + 1) + fgRate = float(goodTracksCount) / (len(self.tracks) + 1) + + if self.frame_idx > 0: + self.assertGreater(fgIndex, 0.9) + self.assertGreater(fgRate, 0.2) + + mask = np.zeros_like(frame_gray) + mask[:] = 255 + for x, y in [np.int32(tr[-1][0]) for tr in self.tracks]: + cv2.circle(mask, (x, y), 5, 0, -1) + p = cv2.goodFeaturesToTrack(frame_gray, mask = mask, **feature_params) + if p is not None: + for x, y in np.float32(p).reshape(-1, 2): + self.tracks.append([[(x, y), self.frame_idx]]) + + self.frame_idx += 1 + self.prev_gray = frame_gray + + if self.frame_idx > 300: + break \ No newline at end of file diff --git a/modules/python/test/test_morphology.py b/modules/python/test/test_morphology.py new file mode 100644 index 0000000000..309c80cfd2 --- /dev/null +++ b/modules/python/test/test_morphology.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +''' +Morphology operations. +''' + +# Python 2/3 compatibility +from __future__ import print_function +import sys +PY3 = sys.version_info[0] == 3 + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +class morphology_test(NewOpenCVTests): + + def test_morphology(self): + + fn = 'samples/data/rubberwhale1.png' + img = self.get_sample(fn) + + modes = ['erode/dilate', 'open/close', 'blackhat/tophat', 'gradient'] + str_modes = ['ellipse', 'rect', 'cross'] + + referenceHashes = { modes[0]: '071a526425b79e45b4d0d71ef51b0562', modes[1] : '071a526425b79e45b4d0d71ef51b0562', + modes[2] : '427e89f581b7df1b60a831b1ed4c8618', modes[3] : '0dd8ad251088a63d0dd022bcdc57361c'} + + def update(cur_mode): + cur_str_mode = str_modes[0] + sz = 10 + iters = 1 + opers = cur_mode.split('/') + if len(opers) > 1: + sz = sz - 10 + op = opers[sz > 0] + sz = abs(sz) + else: + op = opers[0] + sz = sz*2+1 + + str_name = 'MORPH_' + cur_str_mode.upper() + oper_name = 'MORPH_' + op.upper() + + st = cv2.getStructuringElement(getattr(cv2, str_name), (sz, sz)) + return cv2.morphologyEx(img, getattr(cv2, oper_name), st, iterations=iters) + + for mode in modes: + res = update(mode) + self.assertEqual(self.hashimg(res), referenceHashes[mode]) \ No newline at end of file diff --git a/modules/python/test/test_mser.py b/modules/python/test/test_mser.py new file mode 100644 index 0000000000..dfdb30a8ed --- /dev/null +++ b/modules/python/test/test_mser.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +''' +MSER detector test +''' +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +class mser_test(NewOpenCVTests): + def test_mser(self): + + img = self.get_sample('cv/mser/puzzle.png', 0) + smallImg = [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255] + ] + thresharr = [ 0, 70, 120, 180, 255 ] + kDelta = 5 + np.random.seed(10) + + for i in range(100): + + use_big_image = int(np.random.rand(1,1)*7) != 0 + invert = int(np.random.rand(1,1)*2) != 0 + binarize = int(np.random.rand(1,1)*5) != 0 if use_big_image else False + blur = int(np.random.rand(1,1)*2) != 0 + thresh = thresharr[int(np.random.rand(1,1)*5)] + src0 = img if use_big_image else np.array(smallImg).astype('uint8') + src = src0.copy() + + kMinArea = 256 if use_big_image else 10 + kMaxArea = int(src.shape[0]*src.shape[1]/4) + + mserExtractor = cv2.MSER(kDelta, kMinArea, kMaxArea) + if invert: + cv2.bitwise_not(src, src) + if binarize: + _, src = cv2.threshold(src, thresh, 255, cv2.THRESH_BINARY) + if blur: + src = cv2.GaussianBlur(src, (5, 5), 1.5, 1.5) + minRegs = 7 if use_big_image else 2 + maxRegs = 1000 if use_big_image else 15 + if binarize and (thresh == 0 or thresh == 255): + minRegs = maxRegs = 0 + msers = mserExtractor.detect(src) + nmsers = len(msers) + self.assertLessEqual(minRegs, nmsers) + self.assertGreaterEqual(maxRegs, nmsers) \ No newline at end of file diff --git a/modules/python/test/test_peopledetect.py b/modules/python/test/test_peopledetect.py new file mode 100644 index 0000000000..fb0a9e9cae --- /dev/null +++ b/modules/python/test/test_peopledetect.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +''' +example to detect upright people in images using HOG features +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + + +def inside(r, q): + rx, ry, rw, rh = r + qx, qy, qw, qh = q + return rx > qx and ry > qy and rx + rw < qx + qw and ry + rh < qy + qh + +from tests_common import NewOpenCVTests, intersectionRate + +class peopledetect_test(NewOpenCVTests): + def test_peopledetect(self): + + hog = cv2.HOGDescriptor() + hog.setSVMDetector( cv2.HOGDescriptor_getDefaultPeopleDetector() ) + + dirPath = 'samples/data/' + samples = ['basketball1.png', 'basketball2.png'] + + testPeople = [ + [[23, 76, 164, 477], [440, 22, 637, 478]], + [[23, 76, 164, 477], [440, 22, 637, 478]] + ] + + eps = 0.5 + + for sample in samples: + + img = self.get_sample(dirPath + sample, 0) + + found, w = hog.detectMultiScale(img, winStride=(8,8), padding=(32,32), scale=1.05) + found_filtered = [] + for ri, r in enumerate(found): + for qi, q in enumerate(found): + if ri != qi and inside(r, q): + break + else: + found_filtered.append(r) + + matches = 0 + + for i in range(len(found_filtered)): + for j in range(len(testPeople)): + + found_rect = (found_filtered[i][0], found_filtered[i][1], + found_filtered[i][0] + found_filtered[i][2], + found_filtered[i][1] + found_filtered[i][3]) + + if intersectionRate(found_rect, testPeople[j][0]) > eps or intersectionRate(found_rect, testPeople[j][1]) > eps: + matches += 1 + + self.assertGreater(matches, 0) \ No newline at end of file diff --git a/modules/python/test/test_squares.py b/modules/python/test/test_squares.py new file mode 100644 index 0000000000..3b3fbb6d9d --- /dev/null +++ b/modules/python/test/test_squares.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +''' +Simple "Square Detector" program. + +Loads several images sequentially and tries to find squares in each image. +''' + +# Python 2/3 compatibility +import sys +PY3 = sys.version_info[0] == 3 + +if PY3: + xrange = range + +import numpy as np +import cv2 + + +def angle_cos(p0, p1, p2): + d1, d2 = (p0-p1).astype('float'), (p2-p1).astype('float') + return abs( np.dot(d1, d2) / np.sqrt( np.dot(d1, d1)*np.dot(d2, d2) ) ) + +def find_squares(img): + img = cv2.GaussianBlur(img, (5, 5), 0) + squares = [] + for gray in cv2.split(img): + for thrs in xrange(0, 255, 26): + if thrs == 0: + bin = cv2.Canny(gray, 0, 50, apertureSize=5) + bin = cv2.dilate(bin, None) + else: + retval, bin = cv2.threshold(gray, thrs, 255, cv2.THRESH_BINARY) + contours, hierarchy = cv2.findContours(bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) + for cnt in contours: + cnt_len = cv2.arcLength(cnt, True) + cnt = cv2.approxPolyDP(cnt, 0.02*cnt_len, True) + if len(cnt) == 4 and cv2.contourArea(cnt) > 1000 and cv2.isContourConvex(cnt): + cnt = cnt.reshape(-1, 2) + max_cos = np.max([angle_cos( cnt[i], cnt[(i+1) % 4], cnt[(i+2) % 4] ) for i in xrange(4)]) + if max_cos < 0.1 and filterSquares(squares, cnt): + squares.append(cnt) + + return squares + +def intersectionRate(s1, s2): + area, intersection = cv2.intersectConvexConvex(np.array(s1), np.array(s2)) + return 2 * area / (cv2.contourArea(np.array(s1)) + cv2.contourArea(np.array(s2))) + +def filterSquares(squares, square): + + for i in range(len(squares)): + if intersectionRate(squares[i], square) > 0.95: + return False + + return True + +from tests_common import NewOpenCVTests + +class squares_test(NewOpenCVTests): + + def test_squares(self): + + img = self.get_sample('samples/data/pic1.png') + squares = find_squares(img) + + testSquares = [ + [[43, 25], + [43, 129], + [232, 129], + [232, 25]], + + [[252, 87], + [324, 40], + [387, 137], + [315, 184]], + + [[154, 178], + [196, 180], + [198, 278], + [154, 278]], + + [[0, 0], + [400, 0], + [400, 300], + [0, 300]] + ] + + matches_counter = 0 + for i in range(len(squares)): + for j in range(len(testSquares)): + if intersectionRate(squares[i], testSquares[j]) > 0.9: + matches_counter += 1 + + self.assertGreater(matches_counter / len(testSquares), 0.9) + self.assertLess( (len(squares) - matches_counter) / len(squares), 0.2) \ No newline at end of file diff --git a/modules/python/test/test_texture_flow.py b/modules/python/test/test_texture_flow.py new file mode 100644 index 0000000000..7dc3b07040 --- /dev/null +++ b/modules/python/test/test_texture_flow.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +''' +Texture flow direction estimation. + +Sample shows how cv2.cornerEigenValsAndVecs function can be used +to estimate image texture flow direction. +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 +import sys + +from tests_common import NewOpenCVTests + + +class texture_flow_test(NewOpenCVTests): + + def test_texture_flow(self): + + img = self.get_sample('samples/data/pic6.png') + + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + h, w = img.shape[:2] + + eigen = cv2.cornerEigenValsAndVecs(gray, 15, 3) + eigen = eigen.reshape(h, w, 3, 2) # [[e1, e2], v1, v2] + flow = eigen[:,:,2] + + vis = img.copy() + vis[:] = (192 + np.uint32(vis)) / 2 + d = 80 + points = np.dstack( np.mgrid[d/2:w:d, d/2:h:d] ).reshape(-1, 2) + + textureVectors = [] + + for x, y in np.int32(points): + textureVectors.append(np.int32(flow[y, x]*d)) + + eps = 0.05 + + testTextureVectors = [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], + [-38, 70], [-79, 3], [0, 0], [0, 0], [-39, 69], [-79, -1], + [0, 0], [0, 0], [0, -79], [17, -78], [-48, -63], [65, -46], + [-69, -39], [-48, -63], [-45, 66]] + + for i in range(len(textureVectors)): + self.assertLessEqual(cv2.norm(textureVectors[i] - testTextureVectors[i], cv2.NORM_L2), eps) \ No newline at end of file diff --git a/modules/python/test/test_watershed.py b/modules/python/test/test_watershed.py new file mode 100644 index 0000000000..0a1d222f41 --- /dev/null +++ b/modules/python/test/test_watershed.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +''' +Watershed segmentation test +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +class watershed_test(NewOpenCVTests): + def test_watershed(self): + + img = self.get_sample('cv/inpaint/orig.png') + markers = self.get_sample('cv/watershed/wshed_exp.png', 0) + refSegments = self.get_sample('cv/watershed/wshed_segments.png') + + if img is None or markers is None: + self.assertEqual(0, 1, 'Missing test data') + + colors = np.int32( list(np.ndindex(3, 3, 3)) ) * 122 + cv2.watershed(img, np.int32(markers)) + segments = colors[np.maximum(markers, 0)] + + if refSegments is None: + refSegments = segments.copy() + cv2.imwrite(self.extraTestDataPath + '/cv/watershed/wshed_segments.png', refSegments) + + self.assertLess(cv2.norm(segments - refSegments, cv2.NORM_L1) / 255.0, 50) \ No newline at end of file diff --git a/modules/python/test/tests_common.py b/modules/python/test/tests_common.py new file mode 100644 index 0000000000..3a636b255c --- /dev/null +++ b/modules/python/test/tests_common.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import unittest +import sys +import hashlib +import os +import numpy as np +import cv2 + +# Python 3 moved urlopen to urllib.requests +try: + from urllib.request import urlopen +except ImportError: + from urllib import urlopen + +class NewOpenCVTests(unittest.TestCase): + + # path to local repository folder containing 'samples' folder + repoPath = None + extraTestDataPath = None + # github repository url + repoUrl = 'https://raw.github.com/Itseez/opencv/master' + + def get_sample(self, filename, iscolor = cv2.IMREAD_COLOR): + if not filename in self.image_cache: + filedata = None + if NewOpenCVTests.repoPath is not None: + candidate = NewOpenCVTests.repoPath + '/' + filename + if os.path.isfile(candidate): + with open(candidate, 'rb') as f: + filedata = f.read() + if NewOpenCVTests.extraTestDataPath is not None: + candidate = NewOpenCVTests.extraTestDataPath + '/' + filename + if os.path.isfile(candidate): + with open(candidate, 'rb') as f: + filedata = f.read() + if filedata is None: + return None#filedata = urlopen(NewOpenCVTests.repoUrl + '/' + filename).read() + self.image_cache[filename] = cv2.imdecode(np.fromstring(filedata, dtype=np.uint8), iscolor) + return self.image_cache[filename] + + def setUp(self): + self.image_cache = {} + + def hashimg(self, im): + """ Compute a hash for an image, useful for image comparisons """ + return hashlib.md5(im.tostring()).hexdigest() + + if sys.version_info[:2] == (2, 6): + def assertLess(self, a, b, msg=None): + if not a < b: + self.fail('%s not less than %s' % (repr(a), repr(b))) + + def assertLessEqual(self, a, b, msg=None): + if not a <= b: + self.fail('%s not less than or equal to %s' % (repr(a), repr(b))) + + def assertGreater(self, a, b, msg=None): + if not a > b: + self.fail('%s not greater than %s' % (repr(a), repr(b))) + +def intersectionRate(s1, s2): + + x1, y1, x2, y2 = s1 + s1 = np.array([[x1, y1], [x2,y1], [x2, y2], [x1, y2]]) + + x1, y1, x2, y2 = s2 + s2 = np.array([[x1, y1], [x2,y1], [x2, y2], [x1, y2]]) + + area, intersection = cv2.intersectConvexConvex(s1, s2) + return 2 * area / (cv2.contourArea(s1) + cv2.contourArea(s2)) + +def isPointInRect(p, rect): + if rect[0] <= p[0] and rect[1] <=p[1] and p[0] <= rect[2] and p[1] <= rect[3]: + return True + else: + return False \ No newline at end of file diff --git a/modules/python/test/tst_scene_render.py b/modules/python/test/tst_scene_render.py new file mode 100644 index 0000000000..49cde80d25 --- /dev/null +++ b/modules/python/test/tst_scene_render.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +from numpy import pi, sin, cos + +import cv2 + +defaultSize = 512 + +class TestSceneRender(): + + def __init__(self, bgImg = None, fgImg = None, deformation = False, noise = 0.0, speed = 0.25, **params): + self.time = 0.0 + self.timeStep = 1.0 / 30.0 + self.foreground = fgImg + self.deformation = deformation + self.noise = noise + self.speed = speed + + if bgImg is not None: + self.sceneBg = bgImg.copy() + else: + self.sceneBg = np.zeros(defaultSize, defaultSize, np.uint8) + + self.w = self.sceneBg.shape[0] + self.h = self.sceneBg.shape[1] + + if fgImg is not None: + self.foreground = fgImg.copy() + self.center = self.currentCenter = (int(self.w/2 - fgImg.shape[0]/2), int(self.h/2 - fgImg.shape[1]/2)) + + self.xAmpl = self.sceneBg.shape[0] - (self.center[0] + fgImg.shape[0]) + self.yAmpl = self.sceneBg.shape[1] - (self.center[1] + fgImg.shape[1]) + + self.initialRect = np.array([ (self.h/2, self.w/2), (self.h/2, self.w/2 + self.w/10), + (self.h/2 + self.h/10, self.w/2 + self.w/10), (self.h/2 + self.h/10, self.w/2)]).astype(int) + self.currentRect = self.initialRect + np.random.seed(10) + + def getXOffset(self, time): + return int(self.xAmpl*cos(time*self.speed)) + + + def getYOffset(self, time): + return int(self.yAmpl*sin(time*self.speed)) + + def setInitialRect(self, rect): + self.initialRect = rect + + def getRectInTime(self, time): + + if self.foreground is not None: + tmp = np.array(self.center) + np.array((self.getXOffset(time), self.getYOffset(time))) + x0, y0 = tmp + x1, y1 = tmp + self.foreground.shape[0:2] + return np.array([y0, x0, y1, x1]) + else: + x0, y0 = self.initialRect[0] + np.array((self.getXOffset(time), self.getYOffset(time))) + x1, y1 = self.initialRect[2] + np.array((self.getXOffset(time), self.getYOffset(time))) + return np.array([y0, x0, y1, x1]) + + def getCurrentRect(self): + + if self.foreground is not None: + + x0 = self.currentCenter[0] + y0 = self.currentCenter[1] + x1 = self.currentCenter[0] + self.foreground.shape[0] + y1 = self.currentCenter[1] + self.foreground.shape[1] + return np.array([y0, x0, y1, x1]) + else: + x0, y0 = self.currentRect[0] + x1, y1 = self.currentRect[2] + return np.array([x0, y0, x1, y1]) + + def getNextFrame(self): + img = self.sceneBg.copy() + + if self.foreground is not None: + self.currentCenter = (self.center[0] + self.getXOffset(self.time), self.center[1] + self.getYOffset(self.time)) + img[self.currentCenter[0]:self.currentCenter[0]+self.foreground.shape[0], + self.currentCenter[1]:self.currentCenter[1]+self.foreground.shape[1]] = self.foreground + else: + self.currentRect = self.initialRect + np.int( 30*cos(self.time) + 50*sin(self.time/3)) + if self.deformation: + self.currentRect[1:3] += int(self.h/20*cos(self.time)) + cv2.fillConvexPoly(img, self.currentRect, (0, 0, 255)) + + self.time += self.timeStep + + if self.noise: + noise = np.zeros(self.sceneBg.shape, np.int8) + cv2.randn(noise, np.zeros(3), np.ones(3)*255*self.noise) + img = cv2.add(img, noise, dtype=cv2.CV_8UC3) + return img + + def resetTime(self): + self.time = 0.0 + + +if __name__ == '__main__': + + backGr = cv2.imread('../../../samples/data/lena.jpg') + + render = TestSceneRender(backGr, noise = 0.5) + + while True: + + img = render.getNextFrame() + cv2.imshow('img', img) + + ch = 0xFF & cv2.waitKey(3) + if ch == 27: + break + cv2.destroyAllWindows() \ No newline at end of file From aaa30dc5b6c65868a2c50fb7fdbdfc2836d694c4 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Thu, 25 Feb 2016 17:06:20 +0300 Subject: [PATCH 2/4] Make some tests less strict due to improvement of related algorithms in master branch --- modules/python/test/test_feature_homography.py | 2 +- modules/python/test/test_mser.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/python/test/test_feature_homography.py b/modules/python/test/test_feature_homography.py index 280945e98b..f417033883 100644 --- a/modules/python/test/test_feature_homography.py +++ b/modules/python/test/test_feature_homography.py @@ -43,7 +43,7 @@ class feature_homography_test(NewOpenCVTests): def test_feature_homography(self): self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), - self.get_sample('samples/data/box.png'), noise = 0.5, speed = 0.5) + self.get_sample('samples/data/box.png'), noise = 0.4, speed = 0.5) self.frame = self.render.getNextFrame() self.tracker = PlaneTracker() self.tracker.clear() diff --git a/modules/python/test/test_mser.py b/modules/python/test/test_mser.py index dfdb30a8ed..47c7db196f 100644 --- a/modules/python/test/test_mser.py +++ b/modules/python/test/test_mser.py @@ -40,7 +40,7 @@ class mser_test(NewOpenCVTests): use_big_image = int(np.random.rand(1,1)*7) != 0 invert = int(np.random.rand(1,1)*2) != 0 binarize = int(np.random.rand(1,1)*5) != 0 if use_big_image else False - blur = int(np.random.rand(1,1)*2) != 0 + blur = True #int(np.random.rand(1,1)*2) != 0 #binarized images are processed incorrectly thresh = thresharr[int(np.random.rand(1,1)*5)] src0 = img if use_big_image else np.array(smallImg).astype('uint8') src = src0.copy() From 25b4d8a1b5a5a1f90f5006999766fdbd12250ce9 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Thu, 25 Feb 2016 17:25:24 +0300 Subject: [PATCH 3/4] Added images necessary for tests --- modules/python/test/test_calibration.py | 4 ++-- modules/python/test/test_camshift.py | 2 +- modules/python/test/test_dft.py | 2 +- modules/python/test/test_digits.py | 2 +- modules/python/test/test_facedetect.py | 2 +- modules/python/test/test_feature_homography.py | 4 ++-- modules/python/test/test_houghcircles.py | 2 +- modules/python/test/test_houghlines.py | 2 +- modules/python/test/test_letter_recog.py | 2 +- modules/python/test/test_lk_homography.py | 4 ++-- modules/python/test/test_lk_track.py | 2 +- modules/python/test/test_morphology.py | 2 +- modules/python/test/test_peopledetect.py | 2 +- modules/python/test/test_squares.py | 2 +- modules/python/test/test_texture_flow.py | 2 +- modules/python/test/tst_scene_render.py | 17 ----------------- samples/python2/data/graf1.png | Bin 0 -> 951440 bytes samples/python2/data/pca_test1.jpg | Bin 0 -> 33227 bytes 18 files changed, 18 insertions(+), 35 deletions(-) create mode 100644 samples/python2/data/graf1.png create mode 100644 samples/python2/data/pca_test1.jpg diff --git a/modules/python/test/test_calibration.py b/modules/python/test/test_calibration.py index 665521862c..bb54cacdc4 100644 --- a/modules/python/test/test_calibration.py +++ b/modules/python/test/test_calibration.py @@ -21,9 +21,9 @@ class calibration_test(NewOpenCVTests): img_names = [] for i in range(1, 15): if i < 10: - img_names.append('samples/data/left0{}.jpg'.format(str(i))) + img_names.append('samples/cpp/left0{}.jpg'.format(str(i))) elif i != 10: - img_names.append('samples/data/left{}.jpg'.format(str(i))) + img_names.append('samples/cpp/left{}.jpg'.format(str(i))) square_size = 1.0 pattern_size = (9, 6) diff --git a/modules/python/test/test_camshift.py b/modules/python/test/test_camshift.py index a824320eff..6c7c189344 100644 --- a/modules/python/test/test_camshift.py +++ b/modules/python/test/test_camshift.py @@ -39,7 +39,7 @@ class camshift_test(NewOpenCVTests): def prepareRender(self): - self.render = TestSceneRender(self.get_sample('samples/data/pca_test1.jpg'), deformation = True) + self.render = TestSceneRender(self.get_sample('samples/python2/data/pca_test1.jpg'), deformation = True) def runTracker(self): diff --git a/modules/python/test/test_dft.py b/modules/python/test/test_dft.py index f796939970..9019a6a63c 100644 --- a/modules/python/test/test_dft.py +++ b/modules/python/test/test_dft.py @@ -16,7 +16,7 @@ from tests_common import NewOpenCVTests class dft_test(NewOpenCVTests): def test_dft(self): - img = self.get_sample('samples/data/rubberwhale1.png', 0) + img = self.get_sample('samples/gpu/rubberwhale1.png', 0) eps = 0.001 #test direct transform diff --git a/modules/python/test/test_digits.py b/modules/python/test/test_digits.py index 587c355e37..e30361e0eb 100644 --- a/modules/python/test/test_digits.py +++ b/modules/python/test/test_digits.py @@ -36,7 +36,7 @@ from numpy.linalg import norm SZ = 20 # size of each digit is SZ x SZ CLASS_N = 10 -DIGITS_FN = 'samples/data/digits.png' +DIGITS_FN = 'samples/python2/data/digits.png' def split2d(img, cell_size, flatten=True): h, w = img.shape[:2] diff --git a/modules/python/test/test_facedetect.py b/modules/python/test/test_facedetect.py index 8d64fde10f..094659712e 100644 --- a/modules/python/test/test_facedetect.py +++ b/modules/python/test/test_facedetect.py @@ -31,7 +31,7 @@ class facedetect_test(NewOpenCVTests): cascade = cv2.CascadeClassifier(cascade_fn) nested = cv2.CascadeClassifier(nested_fn) - samples = ['samples/data/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png'] + samples = ['samples/c/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png'] faces = [] eyes = [] diff --git a/modules/python/test/test_feature_homography.py b/modules/python/test/test_feature_homography.py index f417033883..a895be6f6a 100644 --- a/modules/python/test/test_feature_homography.py +++ b/modules/python/test/test_feature_homography.py @@ -42,8 +42,8 @@ class feature_homography_test(NewOpenCVTests): def test_feature_homography(self): - self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), - self.get_sample('samples/data/box.png'), noise = 0.4, speed = 0.5) + self.render = TestSceneRender(self.get_sample('samples/python2/data/graf1.png'), + self.get_sample('samples/c/box.png'), noise = 0.4, speed = 0.5) self.frame = self.render.getNextFrame() self.tracker = PlaneTracker() self.tracker.clear() diff --git a/modules/python/test/test_houghcircles.py b/modules/python/test/test_houghcircles.py index 6cf7d74ebb..32b474fc85 100644 --- a/modules/python/test/test_houghcircles.py +++ b/modules/python/test/test_houghcircles.py @@ -39,7 +39,7 @@ class houghcircles_test(NewOpenCVTests): def test_houghcircles(self): - fn = "samples/data/board.jpg" + fn = "samples/cpp/board.jpg" src = self.get_sample(fn, 1) img = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) diff --git a/modules/python/test/test_houghlines.py b/modules/python/test/test_houghlines.py index 081c84984d..993c113436 100644 --- a/modules/python/test/test_houghlines.py +++ b/modules/python/test/test_houghlines.py @@ -26,7 +26,7 @@ class houghlines_test(NewOpenCVTests): def test_houghlines(self): - fn = "/samples/data/pic1.png" + fn = "/samples/cpp/pic1.png" src = self.get_sample(fn) dst = cv2.Canny(src, 50, 200) diff --git a/modules/python/test/test_letter_recog.py b/modules/python/test/test_letter_recog.py index 05f879978e..d64d8e0993 100644 --- a/modules/python/test/test_letter_recog.py +++ b/modules/python/test/test_letter_recog.py @@ -150,7 +150,7 @@ class letter_recog_test(NewOpenCVTests): Model = models[model] classifier = Model() - samples, responses = load_base(self.repoPath + '/samples/data/letter-recognition.data') + samples, responses = load_base(self.repoPath + '/samples/cpp/letter-recognition.data') train_n = int(len(samples)*classifier.train_ratio) classifier.train(samples[:train_n], responses[:train_n]) diff --git a/modules/python/test/test_lk_homography.py b/modules/python/test/test_lk_homography.py index 8e526d0de7..b1d6285a9d 100644 --- a/modules/python/test/test_lk_homography.py +++ b/modules/python/test/test_lk_homography.py @@ -44,8 +44,8 @@ class lk_homography_test(NewOpenCVTests): numFeaturesInRectOnStart = 0 def test_lk_homography(self): - self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), - self.get_sample('samples/data/box.png'), noise = 0.1, speed = 1.0) + self.render = TestSceneRender(self.get_sample('samples/python2/data/graf1.png'), + self.get_sample('samples/c/box.png'), noise = 0.1, speed = 1.0) frame = self.render.getNextFrame() frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) diff --git a/modules/python/test/test_lk_track.py b/modules/python/test/test_lk_track.py index ccc67a5128..920351c5f2 100644 --- a/modules/python/test/test_lk_track.py +++ b/modules/python/test/test_lk_track.py @@ -50,7 +50,7 @@ class lk_track_test(NewOpenCVTests): def test_lk_track(self): - self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), self.get_sample('samples/data/box.png')) + self.render = TestSceneRender(self.get_sample('samples/python2/data/graf1.png'), self.get_sample('samples/c/box.png')) self.runTracker() def runTracker(self): diff --git a/modules/python/test/test_morphology.py b/modules/python/test/test_morphology.py index 309c80cfd2..1062801eac 100644 --- a/modules/python/test/test_morphology.py +++ b/modules/python/test/test_morphology.py @@ -18,7 +18,7 @@ class morphology_test(NewOpenCVTests): def test_morphology(self): - fn = 'samples/data/rubberwhale1.png' + fn = 'samples/gpu/rubberwhale1.png' img = self.get_sample(fn) modes = ['erode/dilate', 'open/close', 'blackhat/tophat', 'gradient'] diff --git a/modules/python/test/test_peopledetect.py b/modules/python/test/test_peopledetect.py index fb0a9e9cae..efb477d426 100644 --- a/modules/python/test/test_peopledetect.py +++ b/modules/python/test/test_peopledetect.py @@ -24,7 +24,7 @@ class peopledetect_test(NewOpenCVTests): hog = cv2.HOGDescriptor() hog.setSVMDetector( cv2.HOGDescriptor_getDefaultPeopleDetector() ) - dirPath = 'samples/data/' + dirPath = 'samples/gpu/' samples = ['basketball1.png', 'basketball2.png'] testPeople = [ diff --git a/modules/python/test/test_squares.py b/modules/python/test/test_squares.py index 3b3fbb6d9d..c933b358f4 100644 --- a/modules/python/test/test_squares.py +++ b/modules/python/test/test_squares.py @@ -61,7 +61,7 @@ class squares_test(NewOpenCVTests): def test_squares(self): - img = self.get_sample('samples/data/pic1.png') + img = self.get_sample('samples/cpp/pic1.png') squares = find_squares(img) testSquares = [ diff --git a/modules/python/test/test_texture_flow.py b/modules/python/test/test_texture_flow.py index 7dc3b07040..8dae6fabd4 100644 --- a/modules/python/test/test_texture_flow.py +++ b/modules/python/test/test_texture_flow.py @@ -21,7 +21,7 @@ class texture_flow_test(NewOpenCVTests): def test_texture_flow(self): - img = self.get_sample('samples/data/pic6.png') + img = self.get_sample('samples/cpp/pic6.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) h, w = img.shape[:2] diff --git a/modules/python/test/tst_scene_render.py b/modules/python/test/tst_scene_render.py index 49cde80d25..68e03acbad 100644 --- a/modules/python/test/tst_scene_render.py +++ b/modules/python/test/tst_scene_render.py @@ -100,20 +100,3 @@ class TestSceneRender(): def resetTime(self): self.time = 0.0 - - -if __name__ == '__main__': - - backGr = cv2.imread('../../../samples/data/lena.jpg') - - render = TestSceneRender(backGr, noise = 0.5) - - while True: - - img = render.getNextFrame() - cv2.imshow('img', img) - - ch = 0xFF & cv2.waitKey(3) - if ch == 27: - break - cv2.destroyAllWindows() \ No newline at end of file diff --git a/samples/python2/data/graf1.png b/samples/python2/data/graf1.png new file mode 100644 index 0000000000000000000000000000000000000000..67e347318648420262f7d4c1f528ab0f22e48109 GIT binary patch literal 951440 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�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