diff --git a/modules/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index 692d3d9fd..32fdabd8a 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -361,7 +361,7 @@ public: */ CV_WRAP virtual void detectRegions( InputArray image, CV_OUT std::vector >& msers, - std::vector& bboxes ) = 0; + CV_OUT std::vector& bboxes ) = 0; CV_WRAP virtual void setDelta(int delta) = 0; CV_WRAP virtual int getDelta() const = 0; diff --git a/modules/python/test/test.py b/modules/python/test/test.py old mode 100644 new mode 100755 index 093979aba..a2d36989b --- a/modules/python/test/test.py +++ b/modules/python/test/test.py @@ -23,48 +23,16 @@ try: except ImportError: from urllib import urlopen -class NewOpenCVTests(unittest.TestCase): - - # path to local repository folder containing 'samples' folder - repoPath = 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 filedata is 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()).digest() - - 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))) +from tests_common import NewOpenCVTests # Tests to run first; check the handful of basic operations that the later tests rely on +basedir = os.path.abspath(os.path.dirname(__file__)) + +def load_tests(loader, tests, pattern): + tests.addTests(loader.discover(basedir, pattern='test_*.py')) + return tests + class Hackathon244Tests(NewOpenCVTests): def test_int_array(self): @@ -165,6 +133,10 @@ if __name__ == '__main__': print("Testing OpenCV", cv2.__version__) print("Local repo path:", args.repo) NewOpenCVTests.repoPath = args.repo + try: + NewOpenCVTests.extraTestDataPath = os.environ['OPENCV_TEST_DATA_PATH'] + except KeyError: + print('Missing opencv extra repository. Some of tests may fail.') random.seed(0) unit_argv = [sys.argv[0]] + other; - unittest.main(argv=unit_argv) + unittest.main(argv=unit_argv) \ No newline at end of file diff --git a/modules/python/test/test_calibration.py b/modules/python/test/test_calibration.py new file mode 100644 index 000000000..665521862 --- /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 000000000..a824320ef --- /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 000000000..f79693997 --- /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 000000000..2d5c90043 --- /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.ml.KNearest_create() + + def train(self, samples, responses): + self.model.train(samples, cv2.ml.ROW_SAMPLE, responses) + + def predict(self, samples): + retval, results, neigh_resp, dists = self.model.findNearest(samples, self.k) + return results.ravel() + +class SVM(StatModel): + def __init__(self, C = 1, gamma = 0.5): + self.model = cv2.ml.SVM_create() + self.model.setGamma(gamma) + self.model.setC(C) + self.model.setKernel(cv2.ml.SVM_RBF) + self.model.setType(cv2.ml.SVM_C_SVC) + + def train(self, samples, responses): + self.model.train(samples, cv2.ml.ROW_SAMPLE, responses) + + def predict(self, samples): + return self.model.predict(samples)[1].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 000000000..8d64fde10 --- /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 000000000..861ff9232 --- /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.AKAZE_create(threshold = 0.003) + 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 no 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 000000000..7de957338 --- /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 = ['DIST_L2', 'DIST_L1', 'DIST_L12', 'DIST_FAIR', 'DIST_WELSCH', '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, 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 000000000..cfd33ece0 --- /dev/null +++ b/modules/python/test/test_gaussian_mix.py @@ -0,0 +1,60 @@ +#!/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.ml.EM_create() + em.setClustersNumber(cluster_n) + em.setCovarianceMatrixType(cv2.ml.EM_COV_MAT_GENERIC) + em.trainEM(points) + means = em.getMeans() + covs = em.getCovs() # 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 000000000..38211f7d8 --- /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 000000000..318153ab7 --- /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.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 000000000..9f056ce3e --- /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 000000000..4f886d9d8 --- /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, None, 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 000000000..574741f44 --- /dev/null +++ b/modules/python/test/test_letter_recog.py @@ -0,0 +1,167 @@ +#!/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.ml.RTrees_create() + + def train(self, samples, responses): + sample_n, var_n = samples.shape + self.model.setMaxDepth(20) + self.model.train(samples, cv2.ml.ROW_SAMPLE, responses.astype(int)) + + def predict(self, samples): + ret, resp = self.model.predict(samples) + return resp.ravel() + + +class KNearest(LetterStatModel): + def __init__(self): + self.model = cv2.ml.KNearest_create() + + def train(self, samples, responses): + self.model.train(samples, cv2.ml.ROW_SAMPLE, responses) + + def predict(self, samples): + retval, results, neigh_resp, dists = self.model.findNearest(samples, k = 10) + return results.ravel() + + +class Boost(LetterStatModel): + def __init__(self): + self.model = cv2.ml.Boost_create() + + 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.ml.VAR_NUMERICAL] * var_n + [cv2.ml.VAR_CATEGORICAL, cv2.ml.VAR_CATEGORICAL], np.uint8) + + self.model.setWeakCount(15) + self.model.setMaxDepth(10) + self.model.train(cv2.ml.TrainData_create(new_samples, cv2.ml.ROW_SAMPLE, new_responses.astype(int), varType = var_types)) + + def predict(self, samples): + new_samples = self.unroll_samples(samples) + ret, resp = self.model.predict(new_samples) + + return resp.ravel().reshape(-1, self.class_n).argmax(1) + + +class SVM(LetterStatModel): + def __init__(self): + self.model = cv2.ml.SVM_create() + + def train(self, samples, responses): + self.model.setType(cv2.ml.SVM_C_SVC) + self.model.setC(1) + self.model.setKernel(cv2.ml.SVM_RBF) + self.model.setGamma(.1) + self.model.train(samples, cv2.ml.ROW_SAMPLE, responses.astype(int)) + + def predict(self, samples): + ret, resp = self.model.predict(samples) + return resp.ravel() + + +class MLP(LetterStatModel): + def __init__(self): + self.model = cv2.ml.ANN_MLP_create() + + 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.setLayerSizes(layer_sizes) + self.model.setTrainMethod(cv2.ml.ANN_MLP_BACKPROP) + self.model.setBackpropMomentumScale(0) + self.model.setBackpropWeightScale(0.001) + self.model.setTermCriteria((cv2.TERM_CRITERIA_COUNT, 20, 0.01)) + self.model.setActivationFunction(cv2.ml.ANN_MLP_SIGMOID_SYM, 2, 1) + + self.model.train(samples, cv2.ml.ROW_SAMPLE, np.float32(new_responses)) + + 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 000000000..8e526d0de --- /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 000000000..ccc67a512 --- /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 000000000..309c80cfd --- /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 000000000..619300b86 --- /dev/null +++ b/modules/python/test/test_mser.py @@ -0,0 +1,69 @@ +#!/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 + mserExtractor = cv2.MSER_create() + mserExtractor.setDelta(kDelta) + 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.setMinArea(kMinArea) + mserExtractor.setMaxArea(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, boxes = mserExtractor.detectRegions(src) + nmsers = len(msers) + self.assertEqual(nmsers, len(boxes)) + 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 000000000..fb0a9e9ca --- /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 000000000..214c64bee --- /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) + bin, 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 000000000..7dc3b0704 --- /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 000000000..0a1d222f4 --- /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 000000000..3a636b255 --- /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 000000000..49cde80d2 --- /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 diff --git a/samples/python/asift.py b/samples/python/asift.py index 8d2774a72..ec74930c5 100755 --- a/samples/python/asift.py +++ b/samples/python/asift.py @@ -112,7 +112,7 @@ if __name__ == '__main__': import sys, getopt opts, args = getopt.getopt(sys.argv[1:], '', ['feature=']) opts = dict(opts) - feature_name = opts.get('--feature', 'sift-flann') + feature_name = opts.get('--feature', 'brisk-flann') try: fn1, fn2 = args except: diff --git a/samples/python/common.py b/samples/python/common.py index 785fb6c8f..09159bbe8 100755 --- a/samples/python/common.py +++ b/samples/python/common.py @@ -173,6 +173,7 @@ class RectSelector: x, y = np.int16([x, y]) # BUG if event == cv2.EVENT_LBUTTONDOWN: self.drag_start = (x, y) + return if self.drag_start: if flags & cv2.EVENT_FLAG_LBUTTON: xo, yo = self.drag_start diff --git a/samples/python/demo.py b/samples/python/demo.py index 864953a31..81c6a8590 100755 --- a/samples/python/demo.py +++ b/samples/python/demo.py @@ -87,8 +87,11 @@ class App: for fn in glob('*.py'): name = splitfn(fn)[1] if fn[0] != '_' and name not in exclude_list: - demos_lb.insert(tk.END, name) self.samples[name] = fn + + for name in sorted(self.samples): + demos_lb.insert(tk.END, name) + demos_lb.bind('<>', self.on_demo_select) self.cmd_entry = cmd_entry = tk.Entry(right) diff --git a/samples/python/digits_video.py b/samples/python/digits_video.py index 5f57cb8f9..c85deb6d0 100755 --- a/samples/python/digits_video.py +++ b/samples/python/digits_video.py @@ -27,9 +27,12 @@ def main(): if not os.path.exists(classifier_fn): print('"%s" not found, run digits.py first' % classifier_fn) return - model = SVM() - model.load(classifier_fn) + if True: + model = cv2.ml.SVM_load(classifier_fn) + else: + model = cv2.ml.SVM_create() + model.load_(classifier_fn) #Known bug: https://github.com/Itseez/opencv/issues/4969 while True: ret, frame = cap.read() diff --git a/samples/python/find_obj.py b/samples/python/find_obj.py index d8d3d4133..09457b80b 100755 --- a/samples/python/find_obj.py +++ b/samples/python/find_obj.py @@ -68,7 +68,7 @@ def filter_matches(kp1, kp2, matches, ratio = 0.75): p1 = np.float32([kp.pt for kp in mkp1]) p2 = np.float32([kp.pt for kp in mkp2]) kp_pairs = zip(mkp1, mkp2) - return p1, p2, kp_pairs + return p1, p2, list(kp_pairs) def explore_match(win, img1, img2, kp_pairs, status = None, H = None): h1, w1 = img1.shape[:2] @@ -119,7 +119,7 @@ def explore_match(win, img1, img2, kp_pairs, status = None, H = None): if flags & cv2.EVENT_FLAG_LBUTTON: cur_vis = vis0.copy() r = 8 - m = (anorm(p1 - (x, y)) < r) | (anorm(p2 - (x, y)) < r) + m = (anorm(np.array(p1) - (x, y)) < r) | (anorm(np.array(p2) - (x, y)) < r) idxs = np.where(m)[0] kp1s, kp2s = [], [] for i in idxs: @@ -143,7 +143,7 @@ if __name__ == '__main__': import sys, getopt opts, args = getopt.getopt(sys.argv[1:], '', ['feature=']) opts = dict(opts) - feature_name = opts.get('--feature', 'sift') + feature_name = opts.get('--feature', 'brisk') try: fn1, fn2 = args except: diff --git a/samples/python/letter_recog.py b/samples/python/letter_recog.py index e68c095bc..7d0c43764 100755 --- a/samples/python/letter_recog.py +++ b/samples/python/letter_recog.py @@ -65,13 +65,12 @@ class RTrees(LetterStatModel): def train(self, samples, responses): sample_n, var_n = samples.shape - var_types = np.array([cv2.ml.VAR_NUMERICAL] * var_n + [cv2.ml.VAR_CATEGORICAL], np.uint8) - #CvRTParams(10,10,0,false,15,0,true,4,100,0.01f,CV_TERMCRIT_ITER)); - params = dict(max_depth=10 ) - self.model.train(samples, cv2.ml.ROW_SAMPLE, responses, varType = var_types, params = params) + self.model.setMaxDepth(20) + self.model.train(samples, cv2.ml.ROW_SAMPLE, responses.astype(int)) def predict(self, samples): - return [self.model.predict(s) for s in samples] + ret, resp = self.model.predict(samples) + return resp.ravel() class KNearest(LetterStatModel): @@ -79,10 +78,10 @@ class KNearest(LetterStatModel): self.model = cv2.ml.KNearest_create() def train(self, samples, responses): - self.model.train(samples, responses) + self.model.train(samples, cv2.ml.ROW_SAMPLE, responses) def predict(self, samples): - retval, results, neigh_resp, dists = self.model.find_nearest(samples, k = 10) + retval, results, neigh_resp, dists = self.model.findNearest(samples, k = 10) return results.ravel() @@ -95,15 +94,16 @@ class Boost(LetterStatModel): new_samples = self.unroll_samples(samples) new_responses = self.unroll_responses(responses) var_types = np.array([cv2.ml.VAR_NUMERICAL] * var_n + [cv2.ml.VAR_CATEGORICAL, cv2.ml.VAR_CATEGORICAL], np.uint8) - #CvBoostParams(CvBoost::REAL, 100, 0.95, 5, false, 0 ) - params = dict(max_depth=5) #, use_surrogates=False) - self.model.train(new_samples, cv2.ml.ROW_SAMPLE, new_responses, varType = var_types, params=params) + + self.model.setWeakCount(15) + self.model.setMaxDepth(10) + self.model.train(cv2.ml.TrainData_create(new_samples, cv2.ml.ROW_SAMPLE, new_responses.astype(int), varType = var_types)) def predict(self, samples): new_samples = self.unroll_samples(samples) - pred = np.array( [self.model.predict(s, returnSum = True) for s in new_samples] ) - pred = pred.reshape(-1, self.class_n).argmax(1) - return pred + ret, resp = self.model.predict(new_samples) + + return resp.ravel().reshape(-1, self.class_n).argmax(1) class SVM(LetterStatModel): @@ -111,13 +111,15 @@ class SVM(LetterStatModel): self.model = cv2.ml.SVM_create() def train(self, samples, responses): - params = dict( kernel_type = cv2.ml.SVM_LINEAR, - svm_type = cv2.ml.SVM_C_SVC, - C = 1 ) - self.model.train(samples, responses, params = params) + self.model.setType(cv2.ml.SVM_C_SVC) + self.model.setC(1) + self.model.setKernel(cv2.ml.SVM_RBF) + self.model.setGamma(.1) + self.model.train(samples, cv2.ml.ROW_SAMPLE, responses.astype(int)) def predict(self, samples): - return self.model.predict_all(samples).ravel() + ret, resp = self.model.predict(samples) + return resp.ravel() class MLP(LetterStatModel): @@ -127,22 +129,23 @@ class MLP(LetterStatModel): 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) - # CvANN_MLP_TrainParams::BACKPROP,0.001 - params = dict( term_crit = (cv2.TERM_CRITERIA_COUNT, 300, 0.01), - train_method = cv2.ml.ANN_MLP_TRAIN_PARAMS_BACKPROP, - bp_dw_scale = 0.001, - bp_moment_scale = 0.0 ) - self.model.train(samples, np.float32(new_responses), None, params = params) + self.model.setLayerSizes(layer_sizes) + self.model.setTrainMethod(cv2.ml.ANN_MLP_BACKPROP) + self.model.setBackpropMomentumScale(0.0) + self.model.setBackpropWeightScale(0.001) + self.model.setTermCriteria((cv2.TERM_CRITERIA_COUNT, 20, 0.01)) + self.model.setActivationFunction(cv2.ml.ANN_MLP_SIGMOID_SYM, 2, 1) + + self.model.train(samples, cv2.ml.ROW_SAMPLE, np.float32(new_responses)) def predict(self, samples): ret, resp = self.model.predict(samples) return resp.argmax(-1) + if __name__ == '__main__': import getopt import sys @@ -155,7 +158,7 @@ if __name__ == '__main__': args, dummy = getopt.getopt(sys.argv[1:], '', ['model=', 'data=', 'load=', 'save=']) args = dict(args) - args.setdefault('--model', 'rtrees') + args.setdefault('--model', 'svm') args.setdefault('--data', '../data/letter-recognition.data') print('loading data %s ...' % args['--data']) @@ -173,8 +176,8 @@ if __name__ == '__main__': model.train(samples[:train_n], responses[:train_n]) print('testing...') - train_rate = np.mean(model.predict(samples[:train_n]) == responses[:train_n]) - test_rate = np.mean(model.predict(samples[train_n:]) == responses[train_n:]) + train_rate = np.mean(model.predict(samples[:train_n]) == responses[:train_n].astype(int)) + test_rate = np.mean(model.predict(samples[train_n:]) == responses[train_n:].astype(int)) print('train rate: %f test rate: %f' % (train_rate*100, test_rate*100))