From cc6f1eb824b37b6a5c9b3b0d303c2ed78402974e Mon Sep 17 00:00:00 2001 From: Yury Zemlyanskiy Date: Sun, 19 Aug 2012 15:56:19 +0400 Subject: [PATCH 01/97] "SimpleFlow" optical flow estimation algorithm (GSoC project) declaration in includes, implementation, usage example, test --- .../motion_analysis_and_object_tracking.rst | 44 + .../video/include/opencv2/video/tracking.hpp | 21 +- modules/video/src/simpleflow.cpp | 757 ++++++++++++++++++ modules/video/src/simpleflow.hpp | 125 +++ modules/video/test/test_simpleflow.cpp | 193 +++++ samples/cpp/simpleflow_demo.cpp | 96 +++ 6 files changed, 1235 insertions(+), 1 deletion(-) create mode 100644 modules/video/src/simpleflow.cpp create mode 100644 modules/video/src/simpleflow.hpp create mode 100644 modules/video/test/test_simpleflow.cpp create mode 100644 samples/cpp/simpleflow_demo.cpp diff --git a/modules/video/doc/motion_analysis_and_object_tracking.rst b/modules/video/doc/motion_analysis_and_object_tracking.rst index 6c196c2ff1..ebb9290cc4 100644 --- a/modules/video/doc/motion_analysis_and_object_tracking.rst +++ b/modules/video/doc/motion_analysis_and_object_tracking.rst @@ -597,6 +597,48 @@ Returns background image See :ocv:func:`BackgroundSubtractor::getBackgroundImage`. +calcOpticalFlowSF +----------- +Calculate an optical flow using "SimpleFlow" algorithm. + +.. ocv:function:: void calcOpticalFlowSF( Mat& prev, Mat& next, Mat& flowX, Mat& flowY, int layers, int averaging_block_size, int max_flow, double sigma_dist, double sigma_color, int postprocess_window, double sigma_dist_fix, double sigma_color_fix, double occ_thr, int upscale_averaging_radiud, double upscale_sigma_dist, double upscale_sigma_color, double speed_up_thr) + + :param prev: First 8-bit 3-channel image. + + :param next: Second 8-bit 3-channel image + + :param flowX: X-coordinate of estimated flow + + :param flowY: Y-coordinate of estimated flow + + :param layers: Number of layers + + :param averaging_block_size: Size of block through which we sum up when calculate cost function for pixel + + :param max_flow: maximal flow that we search at each level + + :param sigma_dist: vector smooth spatial sigma parameter + + :param sigma_color: vector smooth color sigma parameter + + :param postprocess_window: window size for postprocess cross bilateral filter + + :param sigma_dist_fix: spatial sigma for postprocess cross bilateralf filter + + :param sigma_color_fix: color sigma for postprocess cross bilateral filter + + :param occ_thr: threshold for detecting occlusions + + :param upscale_averaging_radiud: window size for bilateral upscale operation + + :param upscale_sigma_dist: spatial sigma for bilateral upscale operation + + :param upscale_sigma_color: color sigma for bilateral upscale operation + + :param speed_up_thr: threshold to detect point with irregular flow - where flow should be recalculated after upscale + +See [Tao2012]_. And site of project - http://graphics.berkeley.edu/papers/Tao-SAN-2012-05/. + .. [Bouguet00] Jean-Yves Bouguet. Pyramidal Implementation of the Lucas Kanade Feature Tracker. .. [Bradski98] Bradski, G.R. "Computer Vision Face Tracking for Use in a Perceptual User Interface", Intel, 1998 @@ -612,3 +654,5 @@ See :ocv:func:`BackgroundSubtractor::getBackgroundImage`. .. [Lucas81] Lucas, B., and Kanade, T. An Iterative Image Registration Technique with an Application to Stereo Vision, Proc. of 7th International Joint Conference on Artificial Intelligence (IJCAI), pp. 674-679. .. [Welch95] Greg Welch and Gary Bishop “An Introduction to the Kalman Filter”, 1995 + +.. [Tao2012] Michael Tao, Jiamin Bai, Pushmeet Kohli and Sylvain Paris. SimpleFlow: A Non-iterative, Sublinear Optical Flow Algorithm. Computer Graphics Forum (Eurographics 2012) diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index 75668d2289..85c18817a9 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -326,7 +326,26 @@ CV_EXPORTS_W void calcOpticalFlowFarneback( InputArray prev, InputArray next, // that maps one 2D point set to another or one image to another. CV_EXPORTS_W Mat estimateRigidTransform( InputArray src, InputArray dst, bool fullAffine); - + +//! computes dense optical flow using Simple Flow algorithm +CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, + Mat& to, + Mat& flowX, + Mat& flowY, + int layers, + int averaging_block_size, + int max_flow, + double sigma_dist, + double sigma_color, + int postprocess_window, + double sigma_dist_fix, + double sigma_color_fix, + double occ_thr, + int upscale_averaging_radius, + double upscale_sigma_dist, + double upscale_sigma_color, + double speed_up_thr); + } #endif diff --git a/modules/video/src/simpleflow.cpp b/modules/video/src/simpleflow.cpp new file mode 100644 index 0000000000..1fda3618c9 --- /dev/null +++ b/modules/video/src/simpleflow.cpp @@ -0,0 +1,757 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" +#include "simpleflow.hpp" + +// +// 2D dense optical flow algorithm from the following paper: +// Michael Tao, Jiamin Bai, Pushmeet Kohli, and Sylvain Paris. +// "SimpleFlow: A Non-iterative, Sublinear Optical Flow Algorithm" +// Computer Graphics Forum (Eurographics 2012) +// http://graphics.berkeley.edu/papers/Tao-SAN-2012-05/ +// + +namespace cv +{ + +WeightedCrossBilateralFilter::WeightedCrossBilateralFilter( + const Mat& _image, + int _windowSize, + double _sigmaDist, + double _sigmaColor) + : image(_image), + windowSize(_windowSize), + sigmaDist(_sigmaDist), + sigmaColor(_sigmaColor) { + + expDist.resize(2*windowSize*windowSize+1); + const double sigmaDistSqr = 2 * sigmaDist * sigmaDist; + for (int i = 0; i <= 2*windowSize*windowSize; ++i) { + expDist[i] = exp(-i/sigmaDistSqr); + } + + const double sigmaColorSqr = 2 * sigmaColor * sigmaColor; + wc.resize(image.rows); + for (int row = 0; row < image.rows; ++row) { + wc[row].resize(image.cols); + for (int col = 0; col < image.cols; ++col) { + int beginRow = max(0, row - windowSize); + int beginCol = max(0, col - windowSize); + int endRow = min(image.rows - 1, row + windowSize); + int endCol = min(image.cols - 1, col + windowSize); + wc[row][col] = build(endRow - beginRow + 1, endCol - beginCol + 1); + + for (int r = beginRow; r <= endRow; ++r) { + for (int c = beginCol; c <= endCol; ++c) { + wc[row][col][r - beginRow][c - beginCol] = + exp(-dist(image.at(row, col), + image.at(r, c)) + / sigmaColorSqr); + } + } + } + } +} + +Mat WeightedCrossBilateralFilter::apply(Mat& matrix, Mat& weights) { + int rows = matrix.rows; + int cols = matrix.cols; + + Mat result = Mat::zeros(rows, cols, CV_64F); + for (int row = 0; row < rows; ++row) { + for(int col = 0; col < cols; ++col) { + result.at(row, col) = + convolution(matrix, row, col, weights); + } + } + return result; +} + +double WeightedCrossBilateralFilter::convolution(Mat& matrix, + int row, int col, + Mat& weights) { + double result = 0, weightsSum = 0; + int beginRow = max(0, row - windowSize); + int beginCol = max(0, col - windowSize); + int endRow = min(matrix.rows - 1, row + windowSize); + int endCol = min(matrix.cols - 1, col + windowSize); + for (int r = beginRow; r <= endRow; ++r) { + double* ptr = matrix.ptr(r); + for (int c = beginCol; c <= endCol; ++c) { + const double w = expDist[dist(row, col, r, c)] * + wc[row][col][r - beginRow][c - beginCol] * + weights.at(r, c); + result += ptr[c] * w; + weightsSum += w; + } + } + return result / weightsSum; +} + +static void removeOcclusions(const Flow& flow, + const Flow& flow_inv, + double occ_thr, + Mat& confidence) { + const int rows = flow.u.rows; + const int cols = flow.v.cols; + int occlusions = 0; + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + if (dist(flow.u.at(r, c), flow.v.at(r, c), + -flow_inv.u.at(r, c), -flow_inv.v.at(r, c)) > occ_thr) { + confidence.at(r, c) = 0; + occlusions++; + } + } + } +} + +static Mat wd(int top_shift, int bottom_shift, int left_shift, int right_shift, double sigma) { + const double factor = 1.0 / (2.0 * sigma * sigma); + Mat d = Mat(top_shift + bottom_shift + 1, right_shift + left_shift + 1, CV_64F); + for (int dr = -top_shift, r = 0; dr <= bottom_shift; ++dr, ++r) { + for (int dc = -left_shift, c = 0; dc <= right_shift; ++dc, ++c) { + d.at(r, c) = -(dr*dr + dc*dc) * factor; + } + } + Mat ed; + exp(d, ed); + return ed; +} + +static Mat wc(const Mat& image, int r0, int c0, int top_shift, int bottom_shift, int left_shift, int right_shift, double sigma) { + const double factor = 1.0 / (2.0 * sigma * sigma); + Mat d = Mat(top_shift + bottom_shift + 1, right_shift + left_shift + 1, CV_64F); + for (int dr = r0-top_shift, r = 0; dr <= r0+bottom_shift; ++dr, ++r) { + for (int dc = c0-left_shift, c = 0; dc <= c0+right_shift; ++dc, ++c) { + d.at(r, c) = -dist(image.at(r0, c0), image.at(dr, dc)) * factor; + } + } + Mat ed; + exp(d, ed); + return ed; +} + +inline static void dist(const Mat& m1, const Mat& m2, Mat& result) { + const int rows = m1.rows; + const int cols = m1.cols; + for (int r = 0; r < rows; ++r) { + const Vec3b *m1_row = m1.ptr(r); + const Vec3b *m2_row = m2.ptr(r); + double* row = result.ptr(r); + for (int c = 0; c < cols; ++c) { + row[c] = dist(m1_row[c], m2_row[c]); + } + } +} + +static void calcOpticalFlowSingleScaleSF(const Mat& prev, + const Mat& next, + const Mat& mask, + Flow& flow, + Mat& confidence, + int averaging_radius, + int max_flow, + double sigma_dist, + double sigma_color) { + const int rows = prev.rows; + const int cols = prev.cols; + confidence = Mat::zeros(rows, cols, CV_64F); + + for (int r0 = 0; r0 < rows; ++r0) { + for (int c0 = 0; c0 < cols; ++c0) { + int u0 = floor(flow.u.at(r0, c0) + 0.5); + int v0 = floor(flow.v.at(r0, c0) + 0.5); + + const int min_row_shift = -min(r0 + u0, max_flow); + const int max_row_shift = min(rows - 1 - (r0 + u0), max_flow); + const int min_col_shift = -min(c0 + v0, max_flow); + const int max_col_shift = min(cols - 1 - (c0 + v0), max_flow); + + double min_cost = DBL_MAX, best_u = u0, best_v = v0; + + Mat w_full_window; + double w_full_window_sum; + Mat diff_storage; + + if (r0 - averaging_radius >= 0 && + r0 + averaging_radius < rows && + c0 - averaging_radius >= 0 && + c0 + averaging_radius < cols && + mask.at(r0, c0)) { + w_full_window = wd(averaging_radius, + averaging_radius, + averaging_radius, + averaging_radius, + sigma_dist).mul( + wc(prev, r0, c0, + averaging_radius, + averaging_radius, + averaging_radius, + averaging_radius, + sigma_color)); + + w_full_window_sum = sum(w_full_window)[0]; + diff_storage = Mat::zeros(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_64F); + } + + bool first_flow_iteration = true; + double sum_e, min_e; + + for (int u = min_row_shift; u <= max_row_shift; ++u) { + for (int v = min_col_shift; v <= max_col_shift; ++v) { + double e = dist(prev.at(r0, c0), next.at(r0 + u0 + u, c0 + v0 + v)); + if (first_flow_iteration) { + sum_e = e; + min_e = e; + first_flow_iteration = false; + } else { + sum_e += e; + min_e = std::min(min_e, e); + } + if (!mask.at(r0, c0)) { + continue; + } + + const int window_top_shift = min(r0, r0 + u + u0, averaging_radius); + const int window_bottom_shift = min(rows - 1 - r0, + rows - 1 - (r0 + u + u0), + averaging_radius); + const int window_left_shift = min(c0, c0 + v + v0, averaging_radius); + const int window_right_shift = min(cols - 1 - c0, + cols - 1 - (c0 + v + v0), + averaging_radius); + + const Range prev_row_range(r0 - window_top_shift, r0 + window_bottom_shift + 1); + const Range prev_col_range(c0 - window_left_shift, c0 + window_right_shift + 1); + + const Range next_row_range(r0 + u0 + u - window_top_shift, + r0 + u0 + u + window_bottom_shift + 1); + const Range next_col_range(c0 + v0 + v - window_left_shift, + c0 + v0 + v + window_right_shift + 1); + + Mat diff2; + Mat w; + double w_sum; + if (window_top_shift == averaging_radius && + window_bottom_shift == averaging_radius && + window_left_shift == averaging_radius && + window_right_shift == averaging_radius) { + w = w_full_window; + w_sum = w_full_window_sum; + diff2 = diff_storage; + + dist(prev(prev_row_range, prev_col_range), next(next_row_range, next_col_range), diff2); + } else { + diff2 = Mat::zeros(window_bottom_shift + window_top_shift + 1, + window_right_shift + window_left_shift + 1, CV_64F); + + dist(prev(prev_row_range, prev_col_range), next(next_row_range, next_col_range), diff2); + + w = wd(window_top_shift, window_bottom_shift, window_left_shift, window_right_shift, sigma_dist).mul( + wc(prev, r0, c0, window_top_shift, window_bottom_shift, window_left_shift, window_right_shift, sigma_color)); + w_sum = sum(w)[0]; + } + multiply(diff2, w, diff2); + + const double cost = sum(diff2)[0] / w_sum; + if (cost < min_cost) { + min_cost = cost; + best_u = u + u0; + best_v = v + v0; + } + } + } + int square = (max_row_shift - min_row_shift + 1) * + (max_col_shift - min_col_shift + 1); + confidence.at(r0, c0) = (square == 0) ? 0 + : sum_e / square - min_e; + if (mask.at(r0, c0)) { + flow.u.at(r0, c0) = best_u; + flow.v.at(r0, c0) = best_v; + } + } + } +} + +static Flow upscaleOpticalFlow(int new_rows, + int new_cols, + const Mat& image, + const Mat& confidence, + const Flow& flow, + int averaging_radius, + double sigma_dist, + double sigma_color) { + const int rows = image.rows; + const int cols = image.cols; + Flow new_flow(new_rows, new_cols); + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + const int window_top_shift = min(r, averaging_radius); + const int window_bottom_shift = min(rows - 1 - r, averaging_radius); + const int window_left_shift = min(c, averaging_radius); + const int window_right_shift = min(cols - 1 - c, averaging_radius); + + const Range row_range(r - window_top_shift, r + window_bottom_shift + 1); + const Range col_range(c - window_left_shift, c + window_right_shift + 1); + + const Mat w = confidence(row_range, col_range).mul( + wd(window_top_shift, window_bottom_shift, window_left_shift, window_right_shift, sigma_dist)).mul( + wc(image, r, c, window_top_shift, window_bottom_shift, window_left_shift, window_right_shift, sigma_color)); + + const double w_sum = sum(w)[0]; + double new_u, new_v; + if (fabs(w_sum) < 1e-9) { + new_u = flow.u.at(r, c); + new_v = flow.v.at(r, c); + } else { + new_u = sum(flow.u(row_range, col_range).mul(w))[0] / w_sum; + new_v = sum(flow.v(row_range, col_range).mul(w))[0] / w_sum; + } + + for (int dr = 0; dr <= 1; ++dr) { + int nr = 2*r + dr; + for (int dc = 0; dc <= 1; ++dc) { + int nc = 2*c + dc; + if (nr < new_rows && nc < new_cols) { + new_flow.u.at(nr, nc) = 2 * new_u; + new_flow.v.at(nr, nc) = 2 * new_v; + } + } + } + } + } + return new_flow; +} + +static Mat calcIrregularityMat(const Flow& flow, int radius) { + const int rows = flow.u.rows; + const int cols = flow.v.cols; + Mat irregularity = Mat::zeros(rows, cols, CV_64F); + for (int r = 0; r < rows; ++r) { + const int start_row = max(0, r - radius); + const int end_row = min(rows - 1, r + radius); + for (int c = 0; c < cols; ++c) { + const int start_col = max(0, c - radius); + const int end_col = min(cols - 1, c + radius); + for (int dr = start_row; dr <= end_row; ++dr) { + for (int dc = start_col; dc <= end_col; ++dc) { + const double diff = dist(flow.u.at(r, c), flow.v.at(r, c), + flow.u.at(dr, dc), flow.v.at(dr, dc)); + if (diff > irregularity.at(r, c)) { + irregularity.at(r, c) = diff; + } + } + } + } + } + return irregularity; +} + +static void selectPointsToRecalcFlow(const Flow& flow, + int irregularity_metric_radius, + int speed_up_thr, + int curr_rows, + int curr_cols, + const Mat& prev_speed_up, + Mat& speed_up, + Mat& mask) { + const int prev_rows = flow.u.rows; + const int prev_cols = flow.v.cols; + + Mat is_flow_regular = calcIrregularityMat(flow, + irregularity_metric_radius) + < speed_up_thr; + Mat done = Mat::zeros(prev_rows, prev_cols, CV_8U); + speed_up = Mat::zeros(curr_rows, curr_cols, CV_8U); + mask = Mat::zeros(curr_rows, curr_cols, CV_8U); + + for (int r = 0; r < is_flow_regular.rows; ++r) { + for (int c = 0; c < is_flow_regular.cols; ++c) { + if (!done.at(r, c)) { + if (is_flow_regular.at(r, c) && + 2*r + 1 < curr_rows && 2*c + 1< curr_cols) { + + bool all_flow_in_region_regular = true; + int speed_up_at_this_point = prev_speed_up.at(r, c); + int step = (1 << speed_up_at_this_point) - 1; + int prev_top = r; + int prev_bottom = std::min(r + step, prev_rows - 1); + int prev_left = c; + int prev_right = std::min(c + step, prev_cols - 1); + + for (int rr = prev_top; rr <= prev_bottom; ++rr) { + for (int cc = prev_left; cc <= prev_right; ++cc) { + done.at(rr, cc) = 1; + if (!is_flow_regular.at(rr, cc)) { + all_flow_in_region_regular = false; + } + } + } + + int curr_top = std::min(2 * r, curr_rows - 1); + int curr_bottom = std::min(2*(r + step) + 1, curr_rows - 1); + int curr_left = std::min(2 * c, curr_cols - 1); + int curr_right = std::min(2*(c + step) + 1, curr_cols - 1); + + if (all_flow_in_region_regular && + curr_top != curr_bottom && + curr_left != curr_right) { + mask.at(curr_top, curr_left) = MASK_TRUE_VALUE; + mask.at(curr_bottom, curr_left) = MASK_TRUE_VALUE; + mask.at(curr_top, curr_right) = MASK_TRUE_VALUE; + mask.at(curr_bottom, curr_right) = MASK_TRUE_VALUE; + for (int rr = curr_top; rr <= curr_bottom; ++rr) { + for (int cc = curr_left; cc <= curr_right; ++cc) { + speed_up.at(rr, cc) = speed_up_at_this_point + 1; + } + } + } else { + for (int rr = curr_top; rr <= curr_bottom; ++rr) { + for (int cc = curr_left; cc <= curr_right; ++cc) { + mask.at(rr, cc) = MASK_TRUE_VALUE; + } + } + } + } else { + done.at(r, c) = 1; + for (int dr = 0; dr <= 1; ++dr) { + int nr = 2*r + dr; + for (int dc = 0; dc <= 1; ++dc) { + int nc = 2*c + dc; + if (nr < curr_rows && nc < curr_cols) { + mask.at(nr, nc) = MASK_TRUE_VALUE; + } + } + } + } + } + } + } +} + +static inline double extrapolateValueInRect(int height, int width, + double v11, double v12, + double v21, double v22, + int r, int c) { + if (r == 0 && c == 0) { return v11;} + if (r == 0 && c == width) { return v12;} + if (r == height && c == 0) { return v21;} + if (r == height && c == width) { return v22;} + + double qr = double(r) / height; + double pr = 1.0 - qr; + double qc = double(c) / width; + double pc = 1.0 - qc; + + return v11*pr*pc + v12*pr*qc + v21*qr*pc + v22*qc*qr; +} + +static void extrapolateFlow(Flow& flow, + const Mat& speed_up) { + const int rows = flow.u.rows; + const int cols = flow.u.cols; + Mat done = Mat::zeros(rows, cols, CV_8U); + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + if (!done.at(r, c) && speed_up.at(r, c) > 1) { + int step = (1 << speed_up.at(r, c)) - 1; + int top = r; + int bottom = std::min(r + step, rows - 1); + int left = c; + int right = std::min(c + step, cols - 1); + + int height = bottom - top; + int width = right - left; + for (int rr = top; rr <= bottom; ++rr) { + for (int cc = left; cc <= right; ++cc) { + done.at(rr, cc) = 1; + flow.u.at(rr, cc) = extrapolateValueInRect( + height, width, + flow.u.at(top, left), + flow.u.at(top, right), + flow.u.at(bottom, left), + flow.u.at(bottom, right), + rr-top, cc-left); + + flow.v.at(rr, cc) = extrapolateValueInRect( + height, width, + flow.v.at(top, left), + flow.v.at(top, right), + flow.v.at(bottom, left), + flow.v.at(bottom, right), + rr-top, cc-left); + } + } + } + } + } +} + +static void buildPyramidWithResizeMethod(Mat& src, + vector& pyramid, + int layers, + int interpolation_type) { + pyramid.push_back(src); + for (int i = 1; i <= layers; ++i) { + Mat prev = pyramid[i - 1]; + if (prev.rows <= 1 || prev.cols <= 1) { + break; + } + + Mat next; + resize(prev, next, Size((prev.cols + 1) / 2, (prev.rows + 1) / 2), 0, 0, interpolation_type); + pyramid.push_back(next); + } +} + +static Flow calcOpticalFlowSF(Mat& from, + Mat& to, + int layers, + int averaging_block_size, + int max_flow, + double sigma_dist, + double sigma_color, + int postprocess_window, + double sigma_dist_fix, + double sigma_color_fix, + double occ_thr, + int upscale_averaging_radius, + double upscale_sigma_dist, + double upscale_sigma_color, + double speed_up_thr) { + vector pyr_from_images; + vector pyr_to_images; + + buildPyramidWithResizeMethod(from, pyr_from_images, layers - 1, INTER_CUBIC); + buildPyramidWithResizeMethod(to, pyr_to_images, layers - 1, INTER_CUBIC); +// buildPyramid(from, pyr_from_images, layers - 1, BORDER_WRAP); +// buildPyramid(to, pyr_to_images, layers - 1, BORDER_WRAP); + + if ((int)pyr_from_images.size() != layers) { + exit(1); + } + + if ((int)pyr_to_images.size() != layers) { + exit(1); + } + + Mat first_from_image = pyr_from_images[layers - 1]; + Mat first_to_image = pyr_to_images[layers - 1]; + + Mat mask = Mat::ones(first_from_image.rows, first_from_image.cols, CV_8U); + Mat mask_inv = Mat::ones(first_from_image.rows, first_from_image.cols, CV_8U); + + Flow flow(first_from_image.rows, first_from_image.cols); + Flow flow_inv(first_to_image.rows, first_to_image.cols); + + Mat confidence; + Mat confidence_inv; + + calcOpticalFlowSingleScaleSF(first_from_image, + first_to_image, + mask, + flow, + confidence, + averaging_block_size, + max_flow, + sigma_dist, + sigma_color); + + calcOpticalFlowSingleScaleSF(first_to_image, + first_from_image, + mask_inv, + flow_inv, + confidence_inv, + averaging_block_size, + max_flow, + sigma_dist, + sigma_color); + + removeOcclusions(flow, + flow_inv, + occ_thr, + confidence); + + removeOcclusions(flow_inv, + flow, + occ_thr, + confidence_inv); + + Mat speed_up = Mat::zeros(first_from_image.rows, first_from_image.cols, CV_8U); + Mat speed_up_inv = Mat::zeros(first_from_image.rows, first_from_image.cols, CV_8U); + + for (int curr_layer = layers - 2; curr_layer >= 0; --curr_layer) { + const Mat curr_from = pyr_from_images[curr_layer]; + const Mat curr_to = pyr_to_images[curr_layer]; + const Mat prev_from = pyr_from_images[curr_layer + 1]; + const Mat prev_to = pyr_to_images[curr_layer + 1]; + + const int curr_rows = curr_from.rows; + const int curr_cols = curr_from.cols; + + Mat new_speed_up, new_speed_up_inv; + + selectPointsToRecalcFlow(flow, + averaging_block_size, + speed_up_thr, + curr_rows, + curr_cols, + speed_up, + new_speed_up, + mask); + + int points_to_recalculate = sum(mask)[0] / MASK_TRUE_VALUE; + + selectPointsToRecalcFlow(flow_inv, + averaging_block_size, + speed_up_thr, + curr_rows, + curr_cols, + speed_up_inv, + new_speed_up_inv, + mask_inv); + + points_to_recalculate = sum(mask_inv)[0] / MASK_TRUE_VALUE; + + speed_up = new_speed_up; + speed_up_inv = new_speed_up_inv; + + flow = upscaleOpticalFlow(curr_rows, + curr_cols, + prev_from, + confidence, + flow, + upscale_averaging_radius, + upscale_sigma_dist, + upscale_sigma_color); + + flow_inv = upscaleOpticalFlow(curr_rows, + curr_cols, + prev_to, + confidence_inv, + flow_inv, + upscale_averaging_radius, + upscale_sigma_dist, + upscale_sigma_color); + + calcOpticalFlowSingleScaleSF(curr_from, + curr_to, + mask, + flow, + confidence, + averaging_block_size, + max_flow, + sigma_dist, + sigma_color); + + calcOpticalFlowSingleScaleSF(curr_to, + curr_from, + mask_inv, + flow_inv, + confidence_inv, + averaging_block_size, + max_flow, + sigma_dist, + sigma_color); + + extrapolateFlow(flow, speed_up); + extrapolateFlow(flow_inv, speed_up_inv); + + removeOcclusions(flow, flow_inv, occ_thr, confidence); + removeOcclusions(flow_inv, flow, occ_thr, confidence_inv); + } + + WeightedCrossBilateralFilter filter_postprocess(pyr_from_images[0], + postprocess_window, + sigma_dist_fix, + sigma_color_fix); + + flow.u = filter_postprocess.apply(flow.u, confidence); + flow.v = filter_postprocess.apply(flow.v, confidence); + + Mat blured_u, blured_v; + GaussianBlur(flow.u, blured_u, Size(3, 3), 5); + GaussianBlur(flow.v, blured_v, Size(3, 3), 5); + + return Flow(blured_v, blured_u); +} + +void calcOpticalFlowSF(Mat& from, + Mat& to, + Mat& flowX, + Mat& flowY, + int layers, + int averaging_block_size, + int max_flow, + double sigma_dist, + double sigma_color, + int postprocess_window, + double sigma_dist_fix, + double sigma_color_fix, + double occ_thr, + int upscale_averaging_radius, + double upscale_sigma_dist, + double upscale_sigma_color, + double speed_up_thr) { + + Flow flow = calcOpticalFlowSF(from, to, + layers, + averaging_block_size, + max_flow, + sigma_dist, + sigma_color, + postprocess_window, + sigma_dist_fix, + sigma_color_fix, + occ_thr, + upscale_averaging_radius, + upscale_sigma_dist, + upscale_sigma_color, + speed_up_thr); + flowX = flow.u; + flowY = flow.v; +} + +} + diff --git a/modules/video/src/simpleflow.hpp b/modules/video/src/simpleflow.hpp new file mode 100644 index 0000000000..55052fd05b --- /dev/null +++ b/modules/video/src/simpleflow.hpp @@ -0,0 +1,125 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_SIMPLEFLOW_H__ +#define __OPENCV_SIMPLEFLOW_H__ + +#include + +using namespace std; + +#define MASK_TRUE_VALUE 255 +#define UNKNOWN_FLOW_THRESH 1e9 + +namespace cv { + +struct Flow { + Mat u, v; + + Flow() {;} + + Flow(Mat& _u, Mat& _v) + : u(_u), v(_v) {;} + + Flow(int rows, int cols) { + u = Mat::zeros(rows, cols, CV_64F); + v = Mat::zeros(rows, cols, CV_64F); + } +}; + +inline static double dist(const Vec3b& p1, const Vec3b& p2) { + return (p1[0] - p2[0]) * (p1[0] - p2[0]) + + (p1[1] - p2[1]) * (p1[1] - p2[1]) + + (p1[2] - p2[2]) * (p1[2] - p2[2]); +} + +inline static double dist(const Point2f& p1, const Point2f& p2) { + return (p1.x - p2.x) * (p1.x - p2.x) + + (p1.y - p2.y) * (p1.y - p2.y); +} + +inline static double dist(double x1, double y1, double x2, double y2) { + return (x1 - x2) * (x1 - x2) + + (y1 - y2) * (y1 - y2); +} + +inline static int dist(int x1, int y1, int x2, int y2) { + return (x1 - x2) * (x1 - x2) + + (y1 - y2) * (y1 - y2); +} + +template +inline static T min(T t1, T t2, T t3) { + return (t1 <= t2 && t1 <= t3) ? t1 : min(t2, t3); +} + +template +vector > build(int n, int m) { + vector > res(n); + for (int i = 0; i < n; ++i) { + res[i].resize(m, 0); + } + return res; +} + +class WeightedCrossBilateralFilter { +public: + WeightedCrossBilateralFilter(const Mat& _image, + int _windowSize, + double _sigmaDist, + double _sigmaColor); + + Mat apply(Mat& matrix, Mat& weights); + +private: + double convolution(Mat& matrix, int row, int col, Mat& weights); + + Mat image; + int windowSize; + double sigmaDist, sigmaColor; + + vector expDist; + vector > > > wc; +}; +} + +#endif diff --git a/modules/video/test/test_simpleflow.cpp b/modules/video/test/test_simpleflow.cpp new file mode 100644 index 0000000000..186ba8f56d --- /dev/null +++ b/modules/video/test/test_simpleflow.cpp @@ -0,0 +1,193 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" + +#include + +using namespace std; + +/* ///////////////////// simpleflow_test ///////////////////////// */ + +class CV_SimpleFlowTest : public cvtest::BaseTest +{ +public: + CV_SimpleFlowTest(); +protected: + void run(int); +}; + + +CV_SimpleFlowTest::CV_SimpleFlowTest() {} + +static void readOpticalFlowFromFile(FILE* file, cv::Mat& flowX, cv::Mat& flowY) { + char header[5]; + if (fread(header, 1, 4, file) < 4 && (string)header != "PIEH") { + return; + } + + int cols, rows; + if (fread(&cols, sizeof(int), 1, file) != 1|| + fread(&rows, sizeof(int), 1, file) != 1) { + return; + } + + flowX = cv::Mat::zeros(rows, cols, CV_64F); + flowY = cv::Mat::zeros(rows, cols, CV_64F); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + float uPoint, vPoint; + if (fread(&uPoint, sizeof(float), 1, file) != 1 || + fread(&vPoint, sizeof(float), 1, file) != 1) { + flowX.release(); + flowY.release(); + return; + } + + flowX.at(i, j) = uPoint; + flowY.at(i, j) = vPoint; + } + } +} + +static bool isFlowCorrect(double u) { + return !isnan(u) && (fabs(u) < 1e9); +} + +static double calc_rmse(cv::Mat flow1X, cv::Mat flow1Y, cv::Mat flow2X, cv::Mat flow2Y) { + long double sum; + int counter = 0; + const int rows = flow1X.rows; + const int cols = flow1X.cols; + + for (int y = 0; y < rows; ++y) { + for (int x = 0; x < cols; ++x) { + double u1 = flow1X.at(y, x); + double v1 = flow1Y.at(y, x); + double u2 = flow2X.at(y, x); + double v2 = flow2Y.at(y, x); + if (isFlowCorrect(u1) && isFlowCorrect(u2) && isFlowCorrect(v1) && isFlowCorrect(v2)) { + sum += (u1-u2)*(u1-u2) + (v1-v2)*(v1-v2); + counter++; + } + } + } + return sqrt((double)sum / (1e-9 + counter)); +} + +void CV_SimpleFlowTest::run(int) { + int code = cvtest::TS::OK; + + const double MAX_RMSE = 0.6; + const string frame1_path = ts->get_data_path() + "optflow/RubberWhale1.png"; + const string frame2_path = ts->get_data_path() + "optflow/RubberWhale2.png"; + const string gt_flow_path = ts->get_data_path() + "optflow/RubberWhale.flo"; + + cv::Mat frame1 = cv::imread(frame1_path); + cv::Mat frame2 = cv::imread(frame2_path); + + if (frame1.empty()) { + ts->printf(cvtest::TS::LOG, "could not read image %s\n", frame2_path.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); + return; + } + + if (frame2.empty()) { + ts->printf(cvtest::TS::LOG, "could not read image %s\n", frame2_path.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); + return; + } + + if (frame1.rows != frame2.rows && frame1.cols != frame2.cols) { + ts->printf(cvtest::TS::LOG, "images should be of equal sizes (%s and %s)", + frame1_path.c_str(), frame2_path.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); + return; + } + + if (frame1.type() != 16 || frame2.type() != 16) { + ts->printf(cvtest::TS::LOG, "images should be of equal type CV_8UC3 (%s and %s)", + frame1_path.c_str(), frame2_path.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); + return; + } + + cv::Mat flowX_gt, flowY_gt; + + FILE* gt_flow_file = fopen(gt_flow_path.c_str(), "rb"); + if (gt_flow_file == NULL) { + ts->printf(cvtest::TS::LOG, "could not read ground-thuth flow from file %s", + gt_flow_path.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); + return; + } + readOpticalFlowFromFile(gt_flow_file, flowX_gt, flowY_gt); + if (flowX_gt.empty() || flowY_gt.empty()) { + ts->printf(cvtest::TS::LOG, "error while reading flow data from file %s", + gt_flow_path.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); + return; + } + fclose(gt_flow_file); + + cv::Mat flowX, flowY; + cv::calcOpticalFlowSF(frame1, frame2, + flowX, flowY, + 3, 4, 2, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10); + + double rmse = calc_rmse(flowX_gt, flowY_gt, flowX, flowY); + + ts->printf(cvtest::TS::LOG, "Optical flow estimation RMSE for SimpleFlow algorithm : %lf\n", + rmse); + + if (rmse > MAX_RMSE) { + ts->printf( cvtest::TS::LOG, + "Too big rmse error : %lf ( >= %lf )\n", rmse, MAX_RMSE); + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + return; + } +} + + +TEST(Video_OpticalFlowSimpleFlow, accuracy) { CV_SimpleFlowTest test; test.safe_run(); } + +/* End of file. */ diff --git a/samples/cpp/simpleflow_demo.cpp b/samples/cpp/simpleflow_demo.cpp new file mode 100644 index 0000000000..6a195fe894 --- /dev/null +++ b/samples/cpp/simpleflow_demo.cpp @@ -0,0 +1,96 @@ +#include "opencv2/video/tracking.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/highgui/highgui.hpp" + +#include +#include + +using namespace cv; +using namespace std; + +static void help() +{ + // print a welcome message, and the OpenCV version + printf("This is a demo of SimpleFlow optical flow algorithm,\n" + "Using OpenCV version %s\n\n", CV_VERSION); + + printf("Usage: simpleflow_demo frame1 frame2 output_flow" + "\nApplication will write estimated flow " + "\nbetween 'frame1' and 'frame2' in binary format" + "\ninto file 'output_flow'" + "\nThen one can use code from http://vision.middlebury.edu/flow/data/" + "\nto convert flow in binary file to image\n"); +} + +// binary file format for flow data specified here: +// http://vision.middlebury.edu/flow/data/ +static void writeOpticalFlowToFile(const Mat& u, const Mat& v, FILE* file) { + int cols = u.cols; + int rows = u.rows; + + fprintf(file, "PIEH"); + + if (fwrite(&cols, sizeof(int), 1, file) != 1 || + fwrite(&rows, sizeof(int), 1, file) != 1) { + fprintf(stderr, "writeOpticalFlowToFile : problem writing header\n"); + exit(1); + } + + for (int i= 0; i < u.rows; ++i) { + for (int j = 0; j < u.cols; ++j) { + float uPoint = u.at(i, j); + float vPoint = v.at(i, j); + + if (fwrite(&uPoint, sizeof(float), 1, file) != 1 || + fwrite(&vPoint, sizeof(float), 1, file) != 1) { + fprintf(stderr, "writeOpticalFlowToFile : problem writing data\n"); + exit(1); + } + } + } +} +int main(int argc, char** argv) { + help(); + + if (argc < 4) { + fprintf(stderr, "Wrong number of command line arguments : %d (expected %d)\n", argc, 4); + exit(1); + } + + Mat frame1 = imread(argv[1]); + Mat frame2 = imread(argv[2]); + + if (frame1.empty() || frame2.empty()) { + fprintf(stderr, "simpleflow_demo : Images cannot be read\n"); + exit(1); + } + + if (frame1.rows != frame2.rows && frame1.cols != frame2.cols) { + fprintf(stderr, "simpleflow_demo : Images should be of equal sizes\n"); + exit(1); + } + + if (frame1.type() != 16 || frame2.type() != 16) { + fprintf(stderr, "simpleflow_demo : Images should be of equal type CV_8UC3\n"); + exit(1); + } + + printf("simpleflow_demo : Read two images of size [rows = %d, cols = %d]\n", + frame1.rows, frame1.cols); + + Mat flowX, flowY; + + calcOpticalFlowSF(frame1, frame2, + flowX, flowY, + 3, 2, 4, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10); + + FILE* file = fopen(argv[3], "wb"); + if (file == NULL) { + fprintf(stderr, "simpleflow_demo : Unable to open file '%s' for writing\n", argv[3]); + exit(1); + } + printf("simpleflow_demo : Writing to file\n"); + writeOpticalFlowToFile(flowX, flowY, file); + fclose(file); + return 0; +} From 5ee632fe90a5425532588bdd8b60e25c847b317b Mon Sep 17 00:00:00 2001 From: Yury Zemlyanskiy Date: Mon, 27 Aug 2012 14:40:57 +0400 Subject: [PATCH 02/97] Updates for SimpleFlow algorithm + New format for flow data - CV_32C2 + Memory optimization + Cross Bilateral Filter optimization + Minor optimizations + Sample for calcOpticalFlowSF improved --- .../video/include/opencv2/video/tracking.hpp | 5 +- modules/video/src/simpleflow.cpp | 448 +++++++----------- modules/video/src/simpleflow.hpp | 53 +-- modules/video/test/test_simpleflow.cpp | 63 +-- samples/cpp/simpleflow_demo.cpp | 243 +++++++--- 5 files changed, 390 insertions(+), 422 deletions(-) diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index 85c18817a9..6800c63373 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -329,9 +329,8 @@ CV_EXPORTS_W Mat estimateRigidTransform( InputArray src, InputArray dst, //! computes dense optical flow using Simple Flow algorithm CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, - Mat& to, - Mat& flowX, - Mat& flowY, + Mat& to, + Mat& flow, int layers, int averaging_block_size, int max_flow, diff --git a/modules/video/src/simpleflow.cpp b/modules/video/src/simpleflow.cpp index 1fda3618c9..0cd320d6ee 100644 --- a/modules/video/src/simpleflow.cpp +++ b/modules/video/src/simpleflow.cpp @@ -54,193 +54,154 @@ namespace cv { -WeightedCrossBilateralFilter::WeightedCrossBilateralFilter( - const Mat& _image, - int _windowSize, - double _sigmaDist, - double _sigmaColor) - : image(_image), - windowSize(_windowSize), - sigmaDist(_sigmaDist), - sigmaColor(_sigmaColor) { - - expDist.resize(2*windowSize*windowSize+1); - const double sigmaDistSqr = 2 * sigmaDist * sigmaDist; - for (int i = 0; i <= 2*windowSize*windowSize; ++i) { - expDist[i] = exp(-i/sigmaDistSqr); - } - - const double sigmaColorSqr = 2 * sigmaColor * sigmaColor; - wc.resize(image.rows); - for (int row = 0; row < image.rows; ++row) { - wc[row].resize(image.cols); - for (int col = 0; col < image.cols; ++col) { - int beginRow = max(0, row - windowSize); - int beginCol = max(0, col - windowSize); - int endRow = min(image.rows - 1, row + windowSize); - int endCol = min(image.cols - 1, col + windowSize); - wc[row][col] = build(endRow - beginRow + 1, endCol - beginCol + 1); - - for (int r = beginRow; r <= endRow; ++r) { - for (int c = beginCol; c <= endCol; ++c) { - wc[row][col][r - beginRow][c - beginCol] = - exp(-dist(image.at(row, col), - image.at(r, c)) - / sigmaColorSqr); - } - } - } - } -} - -Mat WeightedCrossBilateralFilter::apply(Mat& matrix, Mat& weights) { - int rows = matrix.rows; - int cols = matrix.cols; - - Mat result = Mat::zeros(rows, cols, CV_64F); - for (int row = 0; row < rows; ++row) { - for(int col = 0; col < cols; ++col) { - result.at(row, col) = - convolution(matrix, row, col, weights); - } - } - return result; -} - -double WeightedCrossBilateralFilter::convolution(Mat& matrix, - int row, int col, - Mat& weights) { - double result = 0, weightsSum = 0; - int beginRow = max(0, row - windowSize); - int beginCol = max(0, col - windowSize); - int endRow = min(matrix.rows - 1, row + windowSize); - int endCol = min(matrix.cols - 1, col + windowSize); - for (int r = beginRow; r <= endRow; ++r) { - double* ptr = matrix.ptr(r); - for (int c = beginCol; c <= endCol; ++c) { - const double w = expDist[dist(row, col, r, c)] * - wc[row][col][r - beginRow][c - beginCol] * - weights.at(r, c); - result += ptr[c] * w; - weightsSum += w; - } - } - return result / weightsSum; -} - -static void removeOcclusions(const Flow& flow, - const Flow& flow_inv, - double occ_thr, +static void removeOcclusions(const Mat& flow, + const Mat& flow_inv, + float occ_thr, Mat& confidence) { - const int rows = flow.u.rows; - const int cols = flow.v.cols; - int occlusions = 0; + const int rows = flow.rows; + const int cols = flow.cols; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { - if (dist(flow.u.at(r, c), flow.v.at(r, c), - -flow_inv.u.at(r, c), -flow_inv.v.at(r, c)) > occ_thr) { - confidence.at(r, c) = 0; - occlusions++; + if (dist(flow.at(r, c), -flow_inv.at(r, c)) > occ_thr) { + confidence.at(r, c) = 0; + } else { + confidence.at(r, c) = 1; } } } } -static Mat wd(int top_shift, int bottom_shift, int left_shift, int right_shift, double sigma) { - const double factor = 1.0 / (2.0 * sigma * sigma); - Mat d = Mat(top_shift + bottom_shift + 1, right_shift + left_shift + 1, CV_64F); +static void wd(Mat& d, int top_shift, int bottom_shift, int left_shift, int right_shift, float sigma) { + const float factor = 1.0 / (2.0 * sigma * sigma); for (int dr = -top_shift, r = 0; dr <= bottom_shift; ++dr, ++r) { for (int dc = -left_shift, c = 0; dc <= right_shift; ++dc, ++c) { - d.at(r, c) = -(dr*dr + dc*dc) * factor; + d.at(r, c) = -(dr*dr + dc*dc) * factor; } } - Mat ed; - exp(d, ed); - return ed; + exp(d, d); } -static Mat wc(const Mat& image, int r0, int c0, int top_shift, int bottom_shift, int left_shift, int right_shift, double sigma) { - const double factor = 1.0 / (2.0 * sigma * sigma); - Mat d = Mat(top_shift + bottom_shift + 1, right_shift + left_shift + 1, CV_64F); +static void wc(const Mat& image, Mat& d, int r0, int c0, + int top_shift, int bottom_shift, int left_shift, int right_shift, float sigma) { + const float factor = 1.0 / (2.0 * sigma * sigma); + const Vec3b centeral_point = image.at(r0, c0); for (int dr = r0-top_shift, r = 0; dr <= r0+bottom_shift; ++dr, ++r) { + const Vec3b *row = image.ptr(dr); + float *d_row = d.ptr(r); for (int dc = c0-left_shift, c = 0; dc <= c0+right_shift; ++dc, ++c) { - d.at(r, c) = -dist(image.at(r0, c0), image.at(dr, dc)) * factor; + d_row[c] = -dist(centeral_point, row[dc]) * factor; } } - Mat ed; - exp(d, ed); - return ed; + exp(d, d); } -inline static void dist(const Mat& m1, const Mat& m2, Mat& result) { +static void dist(const Mat& m1, const Mat& m2, Mat& result) { const int rows = m1.rows; const int cols = m1.cols; for (int r = 0; r < rows; ++r) { const Vec3b *m1_row = m1.ptr(r); const Vec3b *m2_row = m2.ptr(r); - double* row = result.ptr(r); + float* row = result.ptr(r); for (int c = 0; c < cols; ++c) { row[c] = dist(m1_row[c], m2_row[c]); } } } +static void crossBilateralFilter(const Mat& image, const Mat& edge_image, const Mat confidence, Mat& dst, int d, float sigma_color, float sigma_space, bool flag=false) { + const int rows = image.rows; + const int cols = image.cols; + Mat image_extended, edge_image_extended, confidence_extended; + copyMakeBorder(image, image_extended, d, d, d, d, BORDER_DEFAULT); + copyMakeBorder(edge_image, edge_image_extended, d, d, d, d, BORDER_DEFAULT); + copyMakeBorder(confidence, confidence_extended, d, d, d, d, BORDER_CONSTANT, Scalar(0)); + Mat weights_space(2*d+1, 2*d+1, CV_32F); + wd(weights_space, d, d, d, d, sigma_space); + Mat weights(2*d+1, 2*d+1, CV_32F); + Mat weighted_sum(2*d+1, 2*d+1, CV_32F); + + + vector image_extended_channels; + split(image_extended, image_extended_channels); + + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols; ++col) { + wc(edge_image_extended, weights, row+d, col+d, d, d, d, d, sigma_color); + + Range window_rows(row,row+2*d+1); + Range window_cols(col,col+2*d+1); + + multiply(weights, confidence_extended(window_rows, window_cols), weights); + multiply(weights, weights_space, weights); + float weights_sum = sum(weights)[0]; + + for (int ch = 0; ch < 2; ++ch) { + multiply(weights, image_extended_channels[ch](window_rows, window_cols), weighted_sum); + float total_sum = sum(weighted_sum)[0]; + + dst.at(row, col)[ch] = (flag && fabs(weights_sum) < 1e-9) + ? image.at(row, col) + : total_sum / weights_sum; + } + } + } +} + static void calcOpticalFlowSingleScaleSF(const Mat& prev, const Mat& next, const Mat& mask, - Flow& flow, + Mat& flow, Mat& confidence, int averaging_radius, int max_flow, - double sigma_dist, - double sigma_color) { + float sigma_dist, + float sigma_color) { const int rows = prev.rows; const int cols = prev.cols; - confidence = Mat::zeros(rows, cols, CV_64F); + confidence = Mat::zeros(rows, cols, CV_32F); + + Mat diff_storage(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_32F); + Mat w_full_window(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_32F); + Mat wd_full_window(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_32F); + float w_full_window_sum; + + Mat prev_extended; + copyMakeBorder(prev, prev_extended, + averaging_radius, averaging_radius, averaging_radius, averaging_radius, + BORDER_DEFAULT); + + wd(wd_full_window, averaging_radius, averaging_radius, averaging_radius, averaging_radius, sigma_dist); for (int r0 = 0; r0 < rows; ++r0) { for (int c0 = 0; c0 < cols; ++c0) { - int u0 = floor(flow.u.at(r0, c0) + 0.5); - int v0 = floor(flow.v.at(r0, c0) + 0.5); + Vec2f flow_at_point = flow.at(r0, c0); + int u0 = floor(flow_at_point[0] + 0.5); + if (r0 + u0 < 0) { u0 = -r0; } + if (r0 + u0 >= rows) { u0 = rows - 1 - r0; } + int v0 = floor(flow_at_point[1] + 0.5); + if (c0 + v0 < 0) { v0 = -c0; } + if (c0 + v0 >= cols) { v0 = cols - 1 - c0; } const int min_row_shift = -min(r0 + u0, max_flow); const int max_row_shift = min(rows - 1 - (r0 + u0), max_flow); const int min_col_shift = -min(c0 + v0, max_flow); const int max_col_shift = min(cols - 1 - (c0 + v0), max_flow); - double min_cost = DBL_MAX, best_u = u0, best_v = v0; - - Mat w_full_window; - double w_full_window_sum; - Mat diff_storage; - - if (r0 - averaging_radius >= 0 && - r0 + averaging_radius < rows && - c0 - averaging_radius >= 0 && - c0 + averaging_radius < cols && - mask.at(r0, c0)) { - w_full_window = wd(averaging_radius, - averaging_radius, - averaging_radius, - averaging_radius, - sigma_dist).mul( - wc(prev, r0, c0, - averaging_radius, - averaging_radius, - averaging_radius, - averaging_radius, - sigma_color)); + float min_cost = DBL_MAX, best_u = u0, best_v = v0; + if (mask.at(r0, c0)) { + wc(prev_extended, w_full_window, r0 + averaging_radius, c0 + averaging_radius, + averaging_radius, averaging_radius, averaging_radius, averaging_radius, sigma_color); + multiply(w_full_window, wd_full_window, w_full_window); w_full_window_sum = sum(w_full_window)[0]; - diff_storage = Mat::zeros(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_64F); } bool first_flow_iteration = true; - double sum_e, min_e; + float sum_e, min_e; for (int u = min_row_shift; u <= max_row_shift; ++u) { for (int v = min_col_shift; v <= max_col_shift; ++v) { - double e = dist(prev.at(r0, c0), next.at(r0 + u0 + u, c0 + v0 + v)); + float e = dist(prev.at(r0, c0), next.at(r0 + u0 + u, c0 + v0 + v)); if (first_flow_iteration) { sum_e = e; min_e = e; @@ -269,10 +230,11 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev, r0 + u0 + u + window_bottom_shift + 1); const Range next_col_range(c0 + v0 + v - window_left_shift, c0 + v0 + v + window_right_shift + 1); - + + Mat diff2; Mat w; - double w_sum; + float w_sum; if (window_top_shift == averaging_radius && window_bottom_shift == averaging_radius && window_left_shift == averaging_radius && @@ -280,21 +242,23 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev, w = w_full_window; w_sum = w_full_window_sum; diff2 = diff_storage; - dist(prev(prev_row_range, prev_col_range), next(next_row_range, next_col_range), diff2); } else { - diff2 = Mat::zeros(window_bottom_shift + window_top_shift + 1, - window_right_shift + window_left_shift + 1, CV_64F); + diff2 = diff_storage(Range(averaging_radius - window_top_shift, + averaging_radius + 1 + window_bottom_shift), + Range(averaging_radius - window_left_shift, + averaging_radius + 1 + window_right_shift)); dist(prev(prev_row_range, prev_col_range), next(next_row_range, next_col_range), diff2); - - w = wd(window_top_shift, window_bottom_shift, window_left_shift, window_right_shift, sigma_dist).mul( - wc(prev, r0, c0, window_top_shift, window_bottom_shift, window_left_shift, window_right_shift, sigma_color)); + w = w_full_window(Range(averaging_radius - window_top_shift, + averaging_radius + 1 + window_bottom_shift), + Range(averaging_radius - window_left_shift, + averaging_radius + 1 + window_right_shift)); w_sum = sum(w)[0]; } multiply(diff2, w, diff2); - const double cost = sum(diff2)[0] / w_sum; + const float cost = sum(diff2)[0] / w_sum; if (cost < min_cost) { min_cost = cost; best_u = u + u0; @@ -302,72 +266,37 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev, } } } - int square = (max_row_shift - min_row_shift + 1) * - (max_col_shift - min_col_shift + 1); - confidence.at(r0, c0) = (square == 0) ? 0 - : sum_e / square - min_e; + int windows_square = (max_row_shift - min_row_shift + 1) * + (max_col_shift - min_col_shift + 1); + confidence.at(r0, c0) = (windows_square == 0) ? 0 + : sum_e / windows_square - min_e; + CV_Assert(confidence.at(r0, c0) >= 0); // TODO: remove it after testing if (mask.at(r0, c0)) { - flow.u.at(r0, c0) = best_u; - flow.v.at(r0, c0) = best_v; + flow.at(r0, c0) = Vec2f(best_u, best_v); } } } } -static Flow upscaleOpticalFlow(int new_rows, +static Mat upscaleOpticalFlow(int new_rows, int new_cols, const Mat& image, const Mat& confidence, - const Flow& flow, + Mat& flow, int averaging_radius, - double sigma_dist, - double sigma_color) { - const int rows = image.rows; - const int cols = image.cols; - Flow new_flow(new_rows, new_cols); - for (int r = 0; r < rows; ++r) { - for (int c = 0; c < cols; ++c) { - const int window_top_shift = min(r, averaging_radius); - const int window_bottom_shift = min(rows - 1 - r, averaging_radius); - const int window_left_shift = min(c, averaging_radius); - const int window_right_shift = min(cols - 1 - c, averaging_radius); - - const Range row_range(r - window_top_shift, r + window_bottom_shift + 1); - const Range col_range(c - window_left_shift, c + window_right_shift + 1); - - const Mat w = confidence(row_range, col_range).mul( - wd(window_top_shift, window_bottom_shift, window_left_shift, window_right_shift, sigma_dist)).mul( - wc(image, r, c, window_top_shift, window_bottom_shift, window_left_shift, window_right_shift, sigma_color)); - - const double w_sum = sum(w)[0]; - double new_u, new_v; - if (fabs(w_sum) < 1e-9) { - new_u = flow.u.at(r, c); - new_v = flow.v.at(r, c); - } else { - new_u = sum(flow.u(row_range, col_range).mul(w))[0] / w_sum; - new_v = sum(flow.v(row_range, col_range).mul(w))[0] / w_sum; - } - - for (int dr = 0; dr <= 1; ++dr) { - int nr = 2*r + dr; - for (int dc = 0; dc <= 1; ++dc) { - int nc = 2*c + dc; - if (nr < new_rows && nc < new_cols) { - new_flow.u.at(nr, nc) = 2 * new_u; - new_flow.v.at(nr, nc) = 2 * new_v; - } - } - } - } - } + float sigma_dist, + float sigma_color) { + crossBilateralFilter(flow, image, confidence, flow, averaging_radius, sigma_color, sigma_dist, false); + Mat new_flow; + resize(flow, new_flow, Size(new_cols, new_rows), 0, 0, INTER_NEAREST); + new_flow *= 2; return new_flow; } -static Mat calcIrregularityMat(const Flow& flow, int radius) { - const int rows = flow.u.rows; - const int cols = flow.v.cols; - Mat irregularity = Mat::zeros(rows, cols, CV_64F); +static Mat calcIrregularityMat(const Mat& flow, int radius) { + const int rows = flow.rows; + const int cols = flow.cols; + Mat irregularity(rows, cols, CV_32F); for (int r = 0; r < rows; ++r) { const int start_row = max(0, r - radius); const int end_row = min(rows - 1, r + radius); @@ -376,10 +305,9 @@ static Mat calcIrregularityMat(const Flow& flow, int radius) { const int end_col = min(cols - 1, c + radius); for (int dr = start_row; dr <= end_row; ++dr) { for (int dc = start_col; dc <= end_col; ++dc) { - const double diff = dist(flow.u.at(r, c), flow.v.at(r, c), - flow.u.at(dr, dc), flow.v.at(dr, dc)); - if (diff > irregularity.at(r, c)) { - irregularity.at(r, c) = diff; + const float diff = dist(flow.at(r, c), flow.at(dr, dc)); + if (diff > irregularity.at(r, c)) { + irregularity.at(r, c) = diff; } } } @@ -388,7 +316,7 @@ static Mat calcIrregularityMat(const Flow& flow, int radius) { return irregularity; } -static void selectPointsToRecalcFlow(const Flow& flow, +static void selectPointsToRecalcFlow(const Mat& flow, int irregularity_metric_radius, int speed_up_thr, int curr_rows, @@ -396,11 +324,10 @@ static void selectPointsToRecalcFlow(const Flow& flow, const Mat& prev_speed_up, Mat& speed_up, Mat& mask) { - const int prev_rows = flow.u.rows; - const int prev_cols = flow.v.cols; + const int prev_rows = flow.rows; + const int prev_cols = flow.cols; - Mat is_flow_regular = calcIrregularityMat(flow, - irregularity_metric_radius) + Mat is_flow_regular = calcIrregularityMat(flow, irregularity_metric_radius) < speed_up_thr; Mat done = Mat::zeros(prev_rows, prev_cols, CV_8U); speed_up = Mat::zeros(curr_rows, curr_cols, CV_8U); @@ -470,28 +397,28 @@ static void selectPointsToRecalcFlow(const Flow& flow, } } -static inline double extrapolateValueInRect(int height, int width, - double v11, double v12, - double v21, double v22, +static inline float extrapolateValueInRect(int height, int width, + float v11, float v12, + float v21, float v22, int r, int c) { if (r == 0 && c == 0) { return v11;} if (r == 0 && c == width) { return v12;} if (r == height && c == 0) { return v21;} if (r == height && c == width) { return v22;} - double qr = double(r) / height; - double pr = 1.0 - qr; - double qc = double(c) / width; - double pc = 1.0 - qc; + float qr = float(r) / height; + float pr = 1.0 - qr; + float qc = float(c) / width; + float pc = 1.0 - qc; return v11*pr*pc + v12*pr*qc + v21*qr*pc + v22*qc*qr; } -static void extrapolateFlow(Flow& flow, +static void extrapolateFlow(Mat& flow, const Mat& speed_up) { - const int rows = flow.u.rows; - const int cols = flow.u.cols; - Mat done = Mat::zeros(rows, cols, CV_8U); + const int rows = flow.rows; + const int cols = flow.cols; + Mat done(rows, cols, CV_8U); for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { if (!done.at(r, c) && speed_up.at(r, c) > 1) { @@ -506,21 +433,22 @@ static void extrapolateFlow(Flow& flow, for (int rr = top; rr <= bottom; ++rr) { for (int cc = left; cc <= right; ++cc) { done.at(rr, cc) = 1; - flow.u.at(rr, cc) = extrapolateValueInRect( - height, width, - flow.u.at(top, left), - flow.u.at(top, right), - flow.u.at(bottom, left), - flow.u.at(bottom, right), - rr-top, cc-left); + Vec2f flow_at_point; + Vec2f top_left = flow.at(top, left); + Vec2f top_right = flow.at(top, right); + Vec2f bottom_left = flow.at(bottom, left); + Vec2f bottom_right = flow.at(bottom, right); - flow.v.at(rr, cc) = extrapolateValueInRect( - height, width, - flow.v.at(top, left), - flow.v.at(top, right), - flow.v.at(bottom, left), - flow.v.at(bottom, right), - rr-top, cc-left); + flow_at_point[0] = extrapolateValueInRect(height, width, + top_left[0], top_right[0], + bottom_left[0], bottom_right[0], + rr-top, cc-left); + + flow_at_point[1] = extrapolateValueInRect(height, width, + top_left[1], top_right[1], + bottom_left[1], bottom_right[1], + rr-top, cc-left); + flow.at(rr, cc) = flow_at_point; } } } @@ -545,8 +473,9 @@ static void buildPyramidWithResizeMethod(Mat& src, } } -static Flow calcOpticalFlowSF(Mat& from, +void calcOpticalFlowSF(Mat& from, Mat& to, + Mat& resulted_flow, int layers, int averaging_block_size, int max_flow, @@ -565,8 +494,6 @@ static Flow calcOpticalFlowSF(Mat& from, buildPyramidWithResizeMethod(from, pyr_from_images, layers - 1, INTER_CUBIC); buildPyramidWithResizeMethod(to, pyr_to_images, layers - 1, INTER_CUBIC); -// buildPyramid(from, pyr_from_images, layers - 1, BORDER_WRAP); -// buildPyramid(to, pyr_to_images, layers - 1, BORDER_WRAP); if ((int)pyr_from_images.size() != layers) { exit(1); @@ -582,8 +509,8 @@ static Flow calcOpticalFlowSF(Mat& from, Mat mask = Mat::ones(first_from_image.rows, first_from_image.cols, CV_8U); Mat mask_inv = Mat::ones(first_from_image.rows, first_from_image.cols, CV_8U); - Flow flow(first_from_image.rows, first_from_image.cols); - Flow flow_inv(first_to_image.rows, first_to_image.cols); + Mat flow(first_from_image.rows, first_from_image.cols, CV_32FC2); + Mat flow_inv(first_to_image.rows, first_to_image.cols, CV_32FC2); Mat confidence; Mat confidence_inv; @@ -641,8 +568,6 @@ static Flow calcOpticalFlowSF(Mat& from, new_speed_up, mask); - int points_to_recalculate = sum(mask)[0] / MASK_TRUE_VALUE; - selectPointsToRecalcFlow(flow_inv, averaging_block_size, speed_up_thr, @@ -652,8 +577,6 @@ static Flow calcOpticalFlowSF(Mat& from, new_speed_up_inv, mask_inv); - points_to_recalculate = sum(mask_inv)[0] / MASK_TRUE_VALUE; - speed_up = new_speed_up; speed_up_inv = new_speed_up_inv; @@ -702,55 +625,14 @@ static Flow calcOpticalFlowSF(Mat& from, removeOcclusions(flow_inv, flow, occ_thr, confidence_inv); } - WeightedCrossBilateralFilter filter_postprocess(pyr_from_images[0], - postprocess_window, - sigma_dist_fix, - sigma_color_fix); + crossBilateralFilter(flow, pyr_from_images[0], confidence, flow, + postprocess_window, sigma_color_fix, sigma_dist_fix); - flow.u = filter_postprocess.apply(flow.u, confidence); - flow.v = filter_postprocess.apply(flow.v, confidence); + GaussianBlur(flow, flow, Size(3, 3), 5); - Mat blured_u, blured_v; - GaussianBlur(flow.u, blured_u, Size(3, 3), 5); - GaussianBlur(flow.v, blured_v, Size(3, 3), 5); - - return Flow(blured_v, blured_u); -} - -void calcOpticalFlowSF(Mat& from, - Mat& to, - Mat& flowX, - Mat& flowY, - int layers, - int averaging_block_size, - int max_flow, - double sigma_dist, - double sigma_color, - int postprocess_window, - double sigma_dist_fix, - double sigma_color_fix, - double occ_thr, - int upscale_averaging_radius, - double upscale_sigma_dist, - double upscale_sigma_color, - double speed_up_thr) { - - Flow flow = calcOpticalFlowSF(from, to, - layers, - averaging_block_size, - max_flow, - sigma_dist, - sigma_color, - postprocess_window, - sigma_dist_fix, - sigma_color_fix, - occ_thr, - upscale_averaging_radius, - upscale_sigma_dist, - upscale_sigma_color, - speed_up_thr); - flowX = flow.u; - flowY = flow.v; + resulted_flow = Mat(flow.size(), CV_32FC2); + int from_to[] = {0,1 , 1,0}; + mixChannels(&flow, 1, &resulted_flow, 1, from_to, 2); } } diff --git a/modules/video/src/simpleflow.hpp b/modules/video/src/simpleflow.hpp index 55052fd05b..c4aa02355a 100644 --- a/modules/video/src/simpleflow.hpp +++ b/modules/video/src/simpleflow.hpp @@ -52,32 +52,23 @@ using namespace std; namespace cv { -struct Flow { - Mat u, v; - - Flow() {;} - - Flow(Mat& _u, Mat& _v) - : u(_u), v(_v) {;} - - Flow(int rows, int cols) { - u = Mat::zeros(rows, cols, CV_64F); - v = Mat::zeros(rows, cols, CV_64F); - } -}; - -inline static double dist(const Vec3b& p1, const Vec3b& p2) { +inline static float dist(const Vec3b& p1, const Vec3b& p2) { return (p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]) + (p1[2] - p2[2]) * (p1[2] - p2[2]); } -inline static double dist(const Point2f& p1, const Point2f& p2) { +inline static float dist(const Vec2f& p1, const Vec2f& p2) { + return (p1[0] - p2[0]) * (p1[0] - p2[0]) + + (p1[1] - p2[1]) * (p1[1] - p2[1]); +} + +inline static float dist(const Point2f& p1, const Point2f& p2) { return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y); } -inline static double dist(double x1, double y1, double x2, double y2) { +inline static float dist(float x1, float y1, float x2, float y2) { return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); } @@ -92,34 +83,6 @@ inline static T min(T t1, T t2, T t3) { return (t1 <= t2 && t1 <= t3) ? t1 : min(t2, t3); } -template -vector > build(int n, int m) { - vector > res(n); - for (int i = 0; i < n; ++i) { - res[i].resize(m, 0); - } - return res; -} - -class WeightedCrossBilateralFilter { -public: - WeightedCrossBilateralFilter(const Mat& _image, - int _windowSize, - double _sigmaDist, - double _sigmaColor); - - Mat apply(Mat& matrix, Mat& weights); - -private: - double convolution(Mat& matrix, int row, int col, Mat& weights); - - Mat image; - int windowSize; - double sigmaDist, sigmaColor; - - vector expDist; - vector > > > wc; -}; } #endif diff --git a/modules/video/test/test_simpleflow.cpp b/modules/video/test/test_simpleflow.cpp index 186ba8f56d..050d595d1c 100644 --- a/modules/video/test/test_simpleflow.cpp +++ b/modules/video/test/test_simpleflow.cpp @@ -58,66 +58,67 @@ protected: CV_SimpleFlowTest::CV_SimpleFlowTest() {} -static void readOpticalFlowFromFile(FILE* file, cv::Mat& flowX, cv::Mat& flowY) { +static bool readOpticalFlowFromFile(FILE* file, cv::Mat& flow) { char header[5]; if (fread(header, 1, 4, file) < 4 && (string)header != "PIEH") { - return; + return false; } int cols, rows; if (fread(&cols, sizeof(int), 1, file) != 1|| fread(&rows, sizeof(int), 1, file) != 1) { - return; + return false; } - flowX = cv::Mat::zeros(rows, cols, CV_64F); - flowY = cv::Mat::zeros(rows, cols, CV_64F); + flow = cv::Mat::zeros(rows, cols, CV_32FC2); for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { - float uPoint, vPoint; - if (fread(&uPoint, sizeof(float), 1, file) != 1 || - fread(&vPoint, sizeof(float), 1, file) != 1) { - flowX.release(); - flowY.release(); - return; + cv::Vec2f flow_at_point; + if (fread(&(flow_at_point[0]), sizeof(float), 1, file) != 1 || + fread(&(flow_at_point[1]), sizeof(float), 1, file) != 1) { + return false; } - - flowX.at(i, j) = uPoint; - flowY.at(i, j) = vPoint; + flow.at(i, j) = flow_at_point; } } + + return true; } -static bool isFlowCorrect(double u) { +static bool isFlowCorrect(float u) { return !isnan(u) && (fabs(u) < 1e9); } -static double calc_rmse(cv::Mat flow1X, cv::Mat flow1Y, cv::Mat flow2X, cv::Mat flow2Y) { - long double sum; +static float calc_rmse(cv::Mat flow1, cv::Mat flow2) { + float sum; int counter = 0; - const int rows = flow1X.rows; - const int cols = flow1X.cols; + const int rows = flow1.rows; + const int cols = flow1.cols; for (int y = 0; y < rows; ++y) { for (int x = 0; x < cols; ++x) { - double u1 = flow1X.at(y, x); - double v1 = flow1Y.at(y, x); - double u2 = flow2X.at(y, x); - double v2 = flow2Y.at(y, x); + cv::Vec2f flow1_at_point = flow1.at(y, x); + cv::Vec2f flow2_at_point = flow2.at(y, x); + + float u1 = flow1_at_point[0]; + float v1 = flow1_at_point[1]; + float u2 = flow2_at_point[0]; + float v2 = flow2_at_point[1]; + if (isFlowCorrect(u1) && isFlowCorrect(u2) && isFlowCorrect(v1) && isFlowCorrect(v2)) { sum += (u1-u2)*(u1-u2) + (v1-v2)*(v1-v2); counter++; } } } - return sqrt((double)sum / (1e-9 + counter)); + return sqrt(sum / (1e-9 + counter)); } void CV_SimpleFlowTest::run(int) { int code = cvtest::TS::OK; - const double MAX_RMSE = 0.6; + const float MAX_RMSE = 0.6; const string frame1_path = ts->get_data_path() + "optflow/RubberWhale1.png"; const string frame2_path = ts->get_data_path() + "optflow/RubberWhale2.png"; const string gt_flow_path = ts->get_data_path() + "optflow/RubberWhale.flo"; @@ -151,7 +152,7 @@ void CV_SimpleFlowTest::run(int) { return; } - cv::Mat flowX_gt, flowY_gt; + cv::Mat flow_gt; FILE* gt_flow_file = fopen(gt_flow_path.c_str(), "rb"); if (gt_flow_file == NULL) { @@ -160,8 +161,8 @@ void CV_SimpleFlowTest::run(int) { ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); return; } - readOpticalFlowFromFile(gt_flow_file, flowX_gt, flowY_gt); - if (flowX_gt.empty() || flowY_gt.empty()) { + + if (!readOpticalFlowFromFile(gt_flow_file, flow_gt)) { ts->printf(cvtest::TS::LOG, "error while reading flow data from file %s", gt_flow_path.c_str()); ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); @@ -169,12 +170,12 @@ void CV_SimpleFlowTest::run(int) { } fclose(gt_flow_file); - cv::Mat flowX, flowY; + cv::Mat flow; cv::calcOpticalFlowSF(frame1, frame2, - flowX, flowY, + flow, 3, 4, 2, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10); - double rmse = calc_rmse(flowX_gt, flowY_gt, flowX, flowY); + float rmse = calc_rmse(flow_gt, flow); ts->printf(cvtest::TS::LOG, "Optical flow estimation RMSE for SimpleFlow algorithm : %lf\n", rmse); diff --git a/samples/cpp/simpleflow_demo.cpp b/samples/cpp/simpleflow_demo.cpp index 6a195fe894..332df7821e 100644 --- a/samples/cpp/simpleflow_demo.cpp +++ b/samples/cpp/simpleflow_demo.cpp @@ -8,89 +8,212 @@ using namespace cv; using namespace std; +#define APP_NAME "simpleflow_demo : " + static void help() { - // print a welcome message, and the OpenCV version - printf("This is a demo of SimpleFlow optical flow algorithm,\n" - "Using OpenCV version %s\n\n", CV_VERSION); + // print a welcome message, and the OpenCV version + printf("This is a demo of SimpleFlow optical flow algorithm,\n" + "Using OpenCV version %s\n\n", CV_VERSION); - printf("Usage: simpleflow_demo frame1 frame2 output_flow" - "\nApplication will write estimated flow " - "\nbetween 'frame1' and 'frame2' in binary format" - "\ninto file 'output_flow'" - "\nThen one can use code from http://vision.middlebury.edu/flow/data/" - "\nto convert flow in binary file to image\n"); + printf("Usage: simpleflow_demo frame1 frame2 output_flow" + "\nApplication will write estimated flow " + "\nbetween 'frame1' and 'frame2' in binary format" + "\ninto file 'output_flow'" + "\nThen one can use code from http://vision.middlebury.edu/flow/data/" + "\nto convert flow in binary file to image\n"); } // binary file format for flow data specified here: // http://vision.middlebury.edu/flow/data/ -static void writeOpticalFlowToFile(const Mat& u, const Mat& v, FILE* file) { - int cols = u.cols; - int rows = u.rows; +static void writeOpticalFlowToFile(const Mat& flow, FILE* file) { + int cols = flow.cols; + int rows = flow.rows; fprintf(file, "PIEH"); - + if (fwrite(&cols, sizeof(int), 1, file) != 1 || fwrite(&rows, sizeof(int), 1, file) != 1) { - fprintf(stderr, "writeOpticalFlowToFile : problem writing header\n"); + printf(APP_NAME "writeOpticalFlowToFile : problem writing header\n"); exit(1); } - for (int i= 0; i < u.rows; ++i) { - for (int j = 0; j < u.cols; ++j) { - float uPoint = u.at(i, j); - float vPoint = v.at(i, j); + for (int i= 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + Vec2f flow_at_point = flow.at(i, j); - if (fwrite(&uPoint, sizeof(float), 1, file) != 1 || - fwrite(&vPoint, sizeof(float), 1, file) != 1) { - fprintf(stderr, "writeOpticalFlowToFile : problem writing data\n"); + if (fwrite(&(flow_at_point[0]), sizeof(float), 1, file) != 1 || + fwrite(&(flow_at_point[1]), sizeof(float), 1, file) != 1) { + printf(APP_NAME "writeOpticalFlowToFile : problem writing data\n"); exit(1); } } } } -int main(int argc, char** argv) { - help(); - if (argc < 4) { - fprintf(stderr, "Wrong number of command line arguments : %d (expected %d)\n", argc, 4); - exit(1); - } - - Mat frame1 = imread(argv[1]); - Mat frame2 = imread(argv[2]); - - if (frame1.empty() || frame2.empty()) { - fprintf(stderr, "simpleflow_demo : Images cannot be read\n"); - exit(1); - } - - if (frame1.rows != frame2.rows && frame1.cols != frame2.cols) { - fprintf(stderr, "simpleflow_demo : Images should be of equal sizes\n"); - exit(1); - } - - if (frame1.type() != 16 || frame2.type() != 16) { - fprintf(stderr, "simpleflow_demo : Images should be of equal type CV_8UC3\n"); - exit(1); - } - - printf("simpleflow_demo : Read two images of size [rows = %d, cols = %d]\n", - frame1.rows, frame1.cols); - - Mat flowX, flowY; - - calcOpticalFlowSF(frame1, frame2, - flowX, flowY, - 3, 2, 4, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10); - - FILE* file = fopen(argv[3], "wb"); - if (file == NULL) { - fprintf(stderr, "simpleflow_demo : Unable to open file '%s' for writing\n", argv[3]); +static void run(int argc, char** argv) { + if (argc < 3) { + printf(APP_NAME "Wrong number of command line arguments for mode `run`: %d (expected %d)\n", + argc, 3); exit(1); } - printf("simpleflow_demo : Writing to file\n"); - writeOpticalFlowToFile(flowX, flowY, file); + + Mat frame1 = imread(argv[0]); + Mat frame2 = imread(argv[1]); + + if (frame1.empty()) { + printf(APP_NAME "Image #1 : %s cannot be read\n", argv[0]); + exit(1); + } + + if (frame2.empty()) { + printf(APP_NAME "Image #2 : %s cannot be read\n", argv[1]); + exit(1); + } + + if (frame1.rows != frame2.rows && frame1.cols != frame2.cols) { + printf(APP_NAME "Images should be of equal sizes\n"); + exit(1); + } + + if (frame1.type() != 16 || frame2.type() != 16) { + printf(APP_NAME "Images should be of equal type CV_8UC3\n"); + exit(1); + } + + printf(APP_NAME "Read two images of size [rows = %d, cols = %d]\n", + frame1.rows, frame1.cols); + + Mat flow; + + float start = getTickCount(); + calcOpticalFlowSF(frame1, frame2, + flow, + 3, 2, 4, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10); + printf(APP_NAME "calcOpticalFlowSF : %lf sec\n", (getTickCount() - start) / getTickFrequency()); + + FILE* file = fopen(argv[2], "wb"); + if (file == NULL) { + printf(APP_NAME "Unable to open file '%s' for writing\n", argv[2]); + exit(1); + } + printf(APP_NAME "Writing to file\n"); + writeOpticalFlowToFile(flow, file); fclose(file); +} + +static bool readOpticalFlowFromFile(FILE* file, Mat& flow) { + char header[5]; + if (fread(header, 1, 4, file) < 4 && (string)header != "PIEH") { + return false; + } + + int cols, rows; + if (fread(&cols, sizeof(int), 1, file) != 1|| + fread(&rows, sizeof(int), 1, file) != 1) { + return false; + } + + flow = Mat::zeros(rows, cols, CV_32FC2); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + Vec2f flow_at_point; + if (fread(&(flow_at_point[0]), sizeof(float), 1, file) != 1 || + fread(&(flow_at_point[1]), sizeof(float), 1, file) != 1) { + return false; + } + flow.at(i, j) = flow_at_point; + } + } + + return true; +} + +static bool isFlowCorrect(float u) { + return !isnan(u) && (fabs(u) < 1e9); +} + +static float calc_rmse(Mat flow1, Mat flow2) { + float sum; + int counter = 0; + const int rows = flow1.rows; + const int cols = flow1.cols; + + for (int y = 0; y < rows; ++y) { + for (int x = 0; x < cols; ++x) { + Vec2f flow1_at_point = flow1.at(y, x); + Vec2f flow2_at_point = flow2.at(y, x); + + float u1 = flow1_at_point[0]; + float v1 = flow1_at_point[1]; + float u2 = flow2_at_point[0]; + float v2 = flow2_at_point[1]; + + if (isFlowCorrect(u1) && isFlowCorrect(u2) && isFlowCorrect(v1) && isFlowCorrect(v2)) { + sum += (u1-u2)*(u1-u2) + (v1-v2)*(v1-v2); + counter++; + } + } + } + return sqrt(sum / (1e-9 + counter)); +} + +static void eval(int argc, char** argv) { + if (argc < 2) { + printf(APP_NAME "Wrong number of command line arguments for mode `eval` : %d (expected %d)\n", + argc, 2); + exit(1); + } + + Mat flow1, flow2; + + FILE* flow_file_1 = fopen(argv[0], "rb"); + if (flow_file_1 == NULL) { + printf(APP_NAME "Cannot open file with first flow : %s\n", argv[0]); + exit(1); + } + if (!readOpticalFlowFromFile(flow_file_1, flow1)) { + printf(APP_NAME "Cannot read flow data from file %s\n", argv[0]); + exit(1); + } + fclose(flow_file_1); + + FILE* flow_file_2 = fopen(argv[1], "rb"); + if (flow_file_2 == NULL) { + printf(APP_NAME "Cannot open file with first flow : %s\n", argv[1]); + exit(1); + } + if (!readOpticalFlowFromFile(flow_file_2, flow2)) { + printf(APP_NAME "Cannot read flow data from file %s\n", argv[1]); + exit(1); + } + fclose(flow_file_2); + + float rmse = calc_rmse(flow1, flow2); + printf("%lf\n", rmse); +} + +int main(int argc, char** argv) { + if (argc < 2) { + printf(APP_NAME "Mode is not specified\n"); + help(); + exit(1); + } + string mode = (string)argv[1]; + int new_argc = argc - 2; + char** new_argv = &argv[2]; + + if ("run" == mode) { + run(new_argc, new_argv); + } else if ("eval" == mode) { + eval(new_argc, new_argv); + } else if ("help" == mode) + help(); + else { + printf(APP_NAME "Unknown mode : %s\n", argv[1]); + help(); + } + return 0; } From 7ad4c2545225aa735b9d1b5cd8e043be07d91643 Mon Sep 17 00:00:00 2001 From: Yury Zemlyanskiy Date: Fri, 31 Aug 2012 13:27:14 +0400 Subject: [PATCH 03/97] Small fixes for SimpleFlow algorithm + Fixed warnings + Add new function calcOpticalFlow with smaller number of arguments + Add asserts to algorithm and remove 'exit(1)' --- .../motion_analysis_and_object_tracking.rst | 2 ++ .../video/include/opencv2/video/tracking.hpp | 7 ++++ modules/video/src/simpleflow.cpp | 35 +++++++++++-------- modules/video/test/test_simpleflow.cpp | 8 ++--- samples/cpp/simpleflow_demo.cpp | 2 +- 5 files changed, 32 insertions(+), 22 deletions(-) diff --git a/modules/video/doc/motion_analysis_and_object_tracking.rst b/modules/video/doc/motion_analysis_and_object_tracking.rst index ebb9290cc4..6db525a743 100644 --- a/modules/video/doc/motion_analysis_and_object_tracking.rst +++ b/modules/video/doc/motion_analysis_and_object_tracking.rst @@ -601,6 +601,8 @@ calcOpticalFlowSF ----------- Calculate an optical flow using "SimpleFlow" algorithm. +.. ocv:function:: void calcOpticalFlowSF( Mat& prev, Mat& next, Mat& flowX, Mat& flowY, int layers, int averaging_block_size, int max_flow) + .. ocv:function:: void calcOpticalFlowSF( Mat& prev, Mat& next, Mat& flowX, Mat& flowY, int layers, int averaging_block_size, int max_flow, double sigma_dist, double sigma_color, int postprocess_window, double sigma_dist_fix, double sigma_color_fix, double occ_thr, int upscale_averaging_radiud, double upscale_sigma_dist, double upscale_sigma_color, double speed_up_thr) :param prev: First 8-bit 3-channel image. diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index 6800c63373..2be030d76d 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -328,6 +328,13 @@ CV_EXPORTS_W Mat estimateRigidTransform( InputArray src, InputArray dst, bool fullAffine); //! computes dense optical flow using Simple Flow algorithm +CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, + Mat& to, + Mat& flow, + int layers, + int averaging_block_size, + int max_flow); + CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, Mat& to, Mat& flow, diff --git a/modules/video/src/simpleflow.cpp b/modules/video/src/simpleflow.cpp index 0cd320d6ee..59fafdcf3d 100644 --- a/modules/video/src/simpleflow.cpp +++ b/modules/video/src/simpleflow.cpp @@ -72,26 +72,27 @@ static void removeOcclusions(const Mat& flow, } static void wd(Mat& d, int top_shift, int bottom_shift, int left_shift, int right_shift, float sigma) { - const float factor = 1.0 / (2.0 * sigma * sigma); for (int dr = -top_shift, r = 0; dr <= bottom_shift; ++dr, ++r) { for (int dc = -left_shift, c = 0; dc <= right_shift; ++dc, ++c) { - d.at(r, c) = -(dr*dr + dc*dc) * factor; + d.at(r, c) = -(dr*dr + dc*dc); } } + d *= 1.0 / (2.0 * sigma * sigma); exp(d, d); } static void wc(const Mat& image, Mat& d, int r0, int c0, int top_shift, int bottom_shift, int left_shift, int right_shift, float sigma) { - const float factor = 1.0 / (2.0 * sigma * sigma); const Vec3b centeral_point = image.at(r0, c0); + int left_border = c0-left_shift, right_border = c0+right_shift; for (int dr = r0-top_shift, r = 0; dr <= r0+bottom_shift; ++dr, ++r) { const Vec3b *row = image.ptr(dr); float *d_row = d.ptr(r); - for (int dc = c0-left_shift, c = 0; dc <= c0+right_shift; ++dc, ++c) { - d_row[c] = -dist(centeral_point, row[dc]) * factor; + for (int dc = left_border, c = 0; dc <= right_border; ++dc, ++c) { + d_row[c] = -dist(centeral_point, row[dc]); } } + d *= 1.0 / (2.0 * sigma * sigma); exp(d, d); } @@ -163,7 +164,7 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev, Mat diff_storage(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_32F); Mat w_full_window(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_32F); Mat wd_full_window(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_32F); - float w_full_window_sum; + float w_full_window_sum = 1e-9; Mat prev_extended; copyMakeBorder(prev, prev_extended, @@ -197,7 +198,7 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev, } bool first_flow_iteration = true; - float sum_e, min_e; + float sum_e = 0, min_e = 0; for (int u = min_row_shift; u <= max_row_shift; ++u) { for (int v = min_col_shift; v <= max_col_shift; ++v) { @@ -286,7 +287,7 @@ static Mat upscaleOpticalFlow(int new_rows, int averaging_radius, float sigma_dist, float sigma_color) { - crossBilateralFilter(flow, image, confidence, flow, averaging_radius, sigma_color, sigma_dist, false); + crossBilateralFilter(flow, image, confidence, flow, averaging_radius, sigma_color, sigma_dist, true); Mat new_flow; resize(flow, new_flow, Size(new_cols, new_rows), 0, 0, INTER_NEAREST); new_flow *= 2; @@ -495,13 +496,7 @@ void calcOpticalFlowSF(Mat& from, buildPyramidWithResizeMethod(from, pyr_from_images, layers - 1, INTER_CUBIC); buildPyramidWithResizeMethod(to, pyr_to_images, layers - 1, INTER_CUBIC); - if ((int)pyr_from_images.size() != layers) { - exit(1); - } - - if ((int)pyr_to_images.size() != layers) { - exit(1); - } + CV_Assert((int)pyr_from_images.size() == layers && (int)pyr_to_images.size() == layers); Mat first_from_image = pyr_from_images[layers - 1]; Mat first_to_image = pyr_to_images[layers - 1]; @@ -635,5 +630,15 @@ void calcOpticalFlowSF(Mat& from, mixChannels(&flow, 1, &resulted_flow, 1, from_to, 2); } +CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, + Mat& to, + Mat& flow, + int layers, + int averaging_block_size, + int max_flow) { + calcOpticalFlowSF(from, to, flow, layers, averaging_block_size, max_flow, + 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10); +} + } diff --git a/modules/video/test/test_simpleflow.cpp b/modules/video/test/test_simpleflow.cpp index 050d595d1c..fe96e7004f 100644 --- a/modules/video/test/test_simpleflow.cpp +++ b/modules/video/test/test_simpleflow.cpp @@ -91,7 +91,7 @@ static bool isFlowCorrect(float u) { } static float calc_rmse(cv::Mat flow1, cv::Mat flow2) { - float sum; + float sum = 0; int counter = 0; const int rows = flow1.rows; const int cols = flow1.cols; @@ -116,8 +116,6 @@ static float calc_rmse(cv::Mat flow1, cv::Mat flow2) { } void CV_SimpleFlowTest::run(int) { - int code = cvtest::TS::OK; - const float MAX_RMSE = 0.6; const string frame1_path = ts->get_data_path() + "optflow/RubberWhale1.png"; const string frame2_path = ts->get_data_path() + "optflow/RubberWhale2.png"; @@ -171,9 +169,7 @@ void CV_SimpleFlowTest::run(int) { fclose(gt_flow_file); cv::Mat flow; - cv::calcOpticalFlowSF(frame1, frame2, - flow, - 3, 4, 2, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10); + cv::calcOpticalFlowSF(frame1, frame2, flow, 3, 2, 4); float rmse = calc_rmse(flow_gt, flow); diff --git a/samples/cpp/simpleflow_demo.cpp b/samples/cpp/simpleflow_demo.cpp index 332df7821e..2727fe640a 100644 --- a/samples/cpp/simpleflow_demo.cpp +++ b/samples/cpp/simpleflow_demo.cpp @@ -135,7 +135,7 @@ static bool isFlowCorrect(float u) { } static float calc_rmse(Mat flow1, Mat flow2) { - float sum; + float sum = 0; int counter = 0; const int rows = flow1.rows; const int cols = flow1.cols; From 0c10ed26e3cf3d336689a538c060359e63a30458 Mon Sep 17 00:00:00 2001 From: Yury Zemlyanskiy Date: Mon, 3 Sep 2012 20:35:20 +0400 Subject: [PATCH 04/97] Update to improve performance of SimpleFlow algorithm + Improve performance of calcOpticalFlowSingleScale method + Small refactoring Current results: IMAGE NAMES RMSE Beanbags Dimetrodon 0.329428 DogDance Grove2 0.550852 Grove3 1.464699 Hydrangea 0.523277 MiniCooper RubberWhale 0.367246 Urban2 2.717003 Urban3 4.185070 Venus 0.775422 Walking Time (for Urban3): 17.490248 sec --- modules/video/src/simpleflow.cpp | 313 ++++++++++++++++--------------- modules/video/src/simpleflow.hpp | 16 ++ 2 files changed, 176 insertions(+), 153 deletions(-) diff --git a/modules/video/src/simpleflow.cpp b/modules/video/src/simpleflow.cpp index 59fafdcf3d..9571fe436f 100644 --- a/modules/video/src/simpleflow.cpp +++ b/modules/video/src/simpleflow.cpp @@ -60,6 +60,9 @@ static void removeOcclusions(const Mat& flow, Mat& confidence) { const int rows = flow.rows; const int cols = flow.cols; + if (!confidence.data) { + confidence = Mat::zeros(rows, cols, CV_32F); + } for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { if (dist(flow.at(r, c), -flow_inv.at(r, c)) > occ_thr) { @@ -96,20 +99,12 @@ static void wc(const Mat& image, Mat& d, int r0, int c0, exp(d, d); } -static void dist(const Mat& m1, const Mat& m2, Mat& result) { - const int rows = m1.rows; - const int cols = m1.cols; - for (int r = 0; r < rows; ++r) { - const Vec3b *m1_row = m1.ptr(r); - const Vec3b *m2_row = m2.ptr(r); - float* row = result.ptr(r); - for (int c = 0; c < cols; ++c) { - row[c] = dist(m1_row[c], m2_row[c]); - } - } -} - -static void crossBilateralFilter(const Mat& image, const Mat& edge_image, const Mat confidence, Mat& dst, int d, float sigma_color, float sigma_space, bool flag=false) { +static void crossBilateralFilter(const Mat& image, + const Mat& edge_image, + const Mat confidence, + Mat& dst, int d, + float sigma_color, float sigma_space, + bool flag=false) { const int rows = image.rows; const int cols = image.cols; Mat image_extended, edge_image_extended, confidence_extended; @@ -121,7 +116,6 @@ static void crossBilateralFilter(const Mat& image, const Mat& edge_image, const Mat weights(2*d+1, 2*d+1, CV_32F); Mat weighted_sum(2*d+1, 2*d+1, CV_32F); - vector image_extended_channels; split(image_extended, image_extended_channels); @@ -148,31 +142,15 @@ static void crossBilateralFilter(const Mat& image, const Mat& edge_image, const } } -static void calcOpticalFlowSingleScaleSF(const Mat& prev, - const Mat& next, - const Mat& mask, - Mat& flow, - Mat& confidence, - int averaging_radius, - int max_flow, - float sigma_dist, - float sigma_color) { +static void calcConfidence(const Mat& prev, + const Mat& next, + const Mat& flow, + Mat& confidence, + int max_flow) { const int rows = prev.rows; const int cols = prev.cols; confidence = Mat::zeros(rows, cols, CV_32F); - Mat diff_storage(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_32F); - Mat w_full_window(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_32F); - Mat wd_full_window(averaging_radius*2 + 1, averaging_radius*2 + 1, CV_32F); - float w_full_window_sum = 1e-9; - - Mat prev_extended; - copyMakeBorder(prev, prev_extended, - averaging_radius, averaging_radius, averaging_radius, averaging_radius, - BORDER_DEFAULT); - - wd(wd_full_window, averaging_radius, averaging_radius, averaging_radius, averaging_radius, sigma_dist); - for (int r0 = 0; r0 < rows; ++r0) { for (int c0 = 0; c0 < cols; ++c0) { Vec2f flow_at_point = flow.at(r0, c0); @@ -183,25 +161,16 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev, if (c0 + v0 < 0) { v0 = -c0; } if (c0 + v0 >= cols) { v0 = cols - 1 - c0; } - const int min_row_shift = -min(r0 + u0, max_flow); - const int max_row_shift = min(rows - 1 - (r0 + u0), max_flow); - const int min_col_shift = -min(c0 + v0, max_flow); - const int max_col_shift = min(cols - 1 - (c0 + v0), max_flow); - - float min_cost = DBL_MAX, best_u = u0, best_v = v0; - - if (mask.at(r0, c0)) { - wc(prev_extended, w_full_window, r0 + averaging_radius, c0 + averaging_radius, - averaging_radius, averaging_radius, averaging_radius, averaging_radius, sigma_color); - multiply(w_full_window, wd_full_window, w_full_window); - w_full_window_sum = sum(w_full_window)[0]; - } + const int top_row_shift = -min(r0 + u0, max_flow); + const int bottom_row_shift = min(rows - 1 - (r0 + u0), max_flow); + const int left_col_shift = -min(c0 + v0, max_flow); + const int right_col_shift = min(cols - 1 - (c0 + v0), max_flow); bool first_flow_iteration = true; float sum_e = 0, min_e = 0; - for (int u = min_row_shift; u <= max_row_shift; ++u) { - for (int v = min_col_shift; v <= max_col_shift; ++v) { + for (int u = top_row_shift; u <= bottom_row_shift; ++u) { + for (int v = left_col_shift; v <= right_col_shift; ++v) { float e = dist(prev.at(r0, c0), next.at(r0 + u0 + u, c0 + v0 + v)); if (first_flow_iteration) { sum_e = e; @@ -211,55 +180,83 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev, sum_e += e; min_e = std::min(min_e, e); } - if (!mask.at(r0, c0)) { - continue; + } + } + int windows_square = (bottom_row_shift - top_row_shift + 1) * + (right_col_shift - left_col_shift + 1); + confidence.at(r0, c0) = (windows_square == 0) ? 0 + : sum_e / windows_square - min_e; + CV_Assert(confidence.at(r0, c0) >= 0); + } + } +} + +static void calcOpticalFlowSingleScaleSF(const Mat& prev_extended, + const Mat& next_extended, + const Mat& mask, + Mat& flow, + int averaging_radius, + int max_flow, + float sigma_dist, + float sigma_color) { + const int averaging_radius_2 = averaging_radius << 1; + const int rows = prev_extended.rows - averaging_radius_2; + const int cols = prev_extended.cols - averaging_radius_2; + + Mat weight_window(averaging_radius_2 + 1, averaging_radius_2 + 1, CV_32F); + Mat space_weight_window(averaging_radius_2 + 1, averaging_radius_2 + 1, CV_32F); + + wd(space_weight_window, averaging_radius, averaging_radius, averaging_radius, averaging_radius, sigma_dist); + + for (int r0 = 0; r0 < rows; ++r0) { + for (int c0 = 0; c0 < cols; ++c0) { + if (!mask.at(r0, c0)) { + continue; + } + + // TODO: do smth with this creepy staff + Vec2f flow_at_point = flow.at(r0, c0); + int u0 = floor(flow_at_point[0] + 0.5); + if (r0 + u0 < 0) { u0 = -r0; } + if (r0 + u0 >= rows) { u0 = rows - 1 - r0; } + int v0 = floor(flow_at_point[1] + 0.5); + if (c0 + v0 < 0) { v0 = -c0; } + if (c0 + v0 >= cols) { v0 = cols - 1 - c0; } + + const int top_row_shift = -min(r0 + u0, max_flow); + const int bottom_row_shift = min(rows - 1 - (r0 + u0), max_flow); + const int left_col_shift = -min(c0 + v0, max_flow); + const int right_col_shift = min(cols - 1 - (c0 + v0), max_flow); + + float min_cost = DBL_MAX, best_u = u0, best_v = v0; + + wc(prev_extended, weight_window, r0 + averaging_radius, c0 + averaging_radius, + averaging_radius, averaging_radius, averaging_radius, averaging_radius, sigma_color); + multiply(weight_window, space_weight_window, weight_window); + + const int prev_extended_top_window_row = r0; + const int prev_extended_left_window_col = c0; + + for (int u = top_row_shift; u <= bottom_row_shift; ++u) { + const int next_extended_top_window_row = r0 + u0 + u; + for (int v = left_col_shift; v <= right_col_shift; ++v) { + const int next_extended_left_window_col = c0 + v0 + v; + + float cost = 0; + for (int r = 0; r <= averaging_radius_2; ++r) { + const Vec3b *prev_extended_window_row = prev_extended.ptr(prev_extended_top_window_row + r); + const Vec3b *next_extended_window_row = next_extended.ptr(next_extended_top_window_row + r); + const float* weight_window_row = weight_window.ptr(r); + for (int c = 0; c <= averaging_radius_2; ++c) { + cost += weight_window_row[c] * + dist(prev_extended_window_row[prev_extended_left_window_col + c], + next_extended_window_row[next_extended_left_window_col + c]); + } } + // cost should be divided by sum(weight_window), but because + // we interested only in min(cost) and sum(weight_window) is constant + // for every point - we remove it - const int window_top_shift = min(r0, r0 + u + u0, averaging_radius); - const int window_bottom_shift = min(rows - 1 - r0, - rows - 1 - (r0 + u + u0), - averaging_radius); - const int window_left_shift = min(c0, c0 + v + v0, averaging_radius); - const int window_right_shift = min(cols - 1 - c0, - cols - 1 - (c0 + v + v0), - averaging_radius); - - const Range prev_row_range(r0 - window_top_shift, r0 + window_bottom_shift + 1); - const Range prev_col_range(c0 - window_left_shift, c0 + window_right_shift + 1); - - const Range next_row_range(r0 + u0 + u - window_top_shift, - r0 + u0 + u + window_bottom_shift + 1); - const Range next_col_range(c0 + v0 + v - window_left_shift, - c0 + v0 + v + window_right_shift + 1); - - - Mat diff2; - Mat w; - float w_sum; - if (window_top_shift == averaging_radius && - window_bottom_shift == averaging_radius && - window_left_shift == averaging_radius && - window_right_shift == averaging_radius) { - w = w_full_window; - w_sum = w_full_window_sum; - diff2 = diff_storage; - dist(prev(prev_row_range, prev_col_range), next(next_row_range, next_col_range), diff2); - } else { - diff2 = diff_storage(Range(averaging_radius - window_top_shift, - averaging_radius + 1 + window_bottom_shift), - Range(averaging_radius - window_left_shift, - averaging_radius + 1 + window_right_shift)); - - dist(prev(prev_row_range, prev_col_range), next(next_row_range, next_col_range), diff2); - w = w_full_window(Range(averaging_radius - window_top_shift, - averaging_radius + 1 + window_bottom_shift), - Range(averaging_radius - window_left_shift, - averaging_radius + 1 + window_right_shift)); - w_sum = sum(w)[0]; - } - multiply(diff2, w, diff2); - - const float cost = sum(diff2)[0] / w_sum; if (cost < min_cost) { min_cost = cost; best_u = u + u0; @@ -267,14 +264,7 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev, } } } - int windows_square = (max_row_shift - min_row_shift + 1) * - (max_col_shift - min_col_shift + 1); - confidence.at(r0, c0) = (windows_square == 0) ? 0 - : sum_e / windows_square - min_e; - CV_Assert(confidence.at(r0, c0) >= 0); // TODO: remove it after testing - if (mask.at(r0, c0)) { - flow.at(r0, c0) = Vec2f(best_u, best_v); - } + flow.at(r0, c0) = Vec2f(best_u, best_v); } } } @@ -474,22 +464,22 @@ static void buildPyramidWithResizeMethod(Mat& src, } } -void calcOpticalFlowSF(Mat& from, - Mat& to, - Mat& resulted_flow, - int layers, - int averaging_block_size, - int max_flow, - double sigma_dist, - double sigma_color, - int postprocess_window, - double sigma_dist_fix, - double sigma_color_fix, - double occ_thr, - int upscale_averaging_radius, - double upscale_sigma_dist, - double upscale_sigma_color, - double speed_up_thr) { +CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, + Mat& to, + Mat& resulted_flow, + int layers, + int averaging_radius, + int max_flow, + double sigma_dist, + double sigma_color, + int postprocess_window, + double sigma_dist_fix, + double sigma_color_fix, + double occ_thr, + int upscale_averaging_radius, + double upscale_sigma_dist, + double upscale_sigma_color, + double speed_up_thr) { vector pyr_from_images; vector pyr_to_images; @@ -498,34 +488,43 @@ void calcOpticalFlowSF(Mat& from, CV_Assert((int)pyr_from_images.size() == layers && (int)pyr_to_images.size() == layers); - Mat first_from_image = pyr_from_images[layers - 1]; - Mat first_to_image = pyr_to_images[layers - 1]; + Mat curr_from, curr_to, prev_from, prev_to; + Mat curr_from_extended, curr_to_extended; - Mat mask = Mat::ones(first_from_image.rows, first_from_image.cols, CV_8U); - Mat mask_inv = Mat::ones(first_from_image.rows, first_from_image.cols, CV_8U); + curr_from = pyr_from_images[layers - 1]; + curr_to = pyr_to_images[layers - 1]; - Mat flow(first_from_image.rows, first_from_image.cols, CV_32FC2); - Mat flow_inv(first_to_image.rows, first_to_image.cols, CV_32FC2); + copyMakeBorder(curr_from, curr_from_extended, + averaging_radius, averaging_radius, averaging_radius, averaging_radius, + BORDER_DEFAULT); + copyMakeBorder(curr_to, curr_to_extended, + averaging_radius, averaging_radius, averaging_radius, averaging_radius, + BORDER_DEFAULT); + + Mat mask = Mat::ones(curr_from.size(), CV_8U); + Mat mask_inv = Mat::ones(curr_from.size(), CV_8U); + + Mat flow(curr_from.size(), CV_32FC2); + Mat flow_inv(curr_to.size(), CV_32FC2); Mat confidence; Mat confidence_inv; - calcOpticalFlowSingleScaleSF(first_from_image, - first_to_image, + + calcOpticalFlowSingleScaleSF(curr_from_extended, + curr_to_extended, mask, flow, - confidence, - averaging_block_size, + averaging_radius, max_flow, sigma_dist, sigma_color); - calcOpticalFlowSingleScaleSF(first_to_image, - first_from_image, + calcOpticalFlowSingleScaleSF(curr_to_extended, + curr_from_extended, mask_inv, flow_inv, - confidence_inv, - averaging_block_size, + averaging_radius, max_flow, sigma_dist, sigma_color); @@ -540,14 +539,21 @@ void calcOpticalFlowSF(Mat& from, occ_thr, confidence_inv); - Mat speed_up = Mat::zeros(first_from_image.rows, first_from_image.cols, CV_8U); - Mat speed_up_inv = Mat::zeros(first_from_image.rows, first_from_image.cols, CV_8U); + Mat speed_up = Mat::zeros(curr_from.size(), CV_8U); + Mat speed_up_inv = Mat::zeros(curr_from.size(), CV_8U); for (int curr_layer = layers - 2; curr_layer >= 0; --curr_layer) { - const Mat curr_from = pyr_from_images[curr_layer]; - const Mat curr_to = pyr_to_images[curr_layer]; - const Mat prev_from = pyr_from_images[curr_layer + 1]; - const Mat prev_to = pyr_to_images[curr_layer + 1]; + curr_from = pyr_from_images[curr_layer]; + curr_to = pyr_to_images[curr_layer]; + prev_from = pyr_from_images[curr_layer + 1]; + prev_to = pyr_to_images[curr_layer + 1]; + + copyMakeBorder(curr_from, curr_from_extended, + averaging_radius, averaging_radius, averaging_radius, averaging_radius, + BORDER_DEFAULT); + copyMakeBorder(curr_to, curr_to_extended, + averaging_radius, averaging_radius, averaging_radius, averaging_radius, + BORDER_DEFAULT); const int curr_rows = curr_from.rows; const int curr_cols = curr_from.cols; @@ -555,7 +561,7 @@ void calcOpticalFlowSF(Mat& from, Mat new_speed_up, new_speed_up_inv; selectPointsToRecalcFlow(flow, - averaging_block_size, + averaging_radius, speed_up_thr, curr_rows, curr_cols, @@ -564,7 +570,7 @@ void calcOpticalFlowSF(Mat& from, mask); selectPointsToRecalcFlow(flow_inv, - averaging_block_size, + averaging_radius, speed_up_thr, curr_rows, curr_cols, @@ -593,22 +599,22 @@ void calcOpticalFlowSF(Mat& from, upscale_sigma_dist, upscale_sigma_color); - calcOpticalFlowSingleScaleSF(curr_from, - curr_to, + calcConfidence(curr_from, curr_to, flow, confidence, max_flow); + calcOpticalFlowSingleScaleSF(curr_from_extended, + curr_to_extended, mask, flow, - confidence, - averaging_block_size, + averaging_radius, max_flow, sigma_dist, sigma_color); - calcOpticalFlowSingleScaleSF(curr_to, - curr_from, + calcConfidence(curr_to, curr_from, flow_inv, confidence_inv, max_flow); + calcOpticalFlowSingleScaleSF(curr_to_extended, + curr_from_extended, mask_inv, flow_inv, - confidence_inv, - averaging_block_size, + averaging_radius, max_flow, sigma_dist, sigma_color); @@ -616,11 +622,12 @@ void calcOpticalFlowSF(Mat& from, extrapolateFlow(flow, speed_up); extrapolateFlow(flow_inv, speed_up_inv); + //TODO: should we remove occlusions for the last stage? removeOcclusions(flow, flow_inv, occ_thr, confidence); removeOcclusions(flow_inv, flow, occ_thr, confidence_inv); } - crossBilateralFilter(flow, pyr_from_images[0], confidence, flow, + crossBilateralFilter(flow, curr_from, confidence, flow, postprocess_window, sigma_color_fix, sigma_dist_fix); GaussianBlur(flow, flow, Size(3, 3), 5); diff --git a/modules/video/src/simpleflow.hpp b/modules/video/src/simpleflow.hpp index c4aa02355a..94b92520e9 100644 --- a/modules/video/src/simpleflow.hpp +++ b/modules/video/src/simpleflow.hpp @@ -51,6 +51,22 @@ using namespace std; #define UNKNOWN_FLOW_THRESH 1e9 namespace cv { +/* +template +inline static T sqr(T t) { + return t*t; +} + +static float dist(const Vec3b& p1, const Vec3b& p2) { + return sqr(p1[0] - p2[0]) + + sqr(p1[1] - p2[1]) + + sqr(p1[2] - p2[2]); +} + +inline static float dist(const Vec2f& p1, const Vec2f& p2) { + return sqr(p1[0] - p2[0]) + + sqr(p1[1] - p2[1]); +}*/ inline static float dist(const Vec3b& p1, const Vec3b& p2) { return (p1[0] - p2[0]) * (p1[0] - p2[0]) + From 9d1aa37e214485e18793df09ad473dc76d8c548c Mon Sep 17 00:00:00 2001 From: Yury Zemlyanskiy Date: Wed, 5 Sep 2012 14:19:44 +0400 Subject: [PATCH 05/97] Small fixes --- modules/video/src/simpleflow.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/video/src/simpleflow.cpp b/modules/video/src/simpleflow.cpp index 7f327b17d9..82b8e50109 100644 --- a/modules/video/src/simpleflow.cpp +++ b/modules/video/src/simpleflow.cpp @@ -128,11 +128,11 @@ static void crossBilateralFilter(const Mat& image, multiply(weights, confidence_extended(window_rows, window_cols), weights); multiply(weights, weights_space, weights); - float weights_sum = sum(weights)[0]; + float weights_sum = (float)sum(weights)[0]; for (int ch = 0; ch < 2; ++ch) { multiply(weights, image_extended_channels[ch](window_rows, window_cols), weighted_sum); - float total_sum = sum(weighted_sum)[0]; + float total_sum = (float)sum(weighted_sum)[0]; dst.at(row, col)[ch] = (flag && fabs(weights_sum) < 1e-9) ? image.at(row, col) @@ -154,10 +154,10 @@ static void calcConfidence(const Mat& prev, for (int r0 = 0; r0 < rows; ++r0) { for (int c0 = 0; c0 < cols; ++c0) { Vec2f flow_at_point = flow.at(r0, c0); - int u0 = floor(flow_at_point[0] + 0.5); + int u0 = cvRound(flow_at_point[0]); if (r0 + u0 < 0) { u0 = -r0; } if (r0 + u0 >= rows) { u0 = rows - 1 - r0; } - int v0 = floor(flow_at_point[1] + 0.5); + int v0 = cvRound(flow_at_point[1]); if (c0 + v0 < 0) { v0 = -c0; } if (c0 + v0 >= cols) { v0 = cols - 1 - c0; } @@ -531,12 +531,12 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, removeOcclusions(flow, flow_inv, - occ_thr, + (float)occ_thr, confidence); removeOcclusions(flow_inv, flow, - occ_thr, + (float)occ_thr, confidence_inv); Mat speed_up = Mat::zeros(curr_from.size(), CV_8U); @@ -606,8 +606,8 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, flow, averaging_radius, max_flow, - sigma_dist, - sigma_color); + (float)sigma_dist, + (float)sigma_color); calcConfidence(curr_to, curr_from, flow_inv, confidence_inv, max_flow); calcOpticalFlowSingleScaleSF(curr_to_extended, @@ -616,8 +616,8 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, flow_inv, averaging_radius, max_flow, - sigma_dist, - sigma_color); + (float)sigma_dist, + (float)sigma_color); extrapolateFlow(flow, speed_up); extrapolateFlow(flow_inv, speed_up_inv); @@ -628,7 +628,7 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, } crossBilateralFilter(flow, curr_from, confidence, flow, - postprocess_window, sigma_color_fix, sigma_dist_fix); + postprocess_window, (float)sigma_color_fix, (float)sigma_dist_fix); GaussianBlur(flow, flow, Size(3, 3), 5); From 036258b8da70c027ae3c5756a5dc7ba8e45f35ac Mon Sep 17 00:00:00 2001 From: Yury Zemlyanskiy Date: Wed, 5 Sep 2012 14:36:40 +0400 Subject: [PATCH 06/97] Small fixed #2 --- modules/video/src/simpleflow.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/video/src/simpleflow.cpp b/modules/video/src/simpleflow.cpp index 82b8e50109..d1471307dc 100644 --- a/modules/video/src/simpleflow.cpp +++ b/modules/video/src/simpleflow.cpp @@ -259,8 +259,8 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev_extended, if (cost < min_cost) { min_cost = cost; - best_u = u + u0; - best_v = v + v0; + best_u = (float)(u + u0); + best_v = (float)(v + v0); } } } @@ -309,7 +309,7 @@ static Mat calcIrregularityMat(const Mat& flow, int radius) { static void selectPointsToRecalcFlow(const Mat& flow, int irregularity_metric_radius, - int speed_up_thr, + float speed_up_thr, int curr_rows, int curr_cols, const Mat& prev_speed_up, @@ -562,7 +562,7 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, selectPointsToRecalcFlow(flow, averaging_radius, - (int)speed_up_thr, + speed_up_thr, curr_rows, curr_cols, speed_up, @@ -586,8 +586,8 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, prev_from, confidence, flow, - upscale_averaging_radius, - upscale_sigma_dist, + (float)upscale_averaging_radius, + (float)upscale_sigma_dist, upscale_sigma_color); flow_inv = upscaleOpticalFlow(curr_rows, @@ -596,8 +596,8 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, confidence_inv, flow_inv, upscale_averaging_radius, - upscale_sigma_dist, - upscale_sigma_color); + (float)upscale_sigma_dist, + (float)upscale_sigma_color); calcConfidence(curr_from, curr_to, flow, confidence, max_flow); calcOpticalFlowSingleScaleSF(curr_from_extended, @@ -623,8 +623,8 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, extrapolateFlow(flow_inv, speed_up_inv); //TODO: should we remove occlusions for the last stage? - removeOcclusions(flow, flow_inv, occ_thr, confidence); - removeOcclusions(flow_inv, flow, occ_thr, confidence_inv); + removeOcclusions(flow, flow_inv, (float)occ_thr, confidence); + removeOcclusions(flow_inv, flow, (float)occ_thr, confidence_inv); } crossBilateralFilter(flow, curr_from, confidence, flow, From 66ce62cd0dc66f0d86580ecd7d20bfb7a990a86d Mon Sep 17 00:00:00 2001 From: Yury Zemlyanskiy Date: Wed, 5 Sep 2012 14:39:33 +0400 Subject: [PATCH 07/97] Small fixed #3 --- modules/video/src/simpleflow.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/video/src/simpleflow.cpp b/modules/video/src/simpleflow.cpp index d1471307dc..af99fc5f25 100644 --- a/modules/video/src/simpleflow.cpp +++ b/modules/video/src/simpleflow.cpp @@ -517,8 +517,8 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, flow, averaging_radius, max_flow, - sigma_dist, - sigma_color); + (float)sigma_dist, + (float)sigma_color); calcOpticalFlowSingleScaleSF(curr_to_extended, curr_from_extended, @@ -526,8 +526,8 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, flow_inv, averaging_radius, max_flow, - sigma_dist, - sigma_color); + (float)sigma_dist, + (float)sigma_color); removeOcclusions(flow, flow_inv, @@ -586,9 +586,9 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, prev_from, confidence, flow, - (float)upscale_averaging_radius, + upscale_averaging_radius, (float)upscale_sigma_dist, - upscale_sigma_color); + (float)upscale_sigma_color); flow_inv = upscaleOpticalFlow(curr_rows, curr_cols, From bcd0aefbca846b4c59be4e12eee37211cba526a3 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Wed, 5 Sep 2012 15:51:51 +0400 Subject: [PATCH 08/97] Fix gcc build errors and warnings --- 3rdparty/libjasper/jpc_qmfb.c | 28 +-- .../core/include/opencv2/core/internal.hpp | 5 + modules/highgui/src/cap_gstreamer.cpp | 8 +- modules/nonfree/src/surf.cpp | 165 +++++++++--------- modules/objdetect/src/haar.cpp | 15 +- modules/python/src2/cv2.cv.hpp | 64 +++---- 6 files changed, 147 insertions(+), 138 deletions(-) diff --git a/3rdparty/libjasper/jpc_qmfb.c b/3rdparty/libjasper/jpc_qmfb.c index 925f289fd7..a8460d3cdc 100644 --- a/3rdparty/libjasper/jpc_qmfb.c +++ b/3rdparty/libjasper/jpc_qmfb.c @@ -94,14 +94,14 @@ #define QMFB_SPLITBUFSIZE 4096 #define QMFB_JOINBUFSIZE 4096 -int jpc_ft_analyze(jpc_fix_t *a, int xstart, int ystart, int width, int height, +int jpc_ft_analyze(int *a, int xstart, int ystart, int width, int height, int stride); int jpc_ft_synthesize(int *a, int xstart, int ystart, int width, int height, int stride); -int jpc_ns_analyze(jpc_fix_t *a, int xstart, int ystart, int width, int height, +int jpc_ns_analyze(int *a, int xstart, int ystart, int width, int height, int stride); -int jpc_ns_synthesize(jpc_fix_t *a, int xstart, int ystart, int width, +int jpc_ns_synthesize(int *a, int xstart, int ystart, int width, int height, int stride); void jpc_ft_fwdlift_row(jpc_fix_t *a, int numcols, int parity); @@ -1556,7 +1556,7 @@ void jpc_ft_invlift_colres(jpc_fix_t *a, int numrows, int numcols, int stride, } -int jpc_ft_analyze(jpc_fix_t *a, int xstart, int ystart, int width, int height, +int jpc_ft_analyze(int *a, int xstart, int ystart, int width, int height, int stride) { int numrows = height; @@ -1568,7 +1568,7 @@ int jpc_ft_analyze(jpc_fix_t *a, int xstart, int ystart, int width, int height, int maxcols; maxcols = (numcols / JPC_QMFB_COLGRPSIZE) * JPC_QMFB_COLGRPSIZE; - startptr = &a[0]; + startptr = (jpc_fix_t*)&a[0]; for (i = 0; i < maxcols; i += JPC_QMFB_COLGRPSIZE) { jpc_qmfb_split_colgrp(startptr, numrows, stride, rowparity); jpc_ft_fwdlift_colgrp(startptr, numrows, stride, rowparity); @@ -1581,7 +1581,7 @@ int jpc_ft_analyze(jpc_fix_t *a, int xstart, int ystart, int width, int height, rowparity); } - startptr = &a[0]; + startptr = (jpc_fix_t*)&a[0]; for (i = 0; i < numrows; ++i) { jpc_qmfb_split_row(startptr, numcols, colparity); jpc_ft_fwdlift_row(startptr, numcols, colparity); @@ -1604,7 +1604,7 @@ int jpc_ft_synthesize(int *a, int xstart, int ystart, int width, int height, jpc_fix_t *startptr; int i; - startptr = &a[0]; + startptr = (jpc_fix_t*)&a[0]; for (i = 0; i < numrows; ++i) { jpc_ft_invlift_row(startptr, numcols, colparity); jpc_qmfb_join_row(startptr, numcols, colparity); @@ -1612,7 +1612,7 @@ int jpc_ft_synthesize(int *a, int xstart, int ystart, int width, int height, } maxcols = (numcols / JPC_QMFB_COLGRPSIZE) * JPC_QMFB_COLGRPSIZE; - startptr = &a[0]; + startptr = (jpc_fix_t*)&a[0]; for (i = 0; i < maxcols; i += JPC_QMFB_COLGRPSIZE) { jpc_ft_invlift_colgrp(startptr, numrows, stride, rowparity); jpc_qmfb_join_colgrp(startptr, numrows, stride, rowparity); @@ -3068,7 +3068,7 @@ void jpc_ns_invlift_col(jpc_fix_t *a, int numrows, int stride, } -int jpc_ns_analyze(jpc_fix_t *a, int xstart, int ystart, int width, int height, +int jpc_ns_analyze(int *a, int xstart, int ystart, int width, int height, int stride) { @@ -3081,7 +3081,7 @@ int jpc_ns_analyze(jpc_fix_t *a, int xstart, int ystart, int width, int height, int maxcols; maxcols = (numcols / JPC_QMFB_COLGRPSIZE) * JPC_QMFB_COLGRPSIZE; - startptr = &a[0]; + startptr = (jpc_fix_t*)&a[0]; for (i = 0; i < maxcols; i += JPC_QMFB_COLGRPSIZE) { jpc_qmfb_split_colgrp(startptr, numrows, stride, rowparity); jpc_ns_fwdlift_colgrp(startptr, numrows, stride, rowparity); @@ -3094,7 +3094,7 @@ int jpc_ns_analyze(jpc_fix_t *a, int xstart, int ystart, int width, int height, rowparity); } - startptr = &a[0]; + startptr = (jpc_fix_t*)&a[0]; for (i = 0; i < numrows; ++i) { jpc_qmfb_split_row(startptr, numcols, colparity); jpc_ns_fwdlift_row(startptr, numcols, colparity); @@ -3105,7 +3105,7 @@ int jpc_ns_analyze(jpc_fix_t *a, int xstart, int ystart, int width, int height, } -int jpc_ns_synthesize(jpc_fix_t *a, int xstart, int ystart, int width, +int jpc_ns_synthesize(int *a, int xstart, int ystart, int width, int height, int stride) { @@ -3117,7 +3117,7 @@ int jpc_ns_synthesize(jpc_fix_t *a, int xstart, int ystart, int width, jpc_fix_t *startptr; int i; - startptr = &a[0]; + startptr = (jpc_fix_t*)&a[0]; for (i = 0; i < numrows; ++i) { jpc_ns_invlift_row(startptr, numcols, colparity); jpc_qmfb_join_row(startptr, numcols, colparity); @@ -3125,7 +3125,7 @@ int jpc_ns_synthesize(jpc_fix_t *a, int xstart, int ystart, int width, } maxcols = (numcols / JPC_QMFB_COLGRPSIZE) * JPC_QMFB_COLGRPSIZE; - startptr = &a[0]; + startptr = (jpc_fix_t*)&a[0]; for (i = 0; i < maxcols; i += JPC_QMFB_COLGRPSIZE) { jpc_ns_invlift_colgrp(startptr, numrows, stride, rowparity); jpc_qmfb_join_colgrp(startptr, numrows, stride, rowparity); diff --git a/modules/core/include/opencv2/core/internal.hpp b/modules/core/include/opencv2/core/internal.hpp index 235abcc3c1..ee4053aafb 100644 --- a/modules/core/include/opencv2/core/internal.hpp +++ b/modules/core/include/opencv2/core/internal.hpp @@ -135,6 +135,11 @@ CV_INLINE IppiSize ippiSize(int width, int height) # if defined __AVX__ || (defined _MSC_FULL_VER && _MSC_FULL_VER >= 160040219) # include # define CV_AVX 1 +# if defined(_XCR_XFEATURE_ENABLED_MASK) +# define __xgetbv() _xgetbv(_XCR_XFEATURE_ENABLED_MASK) +# else +# define __xgetbv() 0 +# endif # else # define CV_AVX 0 # endif diff --git a/modules/highgui/src/cap_gstreamer.cpp b/modules/highgui/src/cap_gstreamer.cpp index ecce0b53b8..e8cf213261 100644 --- a/modules/highgui/src/cap_gstreamer.cpp +++ b/modules/highgui/src/cap_gstreamer.cpp @@ -183,7 +183,7 @@ bool CvCapture_GStreamer::grabFrame() IplImage * CvCapture_GStreamer::retrieveFrame(int) { if(!buffer) - return false; + return 0; if(!frame) { gint height, width; @@ -193,7 +193,7 @@ IplImage * CvCapture_GStreamer::retrieveFrame(int) if(!gst_structure_get_int(structure, "width", &width) || !gst_structure_get_int(structure, "height", &height)) - return false; + return 0; frame = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, 3); gst_caps_unref(buff_caps); @@ -572,7 +572,7 @@ CvVideoWriter* cvCreateVideoWriter_GStreamer(const char* filename, int fourcc, d return wrt; delete wrt; - return false; + return 0; } void CvCapture_GStreamer::close() @@ -746,5 +746,5 @@ CvCapture* cvCreateCapture_GStreamer(int type, const char* filename ) return capture; delete capture; - return false; + return 0; } diff --git a/modules/nonfree/src/surf.cpp b/modules/nonfree/src/surf.cpp index 8c601a8b80..9e6ce5464c 100644 --- a/modules/nonfree/src/surf.cpp +++ b/modules/nonfree/src/surf.cpp @@ -257,16 +257,97 @@ interpolateKeypoint( float N9[3][9], int dx, int dy, int ds, KeyPoint& kpt ) return ok; } +// Multi-threaded construction of the scale-space pyramid +struct SURFBuildInvoker +{ + SURFBuildInvoker( const Mat& _sum, const vector& _sizes, + const vector& _sampleSteps, + vector& _dets, vector& _traces ) + { + sum = &_sum; + sizes = &_sizes; + sampleSteps = &_sampleSteps; + dets = &_dets; + traces = &_traces; + } + + void operator()(const BlockedRange& range) const + { + for( int i=range.begin(); i *sizes; + const vector *sampleSteps; + vector* dets; + vector* traces; +}; + +// Multi-threaded search of the scale-space pyramid for keypoints +struct SURFFindInvoker +{ + SURFFindInvoker( const Mat& _sum, const Mat& _mask_sum, + const vector& _dets, const vector& _traces, + const vector& _sizes, const vector& _sampleSteps, + const vector& _middleIndices, vector& _keypoints, + int _nOctaveLayers, float _hessianThreshold ) + { + sum = &_sum; + mask_sum = &_mask_sum; + dets = &_dets; + traces = &_traces; + sizes = &_sizes; + sampleSteps = &_sampleSteps; + middleIndices = &_middleIndices; + keypoints = &_keypoints; + nOctaveLayers = _nOctaveLayers; + hessianThreshold = _hessianThreshold; + } + + static void findMaximaInLayer( const Mat& sum, const Mat& mask_sum, + const vector& dets, const vector& traces, + const vector& sizes, vector& keypoints, + int octave, int layer, float hessianThreshold, int sampleStep ); + + void operator()(const BlockedRange& range) const + { + for( int i=range.begin(); i* dets; + const vector* traces; + const vector* sizes; + const vector* sampleSteps; + const vector* middleIndices; + vector* keypoints; + int nOctaveLayers; + float hessianThreshold; + #ifdef HAVE_TBB -static tbb::mutex findMaximaInLayer_m; + static tbb::mutex findMaximaInLayer_m; #endif +}; + +#ifdef HAVE_TBB +tbb::mutex SURFFindInvoker::findMaximaInLayer_m; +#endif + /* * Find the maxima in the determinant of the Hessian in a layer of the * scale-space pyramid */ -static void -findMaximaInLayer( const Mat& sum, const Mat& mask_sum, +void SURFFindInvoker::findMaximaInLayer( const Mat& sum, const Mat& mask_sum, const vector& dets, const vector& traces, const vector& sizes, vector& keypoints, int octave, int layer, float hessianThreshold, int sampleStep ) @@ -367,84 +448,6 @@ findMaximaInLayer( const Mat& sum, const Mat& mask_sum, } } - -// Multi-threaded construction of the scale-space pyramid -struct SURFBuildInvoker -{ - SURFBuildInvoker( const Mat& _sum, const vector& _sizes, - const vector& _sampleSteps, - vector& _dets, vector& _traces ) - { - sum = &_sum; - sizes = &_sizes; - sampleSteps = &_sampleSteps; - dets = &_dets; - traces = &_traces; - } - - void operator()(const BlockedRange& range) const - { - for( int i=range.begin(); i *sizes; - const vector *sampleSteps; - vector* dets; - vector* traces; -}; - -// Multi-threaded search of the scale-space pyramid for keypoints -struct SURFFindInvoker -{ - SURFFindInvoker( const Mat& _sum, const Mat& _mask_sum, - const vector& _dets, const vector& _traces, - const vector& _sizes, const vector& _sampleSteps, - const vector& _middleIndices, vector& _keypoints, - int _nOctaveLayers, float _hessianThreshold ) - { - sum = &_sum; - mask_sum = &_mask_sum; - dets = &_dets; - traces = &_traces; - sizes = &_sizes; - sampleSteps = &_sampleSteps; - middleIndices = &_middleIndices; - keypoints = &_keypoints; - nOctaveLayers = _nOctaveLayers; - hessianThreshold = _hessianThreshold; - -#ifdef HAVE_TBB - //touch the mutex to ensure that it's initialization is finished - CV_Assert(&findMaximaInLayer_m > 0); -#endif - } - - void operator()(const BlockedRange& range) const - { - for( int i=range.begin(); i* dets; - const vector* traces; - const vector* sizes; - const vector* sampleSteps; - const vector* middleIndices; - vector* keypoints; - int nOctaveLayers; - float hessianThreshold; -}; - struct KeypointGreater { inline bool operator()(const KeyPoint& kp1, const KeyPoint& kp2) const diff --git a/modules/objdetect/src/haar.cpp b/modules/objdetect/src/haar.cpp index 4ba4e7f694..5e2d5324dc 100644 --- a/modules/objdetect/src/haar.cpp +++ b/modules/objdetect/src/haar.cpp @@ -43,6 +43,7 @@ #include "precomp.hpp" #include +#include "opencv2/core/internal.hpp" #if CV_SSE2 || CV_SSE3 @@ -780,7 +781,7 @@ cvRunHaarClassifierCascadeSum( const CvHaarClassifierCascade* _cascade, #ifdef CV_HAAR_USE_AVX bool haveAVX = false; if(cv::checkHardwareSupport(CV_CPU_AVX)) - if(_xgetbv(_XCR_XFEATURE_ENABLED_MASK)&0x6)// Check if the OS will save the YMM registers + if(__xgetbv()&0x6)// Check if the OS will save the YMM registers { haveAVX = true; } @@ -867,7 +868,7 @@ cvRunHaarClassifierCascadeSum( const CvHaarClassifierCascade* _cascade, for( i = start_stage; i < cascade->count; i++ ) { stage_sum = 0.0; - int j = 0; + j = 0; float CV_DECL_ALIGNED(32) buf[8]; if( cascade->stage_classifier[i].two_rects ) { @@ -1020,12 +1021,12 @@ cvRunHaarClassifierCascadeSum( const CvHaarClassifierCascade* _cascade, } else #endif - #if defined CV_HAAR_USE_SSE && CV_HAAR_USE_SSE && !CV_HAAR_USE_AVX //old SSE optimization + #if defined CV_HAAR_USE_SSE && CV_HAAR_USE_SSE && (!defined CV_HAAR_USE_AVX || !CV_HAAR_USE_AVX) //old SSE optimization if(haveSSE2) { for( i = start_stage; i < cascade->count; i++ ) { - __m128d stage_sum = _mm_setzero_pd(); + __m128d vstage_sum = _mm_setzero_pd(); if( cascade->stage_classifier[i].two_rects ) { for( j = 0; j < cascade->stage_classifier[i].count; j++ ) @@ -1040,7 +1041,7 @@ cvRunHaarClassifierCascadeSum( const CvHaarClassifierCascade* _cascade, __m128d sum = _mm_set_sd(calc_sum(node->feature.rect[0],p_offset) * node->feature.rect[0].weight + calc_sum(node->feature.rect[1],p_offset) * node->feature.rect[1].weight); t = _mm_cmpgt_sd(t, sum); - stage_sum = _mm_add_sd(stage_sum, _mm_blendv_pd(b, a, t)); + vstage_sum = _mm_add_sd(vstage_sum, _mm_blendv_pd(b, a, t)); } } else @@ -1060,11 +1061,11 @@ cvRunHaarClassifierCascadeSum( const CvHaarClassifierCascade* _cascade, __m128d sum = _mm_set_sd(_sum); t = _mm_cmpgt_sd(t, sum); - stage_sum = _mm_add_sd(stage_sum, _mm_blendv_pd(b, a, t)); + vstage_sum = _mm_add_sd(vstage_sum, _mm_blendv_pd(b, a, t)); } } __m128d i_threshold = _mm_set1_pd(cascade->stage_classifier[i].threshold); - if( _mm_comilt_sd(stage_sum, i_threshold) ) + if( _mm_comilt_sd(vstage_sum, i_threshold) ) return -i; } } diff --git a/modules/python/src2/cv2.cv.hpp b/modules/python/src2/cv2.cv.hpp index 3379a14e44..0207189a1a 100644 --- a/modules/python/src2/cv2.cv.hpp +++ b/modules/python/src2/cv2.cv.hpp @@ -256,7 +256,7 @@ static PyObject *iplimage_tostring(PyObject *self, PyObject *args) cv::Mat img(i); size_t esz = img.elemSize(); int nrows = img.rows, ncols = img.cols; - + if( !img.isContinuous() ) img = img.clone(); return PyString_FromStringAndSize((char*)img.data, (Py_ssize_t)(esz*nrows*ncols)); @@ -338,7 +338,7 @@ static void cvmat_dealloc(PyObject *self) { cvmat_t *pc = (cvmat_t*)self; Py_XDECREF(pc->data); - //cvDecRefData(pc->a); + //cvDecRefData(pc->a); cvFree(&pc->a); PyObject_Del(self); } @@ -656,7 +656,7 @@ static void cvmatnd_dealloc(PyObject *self) { cvmatnd_t *pc = (cvmatnd_t*)self; Py_XDECREF(pc->data); - cvDecRefData(pc->a); + cvDecRefData(pc->a); cvFree(&pc->a); PyObject_Del(self); } @@ -1186,7 +1186,7 @@ static PyObject* cvseq_map_getitem(PyObject *o, PyObject *item) } } -static +static PySequenceMethods cvseq_sequence = { cvseq_seq_length, NULL, @@ -1606,8 +1606,8 @@ static int convert_to_CvMat(PyObject *o, CvMat **dst, const char *name) *dst = m->a; return 1; } else if (m->data && m->a->data.ptr){ - *dst = m->a; - return 1; + *dst = m->a; + return 1; } else { return failmsg("CvMat argument '%s' has no data", name); @@ -2174,7 +2174,7 @@ static int convert_to_CvSubdiv2DEdge(PyObject *o, CvSubdiv2DEdge *dst, const cha static PyObject *pythonize_CvMat(cvmat_t *m) { - // Need to make this CvMat look like any other, with a Python + // Need to make this CvMat look like any other, with a Python // buffer object as its data. CvMat *mat = m->a; assert(mat->step != 0); @@ -2204,9 +2204,9 @@ static PyObject *pythonize_CvMat(cvmat_t *m) static PyObject *pythonize_IplImage(iplimage_t *cva) { - // Need to make this iplimage look like any other, with a Python + // Need to make this iplimage look like any other, with a Python // string as its data. - // So copy the image data into a Python string object, then release + // So copy the image data into a Python string object, then release // it. IplImage *ipl = (IplImage*)(cva->a); @@ -2233,7 +2233,7 @@ static PyObject *pythonize_IplImage(iplimage_t *cva) static PyObject *pythonize_CvMatND(cvmatnd_t *m, PyObject *backing = NULL) { // - // Need to make this CvMatND look like any other, with a Python + // Need to make this CvMatND look like any other, with a Python // buffer object as its data. // @@ -2341,9 +2341,9 @@ static PyObject *FROM_CvSeqOfCvConvexityDefectPTR(CvSeqOfCvConvexityDefect *r) for (int i = 0; i < r->total; i++) { CvConvexityDefect *pd = CV_GET_SEQ_ELEM(CvConvexityDefect, r, i); PyList_SetItem(pr, i, Py_BuildValue("(ii)(ii)(ii)f", - pd->start->x, pd->start->y, - pd->end->x, pd->end->y, - pd->depth_point->x, pd->depth_point->y, + pd->start->x, pd->start->y, + pd->end->x, pd->end->y, + pd->depth_point->x, pd->depth_point->y, pd->depth)); } // This function has copied the CvSeq data into a list. Hence the @@ -2360,8 +2360,8 @@ static PyObject *FROM_CvSeqOfCvAvgCompPTR(CvSeqOfCvAvgComp *r) for (int i = 0; i < r->total; i++) { CvAvgComp *pd = CV_GET_SEQ_ELEM(CvAvgComp, r, i); PyList_SetItem(pr, i, Py_BuildValue("(iiii)i", - pd->rect.x, pd->rect.y, - pd->rect.width, pd->rect.height, + pd->rect.x, pd->rect.y, + pd->rect.width, pd->rect.height, pd->neighbors)); } // This function has copied the CvSeq data into a list. Hence the @@ -2378,7 +2378,7 @@ static PyObject *FROM_CvSeqOfCvStarKeypointPTR(CvSeqOfCvStarKeypoint *r) for (int i = 0; i < r->total; i++) { CvStarKeypoint *pd = CV_GET_SEQ_ELEM(CvStarKeypoint, r, i); PyList_SetItem(pr, i, Py_BuildValue("(ii)if", - pd->pt.x, pd->pt.y, + pd->pt.x, pd->pt.y, pd->size, pd->response)); } @@ -2396,7 +2396,7 @@ static PyObject *FROM_CvSeqOfCvSURFPointPTR(CvSeqOfCvSURFPoint *r) for (int i = 0; i < r->total; i++) { CvSURFPoint *pd = CV_GET_SEQ_ELEM(CvSURFPoint, r, i); PyList_SetItem(pr, i, Py_BuildValue("(ff)iiff", - pd->pt.x, pd->pt.y, + pd->pt.x, pd->pt.y, pd->laplacian, pd->size, pd->dir, @@ -2587,7 +2587,7 @@ static PyObject *FROM_CvPoints(CvPoints src) /************************************************************************/ -/* A few functions are too odd to be generated, +/* A few functions are too odd to be generated, * so are handwritten here */ static PyObject *pycvWaitKey(PyObject *self, PyObject *args, PyObject *kw) @@ -2808,7 +2808,7 @@ static PyObject *fromarray(PyObject *o, int allowND) else if (pai->itemsize == 8) type = CV_64FC1; break; - + } if (type == -1) { PyErr_SetString(PyExc_TypeError, "the array type is not supported by OpenCV"); @@ -2826,20 +2826,20 @@ static PyObject *fromarray(PyObject *o, int allowND) m->a->step = (int)pai->strides[0]; } else if (pai->nd == 3) { if (pai->shape[2] > CV_CN_MAX) { - Py_DECREF(ao); + Py_DECREF(ao); return failmsg("cv.fromarray too many channels, see allowND argument"), (PyObject*)0; } ERRWRAP(m->a = cvCreateMatHeader((int)pai->shape[0], (int)pai->shape[1], type + ((int)(pai->shape[2] - 1) << CV_CN_SHIFT))); m->a->step = (int)pai->strides[0]; } else { - Py_DECREF(ao); + Py_DECREF(ao); return failmsg("cv.fromarray array can be 2D or 3D only, see allowND argument"), (PyObject*)0; } m->a->data.ptr = (uchar*)pai->data; //retval = pythonize_foreign_CvMat(m); - m->data = o; - m->offset = 0; - retval = (PyObject*)m; + m->data = o; + m->offset = 0; + retval = (PyObject*)m; } else { int dims[CV_MAX_DIM]; int i; @@ -2848,13 +2848,13 @@ static PyObject *fromarray(PyObject *o, int allowND) cvmatnd_t *m = PyObject_NEW(cvmatnd_t, &cvmatnd_Type); ERRWRAP(m->a = cvCreateMatNDHeader(pai->nd, dims, type)); m->a->data.ptr = (uchar*)pai->data; - m->data = o; - m->offset = 0; - retval = (PyObject*)m; + m->data = o; + m->offset = 0; + retval = (PyObject*)m; //retval = pythonize_CvMatND(m, ao); } Py_DECREF(ao); - Py_INCREF(o); + Py_INCREF(o); return retval; } #endif @@ -2875,7 +2875,7 @@ public: rr = new float*[len]; for (Py_ssize_t i = 0; i < len; i++) { PyObject *item = PySequence_Fast_GET_ITEM(fi, i); - floats ff; + floats ff; ff.f = 0; if (!convert_to_floats(item, &ff)) return 0; rr[i] = ff.f; @@ -4040,13 +4040,13 @@ static PyObject* init_cv() PUBLISH(GC_INIT_WITH_RECT); PUBLISH(GC_INIT_WITH_MASK); PUBLISH(GC_EVAL); - + #include "generated2.i" #undef PUBLISH #undef PUBLISHU -#undef PUBLISH2 - +#undef PUBLISH2 + return m; } From d3501f1545f7769fa63a1df12226164b22dd69a9 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Wed, 5 Sep 2012 16:25:14 +0400 Subject: [PATCH 09/97] Android toolchain: restored explicit include_directories of system /usr/include It is needed because of explicit STL include paths. Otherwise some standard headers (like fenv.h) can not not be correctly found by the compiler. --- android/android.toolchain.cmake | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/android/android.toolchain.cmake b/android/android.toolchain.cmake index b8dd429e2c..c1a9d7d9b2 100644 --- a/android/android.toolchain.cmake +++ b/android/android.toolchain.cmake @@ -1175,12 +1175,9 @@ if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES ) endif() # global includes and link directories -if( ANDROID_STL_INCLUDE_DIRS ) - include_directories( SYSTEM ${ANDROID_STL_INCLUDE_DIRS} ) -endif() +include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_STL_INCLUDE_DIRS} ) link_directories( "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ) - # set these global flags for cmake client scripts to change behavior set( ANDROID True ) set( BUILD_ANDROID True ) From 932100c5a374f8681db9a18c9d498178169d8e12 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 5 Sep 2012 17:06:07 +0400 Subject: [PATCH 10/97] fixed build errors and warnings on Windows --- modules/imgproc/test/test_imgwarp_strict.cpp | 27 ++++++++++---------- modules/video/src/simpleflow.cpp | 10 ++++---- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/modules/imgproc/test/test_imgwarp_strict.cpp b/modules/imgproc/test/test_imgwarp_strict.cpp index 22c1eff61c..9209c6973d 100644 --- a/modules/imgproc/test/test_imgwarp_strict.cpp +++ b/modules/imgproc/test/test_imgwarp_strict.cpp @@ -176,8 +176,8 @@ interpolate_method inter_array[] = { &interpolateLinear, &interpolateCubic, &int Size CV_ImageWarpBaseTest::randSize(RNG& rng) const { Size size; - size.width = saturate_cast(std::exp(rng.uniform(0.0f, 7.0f))); - size.height = saturate_cast(std::exp(rng.uniform(0.0f, 7.0f))); + size.width = saturate_cast(std::exp(rng.uniform(0.0, 7.0))); + size.height = saturate_cast(std::exp(rng.uniform(0.0, 7.0))); return size; } @@ -347,7 +347,7 @@ void CV_Resize_Test::run_reference_func() double CV_Resize_Test::getWeight(double a, double b, int x) { - float w = std::min(x + 1, b) - std::max(x, a); + float w = std::min(x + 1., b) - std::max(x + 0., a); CV_Assert(w >= 0); return w; } @@ -435,6 +435,8 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d ofs = 1, ksize = 4; else if (interpolation == INTER_LANCZOS4) ofs = 3, ksize = 8; + cv::AutoBuffer _w(ksize); + float* w = _w; Mat _extended_src_row(1, _src.cols + ksize * 2, _src.type()); uchar* srow = _src.data + dy * _src.step; @@ -453,7 +455,6 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d float *xyD = yD + dx * cn; const float* xyS = _extended_src_row.ptr(0) + (isx + ksize - ofs) * cn; - float w[ksize]; inter_func(fsx, w); for (int r = 0; r < cn; ++r) @@ -706,7 +707,7 @@ void CV_Remap_Test::generate_test_data() { MatIterator_ begin_y = mapy.begin(), end_y = mapy.end(); for ( ; begin_y != end_y; ++begin_y) - begin_y[0] = rng.uniform(0, 1024); + begin_y[0] = (ushort)rng.uniform(0, 1024); } break; @@ -714,7 +715,7 @@ void CV_Remap_Test::generate_test_data() { MatIterator_ begin_y = mapy.begin(), end_y = mapy.end(); for ( ; begin_y != end_y; ++begin_y) - begin_y[0] = rng.uniform(0, 1024); + begin_y[0] = (short)rng.uniform(0, 1024); } break; } @@ -862,7 +863,7 @@ void CV_Remap_Test::remap_nearest(const Mat& _src, Mat& _dst) { if (borderType == BORDER_CONSTANT) for (int r = 0; r < cn; ++r) - xyD[r] = borderValue[r]; + xyD[r] = (float)borderValue[r]; else { sx = borderInterpolate(sx, ssize.width, borderType); @@ -936,7 +937,7 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) (isx >= ssize.width || isx + ksize <= 0 || isy >= ssize.height || isy + ksize <= 0)) for (int r = 0; r < cn; ++r) - xyD[r] = borderValue[r]; + xyD[r] = (float)borderValue[r]; else { int ar_x[8], ar_y[8]; @@ -1244,11 +1245,11 @@ void CV_WarpPerspective_Test::generate_test_data() // generating the M 3x3 matrix RNG& rng = ts->get_rng(); - Point2f sp[] = { Point2f(0, 0), Point2f(src.cols, 0), Point2f(0, src.rows), Point2f(src.cols, src.rows) }; - Point2f dp[] = { Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), - Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), - Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), - Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)) }; + Point2f sp[] = { Point(0, 0), Point(src.cols, 0), Point(0, src.rows), Point(src.cols, src.rows) }; + Point2f dp[] = { Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), + Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), + Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), + Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)) }; M = getPerspectiveTransform(sp, dp); static const int depths[] = { CV_32F, CV_64F }; diff --git a/modules/video/src/simpleflow.cpp b/modules/video/src/simpleflow.cpp index af99fc5f25..90c883f1f6 100644 --- a/modules/video/src/simpleflow.cpp +++ b/modules/video/src/simpleflow.cpp @@ -216,10 +216,10 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev_extended, // TODO: do smth with this creepy staff Vec2f flow_at_point = flow.at(r0, c0); - int u0 = floor(flow_at_point[0] + 0.5); + int u0 = cvRound(flow_at_point[0]); if (r0 + u0 < 0) { u0 = -r0; } if (r0 + u0 >= rows) { u0 = rows - 1 - r0; } - int v0 = floor(flow_at_point[1] + 0.5); + int v0 = cvRound(flow_at_point[1]); if (c0 + v0 < 0) { v0 = -c0; } if (c0 + v0 >= cols) { v0 = cols - 1 - c0; } @@ -228,7 +228,7 @@ static void calcOpticalFlowSingleScaleSF(const Mat& prev_extended, const int left_col_shift = -min(c0 + v0, max_flow); const int right_col_shift = min(cols - 1 - (c0 + v0), max_flow); - float min_cost = DBL_MAX, best_u = u0, best_v = v0; + float min_cost = FLT_MAX, best_u = (float)u0, best_v = (float)v0; wc(prev_extended, weight_window, r0 + averaging_radius, c0 + averaging_radius, averaging_radius, averaging_radius, averaging_radius, averaging_radius, sigma_color); @@ -562,7 +562,7 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, selectPointsToRecalcFlow(flow, averaging_radius, - speed_up_thr, + (float)speed_up_thr, curr_rows, curr_cols, speed_up, @@ -571,7 +571,7 @@ CV_EXPORTS_W void calcOpticalFlowSF(Mat& from, selectPointsToRecalcFlow(flow_inv, averaging_radius, - speed_up_thr, + (float)speed_up_thr, curr_rows, curr_cols, speed_up_inv, From 907a9101eb7aca65e5f096cde387df65be8b271d Mon Sep 17 00:00:00 2001 From: Vsevolod Glumov Date: Wed, 5 Sep 2012 18:13:54 +0400 Subject: [PATCH 11/97] Another batch of fixed 'undocumented parameter' warnings in 'operations_on_arrays.rst'. --- modules/core/doc/operations_on_arrays.rst | 144 +++++++++++++++++++--- 1 file changed, 128 insertions(+), 16 deletions(-) diff --git a/modules/core/doc/operations_on_arrays.rst b/modules/core/doc/operations_on_arrays.rst index 9e9ecaf1e0..b5ae049d60 100644 --- a/modules/core/doc/operations_on_arrays.rst +++ b/modules/core/doc/operations_on_arrays.rst @@ -545,7 +545,7 @@ Performs the per-element comparison of two arrays or an array and scalar value. * **CMP_LE** ``src1`` is less than or equal to ``src2``. * **CMP_NE** ``src1`` is unequal to ``src2``. - :param cmp_op: a flag, that specifies correspondence between the arrays (specific to C syntax; for possible values see 'cmpop above). + :param cmp_op: a flag, that specifies correspondence between the arrays (specific to C syntax; for possible values see 'cmpop' above). The function compares: @@ -1004,7 +1004,7 @@ All of the above improvements have been implemented in :ocv:func:`matchTemplate` divide ----------- +------ Performs per-element division of two arrays or a scalar by an array. .. ocv:function:: void divide(InputArray src1, InputArray src2, OutputArray dst, double scale=1, int dtype=-1) @@ -1066,6 +1066,8 @@ Returns the determinant of a square floating-point matrix. :param mtx: input matrix that must have ``CV_32FC1`` or ``CV_64FC1`` type and square size. + :param mat: input matrix (C-specific; see 'mtx' for details). + The function ``determinant`` calculates and returns the determinant of the specified matrix. For small matrices ( ``mtx.cols=mtx.rows<=3`` ), the direct method is used. For larger matrices, the function uses LU factorization with partial pivoting. @@ -1097,10 +1099,16 @@ Calculates eigenvalues and eigenvectors of a symmetric matrix. :param src: input matrix that must have ``CV_32FC1`` or ``CV_64FC1`` type, square size and be symmetrical (``src`` :sup:`T` == ``src``). + :param mat: input matrix (see 'src' above for details). + :param eigenvalues: output vector of eigenvalues of the same type as ``src``; the eigenvalues are stored in the descending order. + :param evals: output vector of eigenvalues (see 'eigenvalies' above for details). + :param eigenvectors: output matrix of eigenvectors; it has the same size and type as ``src``; the eigenvectors are stored as subsequent matrix rows, in the same order as the corresponding eigenvalues. + :param evects: output matrix of eigenvectors (see 'eigenvectors' above for details). + :param lowindex: optional index of largest eigenvalue/-vector to calculate; the parameter is ignored in the current implementation. :param highindex: optional index of smallest eigenvalue/-vector to calculate; the parameter is ignored in the current implementation. @@ -1196,7 +1204,7 @@ To insert a channel to a new-style matrix, use flip --------- +---- Flips a 2D array around vertical, horizontal, or both axes. .. ocv:function:: void flip(InputArray src, OutputArray dst, int flipCode) @@ -1211,7 +1219,9 @@ Flips a 2D array around vertical, horizontal, or both axes. :param dst: output array of the same size and type as ``src``. - :param flipCode: a flag to specify how to flip the array; 0 means flipping around the x-axis and positive value (for example, 1) means flipping around y-axis. Negative value (for example, -1) means flipping around both axes (see the discussion below for the formulas). + :param flipCode: a flag, that specifies the mode of the flip; 0 means flipping around the x-axis and positive value (for example, 1) means flipping around y-axis. Negative value (for example, -1) means flipping around both axes (see the discussion below for the formulas). + + :param flip_mode: a flag, that specifies the mode of the flip (see 'flipCode' above for details); The function ``flip`` flips the array in one of three different ways (row and column indices are 0-based): @@ -1273,6 +1283,8 @@ Performs generalized matrix multiplication. * **GEMM_2_T** transposes ``src2``. * **GEMM_3_T** transposes ``src3``. + :param tABC: operation flags (see 'flags' above for details). + The function performs generalized matrix multiplication similar to the ``gemm`` functions in BLAS level 3. For example, ``gemm(src1, src2, alpha, src3, beta, dst, GEMM_1_T + GEMM_3_T)`` corresponds to .. math:: @@ -1335,6 +1347,8 @@ Returns the optimal DFT size for a given vector size. :param vecsize: vector size. + :param size0: vector size (see 'vecsize' above for details). + DFT performance is not a monotonic function of a vector size. Therefore, when you calculate convolution of two arrays or perform the spectral analysis of an array, it usually makes sense to pad the input data with zeros to get a bit larger array that can be transformed much faster than the original one. Arrays whose size is a power-of-two (2, 4, 8, 16, 32, ...) are the fastest to process. Though, the arrays whose size is a product of 2's, 3's, and 5's (for example, 300 = 5*5*3*2*2) are also processed quite efficiently. @@ -1422,8 +1436,12 @@ Checks if array elements lie between the elements of two other arrays. :param lowerb: inclusive lower boundary array or a scalar. + :param lower: inclusive lower boundary array or a scalar (see 'lowerb' above). + :param upperb: inclusive upper boundary array or a scalar. + :param upper: inclusive upper boundary array or a scalar (see 'upperb' above). + :param dst: output array of the same size as ``src`` and ``CV_8U`` type. The function checks the range as follows: @@ -1471,6 +1489,8 @@ Finds the inverse or pseudo-inverse of a matrix. * **DECOMP_CHOLESKY** Cholesky decomposition; the matrix must be symmetrical and positively defined. + :param method: inversion method (see 'flags' above for possible values). + The function ``invert`` inverts the matrix ``src`` and stores the result in ``dst`` . When the matrix ``src`` is singular or non-square, the function calculates the pseudo-inverse matrix (the ``dst`` matrix) so that ``norm(src*dst - I)`` is minimal, where I is an identity matrix. @@ -1602,10 +1622,16 @@ Calculates the Mahalanobis distance between two vectors. :param vec1: first 1D input vector. + :param v1: first 1D input vector (see 'vec1' above). + :param vec2: second 1D input vector. + :param v2: first 1D input vector (see 'vec2' above). + :param icovar: inverse covariance matrix. + :param mat: inverse covariance matrix (see 'icovar' above). + The function ``Mahalanobis`` calculates and returns the weighted distance between two vectors: .. math:: @@ -1643,7 +1669,9 @@ Calculates per-element maximum of two arrays or an array and a scalar. :param src1: first input array. - :param src2: second input array of the same size and type as ``src1`` . + :param src2: second input array of the same size and type as ``src1``. + + :param src: single input array. :param value: real scalar value. @@ -1687,7 +1715,9 @@ Calculates an average (mean) of array elements. .. ocv:pyoldfunction:: cv.Avg(arr, mask=None) -> scalar - :param src: input array that should have from 1 to 4 channels so that the result can be stored in :ocv:class:`Scalar_` . + :param src: input array that should have from 1 to 4 channels so that the result can be stored in :ocv:class:`Scalar_`. + + :param arr: input array (see 'src' above). :param mask: optional operation mask. @@ -1722,9 +1752,13 @@ Calculates a mean and standard deviation of array elements. :param src: input array that should have from 1 to 4 channels so that the results can be stored in :ocv:class:`Scalar_` 's. - :param mean: output parameter: calculated mean value. + :param arr: input array (see 'src' above). - :param stddev: output parameter: calculateded standard deviation. + :param mean: calculated mean value. + + :param stddev: calculateded standard deviation. + + :param std_dev: calculateded standard deviation (see 'stddev' above). :param mask: optional operation mask. @@ -1763,6 +1797,14 @@ Creates one multichannel array out of several single-channel ones. :param mv: input array or vector of matrices to be merged; all the matrices in ``mv`` must have the same size and the same depth. + :param src0: first input array or vector of matrix to be merged. + + :param src1: second input array or vector of matrix to be merged. + + :param src2: third input array or vector of matrix to be merged. + + :param src3: fourth input array or vector of matrix to be merged. + :param count: number of input matrices when ``mv`` is a plain C array; it must be greater than zero. :param dst: output array of the same size and the same depth as ``mv[0]``; The number of channels will be the total number of channels in the matrix array. @@ -1808,6 +1850,8 @@ Calculates per-element minimum of two arrays or an array and a scalar. :param src2: second input array of the same size and type as ``src1``. + :param src: single input array. + :param value: real scalar value. :param dst: output array of the same size and type as ``src1``. @@ -1886,14 +1930,24 @@ Finds the global minimum and maximum in an array. :param src: input single-channel array. + :param arr: input single-channel array (see 'src' above). + :param minVal: pointer to the returned minimum value; ``NULL`` is used if not required. + :param min_val: pointer to the returned minimum value (see 'minVal' above). + :param maxVal: pointer to the returned maximum value; ``NULL`` is used if not required. + :param max_val: pointer to the returned minimum value (see 'maxVal' above). + :param minLoc: pointer to the returned minimum location (in 2D case); ``NULL`` is used if not required. + :param min_loc: pointer to the returned minimum value (see 'minLoc' above). + :param maxLoc: pointer to the returned maximum location (in 2D case); ``NULL`` is used if not required. + :param max_loc: pointer to the returned minimum value (see 'maxLoc' above). + :param mask: optional mask used to select a sub-array. The functions ``minMaxLoc`` find the minimum and maximum element values and their positions. The extremums are searched across the whole array or, @@ -1936,14 +1990,22 @@ Copies specified channels from input arrays to the specified channels of output :param nsrcs: number of matrices in ``src``. + :param src_count: number of matrices in ``src`` (see 'nsrcs' above). + :param dst: output array or vector of matrices; all the matrices *must be allocated*; their size and depth must be the same as in ``src[0]``. :param ndsts: number of matrices in ``dst``. + :param dst_count: number of matrices in ``dst`` (see 'ndsts' above). + :param fromTo: array of index pairs specifying which channels are copied and where; ``fromTo[k*2]`` is a 0-based index of the input channel in ``src``, ``fromTo[k*2+1]`` is an index of the output channel in ``dst``; the continuous channel numbering is used: the first input image channels are indexed from ``0`` to ``src[0].channels()-1``, the second input image channels are indexed from ``src[0].channels()`` to ``src[0].channels() + src[1].channels()-1``, and so on, the same scheme is used for the output image channels; as a special case, when ``fromTo[k*2]`` is negative, the corresponding output channel is filled with zero . + :param from_to: array of index pairs specifying which channels are copied and where (see 'fromTo' above for details). + :param npairs: number of index pairs in ``fromTo``. + :param pair_count: number of index pairs in ``fromTo`` (see 'npairs' above). + The functions ``mixChannels`` provide an advanced mechanism for shuffling image channels. :ocv:func:`split` and @@ -1988,9 +2050,15 @@ Performs the per-element multiplication of two Fourier spectrums. :param src1: first input array. - :param src2: second input array of the same size and type as ``src1`` . + :param a: first input array (see 'src1' above). - :param dst: output array of the same size and type as ``src1`` . + :param src2: second input array of the same size and type as ``src1``. + + :param b: second input array (see 'src2' above). + + :param dst: output array of the same size and type as ``src1``. + + :param c: output array (see 'dst' above). :param flags: operation flags; currently, the only supported flag is ``DFT_ROWS``, which indicates that each row of ``src1`` and ``src2`` is an independent 1D Fourier spectrum. @@ -2024,6 +2092,8 @@ Calculates the per-element scaled product of two arrays. :param scale: optional scale factor. + :param dtype: Optional type of the output matrix. + The function ``multiply`` calculates the per-element product of two arrays: .. math:: @@ -2070,13 +2140,15 @@ Calculates the product of a matrix and its transposition. :param dst: output square matrix. - :param aTa: Flag specifying the multiplication ordering. See the description below. + :param aTa: a flag, specifying the multiplication ordering (see the description below). + + :param order: a flag, specifying the multiplication ordering (see 'aTa' above). :param delta: Optional delta matrix subtracted from ``src`` before the multiplication. When the matrix is empty ( ``delta=noArray()`` ), it is assumed to be zero, that is, nothing is subtracted. If it has the same size as ``src`` , it is simply subtracted. Otherwise, it is "repeated" (see :ocv:func:`repeat` ) to cover the full ``src`` and then subtracted. Type of the delta matrix, when it is not empty, must be the same as the type of created output matrix. See the ``dtype`` parameter description below. :param scale: Optional scale factor for the matrix product. - :param dtype: Optional type of the output matrix. When it is negative, the output matrix will have the same type as ``src`` . Otherwise, it will be ``type=CV_MAT_DEPTH(dtype)`` that should be either ``CV_32F`` or ``CV_64F`` . + :param dtype: Optional type of the output matrix. When it is negative, the output matrix will have the same type as ``src`` . Otherwise, it will be ``type=CV_MAT_DEPTH(dtype)`` that should be either ``CV_32F`` or ``CV_64F``. The function ``mulTransposed`` calculates the product of ``src`` and its transposition: @@ -2120,10 +2192,18 @@ Calculates an absolute array norm, an absolute difference norm, or a relative di :param src1: first input array. + :param: arr1: first input array (see 'src1' above). + :param src2: second input array of the same size and the same type as ``src1``. + :param: arr2: second input array (see 'src2' above). + + :param src: single input array. + :param normType: type of the norm (see the details below). + :param norm_type: type of the norm (see 'normType' above). + :param mask: optional operation mask; it must have the same size as ``src1`` and ``CV_8UC1`` type. The functions ``norm`` calculate an absolute norm of ``src1`` (when there is no ``src2`` ): @@ -2178,6 +2258,8 @@ Normalizes the norm or value range of an array. :param normType: normalization type (see the details below). + :param norm_type: normalization type (see 'normType' above). + :param dtype: when negative, the output array has the same type as ``src``; otherwise, it has the same number of channels as ``src`` and the depth ``=CV_MAT_DEPTH(dtype)``. :param mask: optional operation mask. @@ -2267,7 +2349,7 @@ The sample below is the function that takes two matrices. The first function sto PCA::PCA ------------- +-------- PCA constructors .. ocv:function:: PCA::PCA() @@ -2379,6 +2461,8 @@ Performs the perspective matrix transformation of vectors. :param m: ``3x3`` or ``4x4`` floating-point transformation matrix. + :param mat: ``3x3`` or ``4x4`` floating-point transformation matrix (see 'm' above). + The function ``perspectiveTransform`` transforms every element of ``src`` by treating it as a 2D or 3D vector, in the following way: .. math:: @@ -2457,6 +2541,8 @@ Calculates x and y coordinates of 2D vectors from their magnitude and angle. :param angleInDegrees: when true, the input angles are measured in degrees, otherwise, they are measured in radians. + :param angles_in_degrees: when true, the input angles are measured in degrees, otherwise, they are measured in radians (see 'angleInDegrees' above). + The function ``polarToCart`` calculates the Cartesian coordinates of each 2D vector represented by the corresponding elements of ``magnitude`` and ``angle`` : .. math:: @@ -2876,7 +2962,11 @@ Initializes a scaled identity matrix. :param mtx: matrix to initialize (not necessarily square). - :param value: value to assign to diagonal elements. + :param mat: matrix to initialize (see 'mtx' above). + + :param value: scalar value to assign to diagonal elements. + + :param s: scalar value to assign to diagonal elements (see 'value' above). The function :ocv:func:`setIdentity` initializes a scaled identity matrix: @@ -2930,7 +3020,9 @@ Solves one or more linear systems or least-squares problems. * **DECOMP_QR** QR factorization; the system can be over-defined and/or the matrix ``src1`` can be singular. - * **DECOMP_NORMAL** while all the previous flags are mutually exclusive, this flag can be used together with any of the previous; it means that the normal equations :math:`\texttt{src1}^T\cdot\texttt{src1}\cdot\texttt{dst}=\texttt{src1}^T\texttt{src2}` are solved instead of the original system :math:`\texttt{src1}\cdot\texttt{dst}=\texttt{src2}` . + * **DECOMP_NORMAL** while all the previous flags are mutually exclusive, this flag can be used together with any of the previous; it means that the normal equations :math:`\texttt{src1}^T\cdot\texttt{src1}\cdot\texttt{dst}=\texttt{src1}^T\texttt{src2}` are solved instead of the original system :math:`\texttt{src1}\cdot\texttt{dst}=\texttt{src2}`. + + :param method: solution (matrix inversion) method. The function ``solve`` solves a linear system or least-squares problem (the latter is possible with SVD or QR methods, or by specifying the flag ``DECOMP_NORMAL`` ): @@ -2952,7 +3044,7 @@ If ``DECOMP_LU`` or ``DECOMP_CHOLESKY`` method is used, the function returns 1 i solveCubic --------------- +---------- Finds the real roots of a cubic equation. .. ocv:function:: int solveCubic( InputArray coeffs, OutputArray roots ) @@ -3092,8 +3184,20 @@ Divides a multi-channel array into several single-channel arrays. :param src: input multi-channel array. + :param m: input multi-channel array (see 'src' above). + + :param dst0: first output array. + + :param dst1: second output array. + + :param dst2: third output array. + + :param dst3: fourth output array. + :param mv: output array or vector of arrays; in the first variant of the function the number of arrays must match ``src.channels()``; the arrays themselves are reallocated, if needed. + :param mvbegin: output array or vector of arrays (see 'mv' above for details). + The functions ``split`` split a multi-channel array into separate single-channel arrays: .. math:: @@ -3155,6 +3259,8 @@ Calculates the per-element difference between two arrays or array and a scalar. :param src2: second input array or a scalar. + :param src: single input array. + :param dst: output array of the same size and the same number of channels as the input array. :param mask: optional operation mask; this is an 8-bit single channel array that specifies elements of the output array to be changed. @@ -3373,6 +3479,8 @@ Calculates the sum of array elements. :param arr: input array that must have from 1 to 4 channels. + :param src: input array that must have from 1 to 4 channels (see 'arr' above). + The functions ``sum`` calculate and return the sum of array elements, independently for each channel. .. seealso:: @@ -3418,6 +3526,8 @@ Returns the trace of a matrix. :param mat: input matrix. + :param mtx: input matrix (see 'mat' above). + The function ``trace`` returns the sum of the diagonal elements of the matrix ``mtx`` . .. math:: @@ -3444,6 +3554,8 @@ Performs the matrix transformation of every array element. :param m: transformation ``2x2`` or ``2x3`` floating-point matrix. + :param transmat: transformation ``2x2`` or ``2x3`` floating-point matrix (see 'm' above). + :param shiftvec: optional translation vector (when ``m`` is ``2x2``) The function ``transform`` performs the matrix transformation of every element of the array ``src`` and stores the results in ``dst`` : From 3d072dce02aaa4efe55df215e8746bd252d3ce74 Mon Sep 17 00:00:00 2001 From: Vsevolod Glumov Date: Thu, 6 Sep 2012 10:38:03 +0400 Subject: [PATCH 12/97] Revert "Another batch of fixed 'undocumented parameter' warnings in 'operations_on_arrays.rst'." This reverts commit 907a9101eb7aca65e5f096cde387df65be8b271d. --- modules/core/doc/operations_on_arrays.rst | 144 +++------------------- 1 file changed, 16 insertions(+), 128 deletions(-) diff --git a/modules/core/doc/operations_on_arrays.rst b/modules/core/doc/operations_on_arrays.rst index b5ae049d60..9e9ecaf1e0 100644 --- a/modules/core/doc/operations_on_arrays.rst +++ b/modules/core/doc/operations_on_arrays.rst @@ -545,7 +545,7 @@ Performs the per-element comparison of two arrays or an array and scalar value. * **CMP_LE** ``src1`` is less than or equal to ``src2``. * **CMP_NE** ``src1`` is unequal to ``src2``. - :param cmp_op: a flag, that specifies correspondence between the arrays (specific to C syntax; for possible values see 'cmpop' above). + :param cmp_op: a flag, that specifies correspondence between the arrays (specific to C syntax; for possible values see 'cmpop above). The function compares: @@ -1004,7 +1004,7 @@ All of the above improvements have been implemented in :ocv:func:`matchTemplate` divide ------- +---------- Performs per-element division of two arrays or a scalar by an array. .. ocv:function:: void divide(InputArray src1, InputArray src2, OutputArray dst, double scale=1, int dtype=-1) @@ -1066,8 +1066,6 @@ Returns the determinant of a square floating-point matrix. :param mtx: input matrix that must have ``CV_32FC1`` or ``CV_64FC1`` type and square size. - :param mat: input matrix (C-specific; see 'mtx' for details). - The function ``determinant`` calculates and returns the determinant of the specified matrix. For small matrices ( ``mtx.cols=mtx.rows<=3`` ), the direct method is used. For larger matrices, the function uses LU factorization with partial pivoting. @@ -1099,16 +1097,10 @@ Calculates eigenvalues and eigenvectors of a symmetric matrix. :param src: input matrix that must have ``CV_32FC1`` or ``CV_64FC1`` type, square size and be symmetrical (``src`` :sup:`T` == ``src``). - :param mat: input matrix (see 'src' above for details). - :param eigenvalues: output vector of eigenvalues of the same type as ``src``; the eigenvalues are stored in the descending order. - :param evals: output vector of eigenvalues (see 'eigenvalies' above for details). - :param eigenvectors: output matrix of eigenvectors; it has the same size and type as ``src``; the eigenvectors are stored as subsequent matrix rows, in the same order as the corresponding eigenvalues. - :param evects: output matrix of eigenvectors (see 'eigenvectors' above for details). - :param lowindex: optional index of largest eigenvalue/-vector to calculate; the parameter is ignored in the current implementation. :param highindex: optional index of smallest eigenvalue/-vector to calculate; the parameter is ignored in the current implementation. @@ -1204,7 +1196,7 @@ To insert a channel to a new-style matrix, use flip ----- +-------- Flips a 2D array around vertical, horizontal, or both axes. .. ocv:function:: void flip(InputArray src, OutputArray dst, int flipCode) @@ -1219,9 +1211,7 @@ Flips a 2D array around vertical, horizontal, or both axes. :param dst: output array of the same size and type as ``src``. - :param flipCode: a flag, that specifies the mode of the flip; 0 means flipping around the x-axis and positive value (for example, 1) means flipping around y-axis. Negative value (for example, -1) means flipping around both axes (see the discussion below for the formulas). - - :param flip_mode: a flag, that specifies the mode of the flip (see 'flipCode' above for details); + :param flipCode: a flag to specify how to flip the array; 0 means flipping around the x-axis and positive value (for example, 1) means flipping around y-axis. Negative value (for example, -1) means flipping around both axes (see the discussion below for the formulas). The function ``flip`` flips the array in one of three different ways (row and column indices are 0-based): @@ -1283,8 +1273,6 @@ Performs generalized matrix multiplication. * **GEMM_2_T** transposes ``src2``. * **GEMM_3_T** transposes ``src3``. - :param tABC: operation flags (see 'flags' above for details). - The function performs generalized matrix multiplication similar to the ``gemm`` functions in BLAS level 3. For example, ``gemm(src1, src2, alpha, src3, beta, dst, GEMM_1_T + GEMM_3_T)`` corresponds to .. math:: @@ -1347,8 +1335,6 @@ Returns the optimal DFT size for a given vector size. :param vecsize: vector size. - :param size0: vector size (see 'vecsize' above for details). - DFT performance is not a monotonic function of a vector size. Therefore, when you calculate convolution of two arrays or perform the spectral analysis of an array, it usually makes sense to pad the input data with zeros to get a bit larger array that can be transformed much faster than the original one. Arrays whose size is a power-of-two (2, 4, 8, 16, 32, ...) are the fastest to process. Though, the arrays whose size is a product of 2's, 3's, and 5's (for example, 300 = 5*5*3*2*2) are also processed quite efficiently. @@ -1436,12 +1422,8 @@ Checks if array elements lie between the elements of two other arrays. :param lowerb: inclusive lower boundary array or a scalar. - :param lower: inclusive lower boundary array or a scalar (see 'lowerb' above). - :param upperb: inclusive upper boundary array or a scalar. - :param upper: inclusive upper boundary array or a scalar (see 'upperb' above). - :param dst: output array of the same size as ``src`` and ``CV_8U`` type. The function checks the range as follows: @@ -1489,8 +1471,6 @@ Finds the inverse or pseudo-inverse of a matrix. * **DECOMP_CHOLESKY** Cholesky decomposition; the matrix must be symmetrical and positively defined. - :param method: inversion method (see 'flags' above for possible values). - The function ``invert`` inverts the matrix ``src`` and stores the result in ``dst`` . When the matrix ``src`` is singular or non-square, the function calculates the pseudo-inverse matrix (the ``dst`` matrix) so that ``norm(src*dst - I)`` is minimal, where I is an identity matrix. @@ -1622,16 +1602,10 @@ Calculates the Mahalanobis distance between two vectors. :param vec1: first 1D input vector. - :param v1: first 1D input vector (see 'vec1' above). - :param vec2: second 1D input vector. - :param v2: first 1D input vector (see 'vec2' above). - :param icovar: inverse covariance matrix. - :param mat: inverse covariance matrix (see 'icovar' above). - The function ``Mahalanobis`` calculates and returns the weighted distance between two vectors: .. math:: @@ -1669,9 +1643,7 @@ Calculates per-element maximum of two arrays or an array and a scalar. :param src1: first input array. - :param src2: second input array of the same size and type as ``src1``. - - :param src: single input array. + :param src2: second input array of the same size and type as ``src1`` . :param value: real scalar value. @@ -1715,9 +1687,7 @@ Calculates an average (mean) of array elements. .. ocv:pyoldfunction:: cv.Avg(arr, mask=None) -> scalar - :param src: input array that should have from 1 to 4 channels so that the result can be stored in :ocv:class:`Scalar_`. - - :param arr: input array (see 'src' above). + :param src: input array that should have from 1 to 4 channels so that the result can be stored in :ocv:class:`Scalar_` . :param mask: optional operation mask. @@ -1752,13 +1722,9 @@ Calculates a mean and standard deviation of array elements. :param src: input array that should have from 1 to 4 channels so that the results can be stored in :ocv:class:`Scalar_` 's. - :param arr: input array (see 'src' above). + :param mean: output parameter: calculated mean value. - :param mean: calculated mean value. - - :param stddev: calculateded standard deviation. - - :param std_dev: calculateded standard deviation (see 'stddev' above). + :param stddev: output parameter: calculateded standard deviation. :param mask: optional operation mask. @@ -1797,14 +1763,6 @@ Creates one multichannel array out of several single-channel ones. :param mv: input array or vector of matrices to be merged; all the matrices in ``mv`` must have the same size and the same depth. - :param src0: first input array or vector of matrix to be merged. - - :param src1: second input array or vector of matrix to be merged. - - :param src2: third input array or vector of matrix to be merged. - - :param src3: fourth input array or vector of matrix to be merged. - :param count: number of input matrices when ``mv`` is a plain C array; it must be greater than zero. :param dst: output array of the same size and the same depth as ``mv[0]``; The number of channels will be the total number of channels in the matrix array. @@ -1850,8 +1808,6 @@ Calculates per-element minimum of two arrays or an array and a scalar. :param src2: second input array of the same size and type as ``src1``. - :param src: single input array. - :param value: real scalar value. :param dst: output array of the same size and type as ``src1``. @@ -1930,24 +1886,14 @@ Finds the global minimum and maximum in an array. :param src: input single-channel array. - :param arr: input single-channel array (see 'src' above). - :param minVal: pointer to the returned minimum value; ``NULL`` is used if not required. - :param min_val: pointer to the returned minimum value (see 'minVal' above). - :param maxVal: pointer to the returned maximum value; ``NULL`` is used if not required. - :param max_val: pointer to the returned minimum value (see 'maxVal' above). - :param minLoc: pointer to the returned minimum location (in 2D case); ``NULL`` is used if not required. - :param min_loc: pointer to the returned minimum value (see 'minLoc' above). - :param maxLoc: pointer to the returned maximum location (in 2D case); ``NULL`` is used if not required. - :param max_loc: pointer to the returned minimum value (see 'maxLoc' above). - :param mask: optional mask used to select a sub-array. The functions ``minMaxLoc`` find the minimum and maximum element values and their positions. The extremums are searched across the whole array or, @@ -1990,22 +1936,14 @@ Copies specified channels from input arrays to the specified channels of output :param nsrcs: number of matrices in ``src``. - :param src_count: number of matrices in ``src`` (see 'nsrcs' above). - :param dst: output array or vector of matrices; all the matrices *must be allocated*; their size and depth must be the same as in ``src[0]``. :param ndsts: number of matrices in ``dst``. - :param dst_count: number of matrices in ``dst`` (see 'ndsts' above). - :param fromTo: array of index pairs specifying which channels are copied and where; ``fromTo[k*2]`` is a 0-based index of the input channel in ``src``, ``fromTo[k*2+1]`` is an index of the output channel in ``dst``; the continuous channel numbering is used: the first input image channels are indexed from ``0`` to ``src[0].channels()-1``, the second input image channels are indexed from ``src[0].channels()`` to ``src[0].channels() + src[1].channels()-1``, and so on, the same scheme is used for the output image channels; as a special case, when ``fromTo[k*2]`` is negative, the corresponding output channel is filled with zero . - :param from_to: array of index pairs specifying which channels are copied and where (see 'fromTo' above for details). - :param npairs: number of index pairs in ``fromTo``. - :param pair_count: number of index pairs in ``fromTo`` (see 'npairs' above). - The functions ``mixChannels`` provide an advanced mechanism for shuffling image channels. :ocv:func:`split` and @@ -2050,15 +1988,9 @@ Performs the per-element multiplication of two Fourier spectrums. :param src1: first input array. - :param a: first input array (see 'src1' above). + :param src2: second input array of the same size and type as ``src1`` . - :param src2: second input array of the same size and type as ``src1``. - - :param b: second input array (see 'src2' above). - - :param dst: output array of the same size and type as ``src1``. - - :param c: output array (see 'dst' above). + :param dst: output array of the same size and type as ``src1`` . :param flags: operation flags; currently, the only supported flag is ``DFT_ROWS``, which indicates that each row of ``src1`` and ``src2`` is an independent 1D Fourier spectrum. @@ -2092,8 +2024,6 @@ Calculates the per-element scaled product of two arrays. :param scale: optional scale factor. - :param dtype: Optional type of the output matrix. - The function ``multiply`` calculates the per-element product of two arrays: .. math:: @@ -2140,15 +2070,13 @@ Calculates the product of a matrix and its transposition. :param dst: output square matrix. - :param aTa: a flag, specifying the multiplication ordering (see the description below). - - :param order: a flag, specifying the multiplication ordering (see 'aTa' above). + :param aTa: Flag specifying the multiplication ordering. See the description below. :param delta: Optional delta matrix subtracted from ``src`` before the multiplication. When the matrix is empty ( ``delta=noArray()`` ), it is assumed to be zero, that is, nothing is subtracted. If it has the same size as ``src`` , it is simply subtracted. Otherwise, it is "repeated" (see :ocv:func:`repeat` ) to cover the full ``src`` and then subtracted. Type of the delta matrix, when it is not empty, must be the same as the type of created output matrix. See the ``dtype`` parameter description below. :param scale: Optional scale factor for the matrix product. - :param dtype: Optional type of the output matrix. When it is negative, the output matrix will have the same type as ``src`` . Otherwise, it will be ``type=CV_MAT_DEPTH(dtype)`` that should be either ``CV_32F`` or ``CV_64F``. + :param dtype: Optional type of the output matrix. When it is negative, the output matrix will have the same type as ``src`` . Otherwise, it will be ``type=CV_MAT_DEPTH(dtype)`` that should be either ``CV_32F`` or ``CV_64F`` . The function ``mulTransposed`` calculates the product of ``src`` and its transposition: @@ -2192,18 +2120,10 @@ Calculates an absolute array norm, an absolute difference norm, or a relative di :param src1: first input array. - :param: arr1: first input array (see 'src1' above). - :param src2: second input array of the same size and the same type as ``src1``. - :param: arr2: second input array (see 'src2' above). - - :param src: single input array. - :param normType: type of the norm (see the details below). - :param norm_type: type of the norm (see 'normType' above). - :param mask: optional operation mask; it must have the same size as ``src1`` and ``CV_8UC1`` type. The functions ``norm`` calculate an absolute norm of ``src1`` (when there is no ``src2`` ): @@ -2258,8 +2178,6 @@ Normalizes the norm or value range of an array. :param normType: normalization type (see the details below). - :param norm_type: normalization type (see 'normType' above). - :param dtype: when negative, the output array has the same type as ``src``; otherwise, it has the same number of channels as ``src`` and the depth ``=CV_MAT_DEPTH(dtype)``. :param mask: optional operation mask. @@ -2349,7 +2267,7 @@ The sample below is the function that takes two matrices. The first function sto PCA::PCA --------- +------------ PCA constructors .. ocv:function:: PCA::PCA() @@ -2461,8 +2379,6 @@ Performs the perspective matrix transformation of vectors. :param m: ``3x3`` or ``4x4`` floating-point transformation matrix. - :param mat: ``3x3`` or ``4x4`` floating-point transformation matrix (see 'm' above). - The function ``perspectiveTransform`` transforms every element of ``src`` by treating it as a 2D or 3D vector, in the following way: .. math:: @@ -2541,8 +2457,6 @@ Calculates x and y coordinates of 2D vectors from their magnitude and angle. :param angleInDegrees: when true, the input angles are measured in degrees, otherwise, they are measured in radians. - :param angles_in_degrees: when true, the input angles are measured in degrees, otherwise, they are measured in radians (see 'angleInDegrees' above). - The function ``polarToCart`` calculates the Cartesian coordinates of each 2D vector represented by the corresponding elements of ``magnitude`` and ``angle`` : .. math:: @@ -2962,11 +2876,7 @@ Initializes a scaled identity matrix. :param mtx: matrix to initialize (not necessarily square). - :param mat: matrix to initialize (see 'mtx' above). - - :param value: scalar value to assign to diagonal elements. - - :param s: scalar value to assign to diagonal elements (see 'value' above). + :param value: value to assign to diagonal elements. The function :ocv:func:`setIdentity` initializes a scaled identity matrix: @@ -3020,9 +2930,7 @@ Solves one or more linear systems or least-squares problems. * **DECOMP_QR** QR factorization; the system can be over-defined and/or the matrix ``src1`` can be singular. - * **DECOMP_NORMAL** while all the previous flags are mutually exclusive, this flag can be used together with any of the previous; it means that the normal equations :math:`\texttt{src1}^T\cdot\texttt{src1}\cdot\texttt{dst}=\texttt{src1}^T\texttt{src2}` are solved instead of the original system :math:`\texttt{src1}\cdot\texttt{dst}=\texttt{src2}`. - - :param method: solution (matrix inversion) method. + * **DECOMP_NORMAL** while all the previous flags are mutually exclusive, this flag can be used together with any of the previous; it means that the normal equations :math:`\texttt{src1}^T\cdot\texttt{src1}\cdot\texttt{dst}=\texttt{src1}^T\texttt{src2}` are solved instead of the original system :math:`\texttt{src1}\cdot\texttt{dst}=\texttt{src2}` . The function ``solve`` solves a linear system or least-squares problem (the latter is possible with SVD or QR methods, or by specifying the flag ``DECOMP_NORMAL`` ): @@ -3044,7 +2952,7 @@ If ``DECOMP_LU`` or ``DECOMP_CHOLESKY`` method is used, the function returns 1 i solveCubic ----------- +-------------- Finds the real roots of a cubic equation. .. ocv:function:: int solveCubic( InputArray coeffs, OutputArray roots ) @@ -3184,20 +3092,8 @@ Divides a multi-channel array into several single-channel arrays. :param src: input multi-channel array. - :param m: input multi-channel array (see 'src' above). - - :param dst0: first output array. - - :param dst1: second output array. - - :param dst2: third output array. - - :param dst3: fourth output array. - :param mv: output array or vector of arrays; in the first variant of the function the number of arrays must match ``src.channels()``; the arrays themselves are reallocated, if needed. - :param mvbegin: output array or vector of arrays (see 'mv' above for details). - The functions ``split`` split a multi-channel array into separate single-channel arrays: .. math:: @@ -3259,8 +3155,6 @@ Calculates the per-element difference between two arrays or array and a scalar. :param src2: second input array or a scalar. - :param src: single input array. - :param dst: output array of the same size and the same number of channels as the input array. :param mask: optional operation mask; this is an 8-bit single channel array that specifies elements of the output array to be changed. @@ -3479,8 +3373,6 @@ Calculates the sum of array elements. :param arr: input array that must have from 1 to 4 channels. - :param src: input array that must have from 1 to 4 channels (see 'arr' above). - The functions ``sum`` calculate and return the sum of array elements, independently for each channel. .. seealso:: @@ -3526,8 +3418,6 @@ Returns the trace of a matrix. :param mat: input matrix. - :param mtx: input matrix (see 'mat' above). - The function ``trace`` returns the sum of the diagonal elements of the matrix ``mtx`` . .. math:: @@ -3554,8 +3444,6 @@ Performs the matrix transformation of every array element. :param m: transformation ``2x2`` or ``2x3`` floating-point matrix. - :param transmat: transformation ``2x2`` or ``2x3`` floating-point matrix (see 'm' above). - :param shiftvec: optional translation vector (when ``m`` is ``2x2``) The function ``transform`` performs the matrix transformation of every element of the array ``src`` and stores the results in ``dst`` : From 1b6639aa3d73801179f9694fdec30580cf4cb2ad Mon Sep 17 00:00:00 2001 From: yao Date: Thu, 6 Sep 2012 14:39:20 +0800 Subject: [PATCH 13/97] A little optimization on ocl/pyrdown, ocl/canny --- modules/ocl/src/canny.cpp | 14 +-- modules/ocl/src/kernels/pyr_down.cl | 135 ++++++++-------------------- modules/ocl/test/test_canny.cpp | 2 +- 3 files changed, 45 insertions(+), 106 deletions(-) diff --git a/modules/ocl/src/canny.cpp b/modules/ocl/src/canny.cpp index 5881a4cae2..4fd5a3f3ea 100644 --- a/modules/ocl/src/canny.cpp +++ b/modules/ocl/src/canny.cpp @@ -66,7 +66,7 @@ namespace cv } } -cv::ocl::CannyBuf::CannyBuf(const oclMat& dx_, const oclMat& dy_) : dx(dx_), dy(dy_) +cv::ocl::CannyBuf::CannyBuf(const oclMat& dx_, const oclMat& dy_) : dx(dx_), dy(dy_), counter(NULL) { CV_Assert(dx_.type() == CV_32SC1 && dy_.type() == CV_32SC1 && dx_.size() == dy_.size()); @@ -102,6 +102,10 @@ void cv::ocl::CannyBuf::create(const Size& image_size, int apperture_size) float counter_f [1] = { 0 }; int err = 0; + if(counter) + { + openCLFree(counter); + } counter = clCreateBuffer( Context::getContext()->impl->clContext, CL_MEM_COPY_HOST_PTR, sizeof(float), counter_f, &err ); openCLSafeCall(err); } @@ -322,15 +326,11 @@ void canny::calcMap_gpu(oclMat& dx, oclMat& dy, oclMat& mag, oclMat& map, int ro args.push_back( make_pair( sizeof(cl_int), (void *)&map.step)); args.push_back( make_pair( sizeof(cl_int), (void *)&map.offset)); -#if CALCMAP_FIXED + size_t globalThreads[3] = {cols, rows, 1}; string kernelName = "calcMap"; size_t localThreads[3] = {16, 16, 1}; -#else - size_t globalThreads[3] = {cols, rows, 1}; - string kernelName = "calcMap_2"; - size_t localThreads[3] = {256, 1, 1}; -#endif + openCLExecuteKernel(clCxt, &imgproc_canny, kernelName, globalThreads, localThreads, args, -1, -1); } diff --git a/modules/ocl/src/kernels/pyr_down.cl b/modules/ocl/src/kernels/pyr_down.cl index 38b4ec7c7f..19d631e33b 100644 --- a/modules/ocl/src/kernels/pyr_down.cl +++ b/modules/ocl/src/kernels/pyr_down.cl @@ -16,6 +16,7 @@ // // @Authors // Dachuan Zhao, dachuan@multicorewareinc.com +// Yao Wang, bitwangyaoyao@gmail.com // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -118,81 +119,19 @@ uchar4 round_uchar4_float4(float4 v) return round_uchar4_int4(iv); } - - - -int idx_row_low(int y, int last_row) -{ - if(y < 0) - { - y = -y; - } - return y % (last_row + 1); -} - -int idx_row_high(int y, int last_row) -{ - int i; - int j; - if(last_row - y < 0) - { - i = (y - last_row); - } - else - { - i = (last_row - y); - } - if(last_row - i < 0) - { - j = i - last_row; - } - else - { - j = last_row - i; - } - return j % (last_row + 1); -} +#define IDX_ROW_HIGH(y,last_row) (abs_diff((int)abs_diff(last_row,y),last_row) % ((last_row)+1)) +#define IDX_ROW_LOW(y,last_row) (abs(y) % ((last_row) + 1)) +#define IDX_COL_HIGH(x,last_col) abs_diff((int)abs_diff(x,last_col),last_col) +#define IDX_COL_LOW(x,last_col) (abs(x) % ((last_col) + 1)) int idx_row(int y, int last_row) { - return idx_row_low(idx_row_high(y, last_row), last_row); -} - -int idx_col_low(int x, int last_col) -{ - if(x < 0) - { - x = -x; - } - return x % (last_col + 1); -} - -int idx_col_high(int x, int last_col) -{ - int i; - int j; - if(last_col - x < 0) - { - i = (x - last_col); - } - else - { - i = (last_col - x); - } - if(last_col - i < 0) - { - j = i - last_col; - } - else - { - j = last_col - i; - } - return j % (last_col + 1); + return IDX_ROW_LOW(IDX_ROW_HIGH(y,last_row),last_row); } int idx_col(int x, int last_col) { - return idx_col_low(idx_col_high(x, last_col), last_col); + return IDX_COL_LOW(IDX_COL_HIGH(x,last_col),last_col); } __kernel void pyrDown_C1_D0(__global uchar * srcData, int srcStep, int srcOffset, int srcRows, int srcCols, __global uchar *dst, int dstStep, int dstOffset, int dstCols) @@ -210,11 +149,11 @@ __kernel void pyrDown_C1_D0(__global uchar * srcData, int srcStep, int srcOffset sum = 0; - sum = sum + 0.0625f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[idx_col(x, last_col)]); - sum = sum + 0.25f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[idx_col(x, last_col)]); - sum = sum + 0.375f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[idx_col(x, last_col)]); - sum = sum + 0.25f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[idx_col(x, last_col)]); - sum = sum + 0.0625f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[idx_col(x, last_col)]); + sum = sum + 0.0625f * ((__global uchar*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[idx_col(x, last_col)]; + sum = sum + 0.25f * ((__global uchar*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[idx_col(x, last_col)]; + sum = sum + 0.375f * ((__global uchar*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[idx_col(x, last_col)]; + sum = sum + 0.25f * ((__global uchar*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[idx_col(x, last_col)]; + sum = sum + 0.0625f * ((__global uchar*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[idx_col(x, last_col)]; smem[2 + get_local_id(0)] = sum; @@ -224,11 +163,11 @@ __kernel void pyrDown_C1_D0(__global uchar * srcData, int srcStep, int srcOffset sum = 0; - sum = sum + 0.0625f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[idx_col(left_x, last_col)]); - sum = sum + 0.25f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[idx_col(left_x, last_col)]); - sum = sum + 0.375f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[idx_col(left_x, last_col)]); - sum = sum + 0.25f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[idx_col(left_x, last_col)]); - sum = sum + 0.0625f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[idx_col(left_x, last_col)]); + sum = sum + 0.0625f * ((__global uchar*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[idx_col(left_x, last_col)]; + sum = sum + 0.25f * ((__global uchar*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[idx_col(left_x, last_col)]; + sum = sum + 0.375f * ((__global uchar*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[idx_col(left_x, last_col)]; + sum = sum + 0.25f * ((__global uchar*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[idx_col(left_x, last_col)]; + sum = sum + 0.0625f * ((__global uchar*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[idx_col(left_x, last_col)]; smem[get_local_id(0)] = sum; } @@ -239,11 +178,11 @@ __kernel void pyrDown_C1_D0(__global uchar * srcData, int srcStep, int srcOffset sum = 0; - sum = sum + 0.0625f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[idx_col(right_x, last_col)]); - sum = sum + 0.25f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[idx_col(right_x, last_col)]); - sum = sum + 0.375f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[idx_col(right_x, last_col)]); - sum = sum + 0.25f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[idx_col(right_x, last_col)]); - sum = sum + 0.0625f * round_uchar_uchar(((__global uchar*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[idx_col(right_x, last_col)]); + sum = sum + 0.0625f * ((__global uchar*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[idx_col(right_x, last_col)]; + sum = sum + 0.25f * ((__global uchar*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[idx_col(right_x, last_col)]; + sum = sum + 0.375f * ((__global uchar*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[idx_col(right_x, last_col)]; + sum = sum + 0.25f * ((__global uchar*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[idx_col(right_x, last_col)]; + sum = sum + 0.0625f * ((__global uchar*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[idx_col(right_x, last_col)]; smem[4 + get_local_id(0)] = sum; } @@ -288,11 +227,11 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcOffse sum = 0; - sum = sum + co3 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[idx_col(x, last_col)])); - sum = sum + co2 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[idx_col(x, last_col)])); - sum = sum + co1 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[idx_col(x, last_col)])); - sum = sum + co2 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[idx_col(x, last_col)])); - sum = sum + co3 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[idx_col(x, last_col)])); + sum = sum + co3 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[idx_col(x, last_col)]); + sum = sum + co2 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[idx_col(x, last_col)]); + sum = sum + co1 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[idx_col(x, last_col)]); + sum = sum + co2 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[idx_col(x, last_col)]); + sum = sum + co3 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[idx_col(x, last_col)]); smem[2 + get_local_id(0)] = sum; @@ -302,11 +241,11 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcOffse sum = 0; - sum = sum + co3 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[idx_col(left_x, last_col)])); - sum = sum + co2 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[idx_col(left_x, last_col)])); - sum = sum + co1 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[idx_col(left_x, last_col)])); - sum = sum + co2 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[idx_col(left_x, last_col)])); - sum = sum + co3 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[idx_col(left_x, last_col)])); + sum = sum + co3 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[idx_col(left_x, last_col)]); + sum = sum + co2 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[idx_col(left_x, last_col)]); + sum = sum + co1 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[idx_col(left_x, last_col)]); + sum = sum + co2 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[idx_col(left_x, last_col)]); + sum = sum + co3 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[idx_col(left_x, last_col)]); smem[get_local_id(0)] = sum; } @@ -317,11 +256,11 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcOffse sum = 0; - sum = sum + co3 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[idx_col(right_x, last_col)])); - sum = sum + co2 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[idx_col(right_x, last_col)])); - sum = sum + co1 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[idx_col(right_x, last_col)])); - sum = sum + co2 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[idx_col(right_x, last_col)])); - sum = sum + co3 * convert_float4(round_uchar4_uchar4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[idx_col(right_x, last_col)])); + sum = sum + co3 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[idx_col(right_x, last_col)]); + sum = sum + co2 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[idx_col(right_x, last_col)]); + sum = sum + co1 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[idx_col(right_x, last_col)]); + sum = sum + co2 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[idx_col(right_x, last_col)]); + sum = sum + co3 * convert_float4(((__global uchar4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[idx_col(right_x, last_col)]); smem[4 + get_local_id(0)] = sum; } diff --git a/modules/ocl/test/test_canny.cpp b/modules/ocl/test/test_canny.cpp index e6fb885cf4..e728c99be5 100644 --- a/modules/ocl/test/test_canny.cpp +++ b/modules/ocl/test/test_canny.cpp @@ -103,6 +103,6 @@ TEST_P(Canny, Accuracy) EXPECT_MAT_SIMILAR(edges_gold, edges, 1e-2); } -INSTANTIATE_TEST_CASE_P(ocl_ImgProc, Canny, testing::Combine( +INSTANTIATE_TEST_CASE_P(GPU_ImgProc, Canny, testing::Combine( testing::Values(AppertureSize(3), AppertureSize(5)), testing::Values(L2gradient(false), L2gradient(true)))); From 037e3ee2883bb888f24ed0d26c809c572e04b028 Mon Sep 17 00:00:00 2001 From: Vsevolod Glumov Date: Thu, 6 Sep 2012 10:57:28 +0400 Subject: [PATCH 14/97] Fixed some minor formatting issues in 'old_basic_structures' and 'operations_on_arrays'. --- modules/core/doc/old_basic_structures.rst | 4 ++-- modules/core/doc/operations_on_arrays.rst | 26 ++++++++++------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/modules/core/doc/old_basic_structures.rst b/modules/core/doc/old_basic_structures.rst index 3ee7b0f041..0596e04bff 100644 --- a/modules/core/doc/old_basic_structures.rst +++ b/modules/core/doc/old_basic_structures.rst @@ -826,7 +826,7 @@ Returns one of array diagonals. The function returns the header, corresponding to a specified diagonal of the input array. GetDims ---------- +------- Return number of array dimensions .. ocv:cfunction:: int cvGetDims(const CvArr* arr, int* sizes=NULL) @@ -847,7 +847,7 @@ The function returns the array dimensionality and the array of dimension sizes. total *= sizes[i]; GetDimSize ------------- +---------- Returns array size along the specified dimension. .. ocv:cfunction:: int cvGetDimSize(const CvArr* arr, int index) diff --git a/modules/core/doc/operations_on_arrays.rst b/modules/core/doc/operations_on_arrays.rst index 9e9ecaf1e0..4ce6bcebe7 100644 --- a/modules/core/doc/operations_on_arrays.rst +++ b/modules/core/doc/operations_on_arrays.rst @@ -406,8 +406,6 @@ Calculates the covariance matrix of a set of vectors. :param ctype: type of the matrixl; it equals 'CV_64F' by default. - :param cov_mat: output covariance matrix (specific to C syntax). - :param mean: input or output (depending on the flags) array as the average value of the input vectors. :param vects: a set of vectors. @@ -545,8 +543,6 @@ Performs the per-element comparison of two arrays or an array and scalar value. * **CMP_LE** ``src1`` is less than or equal to ``src2``. * **CMP_NE** ``src1`` is unequal to ``src2``. - :param cmp_op: a flag, that specifies correspondence between the arrays (specific to C syntax; for possible values see 'cmpop above). - The function compares: @@ -1004,7 +1000,7 @@ All of the above improvements have been implemented in :ocv:func:`matchTemplate` divide ----------- +------ Performs per-element division of two arrays or a scalar by an array. .. ocv:function:: void divide(InputArray src1, InputArray src2, OutputArray dst, double scale=1, int dtype=-1) @@ -1164,7 +1160,7 @@ To extract a channel from a new-style matrix, use insertImageCOI ---------------- +-------------- Copies the selected image channel from a new-style C++ matrix to the old-style C array. .. ocv:function:: void insertImageCOI( InputArray coiimg, CvArr* arr, int coi=-1 ) @@ -1196,7 +1192,7 @@ To insert a channel to a new-style matrix, use flip --------- +---- Flips a 2D array around vertical, horizontal, or both axes. .. ocv:function:: void flip(InputArray src, OutputArray dst, int flipCode) @@ -2267,7 +2263,7 @@ The sample below is the function that takes two matrices. The first function sto PCA::PCA ------------- +-------- PCA constructors .. ocv:function:: PCA::PCA() @@ -2535,7 +2531,7 @@ http://en.wikipedia.org/wiki/Ziggurat_algorithm RNG::RNG ------------- +-------- The constructors .. ocv:function:: RNG::RNG() @@ -2549,7 +2545,7 @@ These are the RNG constructors. The first form sets the state to some pre-define RNG::next -------------- +--------- Returns the next random number. .. ocv:function:: unsigned RNG::next() @@ -2583,7 +2579,7 @@ Each of the methods updates the state using the MWC algorithm and returns the ne RNG::operator () --------------------- +---------------- Returns the next random number. .. ocv:function:: unsigned RNG::operator ()() @@ -2598,7 +2594,7 @@ The methods transform the state using the MWC algorithm and return the next rand RNG::uniform ----------------- +------------ Returns the next random number sampled from the uniform distribution. .. ocv:function:: int RNG::uniform(int a, int b) @@ -2637,7 +2633,7 @@ The compiler does not take into account the type of the variable to which you as RNG::gaussian ------------------ +------------- Returns the next random number sampled from the Gaussian distribution. .. ocv:function:: double RNG::gaussian(double sigma) @@ -2649,7 +2645,7 @@ The method transforms the state using the MWC algorithm and returns the next ran RNG::fill -------------- +--------- Fills arrays with random numbers. .. ocv:function:: void RNG::fill( InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange=false ) @@ -2952,7 +2948,7 @@ If ``DECOMP_LU`` or ``DECOMP_CHOLESKY`` method is used, the function returns 1 i solveCubic --------------- +---------- Finds the real roots of a cubic equation. .. ocv:function:: int solveCubic( InputArray coeffs, OutputArray roots ) From 283087dd56b392dbcbbaa6a80ee94aee69e39254 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 6 Sep 2012 14:16:18 +0400 Subject: [PATCH 15/97] fixed typo (bug #2327) --- doc/tutorials/ios/video_processing/video_processing.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/tutorials/ios/video_processing/video_processing.rst b/doc/tutorials/ios/video_processing/video_processing.rst index 2765c20593..b71053d551 100644 --- a/doc/tutorials/ios/video_processing/video_processing.rst +++ b/doc/tutorials/ios/video_processing/video_processing.rst @@ -185,8 +185,7 @@ We follow the delegation pattern, which is very common in iOS, to provide access #endif Note that we are using C++ here (cv::Mat). -Important: You have to rename the view controller's extension .m into .mm, so that the compiler compiles it under the assumption of Objective-C++ (Objective-C and C++ mixed). Then, __cplusplus is defined when the compiler is processing the file for C++ code. Therefore, we put our code within a block where __cpluscplus is defined. - +Important: You have to rename the view controller's extension .m into .mm, so that the compiler compiles it under the assumption of Objective-C++ (Objective-C and C++ mixed). Then, __cplusplus is defined when the compiler is processing the file for C++ code. Therefore, we put our code within a block where __cplusplus is defined. Basic video processing From 36dacc688addf219a0fae4298f1470e92804a6ee Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 6 Sep 2012 14:22:40 +0400 Subject: [PATCH 16/97] removed non-existing CoreAnimation framework from the list of required frameworks (bug #2326) --- doc/tutorials/ios/video_processing/video_processing.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/tutorials/ios/video_processing/video_processing.rst b/doc/tutorials/ios/video_processing/video_processing.rst index b71053d551..6143f7717e 100644 --- a/doc/tutorials/ios/video_processing/video_processing.rst +++ b/doc/tutorials/ios/video_processing/video_processing.rst @@ -136,8 +136,6 @@ Additionally, we have to manually add framework dependencies of the opencv frame * CoreVideo -* CoreAnimation - * QuartzCore * UIKit From 186f8b74177e309b0bdee0e1aa6d1d5cd2cb60c4 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 6 Sep 2012 16:00:57 +0400 Subject: [PATCH 17/97] possibility to maintain custom hardware-specific libs folders --- cmake/OpenCVGenAndroidMK.cmake | 8 ++++---- cmake/templates/OpenCV.mk.in | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmake/OpenCVGenAndroidMK.cmake b/cmake/OpenCVGenAndroidMK.cmake index b2206f500f..04ebdb7592 100644 --- a/cmake/OpenCVGenAndroidMK.cmake +++ b/cmake/OpenCVGenAndroidMK.cmake @@ -82,8 +82,8 @@ if(ANDROID) # ------------------------------------------------------------------------------------------- set(OPENCV_INCLUDE_DIRS_CONFIGCMAKE "\"${OPENCV_CONFIG_FILE_INCLUDE_DIR}\" \"${OpenCV_SOURCE_DIR}/include\" \"${OpenCV_SOURCE_DIR}/include/opencv\"") set(OPENCV_BASE_INCLUDE_DIR_CONFIGCMAKE "\"${OpenCV_SOURCE_DIR}\"") - set(OPENCV_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/lib/\$(TARGET_ARCH_ABI)") - set(OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/3rdparty/lib/\$(TARGET_ARCH_ABI)") + set(OPENCV_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/lib/\$(OPENCV_TARGET_ARCH_ABI)") + set(OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/3rdparty/lib/\$(OPENCV_TARGET_ARCH_ABI)") configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV.mk.in" "${CMAKE_BINARY_DIR}/OpenCV.mk" IMMEDIATE @ONLY) @@ -92,8 +92,8 @@ if(ANDROID) # ------------------------------------------------------------------------------------------- set(OPENCV_INCLUDE_DIRS_CONFIGCMAKE "\"\$(LOCAL_PATH)/\$(OPENCV_THIS_DIR)/include/opencv\" \"\$(LOCAL_PATH)/\$(OPENCV_THIS_DIR)/include\"") set(OPENCV_BASE_INCLUDE_DIR_CONFIGCMAKE "") - set(OPENCV_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/../libs/\$(TARGET_ARCH_ABI)") - set(OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/../3rdparty/libs/\$(TARGET_ARCH_ABI)") + set(OPENCV_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/../libs/\$(OPENCV_TARGET_ARCH_ABI)") + set(OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/../3rdparty/libs/\$(OPENCV_TARGET_ARCH_ABI)") configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV.mk.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk" IMMEDIATE @ONLY) install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}) diff --git a/cmake/templates/OpenCV.mk.in b/cmake/templates/OpenCV.mk.in index e99b20e417..274323473b 100644 --- a/cmake/templates/OpenCV.mk.in +++ b/cmake/templates/OpenCV.mk.in @@ -4,6 +4,7 @@ USER_LOCAL_PATH:=$(LOCAL_PATH) LOCAL_PATH:=$(subst ?,,$(firstword ?$(subst \, ,$(subst /, ,$(call my-dir))))) +OPENCV_TARGET_ARCH_ABI:=$(TARGET_ARCH_ABI) OPENCV_THIS_DIR:=$(patsubst $(LOCAL_PATH)\\%,%,$(patsubst $(LOCAL_PATH)/%,%,$(call my-dir))) OPENCV_MK_DIR:=$(dir $(lastword $(MAKEFILE_LIST))) OPENCV_LIBS_DIR:=@OPENCV_LIBS_DIR_CONFIGCMAKE@ From 2cc262bce46cd983203630f43c221cd07c061fc0 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Wed, 5 Sep 2012 20:31:09 +0400 Subject: [PATCH 18/97] Fix Android CMake toolchain compiler and linker flags --- android/android.toolchain.cmake | 280 +++++++++--------- .../camera_wrapper/CMakeLists.txt | 35 +-- 2 files changed, 166 insertions(+), 149 deletions(-) diff --git a/android/android.toolchain.cmake b/android/android.toolchain.cmake index c1a9d7d9b2..4222b4d843 100644 --- a/android/android.toolchain.cmake +++ b/android/android.toolchain.cmake @@ -250,6 +250,9 @@ set( CMAKE_SYSTEM_NAME Linux ) # this one not so much set( CMAKE_SYSTEM_VERSION 1 ) +# rpath makes low sence for Android +set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) + set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) if(NOT DEFINED ANDROID_NDK_SEARCH_PATHS) if( CMAKE_HOST_WIN32 ) @@ -949,20 +952,6 @@ if( APPLE ) mark_as_advanced( CMAKE_INSTALL_NAME_TOOL ) endif() -# flags and definitions -remove_definitions( -DANDROID ) -add_definitions( -DANDROID ) - -if(ANDROID_SYSROOT MATCHES "[ ;\"]") - set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) - if( NOT _CMAKE_IN_TRY_COMPILE ) - # quotes will break try_compile and compiler identification - message(WARNING "Your Android system root has non-alphanumeric symbols. It can break compiler features detection and the whole build.") - endif() -else() - set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) -endif() - # Force set compilers because standard identification works badly for us include( CMakeForceCompiler ) CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU ) @@ -983,60 +972,54 @@ set( CMAKE_ASM_COMPILER_FORCED TRUE ) set( CMAKE_COMPILER_IS_GNUASM 1) set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm ) -# NDK flags -if( ARMEABI OR ARMEABI_V7A ) - # NDK also defines -ffunction-sections -funwind-tables but they result in worse OpenCV performance - set( _CMAKE_CXX_FLAGS "-fPIC -Wno-psabi" ) - set( _CMAKE_C_FLAGS "-fPIC -Wno-psabi" ) - remove_definitions( -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ ) - add_definitions( -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ ) - # extra arm-specific flags - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) -elseif( X86 ) - set( _CMAKE_CXX_FLAGS "-funwind-tables" ) - set( _CMAKE_C_FLAGS "-funwind-tables" ) -elseif( MIPS ) - set( _CMAKE_CXX_FLAGS "-fpic -Wno-psabi -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) - set( _CMAKE_C_FLAGS "-fpic -Wno-psabi -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) +# flags and definitions +remove_definitions( -DANDROID ) +add_definitions( -DANDROID ) + +if(ANDROID_SYSROOT MATCHES "[ ;\"]") + set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) + if( NOT _CMAKE_IN_TRY_COMPILE ) + # quotes will break try_compile and compiler identification + message(WARNING "Your Android system root has non-alphanumeric symbols. It can break compiler features detection and the whole build.") + endif() else() - set( _CMAKE_CXX_FLAGS "" ) - set( _CMAKE_C_FLAGS "" ) + set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) endif() -# release and debug flags +# NDK flags if( ARMEABI OR ARMEABI_V7A ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fpic -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__" ) if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 ) # It is recommended to use the -mthumb compiler flag to force the generation # of 16-bit Thumb-1 instructions (the default being 32-bit ARM ones). - # O3 instead of O2/Os in release mode - like cmake sets for desktop gcc - set( _CMAKE_CXX_FLAGS_RELEASE "-mthumb -O3" ) - set( _CMAKE_C_FLAGS_RELEASE "-mthumb -O3" ) - set( _CMAKE_CXX_FLAGS_DEBUG "-marm -Os -finline-limit=64" ) - set( _CMAKE_C_FLAGS_DEBUG "-marm -Os -finline-limit=64" ) + set( ANDROID_CXX_FLAGS_RELEASE "-mthumb" ) + set( ANDROID_CXX_FLAGS_DEBUG "-marm -finline-limit=64" ) else() # always compile ARMEABI_V6 in arm mode; otherwise there is no difference from ARMEABI # O3 instead of O2/Os in release mode - like cmake sets for desktop gcc - set( _CMAKE_CXX_FLAGS_RELEASE "-marm -O3 -fstrict-aliasing" ) - set( _CMAKE_C_FLAGS_RELEASE "-marm -O3 -fstrict-aliasing" ) - set( _CMAKE_CXX_FLAGS_DEBUG "-marm -O0 -finline-limit=300" ) - set( _CMAKE_C_FLAGS_DEBUG "-marm -O0 -finline-limit=300" ) + set( ANDROID_CXX_FLAGS_RELEASE "-marm" ) + set( ANDROID_CXX_FLAGS_DEBUG "-marm -finline-limit=300" ) endif() elseif( X86 ) - set( _CMAKE_CXX_FLAGS_RELEASE "-O3 -fstrict-aliasing" ) - set( _CMAKE_C_FLAGS_RELEASE "-O3 -fstrict-aliasing" ) - set( _CMAKE_CXX_FLAGS_DEBUG "-O0 -finline-limit=300" ) - set( _CMAKE_C_FLAGS_DEBUG "-O0 -finline-limit=300" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) + set( ANDROID_CXX_FLAGS_RELEASE "" ) + set( ANDROID_CXX_FLAGS_DEBUG "-finline-limit=300" ) elseif( MIPS ) - set( _CMAKE_CXX_FLAGS_RELEASE "-O3 -funswitch-loops -finline-limit=300" ) - set( _CMAKE_C_FLAGS_RELEASE "-O3 -funswitch-loops -finline-limit=300" ) - set( _CMAKE_CXX_FLAGS_DEBUG "-O0 -g" ) - set( _CMAKE_C_FLAGS_DEBUG "-O0 -g" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fpic -funwind-tables -fmessage-length=0 -fno-inline-functions-called-once -frename-registers" ) + set( ANDROID_CXX_FLAGS_RELEASE "-finline-limit=300 -fno-strict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-finline-functions -fgcse-after-reload -frerun-cse-after-loop" ) +elseif() + set( ANDROID_CXX_FLAGS_RELEASE "" ) + set( ANDROID_CXX_FLAGS_DEBUG "" ) endif() -set( _CMAKE_CXX_FLAGS_RELEASE "${_CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer -DNDEBUG" ) -set( _CMAKE_C_FLAGS_RELEASE "${_CMAKE_C_FLAGS_RELEASE} -fomit-frame-pointer -DNDEBUG" ) -set( _CMAKE_CXX_FLAGS_DEBUG "${_CMAKE_CXX_FLAGS_DEBUG} -fno-strict-aliasing -fno-omit-frame-pointer -DDEBUG -D_DEBUG" ) -set( _CMAKE_C_FLAGS_DEBUG "${_CMAKE_C_FLAGS_DEBUG} -fno-strict-aliasing -fno-omit-frame-pointer -DDEBUG -D_DEBUG" ) + +if( NOT X86 ) + set( ANDROID_CXX_FLAGS "-Wno-psabi ${ANDROID_CXX_FLAGS}" ) +endif() + +set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) # good/necessary when porting desktop libraries +set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -fomit-frame-pointer" ) +set( ANDROID_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} -fno-strict-aliasing -fno-omit-frame-pointer" ) # ABI-specific flags if( ARMEABI_V7A ) @@ -1052,125 +1035,142 @@ elseif( ARMEABI_V6 ) set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) elseif( ARMEABI ) set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv5te -mtune=xscale -msoft-float" ) -elseif( X86 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" )#sse? endif() # STL -if( 0 AND EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" ) - # use gcc as a C++ linker to avoid automatic picking of libsdtc++ - set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o ") - set( CMAKE_CXX_CREATE_SHARED_MODULE " -o ") - set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) +if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" ) + if( ANDROID_STL MATCHES "gnustl" ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) + set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) + set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) + else() + set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) + set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) + set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) + endif() if( EXISTS "${__libstl}" ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"") - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"") - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"") + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"" ) endif() if( EXISTS "${__libsupcxx}" ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"") - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"") - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"") + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) # C objects: set( CMAKE_C_CREATE_SHARED_LIBRARY " -o " ) set( CMAKE_C_CREATE_SHARED_MODULE " -o " ) set( CMAKE_C_LINK_EXECUTABLE " -o " ) - set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"") - set( CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"") - set( CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"") + set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) + set( CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) + set( CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) endif() endif() +# variables controlling optional build flags +if (ANDROID_NDK_RELEASE STRLESS "r7") + # libGLESv2.so in NDK's prior to r7 refers to missing external symbols. + # So this flag option is required for all projects using OpenGL from native. + __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) +else() + __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) +endif() +__INIT_VARIABLE( ANDROID_NO_UNDEFINED OBSOLETE_NO_UNDEFINED VALUES ON ) +__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) +__INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON ) +__INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON ) +__INIT_VARIABLE( ANDROID_RELRO VALUES ON ) + +set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" ) +set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker (only avaialble for NDK r8b for ARM and x86 architectures on linux-86 and darwin-x86 hosts)" ) +set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" ) +mark_as_advanced( ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_FUNCTION_LEVEL_LINKING ANDROID_GOLD_LINKER ANDROID_NOEXECSTACK ANDROID_RELRO ) + # linker flags set( ANDROID_LINKER_FLAGS "" ) -__INIT_VARIABLE( ANDROID_NO_UNDEFINED OBSOLETE_NO_UNDEFINED VALUES ON ) -set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" FORCE ) -mark_as_advanced( ANDROID_NO_UNDEFINED ) +if( ARMEABI_V7A ) + # this is *required* to use the following linker flags that routes around + # a CPU bug in some Cortex-A8 implementations: + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--fix-cortex-a8" ) +endif() + if( ANDROID_NO_UNDEFINED ) - set( ANDROID_LINKER_FLAGS "-Wl,--no-undefined ${ANDROID_LINKER_FLAGS}" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined" ) endif() -if (ANDROID_NDK_RELEASE STRLESS "r7") - # libGLESv2.so in NDK's prior to r7 refers to missing external symbols. - # So this flag option is required for all projects using OpenGL from native. - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) -else() - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) -endif() - -set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" FORCE ) -mark_as_advanced( ANDROID_SO_UNDEFINED ) if( ANDROID_SO_UNDEFINED ) set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-allow-shlib-undefined" ) endif() -__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) -set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" FORCE ) -mark_as_advanced( ANDROID_FUNCTION_LEVEL_LINKING ) if( ANDROID_FUNCTION_LEVEL_LINKING ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) - set( ANDROID_LINKER_FLAGS "-Wl,--gc-sections ${ANDROID_LINKER_FLAGS}" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" ) endif() -if( ARMEABI_V7A ) - # this is *required* to use the following linker flags that routes around - # a CPU bug in some Cortex-A8 implementations: - set( ANDROID_LINKER_FLAGS "-Wl,--fix-cortex-a8 ${ANDROID_LINKER_FLAGS}" ) -endif() - -if( CMAKE_HOST_UNIX AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" AND (ARMEABI_V7A OR X86) ) - __INIT_VARIABLE( ANDROID_USE_GOLD_LINKER VALUES ON ) - set( ANDROID_USE_GOLD_LINKER ${ANDROID_USE_GOLD_LINKER} CACHE BOOL "Enables gold linker" FORCE ) - mark_as_advanced( ANDROID_USE_GOLD_LINKER ) - if( ANDROID_USE_GOLD_LINKER ) +if( CMAKE_HOST_UNIX AND (ARMEABI_V7A OR X86) AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" ) + if( ANDROID_GOLD_LINKER ) set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" ) endif() endif() +if( ANDROID_NOEXECSTACK ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Wa,--noexecstack" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,noexecstack" ) +endif() + +if( ANDROID_RELRO ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now" ) +endif() + # cache flags -set( CMAKE_CXX_FLAGS "${_CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags" ) -set( CMAKE_C_FLAGS "${_CMAKE_C_FLAGS}" CACHE STRING "c flags" ) -set( CMAKE_CXX_FLAGS_RELEASE "${_CMAKE_CXX_FLAGS_RELEASE}" CACHE STRING "c++ Release flags" ) -set( CMAKE_C_FLAGS_RELEASE "${_CMAKE_C_FLAGS_RELEASE}" CACHE STRING "c Release flags" ) -set( CMAKE_CXX_FLAGS_DEBUG "${_CMAKE_CXX_FLAGS_DEBUG}" CACHE STRING "c++ Debug flags" ) -set( CMAKE_C_FLAGS_DEBUG "${_CMAKE_C_FLAGS_DEBUG}" CACHE STRING "c Debug flags" ) -set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "linker flags" ) -set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "linker flags" ) -set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "linker flags" ) +set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" ) +set( CMAKE_C_FLAGS "" CACHE STRING "c flags" ) +set( CMAKE_CXX_FLAGS_RELEASE "-O3 -g -DNDEBUG" CACHE STRING "c++ Release flags" ) +set( CMAKE_C_FLAGS_RELEASE "-O3 -g -DNDEBUG" CACHE STRING "c Release flags" ) +set( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c++ Debug flags" ) +set( CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c Debug flags" ) +set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "shared linker flags" ) +set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "module linker flags" ) +set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" ) # finish flags -set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Extra Android compiler flags" ) -set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Extra Android linker flags" ) set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" ) set( CMAKE_C_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" ) +set( CMAKE_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}" ) +set( CMAKE_C_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}" ) +set( CMAKE_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}" ) +set( CMAKE_C_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}" ) +set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) +set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) +set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) + if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" ) - set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/mipself.xsc ${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) - set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/mipself.xsc ${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) - set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/mipself.x ${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) -else() - set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) - set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) - set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) + set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_SHARED_LINKER_FLAGS}" ) + set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_MODULE_LINKER_FLAGS}" ) + set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" ) endif() # configure rtti if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES ) if( ANDROID_RTTI ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frtti" ) + set( CMAKE_CXX_FLAGS "-frtti ${CMAKE_CXX_FLAGS}" ) else() - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" ) + set( CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}" ) endif() endif() # configure exceptios if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES ) if( ANDROID_EXCEPTIONS ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions" ) - set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexceptions" ) + set( CMAKE_CXX_FLAGS "-fexceptions ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fexceptions ${CMAKE_C_FLAGS}" ) else() - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions" ) - set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-exceptions" ) + set( CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fno-exceptions ${CMAKE_C_FLAGS}" ) endif() endif() @@ -1239,11 +1239,13 @@ macro( ANDROID_GET_ABI_RAWNAME TOOLCHAIN_FLAG VAR ) if( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI" ) set( ${VAR} "armeabi" ) elseif( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI_V7A" ) - set( ${VAR} "armeabi-v7a" ) + set( ${VAR} "armeabi-v7a" ) elseif( "${TOOLCHAIN_FLAG}" STREQUAL "X86" ) - set( ${VAR} "x86" ) + set( ${VAR} "x86" ) + elseif( "${TOOLCHAIN_FLAG}" STREQUAL "MIPS" ) + set( ${VAR} "mips" ) else() - set( ${VAR} "unknown" ) + set( ${VAR} "unknown" ) endif() endmacro() @@ -1251,10 +1253,22 @@ endmacro() # export toolchain settings for the try_compile() command if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) set( __toolchain_config "") - foreach( __var NDK_CCACHE ANDROID_ABI ANDROID_FORCE_ARM_BUILD ANDROID_NATIVE_API_LEVEL ANDROID_NO_UNDEFINED - ANDROID_SO_UNDEFINED ANDROID_SET_OBSOLETE_VARIABLES LIBRARY_OUTPUT_PATH_ROOT ANDROID_STL - ANDROID_STL_FORCE_FEATURES ANDROID_FORBID_SYGWIN ANDROID_NDK ANDROID_STANDALONE_TOOLCHAIN - ANDROID_FUNCTION_LEVEL_LINKING ANDROID_USE_GOLD_LINKER ) + foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN ANDROID_SET_OBSOLETE_VARIABLES + ANDROID_NDK + ANDROID_STANDALONE_TOOLCHAIN + ANDROID_TOOLCHAIN_NAME + ANDROID_ABI + ANDROID_NATIVE_API_LEVEL + ANDROID_STL + ANDROID_STL_FORCE_FEATURES + ANDROID_FORCE_ARM_BUILD + ANDROID_NO_UNDEFINED + ANDROID_SO_UNDEFINED + ANDROID_FUNCTION_LEVEL_LINKING + ANDROID_GOLD_LINKER + ANDROID_NOEXECSTACK + ANDROID_RELRO + ) if( DEFINED ${__var} ) if( "${__var}" MATCHES " ") set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) @@ -1286,7 +1300,9 @@ endif() # ANDROID_NO_UNDEFINED : ON/OFF # ANDROID_SO_UNDEFINED : OFF/ON (default depends on NDK version) # ANDROID_FUNCTION_LEVEL_LINKING : ON/OFF -# ANDROID_USE_GOLD_LINKER : ON/OFF (default depends on NDK version and host & target platforms) +# ANDROID_GOLD_LINKER : ON/OFF +# ANDROID_NOEXECSTACK : ON/OFF +# ANDROID_RELRO : ON/OFF # Variables that takes effect only at first run: # ANDROID_FORCE_ARM_BUILD : ON/OFF # ANDROID_STL : gnustl_static/gnustl_shared/stlport_static/stlport_shared/gabi++_static/gabi++_shared/system_re/system/none diff --git a/modules/androidcamera/camera_wrapper/CMakeLists.txt b/modules/androidcamera/camera_wrapper/CMakeLists.txt index fdb09309be..b5cfbc1105 100644 --- a/modules/androidcamera/camera_wrapper/CMakeLists.txt +++ b/modules/androidcamera/camera_wrapper/CMakeLists.txt @@ -6,24 +6,25 @@ link_directories("${ANDROID_SOURCE_TREE}/out/target/product/generic/system/lib") if (ANDROID_VERSION VERSION_LESS "4.1") INCLUDE_DIRECTORIES(BEFORE - ${ANDROID_SOURCE_TREE} - ${ANDROID_SOURCE_TREE}/frameworks/base/include/ui - ${ANDROID_SOURCE_TREE}/frameworks/base/include/surfaceflinger - ${ANDROID_SOURCE_TREE}/frameworks/base/include/camera - ${ANDROID_SOURCE_TREE}/frameworks/base/include/media - ${ANDROID_SOURCE_TREE}/frameworks/base/include - ${ANDROID_SOURCE_TREE}/system/core/include - ${ANDROID_SOURCE_TREE}/hardware/libhardware/include - ${ANDROID_SOURCE_TREE}/frameworks/base/native/include - ) + ${ANDROID_SOURCE_TREE} + ${ANDROID_SOURCE_TREE}/frameworks/base/include/ui + ${ANDROID_SOURCE_TREE}/frameworks/base/include/surfaceflinger + ${ANDROID_SOURCE_TREE}/frameworks/base/include/camera + ${ANDROID_SOURCE_TREE}/frameworks/base/include/media + ${ANDROID_SOURCE_TREE}/frameworks/base/include + ${ANDROID_SOURCE_TREE}/system/core/include + ${ANDROID_SOURCE_TREE}/hardware/libhardware/include + ${ANDROID_SOURCE_TREE}/frameworks/base/native/include + ${ANDROID_SOURCE_TREE}/frameworks/base/opengl/include + ) else() INCLUDE_DIRECTORIES(BEFORE - ${ANDROID_SOURCE_TREE} - ${ANDROID_SOURCE_TREE}/frameworks/native/include - ${ANDROID_SOURCE_TREE}/frameworks/av/include - ${ANDROID_SOURCE_TREE}/system/core/include - ${ANDROID_SOURCE_TREE}/hardware/libhardware/include - ) + ${ANDROID_SOURCE_TREE} + ${ANDROID_SOURCE_TREE}/frameworks/native/include + ${ANDROID_SOURCE_TREE}/frameworks/av/include + ${ANDROID_SOURCE_TREE}/system/core/include + ${ANDROID_SOURCE_TREE}/hardware/libhardware/include + ) endif() set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) @@ -33,8 +34,8 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}") SET(CMAKE_C_FLAGS_RELEASE "") SET(CMAKE_CXX_FLAGS_RELEASE "") -string(REPLACE "-O3" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") string(REPLACE "-O3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +string(REPLACE "-frtti" "-fno-rtti" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") # because Android libraries are built without rtti SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os -fno-strict-aliasing -finline-limit=64 -fuse-cxa-atexit" ) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -fno-strict-aliasing -finline-limit=64 -fuse-cxa-atexit") From b8767f5fbe3f20ad7ebdbea1772ae497d35075b9 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Thu, 6 Sep 2012 14:42:30 +0400 Subject: [PATCH 19/97] Disable TBB on Android for mips --- cmake/OpenCVDetectTBB.cmake | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/OpenCVDetectTBB.cmake b/cmake/OpenCVDetectTBB.cmake index 3dba759f9f..fc6a635bb1 100644 --- a/cmake/OpenCVDetectTBB.cmake +++ b/cmake/OpenCVDetectTBB.cmake @@ -1,4 +1,4 @@ -if(ANDROID) +if(ANDROID AND NOT MIPS) add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/tbb") include_directories(SYSTEM ${TBB_INCLUDE_DIRS}) set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} tbb) @@ -21,11 +21,11 @@ elseif(UNIX AND NOT APPLE) endif() if(NOT HAVE_TBB) - set(TBB_DEFAULT_INCLUDE_DIRS - "/opt/intel/tbb" "/usr/local/include" "/usr/include" - "C:/Program Files/Intel/TBB" "C:/Program Files (x86)/Intel/TBB" - "C:/Program Files (x86)/tbb/include" - "C:/Program Files (x86)/tbb/include" + set(TBB_DEFAULT_INCLUDE_DIRS + "/opt/intel/tbb" "/usr/local/include" "/usr/include" + "C:/Program Files/Intel/TBB" "C:/Program Files (x86)/Intel/TBB" + "C:/Program Files (x86)/tbb/include" + "C:/Program Files (x86)/tbb/include" "${CMAKE_INSTALL_PREFIX}/include") find_path(TBB_INCLUDE_DIRS "tbb/tbb.h" PATHS ${TBB_INCLUDE_DIR} ${TBB_DEFAULT_INCLUDE_DIRS} DOC "The path to TBB headers") From b8adc04545b3fc0c8c24433b660c7ada75bea2fa Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Thu, 6 Sep 2012 14:43:08 +0400 Subject: [PATCH 20/97] Fix Android build warnings --- 3rdparty/openexr/CMakeLists.txt | 2 +- modules/features2d/src/brisk.cpp | 2 +- modules/ts/include/opencv2/ts/ts.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/3rdparty/openexr/CMakeLists.txt b/3rdparty/openexr/CMakeLists.txt index ea8c79ed72..a77a6e1326 100644 --- a/3rdparty/openexr/CMakeLists.txt +++ b/3rdparty/openexr/CMakeLists.txt @@ -37,7 +37,7 @@ endif() source_group("Include" FILES ${lib_hdrs} ) source_group("Src" FILES ${lib_srcs}) -ocv_warnings_disable(CMAKE_CXX_FLAGS -Wshadow -Wunused -Wsign-compare -Wundef -Wmissing-declarations -Wuninitialized -Wswitch -Wparentheses) +ocv_warnings_disable(CMAKE_CXX_FLAGS -Wshadow -Wunused -Wsign-compare -Wundef -Wmissing-declarations -Wuninitialized -Wswitch -Wparentheses -Warray-bounds -Wextra) ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4018 /wd4099 /wd4100 /wd4101 /wd4127 /wd4189 /wd4245 /wd4305 /wd4389 /wd4512 /wd4701 /wd4702 /wd4706 /wd4800) # vs2005 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4334) # vs2005 Win64 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4244) # vs2008 diff --git a/modules/features2d/src/brisk.cpp b/modules/features2d/src/brisk.cpp index 2a54d7c1cc..066817c6d9 100755 --- a/modules/features2d/src/brisk.cpp +++ b/modules/features2d/src/brisk.cpp @@ -1182,7 +1182,7 @@ BriskScaleSpace::refine3D(const int layer, const int x_layer, const int y_layer, const int center = thisLayer.getAgastScore(x_layer, y_layer, 1); // check and get above maximum: - float delta_x_above, delta_y_above; + float delta_x_above = 0, delta_y_above = 0; float max_above = getScoreMaxAbove(layer, x_layer, y_layer, center, ismax, delta_x_above, delta_y_above); if (!ismax) diff --git a/modules/ts/include/opencv2/ts/ts.hpp b/modules/ts/include/opencv2/ts/ts.hpp index b51bfb7753..bf6e7e91f0 100644 --- a/modules/ts/include/opencv2/ts/ts.hpp +++ b/modules/ts/include/opencv2/ts/ts.hpp @@ -12,7 +12,7 @@ #ifdef ANDROID # include -# define GTEST_HAS_CLONE (__ANDROID_API__ > 7 && __arm__) +# define GTEST_HAS_CLONE (__ANDROID_API__ > 7 && !defined __i386__) # define GTEST_HAS_POSIX_RE (__ANDROID_API__ > 7) # if defined _GLIBCXX_USE_WCHAR_T && _GLIBCXX_USE_WCHAR_T # define GTEST_HAS_STD_WSTRING 1 From 70234433d33cfb403e24f1ec72b8e6c5fccb261f Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Thu, 6 Sep 2012 14:55:48 +0400 Subject: [PATCH 21/97] Reduce verbosity of Java API generators --- modules/java/generator/gen_java.py | 4 ++-- modules/java/generator/gen_javadoc.py | 3 --- modules/java/generator/rst_parser.py | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/modules/java/generator/gen_java.py b/modules/java/generator/gen_java.py index 8c4ee6c75b..0471b494e0 100644 --- a/modules/java/generator/gen_java.py +++ b/modules/java/generator/gen_java.py @@ -987,14 +987,14 @@ extern "C" { msg = "// Return type '%s' is not supported, skipping the function\n\n" % fi.ctype self.skipped_func_list.append(c_decl + "\n" + msg) j_code.write( " "*4 + msg ) - print "SKIP:", c_decl, "\n\tdue to RET type", fi.ctype + print "SKIP:", c_decl.strip(), "\t due to RET type", fi.ctype return for a in fi.args: if a.ctype not in type_dict: msg = "// Unknown type '%s' (%s), skipping the function\n\n" % (a.ctype, a.out or "I") self.skipped_func_list.append(c_decl + "\n" + msg) j_code.write( " "*4 + msg ) - print "SKIP:", c_decl, "\n\tdue to ARG type", a.ctype, "/" + (a.out or "I") + print "SKIP:", c_decl.strip(), "\t due to ARG type", a.ctype, "/" + (a.out or "I") return self.ported_func_list.append(c_decl) diff --git a/modules/java/generator/gen_javadoc.py b/modules/java/generator/gen_javadoc.py index ecf6f3c55e..97eac702ad 100644 --- a/modules/java/generator/gen_javadoc.py +++ b/modules/java/generator/gen_javadoc.py @@ -222,7 +222,6 @@ class JavadocGenerator(object): return (doc + " */").replace("::",".") def printSummary(self): - print print "Javadoc Generator Summary:" print " Total markers: %s" % self.markers_processed print " Undocumented markers: %s" % (self.markers_processed - self.markers_documented) @@ -256,7 +255,6 @@ if __name__ == "__main__": parser.print_help() exit(0) - print "Parsing documentation..." parser = rst_parser.RstParser(hdr_parser.CppHeaderParser()) for m in options.modules: parser.parse(m, os.path.join(selfpath, "../../" + m)) @@ -268,7 +266,6 @@ if __name__ == "__main__": generator.show_warnings = options.warnings generator.show_errors = options.errors - print "Generating javadoc comments for " + ", ".join(options.modules) for path in args: folder = os.path.abspath(path) for jfile in [f for f in glob.glob(os.path.join(folder,"*.java")) if not f.endswith("-jdoc.java")]: diff --git a/modules/java/generator/rst_parser.py b/modules/java/generator/rst_parser.py index f622f6a63c..f4437f0c15 100644 --- a/modules/java/generator/rst_parser.py +++ b/modules/java/generator/rst_parser.py @@ -612,7 +612,6 @@ class RstParser(object): return s def printSummary(self): - print print "RST Parser Summary:" print " Total sections: %s" % self.sections_total print " Skipped sections: %s" % self.sections_skipped From 6a112aa87ae3a12dd13e2dfc9263ed4dcdf82f7d Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Thu, 6 Sep 2012 19:38:13 +0400 Subject: [PATCH 22/97] Use TBB 4.1 on Android --- 3rdparty/tbb/CMakeLists.txt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/3rdparty/tbb/CMakeLists.txt b/3rdparty/tbb/CMakeLists.txt index c89b9a3ef6..bf91f4d8b8 100644 --- a/3rdparty/tbb/CMakeLists.txt +++ b/3rdparty/tbb/CMakeLists.txt @@ -5,12 +5,18 @@ endif() project(tbb) -# 4.0 update 5 - works fine -set(tbb_ver "tbb40_20120613oss") -set(tbb_url "http://threadingbuildingblocks.org/uploads/77/187/4.0%20update%205/tbb40_20120613oss_src.tgz") -set(tbb_md5 "da01ed74944ec5950cfae3476901a172") +# 4.1 - works fine +set(tbb_ver "tbb41_20120718oss") +set(tbb_url "http://threadingbuildingblocks.org/uploads/77/188/4.1/tbb41_20120718oss_src.tgz") +set(tbb_md5 "31b9ec300f3d09da2504d5d882788dd4") set(tbb_version_file "version_string.ver") +# 4.0 update 5 - works fine +#set(tbb_ver "tbb40_20120613oss") +#set(tbb_url "http://threadingbuildingblocks.org/uploads/77/187/4.0%20update%205/tbb40_20120613oss_src.tgz") +#set(tbb_md5 "da01ed74944ec5950cfae3476901a172") +#set(tbb_version_file "version_string.ver") + # 4.0 update 4 - works fine #set(tbb_ver "tbb40_20120408oss") #set(tbb_url "http://threadingbuildingblocks.org/uploads/77/185/4.0%20update%204/tbb40_20120408oss_src.tgz") From 54a202b3d522f19a05993998ab40a40733a0d3b8 Mon Sep 17 00:00:00 2001 From: AoD314 Date: Fri, 7 Sep 2012 13:24:48 +0400 Subject: [PATCH 23/97] add new version of CommandLineParser. add empty docs --- modules/core/doc/command_line_parser.rst | 101 +++++ modules/core/doc/core.rst | 1 + modules/core/include/opencv2/core/core.hpp | 210 ++++++----- modules/core/src/cmdparser.cpp | 384 ------------------- modules/core/src/command_line_parser.cpp | 406 +++++++++++++++++++++ modules/gpu/perf/main.cpp | 26 +- modules/gpu/test/main.cpp | 23 +- modules/ts/src/ts_perf.cpp | 54 +-- samples/c/bgfg_codebook.cpp | 8 +- samples/cpp/bgfg_segm.cpp | 9 +- samples/cpp/brief_match_test.cpp | 10 +- samples/cpp/camshiftdemo.cpp | 6 +- samples/cpp/chamfer.cpp | 8 +- samples/cpp/connected_components.cpp | 4 +- samples/cpp/demhist.cpp | 4 +- samples/cpp/dft.cpp | 4 +- samples/cpp/distrans.cpp | 4 +- samples/cpp/edge.cpp | 4 +- samples/cpp/opencv_version.cpp | 16 +- samples/cpp/point_cloud.cpp | 30 +- samples/cpp/videostab.cpp | 100 ++--- samples/gpu/bgfg_segm.cpp | 17 +- samples/gpu/brox_optical_flow.cpp | 27 +- samples/gpu/farneback_optical_flow.cpp | 16 +- samples/gpu/performance/performance.cpp | 21 +- samples/gpu/pyrlk_optical_flow.cpp | 27 +- 26 files changed, 855 insertions(+), 665 deletions(-) create mode 100644 modules/core/doc/command_line_parser.rst delete mode 100644 modules/core/src/cmdparser.cpp create mode 100644 modules/core/src/command_line_parser.cpp diff --git a/modules/core/doc/command_line_parser.rst b/modules/core/doc/command_line_parser.rst new file mode 100644 index 0000000000..5f0e512c82 --- /dev/null +++ b/modules/core/doc/command_line_parser.rst @@ -0,0 +1,101 @@ +Command Line Parser +=================== + +.. highlight:: cpp + +CommandLineParser +-------- +.. ocv:class:: CommandLineParser + +The CommandLineParser class is designed for command line arguments parsing + + + .. ocv:function:: CommandLineParser::CommandLineParser(int argc, const char * const argv[], const std::string keys) + + :param argc: + :param argv: + :param keys: + + .. ocv:function:: T CommandLineParser::get(const std::string& name, bool space_delete = true) + + :param name: + :param space_delete: + + .. ocv:function:: T CommandLineParser::get(int index, bool space_delete = true) + + :param index: + :param space_delete: + + .. ocv:function:: bool CommandLineParser::has(const std::string& name) + + :param name: + + .. ocv:function:: bool CommandLineParser::check() + + + .. ocv:function:: void CommandLineParser::about(std::string message) + + :param message: + + .. ocv:function:: void CommandLineParser::printMessage() + + .. ocv:function:: void CommandLineParser::printErrors() + + .. ocv:function:: std::string CommandLineParser::getPathToApplication() + + +The sample below demonstrates how to use CommandLineParser: + +:: + + CommandLineParser parser(argc, argv, keys); + parser.about("Application name v1.0.0"); + + if (parser.has("help")) + { + parser.printMessage(); + return 0; + } + + int N = parser.get("N"); + double fps = parser.getparser("fps"); + std::string path = parser.get("path"); + + use_time_stamp = parserer.has("timestamp"); + + std::string img1 = parser.get(1); + std::string img2 = parser.get(2); + + int repeat = parser.get(3); + + if (!parser.check()) + { + parser.printErrors(); + return 0; + } + +Syntax: + +:: + + const std::string keys = + "{help h usage ? | | print this message }" + "{@image1 | | image1 for compare }" + "{@image2 | | image2 for compare }" + "{@repeat |1 | number }" + "{path |. | path to file }" + "{fps | -1.0 | fps for output video }" + "{N count |100 | count of objects }" + "{ts timestamp | | use time stamp }" + ; + +Use: + +:: + + # ./app -N=200 1.png 2.jpg 19 -ts + + # ./app -fps=aaa + ERRORS: + Exception: can not convert: [aaa] to [double] + diff --git a/modules/core/doc/core.rst b/modules/core/doc/core.rst index 7eb4e3e63a..90caeebc79 100644 --- a/modules/core/doc/core.rst +++ b/modules/core/doc/core.rst @@ -6,6 +6,7 @@ core. The Core Functionality :maxdepth: 2 basic_structures + command_line_parser old_basic_structures dynamic_structures operations_on_arrays diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index f03f21b74a..2496c805c5 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -4505,113 +4505,143 @@ template<> struct ParamType enum { type = Param::ALGORITHM }; }; +// The CommandLineParser class is designed for command line arguments parsing + +class CV_EXPORTS CommandLineParserParams +{ + public: + std::string help_message; + std::string def_value; + std::vector keys; + int number; +}; + +template +std::string get_type_name() { return "UNKNOW"; } + +bool cmp_params(const CommandLineParserParams & p1, const CommandLineParserParams & p2); + +template +T from_str(const std::string & str) +{ + T value; + std::stringstream ss(str); + ss >> value; + + if (ss.fail()) + { + std::string err_msg = + std::string("can not convert: [") + + str + + std::string("] to [") + + get_type_name() + + std::string("]"); + + CV_Error(CV_StsBadArg, err_msg); + } + + return value; +} + +template<> std::string from_str(const std::string & str); + +template +std::string to_str(T value) +{ + std::ostringstream os; + os << value; + return os.str(); +} -/*! -"\nThe CommandLineParser class is designed for command line arguments parsing\n" - "Keys map: \n" - "Before you start to work with CommandLineParser you have to create a map for keys.\n" - " It will look like this\n" - " const char* keys =\n" - " {\n" - " { s| string| 123asd |string parameter}\n" - " { d| digit | 100 |digit parameter }\n" - " { c|noCamera|false |without camera }\n" - " { 1| |some text|help }\n" - " { 2| |333 |another help }\n" - " };\n" - "Usage syntax: \n" - " \"{\" - start of parameter string.\n" - " \"}\" - end of parameter string\n" - " \"|\" - separator between short name, full name, default value and help\n" - "Supported syntax: \n" - " --key1=arg1 \n" - " -key2=arg2 \n" - "Usage: \n" - " Imagine that the input parameters are next:\n" - " -s=string_value --digit=250 --noCamera lena.jpg 10000\n" - " CommandLineParser parser(argc, argv, keys) - create a parser object\n" - " parser.get(\"s\" or \"string\") will return you first parameter value\n" - " parser.get(\"s\", false or \"string\", false) will return you first parameter value\n" - " without spaces in end and begin\n" - " parser.get(\"d\" or \"digit\") will return you second parameter value.\n" - " It also works with 'unsigned int', 'double', and 'float' types>\n" - " parser.get(\"c\" or \"noCamera\") will return you true .\n" - " If you enter this key in commandline>\n" - " It return you false otherwise.\n" - " parser.get(\"1\") will return you the first argument without parameter (lena.jpg) \n" - " parser.get(\"2\") will return you the second argument without parameter (10000)\n" - " It also works with 'unsigned int', 'double', and 'float' types \n" -*/ class CV_EXPORTS CommandLineParser { public: + CommandLineParser(int argc, const char * const argv[], const std::string keys); - //! the default constructor - CommandLineParser(int argc, const char* const argv[], const char* key_map); + std::string getPathToApplication(); - //! get parameter, you can choose: delete spaces in end and begin or not - template - _Tp get(const std::string& name, bool space_delete=true) - { - if (!has(name)) + template + T get(const std::string& name, bool space_delete = true) { - return _Tp(); + try + { + for (size_t i = 0; i < data.size(); i++) + { + for (size_t j = 0; j < data[i].keys.size(); j++) + { + if (name.compare(data[i].keys[j]) == 0) + { + std::string v = data[i].def_value; + if (space_delete == true) v = cat_string(v); + return from_str(v); + } + } + } + error = true; + error_message += "Unknown parametes " + name + "\n"; + } + catch (std::exception& e) + { + error = true; + error_message += "Exception: " + std::string(e.what()) + "\n"; + } + return T(); } - std::string str = getString(name); - return analyzeValue<_Tp>(str, space_delete); - } - //! print short name, full name, current value and help for all params - void printParams(); + template + T get(int index, bool space_delete = true) + { + try + { + for (size_t i = 0; i < data.size(); i++) + { + if (data[i].number == index - 1) + { + std::string v = data[i].def_value; + if (space_delete == true) v = cat_string(v); + return from_str(v); + } + } + error = true; + error_message += "Unknown parametes #" + to_str(index) + "\n"; + } + catch(std::exception & e) + { + error = true; + error_message += "Exception: " + std::string(e.what()) + "\n"; + } + return T(); + } + + bool has(const std::string& name); + + bool check(); + + void about(std::string message); + + void printMessage(); + void printErrors(); protected: - std::map > data; - std::string getString(const std::string& name); + bool error; + std::string error_message; + std::string about_message; - bool has(const std::string& keys); + std::string path_to_app; + std::string app_name; - template - _Tp analyzeValue(const std::string& str, bool space_delete=false); + std::vector data; - template - static _Tp getData(const std::string& str) - { - _Tp res; - std::stringstream s1(str); - s1 >> res; - return res; - } + std::vector split_range_string(std::string str, char fs, char ss); + std::vector split_string(std::string str, char symbol = ' ', bool create_empty_item = false); + std::string cat_string(std::string str); - template - _Tp fromStringNumber(const std::string& str);//the default conversion function for numbers + void apply_params(std::string key, std::string value); + void apply_params(int i, std::string value); - }; + void sort_params(); -template<> CV_EXPORTS -bool CommandLineParser::get(const std::string& name, bool space_delete); - -template<> CV_EXPORTS -std::string CommandLineParser::analyzeValue(const std::string& str, bool space_delete); - -template<> CV_EXPORTS -int CommandLineParser::analyzeValue(const std::string& str, bool space_delete); - -template<> CV_EXPORTS -unsigned int CommandLineParser::analyzeValue(const std::string& str, bool space_delete); - -template<> CV_EXPORTS -uint64 CommandLineParser::analyzeValue(const std::string& str, bool space_delete); - -template<> CV_EXPORTS -float CommandLineParser::analyzeValue(const std::string& str, bool space_delete); - -template<> CV_EXPORTS -double CommandLineParser::analyzeValue(const std::string& str, bool space_delete); +}; /////////////////////////////// Parallel Primitives ////////////////////////////////// diff --git a/modules/core/src/cmdparser.cpp b/modules/core/src/cmdparser.cpp deleted file mode 100644 index d7be054182..0000000000 --- a/modules/core/src/cmdparser.cpp +++ /dev/null @@ -1,384 +0,0 @@ -#include "precomp.hpp" - -#include -#include - -using namespace std; -using namespace cv; - -namespace { -#if 0 -static void helpParser() -{ - printf("\nThe CommandLineParser class is designed for command line arguments parsing\n" - "Keys map: \n" - "Before you start to work with CommandLineParser you have to create a map for keys.\n" - " It will look like this\n" - " const char* keys =\n" - " {\n" - " { s| string| 123asd |string parameter}\n" - " { d| digit | 100 |digit parameter }\n" - " { c|noCamera|false |without camera }\n" - " { 1| |some text|help }\n" - " { 2| |333 |another help }\n" - " };\n" - "Usage syntax: \n" - " \"{\" - start of parameter string.\n" - " \"}\" - end of parameter string\n" - " \"|\" - separator between short name, full name, default value and help\n" - "Supported syntax: \n" - " --key1=arg1 \n" - " -key2=arg2 \n" - "Usage: \n" - " Imagine that the input parameters are next:\n" - " -s=string_value --digit=250 --noCamera lena.jpg 10000\n" - " CommandLineParser parser(argc, argv, keys) - create a parser object\n" - " parser.get(\"s\" or \"string\") will return you first parameter value\n" - " parser.get(\"s\", false or \"string\", false) will return you first parameter value\n" - " without spaces in end and begin\n" - " parser.get(\"d\" or \"digit\") will return you second parameter value.\n" - " It also works with 'unsigned int', 'double', and 'float' types>\n" - " parser.get(\"c\" or \"noCamera\") will return you true .\n" - " If you enter this key in commandline>\n" - " It return you false otherwise.\n" - " parser.get(\"1\") will return you the first argument without parameter (lena.jpg) \n" - " parser.get(\"2\") will return you the second argument without parameter (10000)\n" - " It also works with 'unsigned int', 'double', and 'float' types \n" - ); -} -#endif - -vector split_string(const string& str, const string& delimiters) -{ - vector res; - - string split_str = str; - size_t pos_delim = split_str.find(delimiters); - - while ( pos_delim != string::npos) - { - if (pos_delim == 0) - { - res.push_back(""); - split_str.erase(0, 1); - } - else - { - res.push_back(split_str.substr(0, pos_delim)); - split_str.erase(0, pos_delim + 1); - } - - pos_delim = split_str.find(delimiters); - } - - res.push_back(split_str); - - return res; -} - -string del_space(string name) -{ - while ((name.find_first_of(' ') == 0) && (name.length() > 0)) - name.erase(0, 1); - - while ((name.find_last_of(' ') == (name.length() - 1)) && (name.length() > 0)) - name.erase(name.end() - 1, name.end()); - - return name; -} - -}//namespace - -CommandLineParser::CommandLineParser(int argc, const char* const argv[], const char* keys) -{ - std::string keys_buffer; - std::string values_buffer; - std::string buffer; - std::string curName; - std::vector keysVector; - std::vector paramVector; - std::map >::iterator it; - size_t flagPosition; - int currentIndex = 1; - //bool isFound = false; - bool withNoKey = false; - bool hasValueThroughEq = false; - - keys_buffer = keys; - while (!keys_buffer.empty()) - { - - flagPosition = keys_buffer.find_first_of('}'); - flagPosition++; - buffer = keys_buffer.substr(0, flagPosition); - keys_buffer.erase(0, flagPosition); - - flagPosition = buffer.find('{'); - if (flagPosition != buffer.npos) - buffer.erase(flagPosition, (flagPosition + 1)); - - flagPosition = buffer.find('}'); - if (flagPosition != buffer.npos) - buffer.erase(flagPosition); - - paramVector = split_string(buffer, "|"); - while (paramVector.size() < 4) paramVector.push_back(""); - - buffer = paramVector[0]; - buffer += '|' + paramVector[1]; - - //if (buffer == "") CV_ERROR(CV_StsBadArg, "In CommandLineParser need set short and full name"); - - paramVector.erase(paramVector.begin(), paramVector.begin() + 2); - data[buffer] = paramVector; - } - - buffer.clear(); - keys_buffer.clear(); - paramVector.clear(); - for (int i = 1; i < argc; i++) - { - if (!argv[i]) - break; - curName = argv[i]; - if (curName.find('-') == 0 && ((curName[1] < '0') || (curName[1] > '9'))) - { - while (curName.find('-') == 0) - curName.erase(curName.begin(), (curName.begin() + 1)); - } - else - withNoKey = true; - if (curName.find('=') != curName.npos) - { - hasValueThroughEq = true; - buffer = curName; - curName.erase(curName.find('=')); - buffer.erase(0, (buffer.find('=') + 1)); - } - - values_buffer = del_space(values_buffer); - - for(it = data.begin(); it != data.end(); it++) - { - keys_buffer = it->first; - keysVector = split_string(keys_buffer, "|"); - - for (size_t j = 0; j < keysVector.size(); j++) keysVector[j] = del_space(keysVector[j]); - - values_buffer = it->second[0]; - if (((curName == keysVector[0]) || (curName == keysVector[1])) && hasValueThroughEq) - { - it->second[0] = buffer; - //isFound = true; - break; - } - - if (!hasValueThroughEq && ((curName == keysVector[0]) || (curName == keysVector[1])) - && ( - values_buffer.find("false") != values_buffer.npos || - values_buffer == "" - )) - { - it->second[0] = "true"; - //isFound = true; - break; - } - - if (!hasValueThroughEq && (values_buffer.find("false") == values_buffer.npos) && - ((curName == keysVector[0]) || (curName == keysVector[1]))) - { - it->second[0] = argv[++i]; - //isFound = true; - break; - } - - - if (withNoKey) - { - std::string noKeyStr = it->first; - if(atoi(noKeyStr.c_str()) == currentIndex) - { - it->second[0] = curName; - currentIndex++; - //isFound = true; - break; - } - } - } - - withNoKey = false; - hasValueThroughEq = false; - //isFound = false; - } -} - -bool CommandLineParser::has(const std::string& keys) -{ - std::map >::iterator it; - std::vector keysVector; - - for(it = data.begin(); it != data.end(); it++) - { - keysVector = split_string(it->first, "|"); - for (size_t i = 0; i < keysVector.size(); i++) keysVector[i] = del_space(keysVector[i]); - - if (keysVector.size() == 1) keysVector.push_back(""); - - if ((del_space(keys).compare(keysVector[0]) == 0) || - (del_space(keys).compare(keysVector[1]) == 0)) - return true; - } - - return false; -} - -std::string CommandLineParser::getString(const std::string& keys) -{ - std::map >::iterator it; - std::vector valueVector; - - for(it = data.begin(); it != data.end(); it++) - { - valueVector = split_string(it->first, "|"); - for (size_t i = 0; i < valueVector.size(); i++) valueVector[i] = del_space(valueVector[i]); - - if (valueVector.size() == 1) valueVector.push_back(""); - - if ((del_space(keys).compare(valueVector[0]) == 0) || - (del_space(keys).compare(valueVector[1]) == 0)) - return it->second[0]; - } - return string(); -} - -template - _Tp CommandLineParser::fromStringNumber(const std::string& str)//the default conversion function for numbers -{ - return getData<_Tp>(str); -} - - void CommandLineParser::printParams() - { - int col_p = 30; - int col_d = 50; - - std::map >::iterator it; - std::vector keysVector; - std::string buf; - for(it = data.begin(); it != data.end(); it++) - { - keysVector = split_string(it->first, "|"); - for (size_t i = 0; i < keysVector.size(); i++) keysVector[i] = del_space(keysVector[i]); - - cout << " "; - buf = ""; - if (keysVector[0] != "") - { - buf = "-" + keysVector[0]; - if (keysVector[1] != "") buf += ", --" + keysVector[1]; - } - else if (keysVector[1] != "") buf += "--" + keysVector[1]; - if (del_space(it->second[0]) != "") buf += "=[" + del_space(it->second[0]) + "]"; - - cout << setw(col_p-2) << left << buf; - - if ((int)buf.length() > col_p-2) - { - cout << endl << " "; - cout << setw(col_p-2) << left << " "; - } - - buf = ""; - if (del_space(it->second[1]) != "") buf += del_space(it->second[1]); - - for(;;) - { - bool tr = ((int)buf.length() > col_d-2) ? true: false; - std::string::size_type pos = 0; - - if (tr) - { - pos = buf.find_first_of(' '); - for(;;) - { - if (buf.find_first_of(' ', pos + 1 ) < (std::string::size_type)(col_d-2) && - buf.find_first_of(' ', pos + 1 ) != std::string::npos) - pos = buf.find_first_of(' ', pos + 1); - else - break; - } - pos++; - cout << setw(col_d-2) << left << buf.substr(0, pos) << endl; - } - else - { - cout << setw(col_d-2) << left << buf<< endl; - break; - } - - buf.erase(0, pos); - cout << " "; - cout << setw(col_p-2) << left << " "; - } - } - } - -template<> -bool CommandLineParser::get(const std::string& name, bool space_delete) -{ - std::string str_buf = getString(name); - - if (space_delete && str_buf != "") - { - str_buf = del_space(str_buf); - } - - if (str_buf == "true") - return true; - - return false; -} -template<> -std::string CommandLineParser::analyzeValue(const std::string& str, bool space_delete) -{ - if (space_delete) - { - return del_space(str); - } - return str; -} - -template<> -int CommandLineParser::analyzeValue(const std::string& str, bool /*space_delete*/) -{ - return fromStringNumber(str); -} - -template<> -unsigned int CommandLineParser::analyzeValue(const std::string& str, bool /*space_delete*/) -{ - return fromStringNumber(str); -} - -template<> -uint64 CommandLineParser::analyzeValue(const std::string& str, bool /*space_delete*/) -{ - return fromStringNumber(str); -} - -template<> -float CommandLineParser::analyzeValue(const std::string& str, bool /*space_delete*/) -{ - return fromStringNumber(str); -} - -template<> -double CommandLineParser::analyzeValue(const std::string& str, bool /*space_delete*/) -{ - return fromStringNumber(str); -} diff --git a/modules/core/src/command_line_parser.cpp b/modules/core/src/command_line_parser.cpp new file mode 100644 index 0000000000..dc137e7296 --- /dev/null +++ b/modules/core/src/command_line_parser.cpp @@ -0,0 +1,406 @@ + +#include "precomp.hpp" + +#include + +namespace cv +{ + bool cmp_params(const CommandLineParserParams & p1, const CommandLineParserParams & p2) + { + if (p1.number > p2.number) + return false; + + if (p1.number == -1 && p2.number == -1) + { + if (p1.keys[0].compare(p2.keys[0]) > 0) + { + return false; + } + } + + return true; + } + + CommandLineParser::CommandLineParser(int argc, const char * const argv[], const std::string keys) + { + // path to application + size_t pos_s = std::string(argv[0]).find_last_of("/\\"); + if (pos_s == std::string::npos) + { + path_to_app = ""; + app_name = std::string(argv[0]); + } + else + { + path_to_app = std::string(argv[0]).substr(0, pos_s); + app_name = std::string(argv[0]).substr(pos_s + 1, std::string(argv[0]).length() - pos_s); + } + + error = false; + error_message = ""; + + // parse keys + std::vector k = split_range_string(keys, '{', '}'); + + int jj = 0; + for (size_t i = 0; i < k.size(); i++) + { + std::vector l = split_string(k[i], '|', true); + CommandLineParserParams p; + p.keys = split_string(l[0]); + p.def_value = l[1]; + p.help_message = cat_string(l[2]); + p.number = -1; + if (p.keys[0][0] == '@') + { + p.number = jj; + jj++; + } + + data.push_back(p); + } + + // parse argv + jj = 0; + for (int i = 1; i < argc; i++) + { + std::string s = std::string(argv[i]); + + if (s.find('=') != std::string::npos && s.find('=') < s.length()) + { + std::vector k_v = split_string(s, '=', true); + for (int h = 0; h < 2; h++) + { + if (k_v[0][0] == '-') + k_v[0] = k_v[0].substr(1, k_v[0].length() -1); + } + apply_params(k_v[0], k_v[1]); + } + else if (s.length() > 1 && s[0] == '-') + { + for (int h = 0; h < 2; h++) + { + if (s[0] == '-') + s = s.substr(1, s.length() - 1); + } + apply_params(s, "true"); + } + else if (s[0] != '-') + { + apply_params(jj, s); + jj++; + } + } + + sort_params(); + } + + void CommandLineParser::about(std::string message) + { + about_message = message; + } + + void CommandLineParser::apply_params(std::string key, std::string value) + { + for (size_t i = 0; i < data.size(); i++) + { + for (size_t k = 0; k < data[i].keys.size(); k++) + { + if (key.compare(data[i].keys[k]) == 0) + { + data[i].def_value = value; + break; + } + } + } + } + + void CommandLineParser::apply_params(int i, std::string value) + { + for (size_t j = 0; j < data.size(); j++) + { + if (data[j].number == i) + { + data[j].def_value = value; + break; + } + } + } + + void CommandLineParser::sort_params() + { + for (size_t i = 0; i < data.size(); i++) + { + sort(data[i].keys.begin(), data[i].keys.end()); + } + + sort (data.begin(), data.end(), cmp_params); + } + + std::string CommandLineParser::cat_string(std::string str) + { + while (!str.empty() && str[0] == ' ') + { + str = str.substr(1, str.length() - 1); + } + + while (!str.empty() && str[str.length() - 1] == ' ') + { + str = str.substr(0, str.length() - 1); + } + + return str; + } + + std::string CommandLineParser::getPathToApplication() + { + return path_to_app; + } + + bool CommandLineParser::has(const std::string& name) + { + for (size_t i = 0; i < data.size(); i++) + { + for (size_t j = 0; j < data[i].keys.size(); j++) + { + if (name.compare(data[i].keys[j]) == 0 && std::string("true").compare(data[i].def_value) == 0) + { + return true; + } + } + } + return false; + } + + bool CommandLineParser::check() + { + return error == false; + } + + void CommandLineParser::printErrors() + { + if (error) + { + std::cout << std::endl << "ERRORS:" << std::endl << error_message << std::endl; + } + } + + void CommandLineParser::printMessage() + { + if (about_message != "") + std::cout << about_message << std::endl; + + std::cout << "Usage: " << app_name << " [params] "; + + for (size_t i = 0; i < data.size(); i++) + { + if (data[i].number > -1) + { + std::string name = data[i].keys[0].substr(1, data[i].keys[0].length() - 1); + std::cout << name << " "; + } + } + + std::cout << std::endl << std::endl; + + for (size_t i = 0; i < data.size(); i++) + { + if (data[i].number == -1) + { + std::cout << "\t"; + for (size_t j = 0; j < data[i].keys.size(); j++) + { + std::string k = data[i].keys[j]; + if (k.length() > 1) + { + std::cout << "--"; + } + else + { + std::cout << "-"; + } + std::cout << k; + + if (j != data[i].keys.size() - 1) + { + std::cout << ", "; + } + } + std::string dv = cat_string(data[i].def_value); + if (dv.compare("") != 0) + { + std::cout << " (value:" << dv << ")"; + } + std::cout << std::endl << "\t\t" << data[i].help_message << std::endl; + } + } + std::cout << std::endl; + + for (size_t i = 0; i < data.size(); i++) + { + if (data[i].number != -1) + { + std::cout << "\t"; + std::string k = data[i].keys[0]; + k = k.substr(1, k.length() - 1); + + std::cout << k; + + std::string dv = cat_string(data[i].def_value); + if (dv.compare("") != 0) + { + std::cout << " (value:" << dv << ")"; + } + std::cout << std::endl << "\t\t" << data[i].help_message << std::endl; + } + } + } + + std::vector CommandLineParser::split_range_string(std::string str, char fs, char ss) + { + std::vector vec; + std::string word = ""; + bool begin = false; + + while (!str.empty()) + { + if (str[0] == fs) + { + if (begin == true) + { + CV_Error(CV_StsParseError, + std::string("error in split_range_string(") + + str + + std::string(", ") + + std::string(1, fs) + + std::string(", ") + + std::string(1, ss) + + std::string(")") + ); + } + begin = true; + word = ""; + str = str.substr(1, str.length() - 1); + } + + if (str[0] == ss) + { + if (begin == false) + { + CV_Error(CV_StsParseError, + std::string("error in split_range_string(") + + str + + std::string(", ") + + std::string(1, fs) + + std::string(", ") + + std::string(1, ss) + + std::string(")") + ); + } + begin = false; + vec.push_back(word); + } + + if (begin == true) + { + word += str[0]; + } + str = str.substr(1, str.length() - 1); + } + + if (begin == true) + { + CV_Error(CV_StsParseError, + std::string("error in split_range_string(") + + str + + std::string(", ") + + std::string(1, fs) + + std::string(", ") + + std::string(1, ss) + + std::string(")") + ); + } + + return vec; + } + + std::vector CommandLineParser::split_string(std::string str, char symbol, bool create_empty_item) + { + std::vector vec; + std::string word = ""; + + while (!str.empty()) + { + if (str[0] == symbol) + { + if (!word.empty() || create_empty_item) + { + vec.push_back(word); + word = ""; + } + } + else + { + word += str[0]; + } + str = str.substr(1, str.length() - 1); + } + + if (word != "" || create_empty_item) + { + vec.push_back(word); + } + + return vec; + } + + #undef clp_get + #define clp_get(T) template<> T CommandLineParser::get(const std::string& name, bool space_delete); + + clp_get(int) + clp_get(unsigned int) + clp_get(long) + clp_get(unsigned long) + clp_get(long long) + clp_get(unsigned long long) + clp_get(size_t) + clp_get(float) + clp_get(double) + clp_get(uint64) + clp_get(int64) + clp_get(std::string) + + #undef clp_from_str + #define clp_from_str(T) template<> T from_str(const std::string & str); + + clp_from_str(int) + clp_from_str(unsigned int) + clp_from_str(long) + clp_from_str(unsigned long) + clp_from_str(long long) + clp_from_str(unsigned long long) + clp_from_str(size_t) + clp_from_str(uint64) + clp_from_str(int64) + clp_from_str(float) + clp_from_str(double) + + template<> + std::string from_str(const std::string & str) + { + return str; + } + + #undef clp_type_name + #define clp_type_name(type, name) template<> std::string get_type_name() { return std::string(name);} + + clp_type_name(int, "int") + clp_type_name(unsigned int, "unsigned int") + clp_type_name(long, "long") + clp_type_name(long long, "long long") + clp_type_name(unsigned long long, "unsigned long long") + clp_type_name(size_t, "size_t") + clp_type_name(float, "float") + clp_type_name(double, "double") + +} diff --git a/modules/gpu/perf/main.cpp b/modules/gpu/perf/main.cpp index aadeee955b..8d34041f36 100644 --- a/modules/gpu/perf/main.cpp +++ b/modules/gpu/perf/main.cpp @@ -73,20 +73,32 @@ void printCudaInfo() int main(int argc, char** argv) { - CommandLineParser cmd(argc, (const char**) argv, - "{ print_info_only | print_info_only | false | Print information about system and exit }" - "{ device | device | 0 | Device on which tests will be executed }" - "{ cpu | cpu | false | Run tests on cpu }" - ); + const std::string keys = + "{ h help ? | | Print help}" + "{ i info | | Print information about system and exit }" + "{ device | 0 | Device on which tests will be executed }" + "{ cpu | | Run tests on cpu }" + ; + + CommandLineParser cmd(argc, (const char**) argv, keys); + + if (cmd.has("help")) + { + cmd.printMessage(); + return 0; + } printOsInfo(); printCudaInfo(); - if (cmd.get("print_info_only")) + + if (cmd.has("info")) + { return 0; + } int device = cmd.get("device"); - bool cpu = cmd.get("cpu"); + bool cpu = cmd.has("cpu"); #ifndef HAVE_CUDA cpu = true; #endif diff --git a/modules/gpu/test/main.cpp b/modules/gpu/test/main.cpp index 8f216c9fea..2e1edf0db9 100644 --- a/modules/gpu/test/main.cpp +++ b/modules/gpu/test/main.cpp @@ -118,17 +118,28 @@ int main(int argc, char** argv) { try { - CommandLineParser cmd(argc, (const char**)argv, - "{ print_info_only | print_info_only | false | Print information about system and exit }" - "{ device | device | -1 | Device on which tests will be executed (-1 means all devices) }" - "{ nvtest_output_level | nvtest_output_level | compact | NVidia test verbosity level }" - ); + const std::string keys = + "{ h help ? | | Print help}" + "{ i info | | Print information about system and exit }" + "{ device | -1 | Device on which tests will be executed (-1 means all devices) }" + "{ nvtest_output_level | compact | NVidia test verbosity level (none, compact, full) }" + ; + + CommandLineParser cmd(argc, (const char**)argv, keys); + + if (cmd.has("help")) + { + cmd.printMessage(); + return 0; + } printOsInfo(); printCudaInfo(); - if (cmd.get("print_info_only")) + if (cmd.has("info")) + { return 0; + } int device = cmd.get("device"); if (device < 0) diff --git a/modules/ts/src/ts_perf.cpp b/modules/ts/src/ts_perf.cpp index cd389f1d8f..0bb5d9ce12 100644 --- a/modules/ts/src/ts_perf.cpp +++ b/modules/ts/src/ts_perf.cpp @@ -10,24 +10,23 @@ int64 TestBase::timeLimitDefault = 0; unsigned int TestBase::iterationsLimitDefault = (unsigned int)(-1); int64 TestBase::_timeadjustment = 0; -const char *command_line_keys = -{ - "{ |perf_max_outliers |8 |percent of allowed outliers}" - "{ |perf_min_samples |10 |minimal required numer of samples}" - "{ |perf_force_samples |100 |force set maximum number of samples for all tests}" - "{ |perf_seed |809564 |seed for random numbers generator}" - "{ |perf_tbb_nthreads |-1 |if TBB is enabled, the number of TBB threads}" - "{ |perf_write_sanity |false |allow to create new records for sanity checks}" +const std::string command_line_keys = + "{ perf_max_outliers |8 |percent of allowed outliers}" + "{ perf_min_samples |10 |minimal required numer of samples}" + "{ perf_force_samples |100 |force set maximum number of samples for all tests}" + "{ perf_seed |809564 |seed for random numbers generator}" + "{ perf_tbb_nthreads |-1 |if TBB is enabled, the number of TBB threads}" + "{ perf_write_sanity | |allow to create new records for sanity checks}" #ifdef ANDROID - "{ |perf_time_limit |6.0 |default time limit for a single test (in seconds)}" - "{ |perf_affinity_mask |0 |set affinity mask for the main thread}" - "{ |perf_log_power_checkpoints |false |additional xml logging for power measurement}" + "{ perf_time_limit |6.0 |default time limit for a single test (in seconds)}" + "{ perf_affinity_mask |0 |set affinity mask for the main thread}" + "{ perf_log_power_checkpoints | |additional xml logging for power measurement}" #else - "{ |perf_time_limit |3.0 |default time limit for a single test (in seconds)}" + "{ perf_time_limit |3.0 |default time limit for a single test (in seconds)}" #endif - "{ |perf_max_deviation |1.0 |}" - "{h |help |false |}" -}; + "{ perf_max_deviation |1.0 |}" + "{ help h | |print help info}" +; static double param_max_outliers; static double param_max_deviation; @@ -526,23 +525,28 @@ performance_metrics::performance_metrics() void TestBase::Init(int argc, const char* const argv[]) { cv::CommandLineParser args(argc, argv, command_line_keys); - param_max_outliers = std::min(100., std::max(0., args.get("perf_max_outliers"))); - param_min_samples = std::max(1u, args.get("perf_min_samples")); + if (args.has("help")) + { + args.printMessage(); + return; + } + + param_max_outliers = std::min(100., std::max(0., args.get("perf_max_outliers"))); + param_min_samples = std::max(1u, args.get("perf_min_samples")); param_max_deviation = std::max(0., args.get("perf_max_deviation")); - param_seed = args.get("perf_seed"); - param_time_limit = std::max(0., args.get("perf_time_limit")); + param_seed = args.get("perf_seed"); + param_time_limit = std::max(0., args.get("perf_time_limit")); param_force_samples = args.get("perf_force_samples"); - param_write_sanity = args.get("perf_write_sanity"); + param_write_sanity = args.has("perf_write_sanity"); param_tbb_nthreads = args.get("perf_tbb_nthreads"); #ifdef ANDROID - param_affinity_mask = args.get("perf_affinity_mask"); - log_power_checkpoints = args.get("perf_log_power_checkpoints"); + param_affinity_mask = args.get("perf_affinity_mask"); + log_power_checkpoints = args.has("perf_log_power_checkpoints"); #endif - if (args.get("help")) + if (!args.check()) { - args.printParams(); - printf("\n\n"); + args.printErrors(); return; } diff --git a/samples/c/bgfg_codebook.cpp b/samples/c/bgfg_codebook.cpp index 2b157283d5..3054e9971b 100644 --- a/samples/c/bgfg_codebook.cpp +++ b/samples/c/bgfg_codebook.cpp @@ -72,9 +72,9 @@ static void help() // const char *keys = { - "{nf|nframes |300 |frames number}" - "{c |camera |false |use the camera or not}" - "{mf|movie_file|tree.avi |used movie video file}" + "{nf nframes |300 |frames number}" + "{c camera |false |use the camera or not}" + "{mf movie_file|tree.avi |used movie video file}" }; int main(int argc, const char** argv) { @@ -82,7 +82,7 @@ int main(int argc, const char** argv) CommandLineParser parser(argc, argv, keys); int nframesToLearnBG = parser.get("nf"); - bool useCamera = parser.get("c"); + bool useCamera = parser.has("c"); string filename = parser.get("mf"); IplImage* rawImage = 0, *yuvImage = 0; //yuvImage is for codebook method IplImage *ImaskCodeBook = 0,*ImaskCodeBookCC = 0; diff --git a/samples/cpp/bgfg_segm.cpp b/samples/cpp/bgfg_segm.cpp index ffb238292b..72566ea8f7 100644 --- a/samples/cpp/bgfg_segm.cpp +++ b/samples/cpp/bgfg_segm.cpp @@ -18,8 +18,8 @@ static void help() const char* keys = { - "{c |camera |true | use camera or not}" - "{fn|file_name|tree.avi | movie file }" + "{c camera | | use camera or not}" + "{fn file_name|tree.avi | movie file }" }; //this is a sample for foreground detection functions @@ -28,7 +28,7 @@ int main(int argc, const char** argv) help(); CommandLineParser parser(argc, argv, keys); - bool useCamera = parser.get("camera"); + bool useCamera = parser.has("camera"); string file = parser.get("file_name"); VideoCapture cap; bool update_bg_model = true; @@ -37,7 +37,8 @@ int main(int argc, const char** argv) cap.open(0); else cap.open(file.c_str()); - parser.printParams(); + + parser.printMessage(); if( !cap.isOpened() ) { diff --git a/samples/cpp/brief_match_test.cpp b/samples/cpp/brief_match_test.cpp index 9223f21319..b4c81ee246 100644 --- a/samples/cpp/brief_match_test.cpp +++ b/samples/cpp/brief_match_test.cpp @@ -53,8 +53,8 @@ static void help() const char* keys = { - "{1| |box.png |the first image}" - "{2| |box_in_scene.png|the second image}" + "{@first_image | box.png | the first image}" + "{@second_image | box_in_scene.png | the second image}" }; int main(int argc, const char ** argv) @@ -62,8 +62,8 @@ int main(int argc, const char ** argv) help(); CommandLineParser parser(argc, argv, keys); - string im1_name = parser.get("1"); - string im2_name = parser.get("2"); + string im1_name = parser.get(1); + string im2_name = parser.get(2); Mat im1 = imread(im1_name, CV_LOAD_IMAGE_GRAYSCALE); Mat im2 = imread(im2_name, CV_LOAD_IMAGE_GRAYSCALE); @@ -72,7 +72,7 @@ int main(int argc, const char ** argv) { cout << "could not open one of the images..." << endl; cout << "the cmd parameters have next current value: " << endl; - parser.printParams(); + parser.printMessage(); return 1; } diff --git a/samples/cpp/camshiftdemo.cpp b/samples/cpp/camshiftdemo.cpp index 0b17239edd..f9f393f9ee 100644 --- a/samples/cpp/camshiftdemo.cpp +++ b/samples/cpp/camshiftdemo.cpp @@ -64,7 +64,7 @@ static void help() const char* keys = { - "{1| | 0 | camera number}" + "{@camera_number| 0 | camera number}" }; int main( int argc, const char** argv ) @@ -77,7 +77,7 @@ int main( int argc, const char** argv ) float hranges[] = {0,180}; const float* phranges = hranges; CommandLineParser parser(argc, argv, keys); - int camNum = parser.get("1"); + int camNum = parser.get(1); cap.open(camNum); @@ -86,7 +86,7 @@ int main( int argc, const char** argv ) help(); cout << "***Could not initialize capturing...***\n"; cout << "Current parameter's value: \n"; - parser.printParams(); + parser.printMessage(); return -1; } diff --git a/samples/cpp/chamfer.cpp b/samples/cpp/chamfer.cpp index 4be87cf768..906c9542a3 100644 --- a/samples/cpp/chamfer.cpp +++ b/samples/cpp/chamfer.cpp @@ -19,8 +19,8 @@ static void help() const char* keys = { - "{1| |logo_in_clutter.png|image edge map }" - "{2| |logo.png |template edge map}" + "{@logo1 |logo_in_clutter.png |image edge map }" + "{@logo2 |logo.png |template edge map}" }; int main( int argc, const char** argv ) @@ -29,8 +29,8 @@ int main( int argc, const char** argv ) help(); CommandLineParser parser(argc, argv, keys); - string image = parser.get("1"); - string templ = parser.get("2"); + string image = parser.get(1); + string templ = parser.get(2); Mat img = imread(image.c_str(), 0); Mat tpl = imread(templ.c_str(), 0); diff --git a/samples/cpp/connected_components.cpp b/samples/cpp/connected_components.cpp index b83660d0cf..c915bcda4a 100644 --- a/samples/cpp/connected_components.cpp +++ b/samples/cpp/connected_components.cpp @@ -45,14 +45,14 @@ static void help() const char* keys = { - "{1| |stuff.jpg|image for converting to a grayscale}" + "{@image |stuff.jpg|image for converting to a grayscale}" }; int main( int argc, const char** argv ) { help(); CommandLineParser parser(argc, argv, keys); - string inputImage = parser.get("1"); + string inputImage = parser.get(1); img = imread(inputImage.c_str(), 0); if(img.empty()) diff --git a/samples/cpp/demhist.cpp b/samples/cpp/demhist.cpp index 59ecf25aad..d982ecb33b 100644 --- a/samples/cpp/demhist.cpp +++ b/samples/cpp/demhist.cpp @@ -62,7 +62,7 @@ static void help() const char* keys = { - "{1| |baboon.jpg|input image file}" + "{@image|baboon.jpg|input image file}" }; int main( int argc, const char** argv ) @@ -70,7 +70,7 @@ int main( int argc, const char** argv ) help(); CommandLineParser parser(argc, argv, keys); - string inputImage = parser.get("1"); + string inputImage = parser.get(1); // Load the source image. HighGUI use. image = imread( inputImage, 0 ); diff --git a/samples/cpp/dft.cpp b/samples/cpp/dft.cpp index dbbc2cc775..62dba20d30 100644 --- a/samples/cpp/dft.cpp +++ b/samples/cpp/dft.cpp @@ -17,14 +17,14 @@ static void help() const char* keys = { - "{1| |lena.jpg|input image file}" + "{@image|lena.jpg|input image file}" }; int main(int argc, const char ** argv) { help(); CommandLineParser parser(argc, argv, keys); - string filename = parser.get("1"); + string filename = parser.get(1); Mat img = imread(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE); if( img.empty() ) diff --git a/samples/cpp/distrans.cpp b/samples/cpp/distrans.cpp index 4e3c3a2b0f..5e805775cf 100644 --- a/samples/cpp/distrans.cpp +++ b/samples/cpp/distrans.cpp @@ -104,14 +104,14 @@ static void help() const char* keys = { - "{1| |stuff.jpg|input image file}" + "{@image |stuff.jpg|input image file}" }; int main( int argc, const char** argv ) { help(); CommandLineParser parser(argc, argv, keys); - string filename = parser.get("1"); + string filename = parser.get(1); gray = imread(filename.c_str(), 0); if(gray.empty()) { diff --git a/samples/cpp/edge.cpp b/samples/cpp/edge.cpp index 6ff30a2cf9..8deda898bf 100644 --- a/samples/cpp/edge.cpp +++ b/samples/cpp/edge.cpp @@ -31,7 +31,7 @@ static void help() const char* keys = { - "{1| |fruits.jpg|input image name}" + "{@image |fruits.jpg|input image name}" }; int main( int argc, const char** argv ) @@ -39,7 +39,7 @@ int main( int argc, const char** argv ) help(); CommandLineParser parser(argc, argv, keys); - string filename = parser.get("1"); + string filename = parser.get(1); image = imread(filename, 1); if(image.empty()) diff --git a/samples/cpp/opencv_version.cpp b/samples/cpp/opencv_version.cpp index 5eb4bcc7ba..635f19151e 100644 --- a/samples/cpp/opencv_version.cpp +++ b/samples/cpp/opencv_version.cpp @@ -3,19 +3,23 @@ const char* keys = { - "{ b |build |false | print complete build info }" - "{ h |help |false | print this help }" + "{ b build | | print complete build info }" + "{ h help | | print this help }" }; int main(int argc, const char* argv[]) { cv::CommandLineParser parser(argc, argv, keys); - if (parser.get("help")) + if (parser.has("help")) { - parser.printParams(); + parser.printMessage(); } - else if (parser.get("build")) + else if (!parser.check()) + { + parser.printErrors(); + } + else if (parser.has("build")) { std::cout << cv::getBuildInformation() << std::endl; } @@ -25,4 +29,4 @@ int main(int argc, const char* argv[]) } return 0; -} \ No newline at end of file +} diff --git a/samples/cpp/point_cloud.cpp b/samples/cpp/point_cloud.cpp index a9dda17ee6..998fafc797 100644 --- a/samples/cpp/point_cloud.cpp +++ b/samples/cpp/point_cloud.cpp @@ -64,20 +64,19 @@ static void openGlDrawCallback(void* userdata) int main(int argc, const char* argv[]) { const char* keys = - "{ l | left | | left image file name }" - "{ r | right | | right image file name }" - "{ i | intrinsic | | intrinsic camera parameters file name }" - "{ e | extrinsic | | extrinsic camera parameters file name }" - "{ d | ndisp | 256 | number of disparities }" - "{ s | scale | 1.0 | scale factor for point cloud }" - "{ h | help | false | print help message }"; + "{ l left | | left image file name }" + "{ r right | | right image file name }" + "{ i intrinsic | | intrinsic camera parameters file name }" + "{ e extrinsic | | extrinsic camera parameters file name }" + "{ d ndisp | 256 | number of disparities }" + "{ s scale | 1.0 | scale factor for point cloud }" + "{ h help | | print help message }"; CommandLineParser cmd(argc, argv, keys); - if (cmd.get("help")) + if (cmd.has("help")) { - cout << "Avaible options:" << endl; - cmd.printParams(); + cmd.printMessage(); return 0; } @@ -88,11 +87,18 @@ int main(int argc, const char* argv[]) int ndisp = cmd.get("ndisp"); double scale = cmd.get("scale"); + if (!cmd.check()) + { + cmd.printErrors(); + return 0; + } + + if (left.empty() || right.empty()) { cout << "Missed input images" << endl; cout << "Avaible options:" << endl; - cmd.printParams(); + cmd.printMessage(); return 0; } @@ -100,7 +106,7 @@ int main(int argc, const char* argv[]) { cout << "Boss camera parameters must be specified" << endl; cout << "Avaible options:" << endl; - cmd.printParams(); + cmd.printMessage(); return 0; } diff --git a/samples/cpp/videostab.cpp b/samples/cpp/videostab.cpp index 7a0ef56f2f..ee5bd1f3be 100644 --- a/samples/cpp/videostab.cpp +++ b/samples/cpp/videostab.cpp @@ -281,56 +281,56 @@ int main(int argc, const char **argv) try { const char *keys = - "{ 1 | | | | }" - "{ m | model | affine | }" - "{ lp | lin-prog-motion-est | no | }" - "{ | subset | auto | }" - "{ | thresh | auto | }" - "{ | outlier-ratio | 0.5 | }" - "{ | min-inlier-ratio | 0.1 | }" - "{ | nkps | 1000 | }" - "{ | extra-kps | 0 | }" - "{ | local-outlier-rejection | no | }" - "{ sm | save-motions | no | }" - "{ lm | load-motions | no | }" - "{ r | radius | 15 | }" - "{ | stdev | auto | }" - "{ lps | lin-prog-stab | no | }" - "{ | lps-trim-ratio | auto | }" - "{ | lps-w1 | 1 | }" - "{ | lps-w2 | 10 | }" - "{ | lps-w3 | 100 | }" - "{ | lps-w4 | 100 | }" - "{ | deblur | no | }" - "{ | deblur-sens | 0.1 | }" - "{ et | est-trim | yes | }" - "{ t | trim-ratio | 0.1 | }" - "{ ic | incl-constr | no | }" - "{ bm | border-mode | replicate | }" - "{ | mosaic | no | }" - "{ ms | mosaic-stdev | 10.0 | }" - "{ mi | motion-inpaint | no | }" - "{ | mi-dist-thresh | 5.0 | }" - "{ ci | color-inpaint | no | }" - "{ | ci-radius | 2 | }" - "{ ws | wobble-suppress | no | }" - "{ | ws-period | 30 | }" - "{ | ws-model | homography | }" - "{ | ws-subset | auto | }" - "{ | ws-thresh | auto | }" - "{ | ws-outlier-ratio | 0.5 | }" - "{ | ws-min-inlier-ratio | 0.1 | }" - "{ | ws-nkps | 1000 | }" - "{ | ws-extra-kps | 0 | }" - "{ | ws-local-outlier-rejection | no | }" - "{ | ws-lp | no | }" - "{ sm2 | save-motions2 | no | }" - "{ lm2 | load-motions2 | no | }" - "{ gpu | | no }" - "{ o | output | stabilized.avi | }" - "{ | fps | auto | }" - "{ q | quiet | false | }" - "{ h | help | false | }"; + "{ @1 | | }" + "{ m model | affine | }" + "{ lp lin-prog-motion-est | no | }" + "{ subset | auto | }" + "{ thresh | auto | }" + "{ outlier-ratio | 0.5 | }" + "{ min-inlier-ratio | 0.1 | }" + "{ nkps | 1000 | }" + "{ extra-kps | 0 | }" + "{ local-outlier-rejection | no | }" + "{ sm save-motions | no | }" + "{ lm load-motions | no | }" + "{ r radius | 15 | }" + "{ stdev | auto | }" + "{ lps lin-prog-stab | no | }" + "{ lps-trim-ratio | auto | }" + "{ lps-w1 | 1 | }" + "{ lps-w2 | 10 | }" + "{ lps-w3 | 100 | }" + "{ lps-w4 | 100 | }" + "{ deblur | no | }" + "{ deblur-sens | 0.1 | }" + "{ et est-trim | yes | }" + "{ t trim-ratio | 0.1 | }" + "{ ic incl-constr | no | }" + "{ bm border-mode | replicate | }" + "{ mosaic | no | }" + "{ ms mosaic-stdev | 10.0 | }" + "{ mi motion-inpaint | no | }" + "{ mi-dist-thresh | 5.0 | }" + "{ ci color-inpaint | no | }" + "{ ci-radius | 2 | }" + "{ ws wobble-suppress | no | }" + "{ ws-period | 30 | }" + "{ ws-model | homography | }" + "{ ws-subset | auto | }" + "{ ws-thresh | auto | }" + "{ ws-outlier-ratio | 0.5 | }" + "{ ws-min-inlier-ratio | 0.1 | }" + "{ ws-nkps | 1000 | }" + "{ ws-extra-kps | 0 | }" + "{ ws-local-outlier-rejection | no | }" + "{ ws-lp | no | }" + "{ sm2 save-motions2 | no | }" + "{ lm2 load-motions2 | no | }" + "{ gpu | no | }" + "{ o output | stabilized.avi | }" + "{ fps | auto | }" + "{ q quiet | | }" + "{ h help | | }"; CommandLineParser cmd(argc, argv, keys); // parse command arguments diff --git a/samples/gpu/bgfg_segm.cpp b/samples/gpu/bgfg_segm.cpp index 4e4c52096c..9456b15e88 100644 --- a/samples/gpu/bgfg_segm.cpp +++ b/samples/gpu/bgfg_segm.cpp @@ -21,20 +21,19 @@ enum Method int main(int argc, const char** argv) { cv::CommandLineParser cmd(argc, argv, - "{ c | camera | false | use camera }" - "{ f | file | 768x576.avi | input video file }" - "{ m | method | mog | method (fgd, mog, mog2, vibe, gmg) }" - "{ h | help | false | print help message }"); + "{ c camera | | use camera }" + "{ f file | 768x576.avi | input video file }" + "{ m method | mog | method (fgd, mog, mog2, vibe, gmg) }" + "{ h help | | print help message }"); - if (cmd.get("help")) + if (cmd.has("help") || !cmd.check()) { - cout << "Usage : bgfg_segm [options]" << endl; - cout << "Avaible options:" << endl; - cmd.printParams(); + cmd.printMessage(); + cmd.printErrors(); return 0; } - bool useCamera = cmd.get("camera"); + bool useCamera = cmd.has("camera"); string file = cmd.get("file"); string method = cmd.get("method"); diff --git a/samples/gpu/brox_optical_flow.cpp b/samples/gpu/brox_optical_flow.cpp index 8ff0df4906..76339c9bf2 100644 --- a/samples/gpu/brox_optical_flow.cpp +++ b/samples/gpu/brox_optical_flow.cpp @@ -25,24 +25,23 @@ int main(int argc, const char* argv[]) try { const char* keys = - "{ h | help | false | print help message }" - "{ l | left | | specify left image }" - "{ r | right | | specify right image }" - "{ s | scale | 0.8 | set pyramid scale factor }" - "{ a | alpha | 0.197 | set alpha }" - "{ g | gamma | 50.0 | set gamma }" - "{ i | inner | 10 | set number of inner iterations }" - "{ o | outer | 77 | set number of outer iterations }" - "{ si | solver | 10 | set number of basic solver iterations }" - "{ t | time_step | 0.1 | set frame interpolation time step }"; + "{ h help | | print help message }" + "{ l left | | specify left image }" + "{ r right | | specify right image }" + "{ s scale | 0.8 | set pyramid scale factor }" + "{ a alpha | 0.197 | set alpha }" + "{ g gamma | 50.0 | set gamma }" + "{ i inner | 10 | set number of inner iterations }" + "{ o outer | 77 | set number of outer iterations }" + "{ si solver | 10 | set number of basic solver iterations }" + "{ t time_step | 0.1 | set frame interpolation time step }"; CommandLineParser cmd(argc, argv, keys); - if (cmd.get("help")) + if (cmd.has("help") || !cmd.check()) { - cout << "Usage: brox_optical_flow [options]" << endl; - cout << "Avaible options:" << endl; - cmd.printParams(); + cmd.printMessage(); + cmd.printErrors(); return 0; } diff --git a/samples/gpu/farneback_optical_flow.cpp b/samples/gpu/farneback_optical_flow.cpp index e33c07e4db..77ee49e1a1 100644 --- a/samples/gpu/farneback_optical_flow.cpp +++ b/samples/gpu/farneback_optical_flow.cpp @@ -43,19 +43,19 @@ static void colorizeFlow(const Mat &u, const Mat &v, Mat &dst) int main(int argc, char **argv) { CommandLineParser cmd(argc, argv, - "{ l | left | | specify left image }" - "{ r | right | | specify right image }" - "{ h | help | false | print help message }"); + "{ l left | | specify left image }" + "{ r right | | specify right image }" + "{ h help | | print help message }"); - if (cmd.get("help")) + cmd.about("Farneback's optical flow sample."); + if (cmd.has("help") || !cmd.check()) { - cout << "Farneback's optical flow sample.\n\n" - << "Usage: farneback_optical_flow_gpu [arguments]\n\n" - << "Arguments:\n"; - cmd.printParams(); + cmd.printMessage(); + cmd.printErrors(); return 0; } + string pathL = cmd.get("left"); string pathR = cmd.get("right"); if (pathL.empty()) cout << "Specify left image path\n"; diff --git a/samples/gpu/performance/performance.cpp b/samples/gpu/performance/performance.cpp index 1cb917605b..2f5448b49a 100644 --- a/samples/gpu/performance/performance.cpp +++ b/samples/gpu/performance/performance.cpp @@ -165,22 +165,23 @@ int main(int argc, const char* argv[]) redirectError(cvErrorCallback); const char* keys = - "{ h | help | false | print help message }" - "{ f | filter | | filter for test }" - "{ w | workdir | | set working directory }" - "{ l | list | false | show all tests }" - "{ d | device | 0 | device id }" - "{ i | iters | 10 | iteration count }"; + "{ h help | | print help message }" + "{ f filter | | filter for test }" + "{ w workdir | | set working directory }" + "{ l list | | show all tests }" + "{ d device | 0 | device id }" + "{ i iters | 10 | iteration count }"; CommandLineParser cmd(argc, argv, keys); - if (cmd.get("help")) + if (cmd.has("help") || !cmd.check()) { - cout << "Avaible options:" << endl; - cmd.printParams(); + cmd.printMessage(); + cmd.printErrors(); return 0; } + int device = cmd.get("device"); if (device < 0 || device >= num_devices) { @@ -198,7 +199,7 @@ int main(int argc, const char* argv[]) string filter = cmd.get("filter"); string workdir = cmd.get("workdir"); - bool list = cmd.get("list"); + bool list = cmd.has("list"); int iters = cmd.get("iters"); if (!filter.empty()) diff --git a/samples/gpu/pyrlk_optical_flow.cpp b/samples/gpu/pyrlk_optical_flow.cpp index 5bff8f94f8..5ebf859377 100644 --- a/samples/gpu/pyrlk_optical_flow.cpp +++ b/samples/gpu/pyrlk_optical_flow.cpp @@ -152,23 +152,22 @@ static void getFlowField(const Mat& u, const Mat& v, Mat& flowField) int main(int argc, const char* argv[]) { const char* keys = - "{ h | help | false | print help message }" - "{ l | left | | specify left image }" - "{ r | right | | specify right image }" - "{ gray | gray | false | use grayscale sources [PyrLK Sparse] }" - "{ win_size | win_size | 21 | specify windows size [PyrLK] }" - "{ max_level | max_level | 3 | specify max level [PyrLK] }" - "{ iters | iters | 30 | specify iterations count [PyrLK] }" - "{ points | points | 4000 | specify points count [GoodFeatureToTrack] }" - "{ min_dist | min_dist | 0 | specify minimal distance between points [GoodFeatureToTrack] }"; + "{ h help | | print help message }" + "{ l left | | specify left image }" + "{ r right | | specify right image }" + "{ gray | | use grayscale sources [PyrLK Sparse] }" + "{ win_size | 21 | specify windows size [PyrLK] }" + "{ max_level | 3 | specify max level [PyrLK] }" + "{ iters | 30 | specify iterations count [PyrLK] }" + "{ points | 4000 | specify points count [GoodFeatureToTrack] }" + "{ min_dist | 0 | specify minimal distance between points [GoodFeatureToTrack] }"; CommandLineParser cmd(argc, argv, keys); - if (cmd.get("help")) + if (cmd.has("help") || !cmd.check()) { - cout << "Usage: pyrlk_optical_flow [options]" << endl; - cout << "Avaible options:" << endl; - cmd.printParams(); + cmd.printMessage(); + cmd.printErrors(); return 0; } @@ -181,7 +180,7 @@ int main(int argc, const char* argv[]) return -1; } - bool useGray = cmd.get("gray"); + bool useGray = cmd.has("gray"); int winSize = cmd.get("win_size"); int maxLevel = cmd.get("max_level"); int iters = cmd.get("iters"); From 53fdae93bdcce290f042eb42366b56b52a89006d Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Fri, 7 Sep 2012 15:25:32 +0400 Subject: [PATCH 24/97] fixed compile error on Windows --- samples/cpp/simpleflow_demo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/cpp/simpleflow_demo.cpp b/samples/cpp/simpleflow_demo.cpp index 2727fe640a..a864ebc22e 100644 --- a/samples/cpp/simpleflow_demo.cpp +++ b/samples/cpp/simpleflow_demo.cpp @@ -131,7 +131,7 @@ static bool readOpticalFlowFromFile(FILE* file, Mat& flow) { } static bool isFlowCorrect(float u) { - return !isnan(u) && (fabs(u) < 1e9); + return !cvIsNaN(u) && (fabs(u) < 1e9); } static float calc_rmse(Mat flow1, Mat flow2) { From 19e1f89cd6ec6ce7c8789ec2aaa71012bdfa6d3c Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Fri, 7 Sep 2012 15:35:18 +0400 Subject: [PATCH 25/97] regenerated opencv_cheatsheet.pdf from the previously updated opencv_cheatsheet.tex --- doc/opencv_cheatsheet.pdf | Bin 215641 -> 172212 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/opencv_cheatsheet.pdf b/doc/opencv_cheatsheet.pdf index 4b4e6744b95b82f0da564d85858df4441a81ac7c..5775aa8065854bb943e239aaefbe171905da5927 100644 GIT binary patch delta 34303 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 b#U+VFB|;TNz$9vD4op{;T&k+B{%%|Vh*!p< delta 78111 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 From 710e070990ac2375a1a450c2d215eb68f15139e5 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 7 Sep 2012 12:43:45 +0400 Subject: [PATCH 26/97] Native camera issue on HTC One S fixed. Camera connect->disconnect->connect leads to sigsegv. Apply properties method reimplemented. --- .../camera_wrapper/camera_wrapper.cpp | 59 +++++++++++-------- modules/androidcamera/src/camera_activity.cpp | 12 ++-- modules/highgui/src/cap.cpp | 6 +- modules/highgui/src/cap_android.cpp | 7 ++- 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/modules/androidcamera/camera_wrapper/camera_wrapper.cpp b/modules/androidcamera/camera_wrapper/camera_wrapper.cpp index c7a55fb39b..770a61f311 100644 --- a/modules/androidcamera/camera_wrapper/camera_wrapper.cpp +++ b/modules/androidcamera/camera_wrapper/camera_wrapper.cpp @@ -357,26 +357,25 @@ const char* CameraHandler::antibandingModesNames[ANDROID_CAMERA_ANTIBANDING_MODE CameraHandler* CameraHandler::initCameraConnect(const CameraCallback& callback, int cameraId, void* userData, CameraParameters* prevCameraParameters) { - typedef sp (*Android22ConnectFuncType)(); typedef sp (*Android23ConnectFuncType)(int); typedef sp (*Android3DConnectFuncType)(int, int); - + enum { CAMERA_SUPPORT_MODE_2D = 0x01, /* Camera Sensor supports 2D mode. */ CAMERA_SUPPORT_MODE_3D = 0x02, /* Camera Sensor supports 3D mode. */ CAMERA_SUPPORT_MODE_NONZSL = 0x04, /* Camera Sensor in NON-ZSL mode. */ CAMERA_SUPPORT_MODE_ZSL = 0x08 /* Camera Sensor supports ZSL mode. */ }; - + const char Android22ConnectName[] = "_ZN7android6Camera7connectEv"; const char Android23ConnectName[] = "_ZN7android6Camera7connectEi"; const char Android3DConnectName[] = "_ZN7android6Camera7connectEii"; - + LOGD("CameraHandler::initCameraConnect(%p, %d, %p, %p)", callback, cameraId, userData, prevCameraParameters); - + sp camera = 0; - + void* CameraHALHandle = dlopen("libcamera_client.so", RTLD_LAZY); if (!CameraHALHandle) @@ -384,7 +383,7 @@ CameraHandler* CameraHandler::initCameraConnect(const CameraCallback& callback, LOGE("Cannot link to \"libcamera_client.so\""); return NULL; } - + // reset errors dlerror(); @@ -392,16 +391,19 @@ CameraHandler* CameraHandler::initCameraConnect(const CameraCallback& callback, { LOGD("Connecting to CameraService v 2.2"); camera = Android22Connect(); + LOGD("Connection to CameraService v 2.2 established"); } else if (Android23ConnectFuncType Android23Connect = (Android23ConnectFuncType)dlsym(CameraHALHandle, Android23ConnectName)) { LOGD("Connecting to CameraService v 2.3"); camera = Android23Connect(cameraId); + LOGD("Connection to CameraService v 2.3 established"); } else if (Android3DConnectFuncType Android3DConnect = (Android3DConnectFuncType)dlsym(CameraHALHandle, Android3DConnectName)) { LOGD("Connecting to CameraService v 3D"); camera = Android3DConnect(cameraId, CAMERA_SUPPORT_MODE_2D); + LOGD("Connection to CameraService v 3D established"); } else { @@ -412,19 +414,23 @@ CameraHandler* CameraHandler::initCameraConnect(const CameraCallback& callback, dlclose(CameraHALHandle); - if ( 0 == camera.get() ) + if ( NULL == camera.get() ) { LOGE("initCameraConnect: Unable to connect to CameraService\n"); return 0; } + LOGD("Creating camera handler"); CameraHandler* handler = new CameraHandler(callback, userData); + LOGD("Setting camera listener"); camera->setListener(handler); + LOGD("Updating camera handler"); handler->camera = camera; handler->cameraId = cameraId; - if (prevCameraParameters != 0) + LOGD("Checking previous camera parameters"); + if (NULL != prevCameraParameters) { LOGI("initCameraConnect: Setting paramers from previous camera handler"); camera->setParameters(prevCameraParameters->flatten()); @@ -454,7 +460,7 @@ CameraHandler* CameraHandler::initCameraConnect(const CameraCallback& callback, #if !defined(ANDROID_r2_2_0) // Set focus mode to continuous-video if supported const char* available_focus_modes = handler->params.get(CameraParameters::KEY_SUPPORTED_FOCUS_MODES); - if (available_focus_modes != 0) + if (NULL != available_focus_modes) { if (strstr(available_focus_modes, "continuous-video") != NULL) { @@ -462,7 +468,7 @@ CameraHandler* CameraHandler::initCameraConnect(const CameraCallback& callback, status_t resParams = handler->camera->setParameters(handler->params.flatten()); - if (resParams != 0) + if (0 != resParams) { LOGE("initCameraConnect: failed to set autofocus mode to \"continuous-video\""); } @@ -560,7 +566,7 @@ CameraHandler* CameraHandler::initCameraConnect(const CameraCallback& callback, { LOGD("Preview started successfully"); } - + return handler; } @@ -576,7 +582,7 @@ void CameraHandler::closeCameraConnect() camera->disconnect(); camera.clear(); - camera=NULL; + camera = NULL; // ATTENTION!!!!!!!!!!!!!!!!!!!!!!!!!! // When we set // camera=NULL @@ -808,43 +814,50 @@ void CameraHandler::applyProperties(CameraHandler** ppcameraHandler) { LOGD("CameraHandler::applyProperties()"); - if (ppcameraHandler == 0) + if (NULL == ppcameraHandler) { LOGE("applyProperties: Passed NULL ppcameraHandler"); return; } - if (*ppcameraHandler == 0) + if (NULL == *ppcameraHandler) { LOGE("applyProperties: Passed null *ppcameraHandler"); return; } LOGD("CameraHandler::applyProperties()"); - CameraHandler* previousCameraHandler=*ppcameraHandler; + +#if !defined(ANDROID_r2_2_0) + LOGD("Reconnect camera"); + (*ppcameraHandler)->camera->reconnect(); + (*ppcameraHandler)->params = (*ppcameraHandler)->camera->getParameters(); +#else + CameraHandler* previousCameraHandler = *ppcameraHandler; CameraParameters curCameraParameters(previousCameraHandler->params.flatten()); - CameraCallback cameraCallback=previousCameraHandler->cameraCallback; - void* userData=previousCameraHandler->userData; - int cameraId=previousCameraHandler->cameraId; + CameraCallback cameraCallback = previousCameraHandler->cameraCallback; + void* userData = previousCameraHandler->userData; + int cameraId = previousCameraHandler->cameraId; LOGD("CameraHandler::applyProperties(): before previousCameraHandler->closeCameraConnect"); previousCameraHandler->closeCameraConnect(); LOGD("CameraHandler::applyProperties(): after previousCameraHandler->closeCameraConnect"); - LOGD("CameraHandler::applyProperties(): before initCameraConnect"); - CameraHandler* handler=initCameraConnect(cameraCallback, cameraId, userData, &curCameraParameters); + CameraHandler* handler = initCameraConnect(cameraCallback, cameraId, userData, &curCameraParameters); LOGD("CameraHandler::applyProperties(): after initCameraConnect, handler=0x%x", (int)handler); if (handler == NULL) { LOGE("ERROR in applyProperties --- cannot reinit camera"); - handler=initCameraConnect(cameraCallback, cameraId, userData, NULL); + handler = initCameraConnect(cameraCallback, cameraId, userData, NULL); LOGD("CameraHandler::applyProperties(): repeate initCameraConnect after ERROR, handler=0x%x", (int)handler); if (handler == NULL) { LOGE("ERROR in applyProperties --- cannot reinit camera AGAIN --- cannot do anything else"); } } - (*ppcameraHandler)=handler; + + (*ppcameraHandler) = handler; +#endif } diff --git a/modules/androidcamera/src/camera_activity.cpp b/modules/androidcamera/src/camera_activity.cpp index 6954e49a53..5a997637e3 100644 --- a/modules/androidcamera/src/camera_activity.cpp +++ b/modules/androidcamera/src/camera_activity.cpp @@ -209,7 +209,7 @@ CameraActivity::ErrorCode CameraWrapperConnector::connectToLib() res = getSymbolFromLib(libHandle, SET_CAMERA_PROPERTY_SYMBOL_NAME, (void**)(&pSetProp_C)); if (res) return res; - \ + res = getSymbolFromLib(libHandle, APPLY_CAMERA_PROPERTIES_SYMBOL_NAME, (void**)(&pApplyProp_C)); if (res) return res; @@ -391,16 +391,14 @@ void CameraActivity::applyProperties() int CameraActivity::getFrameWidth() { - if (frameWidth < 0) - frameWidth = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH); - return frameWidth; + LOGD("CameraActivity::getFrameWidth()"); + return getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH); } int CameraActivity::getFrameHeight() { - if (frameHeight < 0) - frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT); - return frameHeight; + LOGD("CameraActivity::getFrameHeight()"); + return frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT); } void CameraActivity::setPathLibFolder(const char* path) diff --git a/modules/highgui/src/cap.cpp b/modules/highgui/src/cap.cpp index fdc40d14fe..1da6220e33 100644 --- a/modules/highgui/src/cap.cpp +++ b/modules/highgui/src/cap.cpp @@ -448,13 +448,15 @@ VideoCapture::~VideoCapture() bool VideoCapture::open(const string& filename) { - cap = cvCreateFileCapture(filename.c_str()); + if (!isOpened()) + cap = cvCreateFileCapture(filename.c_str()); return isOpened(); } bool VideoCapture::open(int device) { - cap = cvCreateCameraCapture(device); + if (!isOpened()) + cap = cvCreateCameraCapture(device); return isOpened(); } diff --git a/modules/highgui/src/cap_android.cpp b/modules/highgui/src/cap_android.cpp index b67e56dffe..2f3f388c04 100644 --- a/modules/highgui/src/cap_android.cpp +++ b/modules/highgui/src/cap_android.cpp @@ -203,6 +203,7 @@ CvCapture_Android::CvCapture_Android(int cameraId) m_frameFormat = noformat; //try connect to camera + LOGD("CvCapture_Android::CvCapture_Android(%i)", cameraId); m_activity = new HighguiAndroidCameraActivity(this); if (m_activity == 0) return; @@ -328,7 +329,7 @@ bool CvCapture_Android::setProperty( int propIdx, double propValue ) break; default: CV_Error( CV_StsOutOfRange, "Failed attempt to SET unsupported camera property." ); - return false; + return false; } if (propIdx != CV_CAP_PROP_AUTOGRAB) {// property for highgui class CvCapture_Android only @@ -353,10 +354,10 @@ bool CvCapture_Android::grabFrame() { m_activity->applyProperties(); m_CameraParamsChanged = false; - m_dataState= CVCAPTURE_ANDROID_STATE_NO_FRAME;//we will wait new frame + m_dataState = CVCAPTURE_ANDROID_STATE_NO_FRAME;//we will wait new frame } - if (m_dataState!=CVCAPTURE_ANDROID_STATE_HAS_NEW_FRAME_UNGRABBED) + if (m_dataState != CVCAPTURE_ANDROID_STATE_HAS_NEW_FRAME_UNGRABBED) { m_waitingNextFrame = true; pthread_cond_wait(&m_nextFrameCond, &m_nextFrameMutex); From db3a35f90125eb13716cc323802662c240e4cd2a Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 7 Sep 2012 12:57:37 +0400 Subject: [PATCH 27/97] Issue #2248 OpenCV manager fails to determine hardware platfrom on Android Emulator fixed. Search keys for /proc/sysinfo updated. --- android/service/engine/jni/BinderComponent/ProcReader.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/service/engine/jni/BinderComponent/ProcReader.h b/android/service/engine/jni/BinderComponent/ProcReader.h index fd30cdebe8..7b50028fc0 100644 --- a/android/service/engine/jni/BinderComponent/ProcReader.h +++ b/android/service/engine/jni/BinderComponent/ProcReader.h @@ -14,9 +14,9 @@ #define CPU_INFO_SSE2_STR "sse2" #define CPU_INFO_SSSE3_STR "ssse3" -#define CPU_INFO_ARCH_ARMV7_STR "ARMv7" -#define CPU_INFO_ARCH_ARMV6_STR "ARMv6" -#define CPU_INFO_ARCH_ARMV5_STR "ARMv5" +#define CPU_INFO_ARCH_ARMV7_STR "(v7l)" +#define CPU_INFO_ARCH_ARMV6_STR "(v6l)" +#define CPU_INFO_ARCH_ARMV5_STR "(v5l)" #define CPU_INFO_ARCH_X86_STR "x86" From 568d9583c1621e78a966a131a92e515c4d2f5a26 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 7 Sep 2012 17:19:46 +0400 Subject: [PATCH 28/97] All native camera libraries rebuild after HTC One S fix. --- .../armeabi-v7a/libnative_camera_r2.2.0.so | Bin 56492 -> 58776 bytes .../armeabi-v7a/libnative_camera_r2.3.3.so | Bin 43968 -> 46280 bytes .../armeabi-v7a/libnative_camera_r3.0.1.so | Bin 43984 -> 46280 bytes .../armeabi-v7a/libnative_camera_r4.0.0.so | Bin 43984 -> 46280 bytes .../armeabi-v7a/libnative_camera_r4.0.3.so | Bin 43984 -> 46280 bytes .../armeabi-v7a/libnative_camera_r4.1.1.so | Bin 45232 -> 46280 bytes .../lib/armeabi/libnative_camera_r2.2.0.so | Bin 57068 -> 58772 bytes .../lib/armeabi/libnative_camera_r2.3.3.so | Bin 43992 -> 46272 bytes .../lib/armeabi/libnative_camera_r3.0.1.so | Bin 44008 -> 46272 bytes .../lib/armeabi/libnative_camera_r4.0.0.so | Bin 44008 -> 46272 bytes .../lib/armeabi/libnative_camera_r4.0.3.so | Bin 44008 -> 46272 bytes .../lib/armeabi/libnative_camera_r4.1.1.so | Bin 45616 -> 46272 bytes 3rdparty/lib/x86/libnative_camera_r2.3.3.so | Bin 52128 -> 54476 bytes 3rdparty/lib/x86/libnative_camera_r3.0.1.so | Bin 52144 -> 54476 bytes 3rdparty/lib/x86/libnative_camera_r4.0.3.so | Bin 52144 -> 54476 bytes 3rdparty/lib/x86/libnative_camera_r4.1.1.so | Bin 54080 -> 58572 bytes 16 files changed, 0 insertions(+), 0 deletions(-) diff --git a/3rdparty/lib/armeabi-v7a/libnative_camera_r2.2.0.so b/3rdparty/lib/armeabi-v7a/libnative_camera_r2.2.0.so index ba36efcdf5d80aa51079b453309d40347d7d0867..d445715b67efd5938f0d5587f8b68b05b2679e6a 100755 GIT binary patch delta 19772 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 delta 20223 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 literal 43968 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 G-Tw!JmnxzF diff --git a/3rdparty/lib/armeabi-v7a/libnative_camera_r3.0.1.so b/3rdparty/lib/armeabi-v7a/libnative_camera_r3.0.1.so index 295cbc6b03765114cb9b7f315621892f30faf760..3b8e7fd2963ce46672cb2dfa5e8ae690766ee849 100755 GIT binary patch literal 46280 zcmeIbe_T{m{y%;P1{f7}G~AT5y@(bn;tZf7Sqm~uAc!Pr*19_&3^K}(i5U!(T1;!r zEH$k)%Vf*lwXzmkE&Xg3y>oZn?Z;BtuC>0m5oJLI#LXINjrl%b_nym~xig6F?&JIU z{Ly*v;&pz!&g;C+>%7kEoO|!wbAOVaZPsWsf)av-7=cnL#e$Fs+|ncnL4pvHD2PG` z%B~YeOXUhZ;SJBq5JCyPAeHamCkP503KN7qxN%k=Os{$kfu0#5dfIASk0Olgz4xsR~jQsvbfo>jv!cM>zKsF^1 z8f7Hpv+`-cKSf>+a4ujn;(HhkL=)Zu+=Bd-fLK5)D=Pt>j`R|iCNu}>B7hD+VJV91-#lW)xA0qw*U>A*(d3SbIZh*P+eVTzvz zI03zsP=&Ebi;Q*yD?1K+8Sp7f-@!1Uaz?wC zS$PofCZtWkKLnH^u7nVzmjK2AHX?HjU@YSI03JqsC~!7lDWFrS1pFJoXMi6A5>Pe* zPza#V11M$j3sR0CJOTU?AOiW@fm;Ao017tbH2_a#b^bt^2;`C$0WQi(;kUpOkUk7t z4Oosi&GVs%Zw164u7pAssbqPRfZfRJ2K*dgW##{Wq9Fkq8_n{*2RuGNd3b=lD`myA z!jJ)oA_L^dvb>30*)SGQ;qqo9K8s5$(V0p_g%_*RluQHskjtOP#rde2OWexh(ZH*? ze2PY?(t(ot0GV;Bya`Arv%DWMydId=n?PX%ms!i=VZh6{d_Fn@2?L*j_(GNzRrza> z{ytzA%a2f%^T~Ty0?V)P5MV9f7C;Q(QGgQcEOHz092OtR@Nk9|bT5m%!*Hfj0Q^f9 zU&-)$T-kEOHv{eli~{@wa1-D!06yG+#Oo|01GtjK6M+90@INfAW0=r`fMUQYzypAT zfD-^E>_+59z`Fn!KnY1Kau@JAz&`-z0A~ST0!jfCJ^=JdG58~f2^|1T155%;1yFcX zM$-SU@xL5!Ghn1f5Pl7;2d40MKse%K0DnUKB;b0)yMUhoEC5jWn~bFYzsLWNx%BrK zFUooxa5GCEVKmPH=dt*mz-=sE0Q^sY3Gg|9LcWZIVBlO9pU*I%R{+Bq{}&PeKY&Dl z68@@0fPV%^1MMfkHvo?ZEC5UdTn8{B|IYv=>_?=7C4PkR9K;=f?<1bd@~1Go40#l$ z1I&oO3VaN3KY)S9|XT+#a<5aiQu0wLMfH`WyNEkgbtJ68HM(uG12}A zBR|UMmxcJ!XJSC!2EQ0qzJwZPf#y+16xMPKh%Dc*{=Ey4kUVe0_$Lr+%GVxnA6?=k4dH*&_7%6;&_UoZZ?kOk^r zj4E9_PAQf9KT{>oGt@B_sEr1+SBmza7OB0xoZS%rUS5Am-Y3!DuWP;i{r&_H zu|WLhPsCn{>+io{FY910q;INU6sl0m@&&2n`ztld0!5DyQx+(_6oUQ^?NglieE|K& z!1xncn)c&LAex_0!z@sG5Bl>)y_bF@>~kCBi$fWW|I%TM7>JrKgno8%`j3TScRVa# zcToFZP(>Dq|NBF+hgyZT4)cJ@A3^)Zdu0Dc>2h-LED--FGHe#8{@q4gr#yzeFychN zo-$dWXe9VOY8QmPh?Be}Xuo5#eBDO$?~lMf5cXZe%I}zj{oguoe-}Xhf=%+Z3ei7{ z`SxjvcRW`?{);PdZOZD$%s~86`I?64f2$hr8>XWDMz8&5V?6BldE0wv8W^!a@=w8h zQv&tB65~-;@3k)j`hNudCt7OnrRx-Gc|5W)9x1R_s!Q|Z4+i*a2ut&i=4+hFzM7yf zB@oY_qP>mvUilIrZ$9KDGo2JU{VpM(_B4 zOasFLk$TWSB~aRi_K%`J`w%C6XJRu~fc``vP2)Ej7oG<58F7iHu~-q%_8d6w3m2Em1XJpNIaFIQ`$Q z>i-%M_F3oc&sWgb8Tb!U3$<6NvhS#1Tn}31{S?{TZ=sJCZoD2r|MznJ9|n6LwE{N6 z{LRxU|L>Z@gqPZbKSNsk5bg)C_BX+wKEbVrbzJ|cD|_Sd593QgFN_d&tNhnpa*_0( z^#5bb4;TEgGJXd`urDp~`qy_=`k9IG@5Fq8SxEXQfIdY|pJmwS7&gO3Sou-NQ`|4l zMNbvM!+7m%P3jIG}fPcjN zqVc2gDN@AMO1 z^y|I+vQ^`?!HB)Zv+{j5@*jVVMPHk|^YbZAUgA&pf({nT^PA+^KT>I(^dkrtRPyYE z{^@=u1-)?5(+2quV7@B;;p4G#8U3gFA41*(oV+c1?7yny`z+MnovQuIImrJl^fN-L zA11WHzLh}ybl6WQf#jbB`%(gxe}wUk!uTrkOviXx!H>#lJa(w~m2v!t#shz|?0&EP z9s<81oV^@W$(x7%D1qu94pvffn?F+NqfR9+Rkt_Fb@bT(eW3C;o1tF=r{A3_et(aL z?O=Q<(0C+(em~?_`u~xtJQVuf4Sk59Cw+XXvY#_he8vm%^-vW2aWB$INRynD-ii6R z74uQc>Yt)PV1fGoYs}{i&YnkMK40G8U7sF6I`%0v>n39$_}p z{8($i{_a_d)BMv34}gEtT5tOg6Cn%K{&lML>+WdWhvod|J*x7*gWq28Q}i_z^xArF z`gbU|bLA^l^J5F_XL5ynZ-eCjR~**1N4@gx)Ium0NWR@rYUo!9RK5=O z)`tB6O*`V34gN#GpN5I%%P`E}Hk*8JnbK>pKZ||L+g>^7s=*v=B>L6}L|CAq`U}SY zYtRQFF3B6Fl#+g7_jDn^0;OM4*~cNsqXd#SEFSZDi|oIM-r(?~ zUw^}Vq3h2w@FM%#Gz7dj`}i03JG2?4ZqWW93i>>2CE4ZveIM;dq5Tm^Q~Tpp>%k$E z^Xo-|s=uG9`tvd9`T2AgornDTM`+;eSJvlq^#$l84YA3VQ77wu|T+*8gmz zNxqv^<6)YL^$+u(%!IE09-V@EO>#O4>31+t)#xwSNaJaRf7;9K$6{a)bsN3o-vj#% zU+1;ogm6@3fkd~d+Mfsdq&-Z*yGs>xt!<^ zK|lLIpCZwZ6mlW&A+$$sncC|{dG15ye&>a;iTLj;H2+uYO|C-afP^%c~Q@N8%Y#r9Ae;Z%@3h{Y-{Dmmse)zHeiG#5^hp z^^AXvh&c!QAb!+d;&jZ9N923{d(QLs^d&PL^KmHmu>zLh}yhC}{(&io56}FI3kDb*k}57cqA*o;vWN{^^kpM_SL)Pr`mD!G1{$Di5E8 zxybpOSWX_&(Fj$0P0%0jj~~PM^}gWUZ~O`E9pdcisH*-SIR4b%#+$L;z&?i~PWG8c z1I7ZC|8oYO1wJPG6O#AL5cp#F2Sxs9mHlmkeEj})BJ6z>)@wx{A7K3IF@A~QP5jPs z>l?NADd34>um3s>dv1KmJN`2%~W?FT(z1e>F^a z68)LW^=B8#J30M+s?tw5()U%#&wok&4^;9C7E4i0p{2xDQCMbMV@0yqQE4eHtSBy{ zL}pHAz9n_hqSU)A79`C`dy{wP7!%S8%dJ&~84`Wftcpsft)w>HX0w49c~zCRVoO=& z3X8qUR^b#ZcjsiyX4%I0yvi!4wRnlO$XQvHnUiNR&RS)ywX7~ItF~I~g|;e-lX7S0 zIZ<~-QhHvzB|SaeXeh3<6k8q6s><5*+|?_Ir@~SpH5t=a|5w_IM_Wae6=<|RO~eT|1Fl1RTWj1+Y76#>0p{S%OYUZi^}b!V3m~{4J)k91(n6t zoa*xQ+}x}pTb@xG2*nQnP19D)wx5lG&5d$Z+WI;_t8O8;{I#wBZzR3mratDv+%HW_boSRB=MyW~@0!bQv`y_3za zd!#R-BumF%Ag)G;%NmAPFD8;JFf=C82vk{D+pPEIVKGV1^_v-fLKqXJCcJfM>9}Gm zv(QLpg)BKJ6%G4qFaeyYe-gvN$*{fzQo&4^fTNVAyCVmtOU+KrNlPD0Nm5<^8Y(@-8w)F(w&mDrVC!)8 zf~m$XH77rFUTTghGw1ff#FA?I(8KtACT^`XZSx%Q2Fn6$D$4EEPOGKDTH~}hXsVDa z%5%miS}YD{nZ;RJRe7(a%v!O+S!%IXRaI8eJcYIAITNv!T5XKC=hi=%%Z%) z%p&ZK=74pvwXm$LvZxT;mjkbo{a~IGRV)>-VvAI4OzP~6 zxoh!~#Q6BU+<&8%Vendp0BvOs(v)$IPo+h-&zm`G=3sSZ`_!4An?Beim0ffC(%gl4 zix;I^(iSerP0z_o&CgtzGgw=*n7O59Te8!0ZqLt17cklZ=C?BD@eVs(ys+gVJzd$` z^;_NSMb?sen7ea|twos1l@%*sHUalXNkwIq4y)W&8ovQj+G33ujSgp}oh`qZFus%|K3jES=7*_h$h3Tu_338h?7M1U=;s;2KC zsS4R9Q@vMbt>rIK`2~1@{CxjBzv}YVRh?~|ZLf4VO@+?Fd>gg|&cbqgdW{WxM5R-* z?hKaMnG3Atl~uL4a3!Ck6tnS&H&$88tT2K_*x6RmQYCpCd9%sXuj1fUJmU;WaAj?+ zk_O!WQsv(eG<{}KWwm^IXg@iP3m3;q|LAgVqVs4y`OFHI6l!?74 z%iEp}4d(W1+MjK@fR(k#UMm!rVSTFrl6DHfj@okkm*#f>vxB3sI+f|@V{GcV%FMQZ zH2r0mtz01as6g>Iv;+5EPRUIvS3>Eu7FVzoJBn3YYKB^Re6BrH8HQcs-&>U znvcy?b(Iy`_PMT8gqiJS#3+6HJ~Pvo&j52)>XR%FO$NGGV=gOP;lSp?e})_5;T)yBJjS6=f6yY}#_%{C-gS34oWcSGO5Ia!ptAlRA3{nc2R8c8n9Tt!`NTkKU)H?frHbr zJC^-?_!~{Q&El|Dt?s`uOE;fXhC2wk+8+)=o^Wm46R(YY=CzT}x;FCJ*G8VCBKNy> z6hBkZzWR=ydJL|is(L)Gp{japuA!=WoUWm&dd#k&s(Spcp{jZ;uc4|uuK8)I1>Rus zuHLGt>a!hGb`nNIWrf*>dvUln@U@hFikX!a*@X_Le8oUy?4m)L$_imTSY;|(@>JZy zEVtuXOZv(I$c+gy-#e{^tNOW0n=d<7RLg2xm9x6Aj67KW)pf~#r(m4vy|raYU7Qae z$*q?G=o2~mg>dGBkSFlugI>@jany@&n=bt>-yM1Yd1i4{@)s^j!!_WQJ1~=5Ag-pv zJbk)p;h;U9#ql@e0^c;4Ky$bXnK|?LmHxYrC3(i=X3LB`tg5V$)p~Q|evW znYU*QdaokhxAxUmo9SE4oV|GAqI6o#2W@bcZ_Uh{G~DvLs;Wj+PxCTTO@oVN6~)#nmcS!l7F$tmqnPDxdi;Y&md-hNMl9XkbP6R^Rx6bh`jyC;D8sEsRi$$2 zjw2Nns*9vNpR2;c<#-+<*wLW1u-HWw^61 zU8wU-sLNO*>TIqrensAf5^=YEfOb?xtQl1<*NVEt+laBMuwq33W%2opJ)gz#_Q~cL z!+F?>{8eP+@)gEh7SEW^_A-vItae%|ODvM$yz+et7;B$Aj<;_<$DDsy;%r%D0uJpIcPW%`C}n2AJRQXIKfON3;RwKb-KD%cc5`f%!}IsT_~tX7P{V zd}k9q+`+RuMwrACsz*tF4y1mn$6H}PMFP)dr0P7P^O+(Fpv1M?iG69O#qYGyFN2A-;5X#VX~YcBR~pLEDO-iJG*T zwcsaA^z5ECo4E1oWWN@X?ZNx-T@jFfSkIooOG=n!$)N}Jsq|>wl8x50q0wGw^%p1yLCbUS+>$@8dtRZgiPrXDgL zwJaEV3w}v37#WFNYAv+Ozl0c2$T=XZvX(N3uK%4U2Ff( zfyl#LyS7!0|K|*^MofOV2N8fL4a^$TN%@TuGyv7I48EpcBYu@6+rPT8`;V2(kMPwq z%Nk@UPb|WsB45c5LdmW!=(a@vc0t+04^~zA!34Me2(YbS-174V-+#~+_Ufp3acOXU z6cTt7G=NS56Dw-rRWJZ$04)qaDPMP~6cLz~+_p*=0~7iV9=rEMcXs+mbk=e!e(!JX zziRR+wB8dqzrst;^J15l(jBn=21s1&YrQSfZA6E&xMxkdCRezm0Ug_m)=sL654j)Vv%7qTv6z>;oS;$>#`~#&Jbs$ zW)ZUSeh7sF>5?qIl+VLjgKBEIzgIN`Yrr!Z_{IG^FA z3@>B2fZ<|>H!!@B;X@3+!SG3jT@35-UKO<)!EhqONet6_S5%(Q@G^!A7%pbGlwl{s zH4N7=T+i@khPN`jgW;VF?`3!&!}}R-WcUq+n;AaJ@G*vuGkk*KlMK5U?qs-!;Y$qn zGOWdWQq*4^!{H3;86MBD$nYeFV;D|kIEmr845u)h&2TQm`3x^*xR~KmhRYbXGfd~? zkbE@^*D*}*gHrwmhBq?2nc=Mr?_hW*!_P3>!0=v%_c6Sm;YNlJFnoyNHyCba_$b52 z7=Dl87KV>Ae1hSV47(UU!*B<~oecLde2L*+hA%U$#rtp4xG@~gu%2OhKa|SHGCZE) zNess@9LunQVR|o>>LoEemthmb84PDLOy?{SeLll9LunQ z;Y5a$7&bAS!EiRixePC5xRhZ#!%l{47_MV@Gs9aMeum)&hW9hv$nXJ%4>9}(!_5pI zW%wAw?=jrMu#4d{4EHd6SxLh`2H`N>ON8;hWiMgO(aVJCz*zXNpo7pWVZ0BjBm8H) zLrNG2*hUck5aUg_2=5XT)@cNhFb)=;MEDTii6(pm@ADG=70wYMoS_jm6UKX#XBeJ~ zwkf@p(m%oZOAMz_KGHiVje~AG88%Tq(mN@gsu6k^&Y*mx_fr~YdNeY8fMG3_BVWgG zIK!7;!TED|fmaXxjKTZMV+kkVy-&iKc;A?CHr5ZqPP_|C81Llf6Mhd2L)dujDvT33723TiT|@0N5bpTZ^CCVj)eaQ{U!X2 z{Jwn<4s0Ax={e{(VZ8G@i7*ZuHV~eQca8~*n6HGlLmz}O0aFMU;5?oCiHXH{T0}9th)o`OSoN7=OaUpbx?$p%22azh?-;P8$frPWBRx z#yJRtr{JA+!qd=Q!m&8Zh42i#$4%IP^STJ*1>$DH$#|cc@LZfTK{!JY-Xpva?+Fvm z#hHVIm*Bl+!gt|aVZzG=!9}f%kt2m*Sj4!WB4gfp8_}6=6H(6=B#N z9pd@DYK#Zrdodn_Vc+3|>oFdL@osno;fG-#gdfBFBD@9TLillv3*jd*zX(5%`9+xK z7h%|RB4PN*B*K8Xgqtu=_HhJQLr7zc_UBMg7>9^rW!p@lFGq&iL*e(D5a_?eT0aoDwsFb?@XLl_68b`XXi z?Ic_XKSX#1<{@GD$zH;h8bRxt-llc!X&d3%;?niJ*|v89Y~lA?TD5=L+`9MMH`K;#KhDM9DNnCt97mwiL;aptH#V`Mh(?1vQK-Kxww{#U;c*EKNs)h;%B({NiKezi@(Rkk8<%h5Z4M@u;bX; zcB990&W`o>x`W|ny*VT^WWKPq)}-0K#uO?>n?3GL-Q$nwu!fEW*bj~|hnvG(;snjD z9{2xri70RG(j^Mhzx>dCM0iUs*STVwsHC+^oZxZ0P_DUEn0^Tq_JcQ?hnpkJqg=N( zMVVtjbEqq3LKLVDfL1rtYNmYkGs(vF>aJgH&=ISSc_cq8CDZD zCTMQ+xa*{vHC=Ko96xlnCpE%M?Oi`%Xn%Y4s5J-e)^+J-QVak5@JL6`I+82gJjOiC zHMePsnPe{m*C~*z6q3l47bRIDN{5$<<|&}~raQ_UD)r$~ck{^R^MA41k5HT8=IfC! zOb&G|>D3OSw$5~mO`4g%nD^D+7WQ~%#+a!^;jOSN-TbvC{lQ&JwPsy>P=Uw2tD9Pm z#eY3?*?2qF`yj0S2eruU@Q7wj&%eHw%cz~W>=_F~%`+C%nSNs0*cdEEnUl@;nLO@S zyS{gL>ijjPU{^wE!r>{1g`&*k{$TtpKr4!<~7=W##V6+J%+sWqlDls?&&P`b0! zY_>!Th@JjR;u~@(vX*9%(?_&yT|=mCsDlFS!y-{ z-_~U_hg}R8JnsEnVHZV#wDcm%Jnj#=3{qUzDEECC_GUi^6rhi<0DlPh93c1C%0{n?Dk|c(5_l3_XZb%(s|pvmfme9zp-QtTXCNMJ*vGH8UbxLS{^9xxMLQ*NPcJ z&(?a!-%4ks^t@mH3s?B8pcf40;1@may`A57A6tv~uIszEM!w$_v}QO;gHalU(kG?T z;HL~`luo5mk9$eytF9kshR-sXYcU$JUCT0SO)Z_kidD8!-{k1|->(>5Sb6KrMC!cc2wf2{;o>to<`;0mwkS9uN+AAL({L3oeZ|08SDh-3LfS zc?Z(_0AC#`Wul5Ha zPw$?0LrY{$?9|~cqYnqgj`?teYjkF)IC_3;bgfDF8Cg`s`JpaNVsNacjmp9{HY2#zvO96J`NDIVCt_Elv;+jn;bt=;On zqo3M*eqq+>r90<^nu9*f$+_PY=~^JAgFehg>N;08OJyMy?wZe1nMj4WGNqK}!wjUz z_sv3^8No(#ywv7}K75X9`}uMKZ3=6bxPH*z=8kjIrCz|dpO0}R_vZ`eld?jhg3PX^ zH@IeJQD1t`&B_Xsaz?un`{_E*#b@cH+%T6>qC0cWkfoJ!Jnk8n!@BiD9qqCG^vUOk zxMIvWyc7R5Q5yUYit@Ps*hh4)cmH9H&NUG}XCfPW?YTW&+O>DUwn7_ezw_%agnDVP zg`T&%;S&yF{12t#egu3)G5m&fh9UCn5YGUP1x^D#it<~5cLL7>z64CZiTo7#Bl1n; zgUBC|-yt8=3H((Q&XJysvux2W`JVUTd-Ml`OSR^NrpVGL^P=AH@UfURy=Ucpf*pH@ z`qHGsqcgRJ(X0>8ogMF*j1fsbx42g~k}T!<(lpeb(qH?;St0uQg6B(#r#JTlDc61^ z9x}AT-f3SVV3+Vd>?gW_eBf}%F|%nJ!y2 z&(JIbYTsS@bm_zb=r;EGTqv8L3T;F{~_3)|M!tTAb}yYWQb+ z(aNI3r&lJxPRS$7>w8Ye>6My<)60jurkI~wx%2jn!^1N}4Z~UKZ5^~%-qL43@+aK? zI|vzG1HJ&<02~eb+yuN3xD5Cn;OS@WX|zL(&3>S<&J3_a5o;!u-e;OtdO8vwBpLp9&CT66Z|ZX|zi?evMCMvk#Qfi%6&|iN z#TKkFg>`QodaV2AHCpkVE@92<-R*0l&Df=`?Q^$W2$J?TM>_25V^&qo=8`8Xy_-81EYw2p0>45xCmH zF1KTR)_o?LJ-)N$`mFOyg~`?enti`0m{>43A-bf`opXWaTgh^c#3(##NiiHhV;32bbsDkH)g`b46Mh)FN(wZ z+?$bOA9LfxrO5fbx76C_exN(->CoNxBULcjh}|}M3eEQSdvy)dO8eZ}3!nF9hiIaN z=o-^BT>m6@eBPT<{Sy=R9fv>fO`04k8Zif*u0Hpr?o{kW`rNN{*PHh>ecmgA+whC~ zh7i9#x2rqD+W2ur2El2Q6+azdm%*f*Kc0_c79m_X_)fky0Pog{-2A& z$O$dI^NnV$c+)E+@5JtDdkp3~Krz3|XifrNe!*a#{7RquZd|2O{%yFEKpg&p^ev}` zU)-vj_EZYeqc612pY*hr@$c(O#hn6-N!RI25%xo4eG2X1ic`{Nch~1xqWSBkm~-9# z%jLIr{f*^6+5IV(|Cg@6viwcmpK$qq>iU@F-{1Wqm;YwhpILrY_xoJ_t6lH1{F3fJ za{0gP`UA_qyZd)s{;sZfSbk3T+g$!*U2n1cdEH02{0F*zi~Ku#gX>e#`^4@;T$!`$ zHC894`v8|;+Vv{SAK%@`*PLnM)%m?ya?Li7dPv^Ub-*q8|d%; zu1|V(TT@`YeQtYahE!u?_c}9;Nqy&GjBs$v9;{nyOhK+^=l73)*MqFh)-EAtNegE5 zb75xue;o7Urf*DNn{F{*->doUZ!Zc#bXES_i?JhdZ6#fo3sZYOOiPbwS$kuO`Plj3 z;5*Eb=8)}JBRWH#)*W7A9_~uPER96_`#Xgg-CH?ka2cs-Ib=TGKH@jB_0)Av{59mR zkCJRG0(VO0fIhZcE2SrO#x|{n&s{C~T&=4b-mY4Tlb>tpoU{*G=}h^mS1djKlIMD$lkVDm{EaX< z#I+E#wCCt^*K|fnGc~GTg4a69w>5neW1ddeN?U}tHsX4j&XC^;(7|V~z?$*^;2fSS z)&Z{r&^M5t0_+C-8qf%M9ncyH0R8|t1Z&VfU^mjm!0!SRd;(|z(09Q;1sucL@K@md zz<&d#J^3kMC-#(Wz#{N@U<2@%z;xaJFW>^CwP-^J90W}F2&MRXH%zE!kfWy)-3cHFdjgAdbv(9>S$1BSAQKlt5bzKL7yq}Sa_-4 z5nVvC&g;~OU$%b$zFz^J0c-+981}S9h5*5lswj8k>-c#ey) zxf0D%Mnm)j_LS*o#gsj5LTACkb^<1d!p2VDEexj9(VVuVcE* zZ?HPNJsd!t#sD&WbPY1__E60D4JHF`4+hlH(i+G7hB7aju0aOg9$c|9zrkeS?O{La zyca+Z_`XZO?;4CBZx72DzrpG}#EoAJ>Kq6lL*+HdP{r{(foG}2ujlsZXSM6LwUIB? zVI9ykG@Sag8@|)HqfJ||p*2*lt#D>!$;ydlMHud}z`?u8TXSe3=Y10&7-AZXV71`%GzX5APNG`3J zPqb?_PqxWx=7zRCXj@ZX->L~;dRnkIoEl1>u@@Uog<-AL3L9EUChe#v+s?9n`n0v~ zNC)YmPA$K%At+3QteOoWB)hhL!x)mi6VI+Y@%uAcSJX0I{B~d&S6`)!%~y~y?;2#x z7(m8jDj6dLuO1g%g^UsUC)<8@1sSJagN%EzFAt>0Jy(!%(pAb>aRnJQ*C3<7WF&ud z81=|bDE=C7EsOsKn8ug#8-X1x{xYzg#a{$22gJgLi7q&0PrIORI8FBc?pJI-hrQ{ zo-zqME&=-M=c4_ThEufn{D{@3c(4Ka;`X)_5&No?^=Ruz$DMA^xie_{7$6DlklmB~ z#Ea&eMxWYB?fkqD^}+W|@C!9!jv`Mm{Io6fo(J1|Qjc_`gU@F0*Z`n;)U$B<+3-8| zw1wXz&uiVfJ#As)6YV<8Zz1LJw(yn(t-aU-3Wn`%TJh1g9*P&pK1=FrW5cPS6!->Q z(uE2?JcY+#Q?<>z+jOoc+QY#ktoey{%{J^Cx7N35H0#?1L&Ir&a1#7Bw`sScjmD?i z!WtiG4QqV&^aG8LxQ0RA8-j%MLNoI9kOQ(z`=8A&q50vqVU6`JTrsu@jSryyF5n-! zH2SR8pO0!dwLLV*b_FUrF(-ZK^pCbipdVU8l;p!-CLf=> zyG_OumXEW&WjLf0^1 zhIdZq+tF@AT5C}K4(PdF8mAY34j(M+Xir8zhu*QX9bW@J9Re8y!xL>ojT_sktQlow zcjWU0_@ble4_zaou}k(Xft6wxbUSsFQh!IJ8K~W_8I3Up^s$SC(yqW_44(Z4%cUb z;bXLwcX~7)yB^Km({>bnCE2y8rz@cHzbNHHY5x;KbXu^r6Ah#9QhC@S1A6ST1yEV&FuU_ZZ8w0F$gGzzW}b+y}^pP7lGZQh;j!b@-@IDd0`$fpkE!zs}l(EMv*8 za?x*^-(mJGE*i6=RG#uP$k=cy2mEO~LeRHFT=!91zhd=a(?h905lprfOtvK8=E=AQ z!Sl%XV7%lH#MC`)qEXg;1nfO{>kjyXN89x95&A*^d<5BtrU7;gpP=3PXxnhi{|}*e zow%bt47v(!MxCv@F;7YVkGBnTt#?g(wcbT@cL;PLxHh9~U0W2|*9yDa9w#~VyWk5@ z*RZ2~TSyS>ANHkxx-HmH=nBTX9wi97La^__C(fqohzIOJXx!@OKQRA?(!2;q`_CI8 zry;F1IO?f(L0s1sJnG^0`T5 zoW^iN)(x$aoXuujQ!j&krNJ-2Z;X1dU7Nd3A7W@2r4bvh3j?hN_dvp-3({RM;AW)tLE(1aN|};$>&g?{D_ZCcA1BufBXpy(Jo-<;tulFo5J+#T1o%p zKg0BIpVq*>Ap1~~@7JUo5pxTA4ifTDX~hO3_3gkV*e*d%2gY~!J)Q?GK(0+;bNd{&7Pk=ws(-?+ih5GykQRvC(r6=mK-^cGg zp&+^+=y7-A`E{TB2z!3*Unfzj(~IY4fxWtM*{q6sx-6oTX)jA=8^J!QaNon*K$!KB)uiz7lCwN^b1cterwhIs!XH& zZlKS7Cug~ z$zr)lc4K9eYOQ41rTzb$`>BW7}OtT?%3icnryGu>K)6WVdi=ydujinYQ4 z6;xIT<+h3eO41p<$kU5BR#3E6h|)P(qI80i7&F-xJJ~iRP6%A1uy|#)1C<<7-Tsd! z#R)ji+fgdwczUZi*>UA^>1%ZX+6EK)tjU#_c*_T7awmOXM_ev{VCM?V0?-Z2G}ZfQ zoGY;OqZ*i@%1*g)7N?7gEBZAn+U1XY)y4_ll5uW!?*{a_~*G<}{LBhW_S{z95Zw>k-{<@8Bts-%_CoK=n}dhokB!} zML3PZS}gQ`@MIIOf+@Y^CT>LHUY7{}(5cMS2lF z*I6ZI=81QwFIp&0pDtEeaRhz^DK!8+6Lb4%L1WYJJF zx#pVw1!|lG1>-f=s!Ct|Bqz(Cm571&z;txj=dw>*+;-EH{yyG%tK^_!Dk{Yi`XZ0$ ztQ6(*pr%|4H^y14T)qKW==%Xwn+|BMjk?PEvVTp3B}vugL3fBHg=G$_7$cX$J4((NzEr#yXC{fu zt)lcr2+WD-=x9X-d}l+XiDQpFP*9nKG_+r^BR0(-jC!bBHq0#4ugvE9kYykSS zQftvF%D26@|Q~j*JNc^t||Elk-mtg=AB1=I0UF( zySh3to^~t&%*Iz5$!5uaN^Y2V!d?Q@Qqg%u2u#izftc(IJnX|c(#my5pF>XNJpZFi8LiJrrgEO@bW*up?$~2$1BWB3j^k!^ea{)C zy4>-hppni1#kFOTe7reJ`_{rKwzPM`5sCP`*-$|k{D>|U%|J(2NiuS0Mj`!O_)Y`g zY2Z5ze5Zl$H1M4UzSF>W8u(5Ffi&PjFg+-jP&}pBpXzO^Ae;p}fwF4A0>q;z5AlNm z=+zvmL3=eJ{{_4g05}QQ04U%p-GO$7R|&!&D+OT!;1>XOXn_0#l>Y?E4+o&3vI6=S zAiP~62wwx#f#-V`<--Tyt5sKI>4ErOhO7icc16A~;yq&k1?Nln&C7lG%_<{ZEz~|F>mv11iN0t8u@>AA9 zE&xwY=Us}BF<&Joop)(Ko5iYj>AcG_^yd(O=R@aRo>+_L_Q)R(Py(HInS}gA(^0{~^7q92yI}j(?h<+azKZH2RK=~~!u0#9`;uInf zPk&4htbnHgL>Kcc`T1-?t8CWZl02!b=#M+Td z;16KO0%sSUsH5lj$zsgyIg6)=iE)W>n1=t4<)p?zO6kk>f@Tt;A+x8a3ZX;8QgxakK|_N^ zYa=vcG}i}@)r`|b2Hg-W#5qdw%oh)Cgt$`tmOzLruC1VtvP;-mCB)$gEXC!Bz04`Z z;p;B=UsXwu(}Xy>cZ;umRTb6>ad<9nQ{q%ARmM|PVo+FaD-z;XRHCX}5oezZarj{b zeI-nYOI@@euF#36$oPCCo-15K7)HGW&`)j1UlMck(<2n0`Vh1I~AbNkv}FNKTUMx(-k@z zIO0utiVvxyLKY}WK%yvgv~MJcnZ}5Ks7(rZjZ~tec1p3H5sV*6C2}BM6jA{r0Mut% zj|gZzQi4La02x%B=xCiH;OR&v3U>i0PITl42=H!7f6%d52{84Qif9cX5K&PfrFMz7 z5}@#-b&g=8qGXg29f6ahBY#3r45S32p|A!(e5oDsWyOHKh$}(QBG@WP?JD_1_nea8 f$~GbnUUsD@6n<-llHll+J}5;qLAO&$aCHA4Jvx+< literal 43984 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 bPNhh{PY~uQ364(bgHrTu(CtzZ9Nqr|%$N;q diff --git a/3rdparty/lib/armeabi-v7a/libnative_camera_r4.0.0.so b/3rdparty/lib/armeabi-v7a/libnative_camera_r4.0.0.so index d34fa65d814d3393c7c7309203f7a80db2d135b9..8121ebdc533f692e78cb206fbc0526beeb1e3f33 100755 GIT binary patch literal 46280 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%0D^#bKwpye1;33;=(7m@P}OZ7#BXm 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 E1D58bYybcN literal 43984 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 M-3q#0N`j;NfAy9f{r~^~ diff --git a/3rdparty/lib/armeabi-v7a/libnative_camera_r4.0.3.so b/3rdparty/lib/armeabi-v7a/libnative_camera_r4.0.3.so index b6b8f4df50dacf37d80880b13f22f7ecb0d3f672..a8ad60aa9c6d386c0d278007e651abc1e7e3ab89 100755 GIT binary patch literal 46280 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 literal 43984 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 GeE%Qyl7Vjk literal 45232 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 A`v3p{ diff --git a/3rdparty/lib/armeabi/libnative_camera_r2.3.3.so b/3rdparty/lib/armeabi/libnative_camera_r2.3.3.so index 78c104c114703a6822e55143b3e9123fb167f71a..09e8042061791f65f562b3758b0b3864dbbcc27c 100755 GIT binary patch literal 46272 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 diff --git a/3rdparty/lib/armeabi/libnative_camera_r3.0.1.so b/3rdparty/lib/armeabi/libnative_camera_r3.0.1.so index c5532e36b5e120b3de0101efa1a3c69de58c5bc2..85b9976ab7f48e46ce23165c23cb438799622673 100755 GIT binary patch literal 46272 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

6f0)^-Mrbs)K-P_~l#YCb zi9F2J_qM9?e_Y^|sL2oa7z*&Lfkk6FDc`F?51?yV0$eAr) zbV%UM(kOirm|5u-z6(a-4A2XsaLPAps*VWEOKw}$7o#)!4jy}IMbCsrMs(h48@_69 z8@b2w$+X=YIK9@(&&y)>qtdgpkqSs%?0>yAVl#q`Wkr*`c^HXdhXA?)-`!C%Y!pG- zYHVz!jRsFkzX72f+F9W4ks+QH)|NT#_~in7Ojw_iXiPNGgTADyx*A(jS+%VRUt+b@ zC*iZzwfI(7L(+15{UYlQ>oR<_fn@qJM)~?A_S29ge64_!CJDn$dkuZZJ4yaB3#x2L zVoB-${7UTj%?Dgp(v?+{DgV%=sIIZT+?FJ%n}l1^Bz)HZJB=hqeO(21!bxlBJ7M;6 z)&i1^-vqsjAI8y7enQ)1j9@4-oWU@TVFE)V!&HW83>PviWw@2$Hij(>cQV}1@BqVu z4BHtVVtAb435KEgjTY%0&M<+Ykzpyr3Wi%4Ze>Wn@uKqk86ITV&hQY!cNiXJ_yNO@ z7LzLl3^6X84TkXCNMNIT*xqkp_$YGw=&$uu!Z3shHVV@Gd#fX5W{yEzRR$K;ZcSkF#L#NC&S|m zPcS^m(8cf!!(N8{42KwAV0e+?C5H4{K73*IU7`ieWWFC&Nt)w=&$uu!Z4HhOG?uGd#fXAj5Ws z?=bv;VJE}m3{NmT$*`Z{5W`Ch1^fn=`YoJc1j9&%Q49?XMTRpN#xYD_Xk?hd(9E!a z;VLD+4QD^#kX~=u5a5 zzilR5ir<_P;&-HNg!ujHA;Mi)zXcMzuffpz~fHQ~*zlI+``gNDE9uj_sej>yH&S``=#CjoNx<*(+hyx(=2yuXLF(D4* zEg>w^NWbCN?!j-x5!Y_UcoDiVUWC6!dkJ^Tzvb7KYJ^P`pO1DEK8$t~;t=8%!kh8? zSwj5wwv})@`h)N~^atS&u#OSN;@mF6JdJc_gBFJ!AEI~`&NL#t9sRN&XE6lfH|T`H zIID~hKXyJrh~K84B!u0$2w|UR2w^9^gfTeVnD8e2W}gthg}y)-kNzXXZ;vk#&cXP< z3dC>Ib%gk_a3~>u#2ikT4!=s6gWnYsF2iri3G?As35)T2aKaV%tu^7DI8T7E1m{K& zmf}n`!ZM5xVL9xU(1zb46Rv?@C9KBTCxkVaXN0wwXN0iN0>TE&Gr~s9GeX$?D#8bG z?g8Nzj341+7(c>o7(c?NFn)wjWBdqPU{8cQG2aMb+s%YnW3~{&wzd*to^K<31M`dU zO^hqyTNqcuw=u4SKf|~Z{tL#H@DRq8@GyQGPxy13p+SiGeTeXVj4$EuF&_v&!h9h7 z56lO`k8vI+;U^e>!cQ^&g#U%{Cp?AmCp?4kC+x%c6Aohh3BSbn6aE$BPw2t;6XNjf z3xqh_{30Rz&?UlaG}3|U*W<8i9ol~VRE-cyI9(%z6XKxj2tpjJ9!Z#>5uym;{|toi zV9xqYWG#$frRUwQlL0!R0A^cjvm$bdJjkMwd)Ha6^P4*b=<-p?0A~vtxsKqdC?PEPC9IL2Y})kxs1NT?mfD*BQbMAue&M z$K5(08a(cu1EHy!TZK7aA9Eb($dU8Mxr79LC*{67K%DO|p060sG{ZH9NW)~8Fejul z#t;X-CkEoC#(?utiGQsmxoRMEE@a;(B%WjCP72pdnP!NbGT9Y+Yv@96>9wqMrBpgk zD!pVtDS7em^@gd?`i80Fp;t(!$v~PFfOi2fkLmX1v5S)a)4+)~N}TZ$=R$DCL$@?= zhR!9aLrPn#!mHv8F_6+f7;gxcxW5?uW@5)gW3=PQ#lsT}HyHE=VMee^^9QQy#GnwT z>(rzkTJ+6lni&;~L<7|saX2J5WU0<@zj=2_XtK5xeL6@QM&o}3@|$kMo<>V!q(f@2 zhw=*1M#{su86y#23(1;JypH6UAnIJC(VoGqX|0*WGj#Z|_D##Q&j#(-v~*M6M8gB- zV8ceUwl!$iEe4GB_8>9du*vLkZyK;Am3G>af=_Ca44wL<*_}etxXzDVYm$Ux+wi~k zv#KPa^F!CmuJC#KpBN3g*FEl`ev(5YtlzCM=vs`1Ymlo$t{%BAmV018ze_9SdfdDF z+g+pfx9d^gG_H3&`*m-8=^X8nNJUB4Q-1;P99jK|Lk^)%v0FE-L>3O zuyi&`Z!`ys!cLD{9C*x?XIZv1SfsQcy5!Qe1G>1$Czj+k3#Uu|SIOdBqaodJs>I{2 z^ayc5okl~rF)O#wm9=!E`3~v--K<>UKrqUEpOw39q}rW- z1m)(ja&t$@C61Ic|AIr)`4+O>Ccw!DKmoQRpGRlQ?#v>kXJXMm85gqKm z)^!ug#~W^uX6F9>@vd0JgE4+O*L2JDrFy9cJnrZFL)wRqT(}u$6Jq~;=~ldl1oRn% zqX>xzA0k8}^diu?)~N_igl@z=2&JG~5oRF#6>&O)w-;NsJqQ;Nh$jYVdB_(K=nSGr z#AhKy-GnnZff0v2?)QcxjyMiy*>(173}>RXv3D=J@y1f}wVe($7}!WaUbu~xHLnC zLD{w+Y1bJh7`87e?ktEdvuj#n?Jq6T9*Z$Vm;_f$)$gMrUbGN@@#LpSOL2&X2c~%32R$=OZz@|;7U^m*d>l;@w6395n)DCAFU;2-XY&ah8M?@W$z1%LsrhgZ0kMg+hhgTq9GIaRycI|d; z{DKqHa>I?fH}5x3sq(n*>R*>*NC}aAQP7S!1I^gdK974=zvj0gFYH}(BkD+)HK9|O zr9Gxih&(pIHPI5WG$EGFu^)NdwSD7Un$(~KP3Jv^&wI2l>=txybe4p;LM)+d-g(@s z`hu5?gWZV>VPCPZJ=np^z*Hdac5p{a>qod@|M_gzgcJ5t`j|Im|bK()By zFjGMN*YmE;u60OxjsE}TC08~0-*CMm-#p++3aEtCCjOoh5IR zIof5(m19c~yVkXs#c~h}cjd5HHe!00S&C_nWg$j3`UGlSGE(c{a9<5x#q=y*4b-aH zRp`29q|U)}uk?_<@bi5y_RJeeuk1_B)f)6MLnSx5l5?pwtNKhx2|>zamvMxzurCp* zp-2sJB}jZr`eq|VhZK+d*I)t_u>||yjEr-@akNXesIq)4-I)gAfxHGyc#t@z! zQ56;CahIH>LD=~@QFaL| z?3V9DgAq6%NEY`Oq=y2RdB*3KVjb@*xvT1hs_CWS>65EoDe$8ueW8*xlr8iO@*Bm7Fl*#lK3^vBC*4I^p6XK%(>r=At!rgUaD1vTBURM@_` zX`@-Q{kJ8w6E&z)w{ys^J1`v^0>Qt zW$DhdVX*p+oKNw5E)ng0WIE2>NB9N8X@mg;vb)f#Yp??8j!m^pTT0$91bT>PgQtZY zyU}u^ltO#%pZCVQ;$S^py~KOHan{w|rN0qKDrF5Y;Hg8%CyjPXRD3*HMi_Qx1 z^N$f<@A+xVbS?Vr5140Ft*dH|-j%6s3ECc-71w!FRTTNVDr2ahF3d5s~>FzkNzyl$6%cSLS{>Dp4N?Iy^+)O$_dgk?11o6HZGbr^$Lh7{;9t(W#Y z!?3r0gcArI2tPxJTUVQ34>V52dE;motv`Ch^(gbS=X9{{lQ;v}MzSZ`6&oDqN>BFwav(mY(4GQBjm^3~E|ciVZId23excy;$` zv7)rXPmZAv^RthutR!*IKG_3XGH%)-voq zK4{M|n#OOhv`m{mzIB6n*!|I9aBF$qEmh-o2`#IahO}ZPZr8TZd5_`_O zrqJy|EcSRYNjp65{k{5W+G8uRSIuz^yYtVRaL+gF4m}?{ZCvLH%*r6l)Wn0s?sT-b zeQ^v{m@rtv#lhfdVV(Ibe-83zAwPIm`{D?2j0eXT;E3wXVH^qIh?Y1s(;>wW+Ujv< z!Do5g7cbF0W^-@S3&QM4$HGwB<8JC*Anl!Fpvmx{aMOmad6xNXtiSH5!YsV4H+Y(^ zb3v8SFm5+IizjLC?B|8V;Len)*{_oaH%1M+BL-(5q&vW2_g&{_Lps0S47_Z2#fU(;ZWH2-cJ{Ea0IE5opR;rXLWgADO(e5(fad)~`U!@bn7 zJMsLlxtvvlM_Bsw^S|WM3kLsg=;7?il zUkBgd(#65oS^5`)uW{*-gRipmzYM;@rH2l_#M1v`a37a`X<%=zzV&E{+|wO{ySco< zf!198?li+OwEEEC^IVQ=U4t9}jNla*nex+k<=a_|9z>>dTKo$9C#>KMoC! zl{}vNG303n4^^$p*^?4wSYQa=G3@?JKUw*(`}*@bLvZV``%J%(5Y+iY*e34UT@Nm$ z*%FW1_7B){^?T%zp!@$jI-4v_%XEg2mf)5RW0HH@ z70{hlJ^c&AZu3CgfnoRh{@a%fySEPNrUl`7QAEtJ`IB+_}J#KH|f}29jF!G4u0=-O~r= zE!_l-OWy_Y+nj36*rky0t zGW-xSSNG$7_1I$gQJpJ=cCyE^K~wJyyNmlbxMsm;(VCFoKNl9Bnu49L33dn@uj*Tu zlK_j?g8m8U$P{6E6z)fY5RV&}o2%UwcHnbN{99E!9Iq zDm@#Pd#O*DbK|iaM@Ihw>ci)+vBYb;biEJn7vR+&n+&^>d-wCcxuts8b6)>M>_j?p z-n|a{5cKDVv+@2xpcZ41pv7lzpua}A1@RvsJd1D;VGx1tT`brWM;iBaM~eHpgN)C( zwBnAQQ1PKIL2T}x=1DqLfzMBmYp|wYh9`sZ5I+S+#+0Z0x~4sGulp( z{v=0-`XW%z!7D8D2d+#LE29(87U3Bewe21Bn_pbhBJ~y3FQD!tOvYV*^VYBAea`ZV zxV(jE$3oT);-fJjIXcMIMQrK9CxfTtG1&q=gfQK=vfP(BUC-e8*9nZ1QqNhIx8NVB z=Nv1$m8)kHt7ojfKh2dfqRf6yxAQD-GN;>E^8U!lbJ6|-^2REo^no<~D03iy4qsmM zj?q~1KH+p&#pI1u<^!%w9Llr>(4q4xbU4JxJAr2%ByTJoUgOH_N0~zbba?A3blAbk zD`xV>(!t7=5m9C+fDTVzg$}u#yiPp3A$eozz}v$fl=&cl4mDSy18)y`Ox{>J@b(ae zG6wiDWzH(|BJV16;O*f9JO?6qW9h)#!%mbr6QCbvUxg05J(!uiv2=Kp8@~vY`6z%6 zVOOC;JtwaN_rfG^Xz`r0y3O0W$(M#=9niG3oeCkDrk&lo(k)%VLeVLmxbr$qLR*l= z(cFbMRh-g@i>C*bw#8@*+G6mUfLX)?QDH$S|6`O50iJSu@NJZW(>l$TZcjm5kgjZt z9_1$MHt*IWT&EW?D@)rZLB=GVW6O2HC=(jdc4`r;TM*k$>&5qbXl)vL>`3oW=A5%F z1^c=+rC7Hz+D=6ed-}FuP0$z6n)zIhPV;=Xyk>6c{uAofG&gr?!b?u$3wEc%=vB`m z+J&`NCv52=oph6)@4h)uU7NAqqRzL~`s1lyhzMOZTlA#6u6fIKqiS%qvNV8)bZeDbewk;I`-mOva*Nx4&^!!$TxipJi=%<@E44}0Q^@*e+fLq=s_Ua zSORRA$_Hia>k%T_PLsWV_YK<*f)0w<*AoGrAQ6~xq&LCsIY;*(F7(v}1S9esN_ok? zF*hLBfOL{Y`&A)h2lkl{cmD+Gltz0>x)*UFjFc}x{TXeiXzv-u$`c)AM7p@6J43|2 zD&t|)b)>fyI-Eh>A0VWm9i^@Kx4NXK(Mn(f#(wlsHZ zG!OR(#4(~%bd86;o6#3S2ht;;2XvYJ@7r8> zv2XYI_GTB}nAR<{KZNqTfp@z!5xHG2O=>%}BUtM?=kbJ%hp#;8@jMZNy0s$sp_?E+ zER9Qe=Tlun&m8HcwBU%gQ&iWl&`-qse8zOhegbp;;qI@{FPHHA_gxLXBdLW)MK}oB zh%u-B2#t8ED-!L{8Dk_L{!{Yt1$(dd|p@U$2t~<=MwTJRF=x?$+^7#UM(NVOA?vdQ+2Q!`*Q~NUz{u}WUv`w;o zvQe^eq4;zVpw43I4h~pU-joBs2&-f8^Y&-P~Ow>n}8h{<4v&l1Ci)^_yaL>U$6JI zG6eVtvJXug>=-^l*YZ^NHJJa$(C?w*&YlqTRd5H&wCu$^rT%}md%Wvm*X*BRK4R{U zLthB4ZOGf)y&CoFguUG#lAaN};R{gKxU*-wUJLt&eMP*`9b_zX1z}!K!XqC&_B~PX zF`*;__8_#k`1uda|1g>t;i&%=6ZAA@bp^%j>Jh}v-9eKc?>UbC)ry;4nil9LKG_`# ze-#dS--j;a&}Wg%Pf&gk;$)+Tpx<*Xv9gaaKH2>xWc0)7(bzc_4uadtgC4X_pX>>Vs-O?rLiAQT1Vby8UR~G@lG3k*WUBTuE zy|HbQMr^w_1iTtN0|`f8P~QbXFP-sW7x|2!(wwg6i({D2*xVh8HqbnbfIgBB#CP19 zVfPc@&xHI8bC(Y5 z5~T~qN4s@p^19^unr!|^?^i(haY-iiCCQ||MBAUZi2bLp9O#MXHM-lId#J6nPM$`2 z@*_SvIb<0^#Iw&~h;{?f7q^qIej+4da~JhL`OlDu_fBhIU(h{_^m~i?M#S7gKWl~J zQ#!HDL~Z-xg6uDKNQW);1w%iYe>A_S4Luh;=kPq`9Ojte2TGR8=ID|r$hjvQ&NN z{>8bDi4l0VsKkGEE^AR%W)%A#pvV1{M-y9;Ap{E(`0oIG=U&L_WWQ(4mwve_p9Cz( zz}}6Po}Mqg94%d}X;@Q?BV%rzSs~(xY!ky|V3@jybVfFQ9E@F=N+%?VaWm}+Gwtz- zLf{f*6>A$CP^dvFJMz}1L;+`RH&lr@#NH;(Y`A>B^n$klbwdcfHsx|cy!oRGxs%@I zBd(TT=5rZg0r*B2n(2KF&t*jVag8oeWg6T#i*v*kwIe+%;>W|%@Fxm*MGg*h!zt}} zH3ICUmPQXR1*;G%Y}Gh$4?m;E=#|IQh=S?XnGJ$PKJQGFUbZCSg&5NNXc`jLRz@em zvua>v@xq42^>}MagF_~a3XyCV>esBs>q>;R4jZY!z4#d4__nMheR zj(VurAm*>g%k!0nCZP$^=|RL;j-O)NDuj_2Lx|<8(?!glnbpibb?4j`@Z}V7 zOdLABL9D2&wFx+G+?us0T{Je$Y`Us{fhs3OA$X&$zRtIQ($nRaPsB+tClnhT3)u@W zZo4Udq>Z=UN*yc4)z*oX^kyH?StrWpJ;h&*G$vWBTTMUQCw=NFC6z>ai^bJQTGUus z2_t3i=pae{BR|rz^>}mStOThJs}qfqtxYloI^&;8(+S5LVn)$GU0yc3ELf6MTpsiW zv9heX!6wErqhv3PfiD&B!&yh-YMUs%5dw1}Ha1q#0q@xmDSG$JihD%qB_$2;%!$dB zB68H~C^jWxR>Nzf3vHOOutLmAdVNB=SeSXIpWh$5R6$*XlU`6KkL}+ffX?)XhajJT zLySl(9L2<%$d)Yl69-!6@Us`c)AVIVTA)|~9Bv@@DR*Qn^I6sQ+I3`$(*y8K7uZ`p zFdd0U9`z!ePH!)_iR;DW#N;tKQpVs&F=LD*@5zSSucAbeX(7U`R@GIA_m#;DUM1cI zQxW6;GJ$jqZrSQ;X^M>|G%QfDBAe4mO~rdB$t}Vj>+5RZ^YI?RIxL0EEZHe-fdt$= zv*8{=W}8FD@iy5Sgd*i-imdw@;4Jyn0(&{Vm_wv@8;+JIzuUtvSANk(;F2uw@+GC| zBGOC7)Uu1K8f$Qjh`l(na-t2hGV#qOyjTo^x>#_LM1sbSIvz@;aBec&S+pn7w$5 z7!_pGhs{WMb?iR`rVI8+D2fqjXe=+cH8fNQ2Wp zhmp69xE-GYb8hp0ciNcENf*m1X+J6%45r0DLn2es&n&N48NZSVY&%qcEA7^V?83t3 zg(7y(s?ot7aSlvLS~2QvB!wB)71UgBbI^%`kg9rX73(0W&VN)7F>`UIC7)NbqE0NU z-QcXENnpbU2e;BVPSWovTcoMar6oR}185QVJ!e*ymF#n(teob5ZY<;VIW|^KQym2> z>F0CkE9%7wld99uSy|shU8yX0Rx5a<16OfxT`r$4&*HwNa6S`lWO2?U-bjZJ7RNs4 zOvN+W`CpQb+`(Ij|NrYB>j4iglCXaZz5|Y{y~0=Ct(AgcLl_4`Ka0HI0rw)<5au9^ zb$xvWJS1a(8@>aGknj?|Ck8APyw`iE_w{@6o0~d(Z@5+v768>(8+4~Y>G8;47J!HH zO6gyKYX#&!g`mE8xep-!r2srsMyD>w;-ksE1YIc*+vVia*)m%Y=sNl`>UjX4Cn9XS zUrwX{ok-t`w0BkMCy^fhAU+F1;Q8qssmLGV`<)1U`URx7BJCqpy6_6V57LZJV-R@$ zaHO{(?YJsE3hB+8@ckeJo__|?TcFEPl|FQi)JJGfl!~9ukxD~)JHjTey>yOL>qh(z z3u)B|bPkkxFUkXnF5&2Ej@|^CbReDeGU7I|AizMLw0Ii}+R|eLDilZAYF4=@X~P z7{zb~!!(B343{%p$*_`PEpP(lnDHHad?!V^CgQ&paePG>72~_PfN%tSR3d~Uj*q>h zINA3h1U^wfbS}RUpW9= zct9Sh6QJozWpSd1kd_8MGt%0D%?QdRf~K>gS}qI2|_7oCqf?R5`-bp1wj7lMA{P89!j6_h&R{4)#Xk7 zCsT{FveL!4#rZ4Z#ni;qL`=p1V=0+&(7lXK3KcYPC4zn~UI!Jd3)PI%Ox8teuG8EQ z6s4J>iPj2<4OMu^>?~U?Bv#?W3n8&$LoK}pRzhdJkcdaVM9YMu+9@RB7c@JaJ7Oipm9Xf~t^+Ph#jDBtl|l;j%=$ z5VoFPxMXX%nw}?rM!rsqKz@(H349rZ;&hQeq(HuncqyIyA%#vjGq^q}5D)oU@~@Oe zeB?hVkl$1;4I_{&pc;vf{3HePlYG6Q$e+dWkzb`Cq5|S0-%RxqUm^m@rZmMj9;Hln z5lMkmQTS*NNZ~>}V*-#Yx)vZ1AJwxT>k|covX~%^WYLw0Fad$uOzR2-T33`y;ai3T zT7!s>)*C5+RxYZGu9XNx6CcfE3N4awB;Zo?!&X&lD`nBzKp_eR6;`U3cxij6$fNa& zLYvab$Rj=qPL7ZKffSIYT*N~cwj+{Us)u|Lg*MR2rF0RtR#Lr6I`LI25iV~lXvlIX ZSqTTQuT>%(pV9^;YX$gPl?cc8zX5Xt{viMW diff --git a/3rdparty/lib/armeabi/libnative_camera_r4.0.0.so b/3rdparty/lib/armeabi/libnative_camera_r4.0.0.so index fae5d9bad0cf16267e22f32e527fb4fb654afb52..a1e5743834ac29e2e76ad31c222702cd7efda758 100755 GIT binary patch literal 46272 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 literal 44008 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 diff --git a/3rdparty/lib/armeabi/libnative_camera_r4.0.3.so b/3rdparty/lib/armeabi/libnative_camera_r4.0.3.so index 85435031069e4ea7fdb75d99fb905ff9c3ae499d..ddb72eed8af9e645785c6e2cf1cfef7d6d1cf568 100755 GIT binary patch literal 46272 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 literal 45616 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 CGY1U- diff --git a/3rdparty/lib/x86/libnative_camera_r2.3.3.so b/3rdparty/lib/x86/libnative_camera_r2.3.3.so index 4e04a3e23ff5889180a3dfab503d99d770eefd5f..bb4f2f14cf893369453f7f226aaf2c8dabea95f7 100755 GIT binary patch literal 54476 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 CljH0F literal 52128 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�)ca= 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 Jpd$vl{{!@#zKvEG7I_>Q~c)DA__lN)tR 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 S7Oqp4jeHrlCF2AJ-v0xO2H8pg literal 52144 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 diff --git a/3rdparty/lib/x86/libnative_camera_r4.1.1.so b/3rdparty/lib/x86/libnative_camera_r4.1.1.so index df22898b45b2efe5d35083dda1b9b303d8e15af3..dc8f06a49dbdf38d43f9ef4c1eb54275e60cd63d 100644 GIT binary patch literal 58572 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 Rp*CgRkoVw&GEbn>{U2xB2c`f3 literal 54080 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 From b16b50d61b552a7f7eb3f86c2b28bcb9d933f0a4 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 7 Sep 2012 17:22:11 +0400 Subject: [PATCH 29/97] Build script for all native cameras added. --- android/scripts/camera_build.conf | 16 ++++++ android/scripts/cmake_android_all_cameras.py | 51 ++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 android/scripts/camera_build.conf create mode 100755 android/scripts/cmake_android_all_cameras.py diff --git a/android/scripts/camera_build.conf b/android/scripts/camera_build.conf new file mode 100644 index 0000000000..26fbf81f3e --- /dev/null +++ b/android/scripts/camera_build.conf @@ -0,0 +1,16 @@ +native_camera_r2.2.0;armeabi;8;/home/alexander/Projects/AndroidSource/2.2.2 +native_camera_r2.2.0;armeabi-v7a;8;/home/alexander/Projects/AndroidSource/2.2.2 +native_camera_r2.3.3;armeabi;9;/home/alexander/Projects/AndroidSource/2.3.3 +native_camera_r2.3.3;armeabi-v7a;9;/home/alexander/Projects/AndroidSource/2.3.3 +native_camera_r2.3.3;x86;9;/home/alexander/Projects/AndroidSource/2.3.3 +native_camera_r3.0.1;armeabi;9;/home/alexander/Projects/AndroidSource/3.0.1 +native_camera_r3.0.1;armeabi-v7a;9;/home/alexander/Projects/AndroidSource/3.0.1 +native_camera_r3.0.1;x86;9;/home/alexander/Projects/AndroidSource/3.0.1 +native_camera_r4.0.3;armeabi;14;/home/alexander/Projects/AndroidSource/4.0.3 +native_camera_r4.0.3;armeabi-v7a;14;/home/alexander/Projects/AndroidSource/4.0.3 +native_camera_r4.0.3;x86;14;/home/alexander/Projects/AndroidSource/4.0.3 +native_camera_r4.0.0;armeabi;14;/home/alexander/Projects/AndroidSource/4.0.0 +native_camera_r4.0.0;armeabi-v7a;14;/home/alexander/Projects/AndroidSource/4.0.0 +native_camera_r4.1.1;armeabi;14;/home/alexander/Projects/AndroidSource/4.1.1 +native_camera_r4.1.1;armeabi-v7a;14;/home/alexander/Projects/AndroidSource/4.1.1 +native_camera_r4.1.1;x86;14;/home/alexander/Projects/AndroidSource/4.1.1 \ No newline at end of file diff --git a/android/scripts/cmake_android_all_cameras.py b/android/scripts/cmake_android_all_cameras.py new file mode 100755 index 0000000000..5d035c5828 --- /dev/null +++ b/android/scripts/cmake_android_all_cameras.py @@ -0,0 +1,51 @@ +#!/usr/bin/python + +import os +import sys +import shutil + +ScriptHome = os.path.split(sys.argv[0])[0] +ConfFile = open(os.path.join(ScriptHome, "camera_build.conf"), "rt") +HomeDir = os.getcwd() +for s in ConfFile.readlines(): + keys = s.split(";") + if (len(keys) < 4): + print("Error: invalid config line: \"%s\"" % s) + continue + MakeTarget = keys[0] + Arch = keys[1] + NativeApiLevel = keys[2] + AndroidTreeRoot = keys[3] + AndroidTreeRoot = str.strip(AndroidTreeRoot, "\n") + print("Building %s for %s" % (MakeTarget, Arch)) + BuildDir = os.path.join(HomeDir, MakeTarget + "_" + Arch) + if (os.path.exists(BuildDir)): + shutil.rmtree(BuildDir) + try: + os.mkdir(BuildDir) + except: + print("Error: cannot create direcotry \"%s\"" % BuildDir) + continue + shutil.rmtree(os.path.join(AndroidTreeRoot, "out", "target", "product", "generic", "system"), ignore_errors=True) + if (Arch == "x86"): + shutil.copytree(os.path.join(AndroidTreeRoot, "bin_x86", "system"), os.path.join(AndroidTreeRoot, "out", "target", "product", "generic", "system")) + else: + shutil.copytree(os.path.join(AndroidTreeRoot, "bin_arm", "system"), os.path.join(AndroidTreeRoot, "out", "target", "product", "generic", "system")) + os.chdir(BuildDir) + BuildLog = os.path.join(BuildDir, "build.log") + CmakeCmdLine = "cmake -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DANDROID_SOURCE_TREE=\"%s\" -DANDROID_NATIVE_API_LEVEL=\"%s\" -DANDROID_ABI=\"%s\" -DANDROID_USE_STLPORT=ON ../../ > \"%s\" 2>&1" % (AndroidTreeRoot, NativeApiLevel, Arch, BuildLog) + MakeCmdLine = "make %s >> \"%s\" 2>&1" % (MakeTarget, BuildLog); + #print(CmakeCmdLine) + os.system(CmakeCmdLine) + #print(MakeCmdLine) + os.system(MakeCmdLine) + os.chdir(HomeDir) + CameraLib = os.path.join(BuildDir, "lib", Arch, "lib" + MakeTarget + ".so") + if (os.path.exists(CameraLib)): + try: + shutil.copyfile(CameraLib, os.path.join("..", "3rdparty", "lib", Arch, "lib" + MakeTarget + ".so")) + print("Building %s for %s\t[OK]" % (MakeTarget, Arch)); + except: + print("Building %s for %s\t[FAILED]" % (MakeTarget, Arch)); +ConfFile.close() + From 0598f33a85191cdd686038db04c4ba80fe810bea Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Fri, 7 Sep 2012 17:44:01 +0400 Subject: [PATCH 30/97] refactored command line parser, fixed the docs --- modules/core/doc/command_line_parser.rst | 10 +- modules/core/include/opencv2/core/core.hpp | 181 ++--- modules/core/src/command_line_parser.cpp | 747 ++++++++++++--------- 3 files changed, 484 insertions(+), 454 deletions(-) diff --git a/modules/core/doc/command_line_parser.rst b/modules/core/doc/command_line_parser.rst index 5f0e512c82..c54e65b80b 100644 --- a/modules/core/doc/command_line_parser.rst +++ b/modules/core/doc/command_line_parser.rst @@ -58,15 +58,15 @@ The sample below demonstrates how to use CommandLineParser: } int N = parser.get("N"); - double fps = parser.getparser("fps"); + double fps = parser.get("fps"); std::string path = parser.get("path"); - use_time_stamp = parserer.has("timestamp"); + use_time_stamp = parser.has("timestamp"); - std::string img1 = parser.get(1); - std::string img2 = parser.get(2); + std::string img1 = parser.get(0); + std::string img2 = parser.get(1); - int repeat = parser.get(3); + int repeat = parser.get(2); if (!parser.check()) { diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index 2496c805c5..44941aff45 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -221,7 +221,7 @@ CV_EXPORTS void setNumThreads(int nthreads); CV_EXPORTS int getNumThreads(); CV_EXPORTS int getThreadNum(); -CV_EXPORTS_W const std::string& getBuildInformation(); +CV_EXPORTS_W const string& getBuildInformation(); //! Returns the number of ticks. @@ -4434,7 +4434,7 @@ protected: struct CV_EXPORTS Param { - enum { INT=0, BOOLEAN=1, REAL=2, STRING=3, MAT=4, MAT_VECTOR=5, ALGORITHM=6 }; + enum { INT=0, BOOLEAN=1, REAL=2, STRING=3, MAT=4, MAT_VECTOR=5, ALGORITHM=6, FLOAT=7, UNSIGNED_INT=8, UINT64=9 }; Param(); Param(int _type, bool _readonly, int _offset, @@ -4505,142 +4505,73 @@ template<> struct ParamType enum { type = Param::ALGORITHM }; }; -// The CommandLineParser class is designed for command line arguments parsing - -class CV_EXPORTS CommandLineParserParams +template<> struct ParamType { - public: - std::string help_message; - std::string def_value; - std::vector keys; - int number; + typedef float const_param_type; + typedef float member_type; + + enum { type = Param::FLOAT }; +}; + +template<> struct ParamType +{ + typedef unsigned const_param_type; + typedef unsigned member_type; + + enum { type = Param::UNSIGNED_INT }; }; -template -std::string get_type_name() { return "UNKNOW"; } - -bool cmp_params(const CommandLineParserParams & p1, const CommandLineParserParams & p2); - -template -T from_str(const std::string & str) +template<> struct ParamType { - T value; - std::stringstream ss(str); - ss >> value; + typedef uint64 const_param_type; + typedef uint64 member_type; + + enum { type = Param::UINT64 }; +}; - if (ss.fail()) - { - std::string err_msg = - std::string("can not convert: [") - + str - + std::string("] to [") - + get_type_name() - + std::string("]"); - CV_Error(CV_StsBadArg, err_msg); - } - - return value; -} - -template<> std::string from_str(const std::string & str); - -template -std::string to_str(T value) -{ - std::ostringstream os; - os << value; - return os.str(); -} +// The CommandLineParser class is designed for command line arguments parsing class CV_EXPORTS CommandLineParser { - public: - CommandLineParser(int argc, const char * const argv[], const std::string keys); +public: + CommandLineParser(int argc, const char* const argv[], const string& keys); + CommandLineParser(const CommandLineParser& parser); + CommandLineParser& operator = (const CommandLineParser& parser); - std::string getPathToApplication(); + string getPathToApplication() const; - template - T get(const std::string& name, bool space_delete = true) - { - try - { - for (size_t i = 0; i < data.size(); i++) - { - for (size_t j = 0; j < data[i].keys.size(); j++) - { - if (name.compare(data[i].keys[j]) == 0) - { - std::string v = data[i].def_value; - if (space_delete == true) v = cat_string(v); - return from_str(v); - } - } - } - error = true; - error_message += "Unknown parametes " + name + "\n"; - } - catch (std::exception& e) - { - error = true; - error_message += "Exception: " + std::string(e.what()) + "\n"; - } - return T(); - } + template + T get(const string& name, bool space_delete = true) const + { + T val = T(); + getByName(name, space_delete, ParamType::type, (void*)&val); + return val; + } - template - T get(int index, bool space_delete = true) - { - try - { - for (size_t i = 0; i < data.size(); i++) - { - if (data[i].number == index - 1) - { - std::string v = data[i].def_value; - if (space_delete == true) v = cat_string(v); - return from_str(v); - } - } - error = true; - error_message += "Unknown parametes #" + to_str(index) + "\n"; - } - catch(std::exception & e) - { - error = true; - error_message += "Exception: " + std::string(e.what()) + "\n"; - } - return T(); - } - - bool has(const std::string& name); - - bool check(); - - void about(std::string message); - - void printMessage(); - void printErrors(); - - protected: - bool error; - std::string error_message; - std::string about_message; - - std::string path_to_app; - std::string app_name; - - std::vector data; - - std::vector split_range_string(std::string str, char fs, char ss); - std::vector split_string(std::string str, char symbol = ' ', bool create_empty_item = false); - std::string cat_string(std::string str); - - void apply_params(std::string key, std::string value); - void apply_params(int i, std::string value); - - void sort_params(); + template + T get(int index, bool space_delete = true) const + { + T val = T(); + getByIndex(index, space_delete, ParamType::type, (void*)&val); + return val; + } + + bool has(const string& name) const; + + bool check() const; + + void about(const string& message); + + void printMessage() const; + void printErrors() const; +protected: + void getByName(const string& name, bool space_delete, int type, void* dst) const; + void getByIndex(int index, bool space_delete, int type, void* dst) const; + + struct Impl; + Impl* impl; }; /////////////////////////////// Parallel Primitives ////////////////////////////////// diff --git a/modules/core/src/command_line_parser.cpp b/modules/core/src/command_line_parser.cpp index dc137e7296..8cfeaf3ca1 100644 --- a/modules/core/src/command_line_parser.cpp +++ b/modules/core/src/command_line_parser.cpp @@ -5,402 +5,501 @@ namespace cv { - bool cmp_params(const CommandLineParserParams & p1, const CommandLineParserParams & p2) + +struct CommandLineParserParams +{ +public: + string help_message; + string def_value; + vector keys; + int number; +}; + + +struct CommandLineParser::Impl +{ + bool error; + string error_message; + string about_message; + + string path_to_app; + string app_name; + + vector data; + + vector split_range_string(const string& str, char fs, char ss) const; + vector split_string(const string& str, char symbol = ' ', bool create_empty_item = false) const; + string cat_string(const string& str) const; + + void apply_params(const string& key, const string& value); + void apply_params(int i, string value); + + void sort_params(); + int refcount; +}; + + +static string get_type_name(int type) +{ + if( type == Param::INT ) + return "int"; + if( type == Param::UNSIGNED_INT ) + return "unsigned"; + if( type == Param::UINT64 ) + return "unsigned long long"; + if( type == Param::FLOAT ) + return "float"; + if( type == Param::REAL ) + return "double"; + if( type == Param::STRING ) + return "string"; + return "unknown"; +} + +static void from_str(const string& str, int type, void* dst) +{ + std::stringstream ss(str); + if( type == Param::INT ) + ss >> *(int*)dst; + else if( type == Param::UNSIGNED_INT ) + ss >> *(unsigned*)dst; + else if( type == Param::UINT64 ) + ss >> *(uint64*)dst; + else if( type == Param::FLOAT ) + ss >> *(float*)dst; + else if( type == Param::REAL ) + ss >> *(double*)dst; + else if( type == Param::STRING ) + ss >> *(string*)dst; + + if (ss.fail()) { - if (p1.number > p2.number) + string err_msg = "can not convert: [" + str + + + "] to [" + get_type_name(type) + "]"; + + CV_Error(CV_StsBadArg, err_msg); + } +} + +void CommandLineParser::getByName(const string& name, bool space_delete, int type, void* dst) const +{ + try + { + for (size_t i = 0; i < impl->data.size(); i++) + { + for (size_t j = 0; j < impl->data[i].keys.size(); j++) + { + if (name.compare(impl->data[i].keys[j]) == 0) + { + string v = impl->data[i].def_value; + if (space_delete) + v = impl->cat_string(v); + from_str(v, type, dst); + return; + } + } + } + impl->error = true; + impl->error_message += "Unknown parametes " + name + "\n"; + } + catch (std::exception& e) + { + impl->error = true; + impl->error_message += "Exception: " + string(e.what()) + "\n"; + } +} + + +void CommandLineParser::getByIndex(int index, bool space_delete, int type, void* dst) const +{ + try + { + for (size_t i = 0; i < impl->data.size(); i++) + { + if (impl->data[i].number == index) + { + string v = impl->data[i].def_value; + if (space_delete == true) v = impl->cat_string(v); + from_str(v, type, dst); + return; + } + } + impl->error = true; + impl->error_message += "Unknown parametes #" + format("%d", index) + "\n"; + } + catch(std::exception & e) + { + impl->error = true; + impl->error_message += "Exception: " + string(e.what()) + "\n"; + } +} + +static bool cmp_params(const CommandLineParserParams & p1, const CommandLineParserParams & p2) +{ + if (p1.number > p2.number) + return false; + + if (p1.number == -1 && p2.number == -1) + { + if (p1.keys[0].compare(p2.keys[0]) > 0) + { return false; - - if (p1.number == -1 && p2.number == -1) - { - if (p1.keys[0].compare(p2.keys[0]) > 0) - { - return false; - } - } - - return true; - } - - CommandLineParser::CommandLineParser(int argc, const char * const argv[], const std::string keys) - { - // path to application - size_t pos_s = std::string(argv[0]).find_last_of("/\\"); - if (pos_s == std::string::npos) - { - path_to_app = ""; - app_name = std::string(argv[0]); - } - else - { - path_to_app = std::string(argv[0]).substr(0, pos_s); - app_name = std::string(argv[0]).substr(pos_s + 1, std::string(argv[0]).length() - pos_s); - } - - error = false; - error_message = ""; - - // parse keys - std::vector k = split_range_string(keys, '{', '}'); - - int jj = 0; - for (size_t i = 0; i < k.size(); i++) - { - std::vector l = split_string(k[i], '|', true); - CommandLineParserParams p; - p.keys = split_string(l[0]); - p.def_value = l[1]; - p.help_message = cat_string(l[2]); - p.number = -1; - if (p.keys[0][0] == '@') - { - p.number = jj; - jj++; - } - - data.push_back(p); - } - - // parse argv - jj = 0; - for (int i = 1; i < argc; i++) - { - std::string s = std::string(argv[i]); - - if (s.find('=') != std::string::npos && s.find('=') < s.length()) - { - std::vector k_v = split_string(s, '=', true); - for (int h = 0; h < 2; h++) - { - if (k_v[0][0] == '-') - k_v[0] = k_v[0].substr(1, k_v[0].length() -1); - } - apply_params(k_v[0], k_v[1]); - } - else if (s.length() > 1 && s[0] == '-') - { - for (int h = 0; h < 2; h++) - { - if (s[0] == '-') - s = s.substr(1, s.length() - 1); - } - apply_params(s, "true"); - } - else if (s[0] != '-') - { - apply_params(jj, s); - jj++; - } - } - - sort_params(); - } - - void CommandLineParser::about(std::string message) - { - about_message = message; - } - - void CommandLineParser::apply_params(std::string key, std::string value) - { - for (size_t i = 0; i < data.size(); i++) - { - for (size_t k = 0; k < data[i].keys.size(); k++) - { - if (key.compare(data[i].keys[k]) == 0) - { - data[i].def_value = value; - break; - } - } } } - void CommandLineParser::apply_params(int i, std::string value) + return true; +} + +CommandLineParser::CommandLineParser(int argc, const char* const argv[], const string& keys) +{ + impl = new Impl; + impl->refcount = 1; + + // path to application + size_t pos_s = string(argv[0]).find_last_of("/\\"); + if (pos_s == string::npos) { - for (size_t j = 0; j < data.size(); j++) + impl->path_to_app = ""; + impl->app_name = string(argv[0]); + } + else + { + impl->path_to_app = string(argv[0]).substr(0, pos_s); + impl->app_name = string(argv[0]).substr(pos_s + 1, string(argv[0]).length() - pos_s); + } + + impl->error = false; + impl->error_message = ""; + + // parse keys + vector k = impl->split_range_string(keys, '{', '}'); + + int jj = 0; + for (size_t i = 0; i < k.size(); i++) + { + vector l = impl->split_string(k[i], '|', true); + CommandLineParserParams p; + p.keys = impl->split_string(l[0]); + p.def_value = l[1]; + p.help_message = impl->cat_string(l[2]); + p.number = -1; + if (p.keys[0][0] == '@') { - if (data[j].number == i) + p.number = jj; + jj++; + } + + impl->data.push_back(p); + } + + // parse argv + jj = 0; + for (int i = 1; i < argc; i++) + { + string s = string(argv[i]); + + if (s.find('=') != string::npos && s.find('=') < s.length()) + { + vector k_v = impl->split_string(s, '=', true); + for (int h = 0; h < 2; h++) { - data[j].def_value = value; + if (k_v[0][0] == '-') + k_v[0] = k_v[0].substr(1, k_v[0].length() -1); + } + impl->apply_params(k_v[0], k_v[1]); + } + else if (s.length() > 1 && s[0] == '-') + { + for (int h = 0; h < 2; h++) + { + if (s[0] == '-') + s = s.substr(1, s.length() - 1); + } + impl->apply_params(s, "true"); + } + else if (s[0] != '-') + { + impl->apply_params(jj, s); + jj++; + } + } + + impl->sort_params(); +} + + +CommandLineParser::CommandLineParser(const CommandLineParser& parser) +{ + impl = parser.impl; + CV_XADD(&impl->refcount, 1); +} + +CommandLineParser& CommandLineParser::operator = (const CommandLineParser& parser) +{ + if( this != &parser ) + { + if(CV_XADD(&impl->refcount, -1) == 1) + delete impl; + impl = parser.impl; + CV_XADD(&impl->refcount, 1); + } + return *this; +} + +void CommandLineParser::about(const string& message) +{ + impl->about_message = message; +} + +void CommandLineParser::Impl::apply_params(const string& key, const string& value) +{ + for (size_t i = 0; i < data.size(); i++) + { + for (size_t k = 0; k < data[i].keys.size(); k++) + { + if (key.compare(data[i].keys[k]) == 0) + { + data[i].def_value = value; break; } } } +} - void CommandLineParser::sort_params() +void CommandLineParser::Impl::apply_params(int i, string value) +{ + for (size_t j = 0; j < data.size(); j++) { - for (size_t i = 0; i < data.size(); i++) + if (data[j].number == i) { - sort(data[i].keys.begin(), data[i].keys.end()); + data[j].def_value = value; + break; } + } +} - sort (data.begin(), data.end(), cmp_params); +void CommandLineParser::Impl::sort_params() +{ + for (size_t i = 0; i < data.size(); i++) + { + sort(data[i].keys.begin(), data[i].keys.end()); } - std::string CommandLineParser::cat_string(std::string str) + sort (data.begin(), data.end(), cmp_params); +} + +string CommandLineParser::Impl::cat_string(const string& str) const +{ + int left = 0, right = (int)str.length(); + while( left <= right && str[left] == ' ' ) + left++; + while( right > left && str[right-1] == ' ' ) + right--; + return left >= right ? string("") : str.substr(left, right-left); +} + +string CommandLineParser::getPathToApplication() const +{ + return impl->path_to_app; +} + +bool CommandLineParser::has(const string& name) const +{ + for (size_t i = 0; i < impl->data.size(); i++) { - while (!str.empty() && str[0] == ' ') + for (size_t j = 0; j < impl->data[i].keys.size(); j++) { - str = str.substr(1, str.length() - 1); - } - - while (!str.empty() && str[str.length() - 1] == ' ') - { - str = str.substr(0, str.length() - 1); - } - - return str; - } - - std::string CommandLineParser::getPathToApplication() - { - return path_to_app; - } - - bool CommandLineParser::has(const std::string& name) - { - for (size_t i = 0; i < data.size(); i++) - { - for (size_t j = 0; j < data[i].keys.size(); j++) + if (name.compare(impl->data[i].keys[j]) == 0 && string("true").compare(impl->data[i].def_value) == 0) { - if (name.compare(data[i].keys[j]) == 0 && std::string("true").compare(data[i].def_value) == 0) - { - return true; - } + return true; } } - return false; } + return false; +} - bool CommandLineParser::check() +bool CommandLineParser::check() const +{ + return impl->error == false; +} + +void CommandLineParser::printErrors() const +{ + if (impl->error) { - return error == false; + std::cout << std::endl << "ERRORS:" << std::endl << impl->error_message << std::endl; } +} - void CommandLineParser::printErrors() +void CommandLineParser::printMessage() const +{ + if (impl->about_message != "") + std::cout << impl->about_message << std::endl; + + std::cout << "Usage: " << impl->app_name << " [params] "; + + for (size_t i = 0; i < impl->data.size(); i++) { - if (error) + if (impl->data[i].number > -1) { - std::cout << std::endl << "ERRORS:" << std::endl << error_message << std::endl; + string name = impl->data[i].keys[0].substr(1, impl->data[i].keys[0].length() - 1); + std::cout << name << " "; } } - void CommandLineParser::printMessage() + std::cout << std::endl << std::endl; + + for (size_t i = 0; i < impl->data.size(); i++) { - if (about_message != "") - std::cout << about_message << std::endl; - - std::cout << "Usage: " << app_name << " [params] "; - - for (size_t i = 0; i < data.size(); i++) + if (impl->data[i].number == -1) { - if (data[i].number > -1) + std::cout << "\t"; + for (size_t j = 0; j < impl->data[i].keys.size(); j++) { - std::string name = data[i].keys[0].substr(1, data[i].keys[0].length() - 1); - std::cout << name << " "; - } - } - - std::cout << std::endl << std::endl; - - for (size_t i = 0; i < data.size(); i++) - { - if (data[i].number == -1) - { - std::cout << "\t"; - for (size_t j = 0; j < data[i].keys.size(); j++) + string k = impl->data[i].keys[j]; + if (k.length() > 1) { - std::string k = data[i].keys[j]; - if (k.length() > 1) - { - std::cout << "--"; - } - else - { - std::cout << "-"; - } - std::cout << k; - - if (j != data[i].keys.size() - 1) - { - std::cout << ", "; - } + std::cout << "--"; } - std::string dv = cat_string(data[i].def_value); - if (dv.compare("") != 0) + else { - std::cout << " (value:" << dv << ")"; + std::cout << "-"; } - std::cout << std::endl << "\t\t" << data[i].help_message << std::endl; - } - } - std::cout << std::endl; - - for (size_t i = 0; i < data.size(); i++) - { - if (data[i].number != -1) - { - std::cout << "\t"; - std::string k = data[i].keys[0]; - k = k.substr(1, k.length() - 1); - std::cout << k; - std::string dv = cat_string(data[i].def_value); - if (dv.compare("") != 0) + if (j != impl->data[i].keys.size() - 1) { - std::cout << " (value:" << dv << ")"; + std::cout << ", "; } - std::cout << std::endl << "\t\t" << data[i].help_message << std::endl; } + string dv = impl->cat_string(impl->data[i].def_value); + if (dv.compare("") != 0) + { + std::cout << " (value:" << dv << ")"; + } + std::cout << std::endl << "\t\t" << impl->data[i].help_message << std::endl; } } + std::cout << std::endl; - std::vector CommandLineParser::split_range_string(std::string str, char fs, char ss) + for (size_t i = 0; i < impl->data.size(); i++) { - std::vector vec; - std::string word = ""; - bool begin = false; - - while (!str.empty()) + if (impl->data[i].number != -1) { - if (str[0] == fs) - { - if (begin == true) - { - CV_Error(CV_StsParseError, - std::string("error in split_range_string(") - + str - + std::string(", ") - + std::string(1, fs) - + std::string(", ") - + std::string(1, ss) - + std::string(")") - ); - } - begin = true; - word = ""; - str = str.substr(1, str.length() - 1); - } + std::cout << "\t"; + string k = impl->data[i].keys[0]; + k = k.substr(1, k.length() - 1); - if (str[0] == ss) - { - if (begin == false) - { - CV_Error(CV_StsParseError, - std::string("error in split_range_string(") - + str - + std::string(", ") - + std::string(1, fs) - + std::string(", ") - + std::string(1, ss) - + std::string(")") - ); - } - begin = false; - vec.push_back(word); - } + std::cout << k; + string dv = impl->cat_string(impl->data[i].def_value); + if (dv.compare("") != 0) + { + std::cout << " (value:" << dv << ")"; + } + std::cout << std::endl << "\t\t" << impl->data[i].help_message << std::endl; + } + } +} + +vector CommandLineParser::Impl::split_range_string(const string& _str, char fs, char ss) const +{ + string str = _str; + vector vec; + string word = ""; + bool begin = false; + + while (!str.empty()) + { + if (str[0] == fs) + { if (begin == true) { - word += str[0]; + CV_Error(CV_StsParseError, + string("error in split_range_string(") + + str + + string(", ") + + string(1, fs) + + string(", ") + + string(1, ss) + + string(")") + ); } + begin = true; + word = ""; str = str.substr(1, str.length() - 1); } + if (str[0] == ss) + { + if (begin == false) + { + CV_Error(CV_StsParseError, + string("error in split_range_string(") + + str + + string(", ") + + string(1, fs) + + string(", ") + + string(1, ss) + + string(")") + ); + } + begin = false; + vec.push_back(word); + } + if (begin == true) { - CV_Error(CV_StsParseError, - std::string("error in split_range_string(") - + str - + std::string(", ") - + std::string(1, fs) - + std::string(", ") - + std::string(1, ss) - + std::string(")") - ); + word += str[0]; } - - return vec; + str = str.substr(1, str.length() - 1); } - std::vector CommandLineParser::split_string(std::string str, char symbol, bool create_empty_item) + if (begin == true) { - std::vector vec; - std::string word = ""; - - while (!str.empty()) - { - if (str[0] == symbol) - { - if (!word.empty() || create_empty_item) - { - vec.push_back(word); - word = ""; - } - } - else - { - word += str[0]; - } - str = str.substr(1, str.length() - 1); - } - - if (word != "" || create_empty_item) - { - vec.push_back(word); - } - - return vec; + CV_Error(CV_StsParseError, + string("error in split_range_string(") + + str + + string(", ") + + string(1, fs) + + string(", ") + + string(1, ss) + + string(")") + ); } - #undef clp_get - #define clp_get(T) template<> T CommandLineParser::get(const std::string& name, bool space_delete); + return vec; +} - clp_get(int) - clp_get(unsigned int) - clp_get(long) - clp_get(unsigned long) - clp_get(long long) - clp_get(unsigned long long) - clp_get(size_t) - clp_get(float) - clp_get(double) - clp_get(uint64) - clp_get(int64) - clp_get(std::string) +vector CommandLineParser::Impl::split_string(const string& _str, char symbol, bool create_empty_item) const +{ + string str = _str; + vector vec; + string word = ""; - #undef clp_from_str - #define clp_from_str(T) template<> T from_str(const std::string & str); - - clp_from_str(int) - clp_from_str(unsigned int) - clp_from_str(long) - clp_from_str(unsigned long) - clp_from_str(long long) - clp_from_str(unsigned long long) - clp_from_str(size_t) - clp_from_str(uint64) - clp_from_str(int64) - clp_from_str(float) - clp_from_str(double) - - template<> - std::string from_str(const std::string & str) + while (!str.empty()) { - return str; + if (str[0] == symbol) + { + if (!word.empty() || create_empty_item) + { + vec.push_back(word); + word = ""; + } + } + else + { + word += str[0]; + } + str = str.substr(1, str.length() - 1); } - #undef clp_type_name - #define clp_type_name(type, name) template<> std::string get_type_name() { return std::string(name);} + if (word != "" || create_empty_item) + { + vec.push_back(word); + } - clp_type_name(int, "int") - clp_type_name(unsigned int, "unsigned int") - clp_type_name(long, "long") - clp_type_name(long long, "long long") - clp_type_name(unsigned long long, "unsigned long long") - clp_type_name(size_t, "size_t") - clp_type_name(float, "float") - clp_type_name(double, "double") + return vec; +} } From 7c13dc6cb38eef573c56b7638626702152d898db Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Fri, 7 Sep 2012 17:49:38 +0400 Subject: [PATCH 31/97] little fix in command line parser - catch unknown parameter types --- modules/core/src/command_line_parser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/core/src/command_line_parser.cpp b/modules/core/src/command_line_parser.cpp index 8cfeaf3ca1..3dbb1ac751 100644 --- a/modules/core/src/command_line_parser.cpp +++ b/modules/core/src/command_line_parser.cpp @@ -71,6 +71,8 @@ static void from_str(const string& str, int type, void* dst) ss >> *(double*)dst; else if( type == Param::STRING ) ss >> *(string*)dst; + else + CV_Error(CV_StsBadArg, "unknown/unsupported parameter type"); if (ss.fail()) { From e2d9fc4dcc9d4f9bc3daefdaaa55e36e34fac7af Mon Sep 17 00:00:00 2001 From: Andrey Morozov Date: Fri, 7 Sep 2012 18:01:06 +0400 Subject: [PATCH 32/97] fixed compile error under linux --- modules/ts/src/ts_perf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ts/src/ts_perf.cpp b/modules/ts/src/ts_perf.cpp index 0bb5d9ce12..84f414a3fa 100644 --- a/modules/ts/src/ts_perf.cpp +++ b/modules/ts/src/ts_perf.cpp @@ -534,7 +534,7 @@ void TestBase::Init(int argc, const char* const argv[]) param_max_outliers = std::min(100., std::max(0., args.get("perf_max_outliers"))); param_min_samples = std::max(1u, args.get("perf_min_samples")); param_max_deviation = std::max(0., args.get("perf_max_deviation")); - param_seed = args.get("perf_seed"); + param_seed = args.get("perf_seed"); param_time_limit = std::max(0., args.get("perf_time_limit")); param_force_samples = args.get("perf_force_samples"); param_write_sanity = args.has("perf_write_sanity"); From 4bd2c6b50da08e9863fb09fa335b682ce1968ba5 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Fri, 7 Sep 2012 18:27:47 +0400 Subject: [PATCH 33/97] Android toolchain: added fixes/workarounds for NDK r8b and NDK r6 bugs --- android/android.toolchain.cmake | 39 ++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/android/android.toolchain.cmake b/android/android.toolchain.cmake index 4222b4d843..e6a157e3ee 100644 --- a/android/android.toolchain.cmake +++ b/android/android.toolchain.cmake @@ -474,7 +474,7 @@ elseif( ANDROID_STANDALONE_TOOLCHAIN ) set( BUILD_WITH_STANDALONE_TOOLCHAIN True ) else() list(GET ANDROID_NDK_SEARCH_PATHS 0 ANDROID_NDK_SEARCH_PATH) - message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolcahin. + message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolchain. You should either set an environment variable: export ANDROID_NDK=~/my-android-ndk or @@ -773,6 +773,27 @@ You are strongly recommended to switch to another NDK release. " ) endif() +if( NOT _CMAKE_IN_TRY_COMPILE AND X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" ) + message( WARNING "The x86 system header file from NDK r6 has incorrect definition for ptrdiff_t. You are recommended to upgrade to a newer NDK release or manually patch the header: +See https://android.googlesource.com/platform/development.git f907f4f9d4e56ccc8093df6fee54454b8bcab6c2 + diff --git a/ndk/platforms/android-9/arch-x86/include/machine/_types.h b/ndk/platforms/android-9/arch-x86/include/machine/_types.h + index 5e28c64..65892a1 100644 + --- a/ndk/platforms/android-9/arch-x86/include/machine/_types.h + +++ b/ndk/platforms/android-9/arch-x86/include/machine/_types.h + @@ -51,7 +51,11 @@ typedef long int ssize_t; + #endif + #ifndef _PTRDIFF_T + #define _PTRDIFF_T + -typedef long ptrdiff_t; + +# ifdef __ANDROID__ + + typedef int ptrdiff_t; + +# else + + typedef long ptrdiff_t; + +# endif + #endif +" ) +endif() + # setup paths and STL for NDK if( BUILD_WITH_ANDROID_NDK ) set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" ) @@ -1048,6 +1069,11 @@ if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" ) set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) endif() + if ( X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" ) + # workaround "undefined reference to `__dso_handle'" problem + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) + endif() if( EXISTS "${__libstl}" ) set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" ) set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" ) @@ -1111,10 +1137,13 @@ if( ANDROID_FUNCTION_LEVEL_LINKING ) set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" ) endif() -if( CMAKE_HOST_UNIX AND (ARMEABI_V7A OR X86) AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" ) - if( ANDROID_GOLD_LINKER ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" ) - endif() +if( ANDROID_GOLD_LINKER AND CMAKE_HOST_UNIX AND (ARMEABI OR ARMEABI_V7A OR X86) AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" ) +elseif( ANDROID_NDK_RELEASE STREQUAL "r8b" AND ARMEABI AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "The default bfd linker from arm GCC 4.6 toolchain can fail with 'unresolvable R_ARM_THM_CALL relocation' error message. See https://code.google.com/p/android/issues/detail?id=35342 + On Linux and OS X host platform you can workaround this problem using gold linker (default). + Rerun cmake with -DANDROID_GOLD_LINKER=ON option. +" ) endif() if( ANDROID_NOEXECSTACK ) From a5e37779fb5a0528517e3f7eaf7d91f77ccc4a49 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Fri, 7 Sep 2012 17:33:44 +0200 Subject: [PATCH 34/97] Added a simple CMakeLists.txt to the facerec_samples, which are not included in the samples folder. Makes building these samples a lot easier. --- .../contrib/doc/facerec/src/CMakeLists.txt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 modules/contrib/doc/facerec/src/CMakeLists.txt diff --git a/modules/contrib/doc/facerec/src/CMakeLists.txt b/modules/contrib/doc/facerec/src/CMakeLists.txt new file mode 100644 index 0000000000..ef5f0dbc66 --- /dev/null +++ b/modules/contrib/doc/facerec/src/CMakeLists.txt @@ -0,0 +1,26 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +set(name "facerec") +project(facerec_cpp_samples) + +#SET(OpenCV_DIR /path/to/your/opencv/installation) + +# packages +find_package(OpenCV REQUIRED) # http://opencv.willowgarage.com + +# probably you should loop through the sample files here +add_executable(facerec_demo facerec_demo.cpp) +target_link_libraries(facerec_demo opencv_core opencv_contrib opencv_imgproc opencv_highgui) + +add_executable(facerec_video facerec_video.cpp) +target_link_libraries(facerec_video opencv_contrib opencv_core opencv_imgproc opencv_highgui opencv_objdetect opencv_imgproc) + +add_executable(facerec_eigenfaces facerec_eigenfaces.cpp) +target_link_libraries(facerec_eigenfaces opencv_contrib opencv_core opencv_imgproc opencv_highgui) + +add_executable(facerec_fisherfaces facerec_fisherfaces.cpp) +target_link_libraries(facerec_fisherfaces opencv_contrib opencv_core opencv_imgproc opencv_highgui) + +add_executable(facerec_lbph facerec_lbph.cpp) +target_link_libraries(facerec_lbph opencv_contrib opencv_core opencv_imgproc opencv_highgui) + From f268af8ef05712557aae949eda8055f6c673e544 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Wed, 5 Sep 2012 01:15:00 +0400 Subject: [PATCH 35/97] Removed remaining SWIG marks from headers --- modules/highgui/src/makeswig.sh | 2 -- modules/legacy/include/opencv2/legacy/legacy.hpp | 2 -- modules/ml/include/opencv2/ml/ml.hpp | 16 ---------------- 3 files changed, 20 deletions(-) delete mode 100644 modules/highgui/src/makeswig.sh diff --git a/modules/highgui/src/makeswig.sh b/modules/highgui/src/makeswig.sh deleted file mode 100644 index 06e88e1bb2..0000000000 --- a/modules/highgui/src/makeswig.sh +++ /dev/null @@ -1,2 +0,0 @@ -swig -DSKIP_INCLUDES -python -small highgui.i -gcc -I/usr/include/python2.3/ -I../../cxcore/include -D CV_NO_BACKWARD_COMPATIBILITY -c highgui_wrap.c diff --git a/modules/legacy/include/opencv2/legacy/legacy.hpp b/modules/legacy/include/opencv2/legacy/legacy.hpp index f8ecb4d1b1..1144131281 100644 --- a/modules/legacy/include/opencv2/legacy/legacy.hpp +++ b/modules/legacy/include/opencv2/legacy/legacy.hpp @@ -1787,7 +1787,6 @@ public: virtual float predict( const CvMat* sample, CV_OUT CvMat* probs ) const; -#ifndef SWIG CV_WRAP CvEM( const cv::Mat& samples, const cv::Mat& sampleIdx=cv::Mat(), CvEMParams params=CvEMParams() ); @@ -1806,7 +1805,6 @@ public: CV_WRAP cv::Mat getProbs() const; CV_WRAP inline double getLikelihood() const { return emObj.isTrained() ? logLikelihood : DBL_MAX; } -#endif CV_WRAP virtual void clear(); diff --git a/modules/ml/include/opencv2/ml/ml.hpp b/modules/ml/include/opencv2/ml/ml.hpp index 8def877a59..7ba97c8e47 100644 --- a/modules/ml/include/opencv2/ml/ml.hpp +++ b/modules/ml/include/opencv2/ml/ml.hpp @@ -201,14 +201,12 @@ public: virtual float predict( const CvMat* samples, CV_OUT CvMat* results=0 ) const; CV_WRAP virtual void clear(); -#ifndef SWIG CV_WRAP CvNormalBayesClassifier( const cv::Mat& trainData, const cv::Mat& responses, const cv::Mat& varIdx=cv::Mat(), const cv::Mat& sampleIdx=cv::Mat() ); CV_WRAP virtual bool train( const cv::Mat& trainData, const cv::Mat& responses, const cv::Mat& varIdx = cv::Mat(), const cv::Mat& sampleIdx=cv::Mat(), bool update=false ); CV_WRAP virtual float predict( const cv::Mat& samples, CV_OUT cv::Mat* results=0 ) const; -#endif virtual void write( CvFileStorage* storage, const char* name ) const; virtual void read( CvFileStorage* storage, CvFileNode* node ); @@ -249,7 +247,6 @@ public: virtual float find_nearest( const CvMat* samples, int k, CV_OUT CvMat* results=0, const float** neighbors=0, CV_OUT CvMat* neighborResponses=0, CV_OUT CvMat* dist=0 ) const; -#ifndef SWIG CV_WRAP CvKNearest( const cv::Mat& trainData, const cv::Mat& responses, const cv::Mat& sampleIdx=cv::Mat(), bool isRegression=false, int max_k=32 ); @@ -262,7 +259,6 @@ public: cv::Mat* dist=0 ) const; CV_WRAP virtual float find_nearest( const cv::Mat& samples, int k, CV_OUT cv::Mat& results, CV_OUT cv::Mat& neighborResponses, CV_OUT cv::Mat& dists) const; -#endif virtual void clear(); int get_max_k() const; @@ -490,7 +486,6 @@ public: virtual float predict( const CvMat* sample, bool returnDFVal=false ) const; virtual float predict( const CvMat* samples, CV_OUT CvMat* results ) const; -#ifndef SWIG CV_WRAP CvSVM( const cv::Mat& trainData, const cv::Mat& responses, const cv::Mat& varIdx=cv::Mat(), const cv::Mat& sampleIdx=cv::Mat(), CvSVMParams params=CvSVMParams() ); @@ -511,7 +506,6 @@ public: bool balanced=false); CV_WRAP virtual float predict( const cv::Mat& sample, bool returnDFVal=false ) const; CV_WRAP_AS(predict_all) virtual void predict( cv::InputArray samples, cv::OutputArray results ) const; -#endif CV_WRAP virtual int get_support_vector_count() const; virtual const float* get_support_vector(int i) const; @@ -868,7 +862,6 @@ public: virtual CvDTreeNode* predict( const CvMat* sample, const CvMat* missingDataMask=0, bool preprocessedInput=false ) const; -#ifndef SWIG CV_WRAP virtual bool train( const cv::Mat& trainData, int tflag, const cv::Mat& responses, const cv::Mat& varIdx=cv::Mat(), const cv::Mat& sampleIdx=cv::Mat(), const cv::Mat& varType=cv::Mat(), @@ -878,7 +871,6 @@ public: CV_WRAP virtual CvDTreeNode* predict( const cv::Mat& sample, const cv::Mat& missingDataMask=cv::Mat(), bool preprocessedInput=false ) const; CV_WRAP virtual cv::Mat getVarImportance(); -#endif virtual const CvMat* get_var_importance(); CV_WRAP virtual void clear(); @@ -1011,7 +1003,6 @@ public: virtual float predict( const CvMat* sample, const CvMat* missing = 0 ) const; virtual float predict_prob( const CvMat* sample, const CvMat* missing = 0 ) const; -#ifndef SWIG CV_WRAP virtual bool train( const cv::Mat& trainData, int tflag, const cv::Mat& responses, const cv::Mat& varIdx=cv::Mat(), const cv::Mat& sampleIdx=cv::Mat(), const cv::Mat& varType=cv::Mat(), @@ -1020,7 +1011,6 @@ public: CV_WRAP virtual float predict( const cv::Mat& sample, const cv::Mat& missing = cv::Mat() ) const; CV_WRAP virtual float predict_prob( const cv::Mat& sample, const cv::Mat& missing = cv::Mat() ) const; CV_WRAP virtual cv::Mat getVarImportance(); -#endif CV_WRAP virtual void clear(); @@ -1107,13 +1097,11 @@ public: const CvMat* sampleIdx=0, const CvMat* varType=0, const CvMat* missingDataMask=0, CvRTParams params=CvRTParams()); -#ifndef SWIG CV_WRAP virtual bool train( const cv::Mat& trainData, int tflag, const cv::Mat& responses, const cv::Mat& varIdx=cv::Mat(), const cv::Mat& sampleIdx=cv::Mat(), const cv::Mat& varType=cv::Mat(), const cv::Mat& missingDataMask=cv::Mat(), CvRTParams params=CvRTParams()); -#endif virtual bool train( CvMLData* data, CvRTParams params=CvRTParams() ); protected: virtual std::string getName() const; @@ -1220,7 +1208,6 @@ public: CvMat* weak_responses=0, CvSlice slice=CV_WHOLE_SEQ, bool raw_mode=false, bool return_sum=false ) const; -#ifndef SWIG CV_WRAP CvBoost( const cv::Mat& trainData, int tflag, const cv::Mat& responses, const cv::Mat& varIdx=cv::Mat(), const cv::Mat& sampleIdx=cv::Mat(), const cv::Mat& varType=cv::Mat(), @@ -1237,7 +1224,6 @@ public: CV_WRAP virtual float predict( const cv::Mat& sample, const cv::Mat& missing=cv::Mat(), const cv::Range& slice=cv::Range::all(), bool rawMode=false, bool returnSum=false ) const; -#endif virtual float calc_error( CvMLData* _data, int type , std::vector *resp = 0 ); // type in {CV_TRAIN_ERROR, CV_TEST_ERROR} @@ -1904,7 +1890,6 @@ public: int flags=0 ); virtual float predict( const CvMat* inputs, CV_OUT CvMat* outputs ) const; -#ifndef SWIG CV_WRAP CvANN_MLP( const cv::Mat& layerSizes, int activateFunc=CvANN_MLP::SIGMOID_SYM, double fparam1=0, double fparam2=0 ); @@ -1919,7 +1904,6 @@ public: int flags=0 ); CV_WRAP virtual float predict( const cv::Mat& inputs, CV_OUT cv::Mat& outputs ) const; -#endif CV_WRAP virtual void clear(); From cfa250eff58324bfecac841f1524907709df84eb Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Fri, 7 Sep 2012 22:42:46 +0200 Subject: [PATCH 36/97] The labels of a model are now cloned instead of using Mat::copyTo, because Mat::copyTo leads to a crash with the Python wrapper. I need to further investigate it. --- modules/contrib/src/facerec.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/contrib/src/facerec.cpp b/modules/contrib/src/facerec.cpp index 8c93906b42..b74cc5cf21 100644 --- a/modules/contrib/src/facerec.cpp +++ b/modules/contrib/src/facerec.cpp @@ -345,6 +345,7 @@ void Eigenfaces::train(InputArrayOfArrays _src, InputArray _local_labels) { Mat labels = _local_labels.getMat(); // observations in row Mat data = asRowMatrix(_src, CV_64FC1); + // number of samples int n = data.rows; // assert there are as much samples as labels @@ -358,6 +359,7 @@ void Eigenfaces::train(InputArrayOfArrays _src, InputArray _local_labels) { // clip number of components to be valid if((_num_components <= 0) || (_num_components > n)) _num_components = n; + // perform the PCA PCA pca(data, Mat(), CV_PCA_DATA_AS_ROW, _num_components); // copy the PCA results @@ -365,7 +367,7 @@ void Eigenfaces::train(InputArrayOfArrays _src, InputArray _local_labels) { _eigenvalues = pca.eigenvalues.clone(); // eigenvalues by row transpose(pca.eigenvectors, _eigenvectors); // eigenvectors by column // store labels for prediction - labels.copyTo(_labels); + _labels = labels.clone(); // save projections for(int sampleIdx = 0; sampleIdx < data.rows; sampleIdx++) { Mat p = subspaceProject(_eigenvectors, _mean, data.row(sampleIdx)); @@ -481,7 +483,7 @@ void Fisherfaces::train(InputArrayOfArrays src, InputArray _lbls) { // store the total mean vector _mean = pca.mean.reshape(1,1); // store labels - labels.copyTo(_labels); + _labels = labels.clone(); // store the eigenvalues of the discriminants lda.eigenvalues().convertTo(_eigenvalues, CV_64FC1); // Now calculate the projection matrix as pca.eigenvectors * lda.eigenvectors. From 7d83db7d4d8aa79acf0b0edbe3aa97c3b6d8acc3 Mon Sep 17 00:00:00 2001 From: yao Date: Mon, 10 Sep 2012 14:52:28 +0800 Subject: [PATCH 37/97] fix ocl::columnsum bug --- modules/ocl/src/columnsum.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ocl/src/columnsum.cpp b/modules/ocl/src/columnsum.cpp index 245082a2a4..c33c9a9678 100644 --- a/modules/ocl/src/columnsum.cpp +++ b/modules/ocl/src/columnsum.cpp @@ -84,7 +84,7 @@ void cv::ocl::columnSum(const oclMat& src,oclMat& dst) args.push_back( make_pair( sizeof(cl_int), (void *)&src.step)); args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step)); - size_t globalThreads[3] = {dst.cols, dst.rows, 1}; + size_t globalThreads[3] = {dst.cols, 1, 1}; size_t localThreads[3] = {16, 16, 1}; openCLExecuteKernel(clCxt, &imgproc_columnsum, kernelName, globalThreads, localThreads, args, src.channels(), src.depth()); From 1de829c359384d9dd8a655854f5697c1b4077e7a Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 10 Sep 2012 11:35:22 +0400 Subject: [PATCH 38/97] abi-compatibility-check tool configuration script for adnroid added. --- android/scripts/ABI_compat_generator.py | 102 ++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100755 android/scripts/ABI_compat_generator.py diff --git a/android/scripts/ABI_compat_generator.py b/android/scripts/ABI_compat_generator.py new file mode 100755 index 0000000000..c3f77a8ded --- /dev/null +++ b/android/scripts/ABI_compat_generator.py @@ -0,0 +1,102 @@ +#!/usr/bin/python + +import os +import sys + +ANDROID_SDK_PATH="/opt/android-sdk-linux" +ANDROID_NDK_PATH="/opt/android-ndk-r8" +INSTALL_DIRECTORY="OpenCV-2.4.2-branch" +CLASS_PATH=os.path.join(INSTALL_DIRECTORY, "sdk/java/bin/classes"); +TMP_HEADER_PATH="tmp_include" +HEADER_EXTS = set(['h', 'hpp']) +SYS_INCLUDES = ["platforms/android-8/arch-arm/usr/include", "sources/cxx-stl/gnu-libstdc++/include", "sources/cxx-stl/gnu-libstdc++/libs/armeabi/include"] + +PROJECT_NAME = "OpenCV-branch" +TARGET_LIBS = ["libopencv_java.so"] +ARCH = "armeabi" +GCC_OPTIONS = "-fpermissive" +EXCLUDE_HEADERS = set(["hdf5.h", "eigen.hpp", "cxeigen.hpp"]); + +def FindClasses(root, prefix): + classes = [] + if ("" != prefix): + prefix = prefix + "." + for path in os.listdir(root): + currentPath = os.path.join(root, path) + if (os.path.isdir(currentPath)): + classes += FindClasses(currentPath, prefix + path) + else: + name = str.split(path, ".")[0] + ext = str.split(path, ".")[1] + if (ext == "class"): + #print("class: %s" % (prefix + name)) + classes.append(prefix+name) + return classes + +def FindHeaders(root): + headers = [] + for path in os.listdir(root): + currentPath = os.path.join(root, path) + if (os.path.isdir(currentPath)): + headers += FindHeaders(currentPath) + else: + ext = str.split(path, ".")[-1] + #print("%s: \"%s\"" % (currentPath, ext)) + if (ext in HEADER_EXTS): + #print("Added as header file") + if (path not in EXCLUDE_HEADERS): + headers.append(currentPath) + return headers + +outputFileName = PROJECT_NAME + ".xml" +try: + outputFile = open(outputFileName, "w") +except: + print("Error: Cannot open output file \"%s\" for writing" % outputFileName) + +allJavaClasses = FindClasses(CLASS_PATH, "") +if (not allJavaClasses): + print("Error: No Java classes found :(") + exit(-1) + +if (not os.path.exists(TMP_HEADER_PATH)): + os.makedirs(os.path.join(os.getcwd(), TMP_HEADER_PATH)) + +print("Generating JNI headers for Java API ...") +AndroidJavaDeps = os.path.join(ANDROID_SDK_PATH, "platforms/android-11/android.jar") +for currentClass in allJavaClasses: + os.system("javah -d %s -classpath %s:%s %s" % (TMP_HEADER_PATH, CLASS_PATH, AndroidJavaDeps, currentClass)) + +print("Building JNI headers list ...") +jniHeaders = FindHeaders(TMP_HEADER_PATH) +#print(jniHeaders) + +print("Building Native OpenCV header list ...") +cHeaders = FindHeaders(os.path.join(INSTALL_DIRECTORY, "sdk/native/jni/include/opencv")) +cppHeaders = FindHeaders(os.path.join(INSTALL_DIRECTORY, "sdk/native/jni/include/opencv2")) +#print(cHeaders) +#print(cppHeaders) + +print("Writing config file ...") +outputFile.write("\n\n\n\t%s\n\n\n\n" % PROJECT_NAME) +outputFile.write("\t" + "\n\t".join(cHeaders)) +outputFile.write("\n\t" + "\n\t".join(cppHeaders)) +outputFile.write("\n\t" + "\n\t".join(jniHeaders)) +outputFile.write("\n\n\n") + +includes = [] +for inc in SYS_INCLUDES: + includes.append(os.path.join(ANDROID_NDK_PATH, inc)) + +outputFile.write("\n\tOpenCV-2.4.2-branch/sdk/native/jni/include\n\tOpenCV-2.4.2-branch/sdk/native/jni/include/opencv\n\tOpenCV-2.4.2-branch/sdk/native/jni/include/opencv2\n") + +outputFile.write("\t%s\n\n\n" % "\n\t".join(includes)) + +libraries = [] +for lib in TARGET_LIBS: + libraries.append(os.path.join(INSTALL_DIRECTORY, "sdk/native/libs", ARCH, lib)) + +outputFile.write("\n\t%s\n\n\n" % "\n\t".join(libraries)) +outputFile.write("\n\t%s\n\n\n" % GCC_OPTIONS) + +print("done!") From 43a401ba192b4bc1f6e6fa798a442aedd97306d3 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 10 Sep 2012 11:37:04 +0400 Subject: [PATCH 39/97] OpenCV Manager version incremented. --- android/service/engine/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/service/engine/AndroidManifest.xml b/android/service/engine/AndroidManifest.xml index 5fee38f1b4..0e24f82aa6 100644 --- a/android/service/engine/AndroidManifest.xml +++ b/android/service/engine/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="14" + android:versionName="1.4" > From e564a53ccdf54c563da100486918a18db179648d Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Mon, 10 Sep 2012 15:48:57 +0400 Subject: [PATCH 40/97] Add gitignore for Android tests and samples --- modules/java/android_test/.gitignore | 7 +++++++ samples/android/.gitignore | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 modules/java/android_test/.gitignore create mode 100644 samples/android/.gitignore diff --git a/modules/java/android_test/.gitignore b/modules/java/android_test/.gitignore new file mode 100644 index 0000000000..2d406cbbcc --- /dev/null +++ b/modules/java/android_test/.gitignore @@ -0,0 +1,7 @@ +bin/ +gen/ +build.xml +local.properties +proguard-project.txt +project.properties +default.properties \ No newline at end of file diff --git a/samples/android/.gitignore b/samples/android/.gitignore new file mode 100644 index 0000000000..2d406cbbcc --- /dev/null +++ b/samples/android/.gitignore @@ -0,0 +1,7 @@ +bin/ +gen/ +build.xml +local.properties +proguard-project.txt +project.properties +default.properties \ No newline at end of file From 86c7e183d2826851a7dae737250ece8d75ea98d1 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 10 Sep 2012 16:28:56 +0400 Subject: [PATCH 41/97] Issue #2316 Change default libnative_camera path implemented. --- modules/androidcamera/CMakeLists.txt | 2 +- modules/androidcamera/src/camera_activity.cpp | 98 +++++++++++++------ 2 files changed, 67 insertions(+), 33 deletions(-) diff --git a/modules/androidcamera/CMakeLists.txt b/modules/androidcamera/CMakeLists.txt index 832ed553f8..f55b55136f 100644 --- a/modules/androidcamera/CMakeLists.txt +++ b/modules/androidcamera/CMakeLists.txt @@ -5,7 +5,7 @@ ENDIF() set(the_description "Auxiliary module for Android native camera support") set(OPENCV_MODULE_TYPE STATIC) -ocv_define_module(androidcamera INTERNAL log dl) +ocv_define_module(androidcamera INTERNAL opencv_core log dl) ocv_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/camera_wrapper") # Android source tree for native camera diff --git a/modules/androidcamera/src/camera_activity.cpp b/modules/androidcamera/src/camera_activity.cpp index 5a997637e3..8ce53a816c 100644 --- a/modules/androidcamera/src/camera_activity.cpp +++ b/modules/androidcamera/src/camera_activity.cpp @@ -1,8 +1,12 @@ #include +#include +#include +#include #include #include #include #include +#include #include "camera_activity.hpp" #include "camera_wrapper.h" @@ -41,6 +45,7 @@ private: static bool isConnectedToLib; static std::string getPathLibFolder(); + static std::string getDefaultPathLibFolder(); static CameraActivity::ErrorCode connectToLib(); static CameraActivity::ErrorCode getSymbolFromLib(void * libHandle, const char* symbolName, void** ppSymbol); static void fillListWrapperLibs(const string& folderPath, vector& listLibs); @@ -55,8 +60,6 @@ private: }; std::string CameraWrapperConnector::pathLibFolder; -#define DEFAULT_WRAPPER_PACKAGE_NAME "com.NativeCamera" -#define DEFAULT_PATH_LIB_FOLDER "/data/data/" DEFAULT_WRAPPER_PACKAGE_NAME "/lib/" bool CameraWrapperConnector::isConnectedToLib=false; InitCameraConnectC CameraWrapperConnector::pInitCameraC = 0; @@ -163,7 +166,13 @@ CameraActivity::ErrorCode CameraWrapperConnector::connectToLib() } dlerror(); - string folderPath=getPathLibFolder(); + string folderPath = getPathLibFolder(); + if (folderPath.empty()) + { + LOGD("Trying to find native camera in default OpenCV packages"); + folderPath = getDefaultPathLibFolder(); + } + LOGD("CameraWrapperConnector::connectToLib: folderPath=%s", folderPath.c_str()); vector listLibs; @@ -256,6 +265,31 @@ void CameraWrapperConnector::fillListWrapperLibs(const string& folderPath, vecto } } +std::string CameraWrapperConnector::getDefaultPathLibFolder() +{ + const string packageList[] = {"tegra3", "armv7a_neon", "armv7a", "armv5", "x86"}; + for (size_t i = 0; i < 5; i++) + { + char path[128]; + sprintf(path, "/data/data/org.opencv.lib_v%d%d_%s/lib/", CV_MAJOR_VERSION, CV_MINOR_VERSION, packageList[i].c_str()); + LOGD("Trying package \"%s\" (\"%s\")", packageList[i], path); + + DIR* dir = opendir(path); + if (!dir) + { + LOGD("Package not found"); + continue; + } + else + { + closedir(dir); + return path; + } + } + + return string(); +} + std::string CameraWrapperConnector::getPathLibFolder() { if (!pathLibFolder.empty()) @@ -278,41 +312,41 @@ std::string CameraWrapperConnector::getPathLibFolder() { while (fgets(lineBuf, sizeof lineBuf, file) != NULL) { - //verify that line ends with library name - int lineLength = strlen(lineBuf); - int libNameLength = strlen(libName); + //verify that line ends with library name + int lineLength = strlen(lineBuf); + int libNameLength = strlen(libName); - //trim end - for(int i = lineLength - 1; i >= 0 && isspace(lineBuf[i]); --i) - { - lineBuf[i] = 0; - --lineLength; - } + //trim end + for(int i = lineLength - 1; i >= 0 && isspace(lineBuf[i]); --i) + { + lineBuf[i] = 0; + --lineLength; + } - if (0 != strncmp(lineBuf + lineLength - libNameLength, libName, libNameLength)) - { - //the line does not contain the library name - continue; - } + if (0 != strncmp(lineBuf + lineLength - libNameLength, libName, libNameLength)) + { + //the line does not contain the library name + continue; + } - //extract path from smaps line - char* pathBegin = strchr(lineBuf, '/'); - if (0 == pathBegin) - { - LOGE("Strange error: could not find path beginning in lin \"%s\"", lineBuf); - continue; - } + //extract path from smaps line + char* pathBegin = strchr(lineBuf, '/'); + if (0 == pathBegin) + { + LOGE("Strange error: could not find path beginning in lin \"%s\"", lineBuf); + continue; + } - char* pathEnd = strrchr(pathBegin, '/'); - pathEnd[1] = 0; + char* pathEnd = strrchr(pathBegin, '/'); + pathEnd[1] = 0; - LOGD("Libraries folder found: %s", pathBegin); + LOGD("Libraries folder found: %s", pathBegin); - fclose(file); - return pathBegin; + fclose(file); + return pathBegin; } fclose(file); - LOGE("Could not find library path."); + LOGE("Could not find library path"); } else { @@ -321,10 +355,10 @@ std::string CameraWrapperConnector::getPathLibFolder() } else { - LOGE("Could not get library name and base address."); + LOGE("Could not get library name and base address"); } - return DEFAULT_PATH_LIB_FOLDER ; + return string(); } void CameraWrapperConnector::setPathLibFolder(const string& path) From 98c92f196ea6ac9e1a60b1b44d4fb34f87c4ff2a Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 10 Sep 2012 16:24:55 +0400 Subject: [PATCH 42/97] added Generalized Hough implementation --- modules/gpu/include/opencv2/gpu/gpu.hpp | 36 +- modules/gpu/perf/perf_imgproc.cpp | 94 ++ modules/gpu/src/cuda/hough.cu | 1034 ++++++++++++- modules/gpu/src/hough.cpp | 1097 +++++++++++++- modules/gpu/test/test_hough.cpp | 256 ++++ modules/gpu/test/test_imgproc.cpp | 136 -- .../include/opencv2/imgproc/imgproc.hpp | 36 + modules/imgproc/src/generalized_hough.cpp | 1293 +++++++++++++++++ samples/cpp/generalized_hough.cpp | 209 +++ samples/cpp/templ.png | Bin 0 -> 1635 bytes 10 files changed, 4037 insertions(+), 154 deletions(-) create mode 100644 modules/gpu/test/test_hough.cpp create mode 100644 modules/imgproc/src/generalized_hough.cpp create mode 100644 samples/cpp/generalized_hough.cpp create mode 100644 samples/cpp/templ.png diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index 2faa1751d4..3f0affe877 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -770,11 +770,11 @@ CV_EXPORTS void blendLinear(const GpuMat& img1, const GpuMat& img2, const GpuMat GpuMat& result, Stream& stream = Stream::Null()); //! Performa bilateral filtering of passsed image -CV_EXPORTS void bilateralFilter(const GpuMat& src, GpuMat& dst, int kernel_size, float sigma_color, float sigma_spatial, +CV_EXPORTS void bilateralFilter(const GpuMat& src, GpuMat& dst, int kernel_size, float sigma_color, float sigma_spatial, int borderMode = BORDER_DEFAULT, Stream& stream = Stream::Null()); //! Brute force non-local means algorith (slow but universal) -CV_EXPORTS void nonLocalMeans(const GpuMat& src, GpuMat& dst, float h, +CV_EXPORTS void nonLocalMeans(const GpuMat& src, GpuMat& dst, float h, int search_widow_size = 11, int block_size = 7, int borderMode = BORDER_DEFAULT, Stream& s = Stream::Null()); @@ -854,6 +854,38 @@ CV_EXPORTS void HoughCircles(const GpuMat& src, GpuMat& circles, int method, flo CV_EXPORTS void HoughCircles(const GpuMat& src, GpuMat& circles, HoughCirclesBuf& buf, int method, float dp, float minDist, int cannyThreshold, int votesThreshold, int minRadius, int maxRadius, int maxCircles = 4096); CV_EXPORTS void HoughCirclesDownload(const GpuMat& d_circles, OutputArray h_circles); +//! finds arbitrary template in the grayscale image using Generalized Hough Transform +//! Ballard, D.H. (1981). Generalizing the Hough transform to detect arbitrary shapes. Pattern Recognition 13 (2): 111-122. +//! Guil, N., González-Linares, J.M. and Zapata, E.L. (1999). Bidimensional shape detection using an invariant approach. Pattern Recognition 32 (6): 1025-1038. +class CV_EXPORTS GeneralizedHough_GPU : public Algorithm +{ +public: + static Ptr create(int method); + + virtual ~GeneralizedHough_GPU(); + + //! set template to search + void setTemplate(const GpuMat& templ, int cannyThreshold = 100, Point templCenter = Point(-1, -1)); + void setTemplate(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Point templCenter = Point(-1, -1)); + + //! find template on image + void detect(const GpuMat& image, GpuMat& positions, int cannyThreshold = 100); + void detect(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, GpuMat& positions); + + void download(const GpuMat& d_positions, OutputArray h_positions, OutputArray h_votes = noArray()); + + void release(); + +protected: + virtual void setTemplateImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Point templCenter) = 0; + virtual void detectImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, GpuMat& positions) = 0; + virtual void releaseImpl() = 0; + +private: + GpuMat edges_; + CannyBuf cannyBuf_; +}; + ////////////////////////////// Matrix reductions ////////////////////////////// //! computes mean value and standard deviation of all or selected array elements diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 761510da35..9769bfad19 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -1713,4 +1713,98 @@ PERF_TEST_P(Sz_Dp_MinDist, ImgProc_HoughCircles, Combine(GPU_TYPICAL_MAT_SIZES, } } +////////////////////////////////////////////////////////////////////// +// GeneralizedHough + +CV_FLAGS(GHMethod, cv::GHT_POSITION, cv::GHT_SCALE, cv::GHT_ROTATION); + +DEF_PARAM_TEST(Method_Sz, GHMethod, cv::Size); + +PERF_TEST_P(Method_Sz, GeneralizedHough, Combine( + Values(GHMethod(cv::GHT_POSITION), GHMethod(cv::GHT_POSITION | cv::GHT_SCALE), GHMethod(cv::GHT_POSITION | cv::GHT_ROTATION), GHMethod(cv::GHT_POSITION | cv::GHT_SCALE | cv::GHT_ROTATION)), + GPU_TYPICAL_MAT_SIZES)) +{ + declare.time(10); + + const int method = GET_PARAM(0); + const cv::Size imageSize = GET_PARAM(1); + + const cv::Mat templ = readImage("cv/shared/templ.png", cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(templ.empty()); + + cv::Mat image(imageSize, CV_8UC1, cv::Scalar::all(0)); + + cv::RNG rng(123456789); + const int objCount = rng.uniform(5, 15); + for (int i = 0; i < objCount; ++i) + { + double scale = rng.uniform(0.7, 1.3); + bool rotate = rng.uniform(0, 2); + + cv::Mat obj; + cv::resize(templ, obj, cv::Size(), scale, scale); + if (rotate) + obj = obj.t(); + + cv::Point pos; + + pos.x = rng.uniform(0, image.cols - obj.cols); + pos.y = rng.uniform(0, image.rows - obj.rows); + + cv::Mat roi = image(cv::Rect(pos, obj.size())); + cv::add(roi, obj, roi); + } + + cv::Mat edges; + cv::Canny(image, edges, 50, 100); + + cv::Mat dx, dy; + cv::Sobel(image, dx, CV_32F, 1, 0); + cv::Sobel(image, dy, CV_32F, 0, 1); + + if (runOnGpu) + { + cv::gpu::GpuMat d_edges(edges); + cv::gpu::GpuMat d_dx(dx); + cv::gpu::GpuMat d_dy(dy); + cv::gpu::GpuMat d_position; + + cv::Ptr d_hough = cv::gpu::GeneralizedHough_GPU::create(method); + if (method & cv::GHT_ROTATION) + { + d_hough->set("maxAngle", 90.0); + d_hough->set("angleStep", 2.0); + } + + d_hough->setTemplate(cv::gpu::GpuMat(templ)); + + d_hough->detect(d_edges, d_dx, d_dy, d_position); + + TEST_CYCLE() + { + d_hough->detect(d_edges, d_dx, d_dy, d_position); + } + } + else + { + cv::Mat positions; + + cv::Ptr hough = cv::GeneralizedHough::create(method); + if (method & cv::GHT_ROTATION) + { + hough->set("maxAngle", 90.0); + hough->set("angleStep", 2.0); + } + + hough->setTemplate(templ); + + hough->detect(edges, dx, dy, positions); + + TEST_CYCLE() + { + hough->detect(edges, dx, dy, positions); + } + } +} + } // namespace diff --git a/modules/gpu/src/cuda/hough.cu b/modules/gpu/src/cuda/hough.cu index 9ee7621c2b..712b91ab16 100644 --- a/modules/gpu/src/cuda/hough.cu +++ b/modules/gpu/src/cuda/hough.cu @@ -43,6 +43,9 @@ #include #include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/emulation.hpp" +#include "opencv2/gpu/device/vec_math.hpp" +#include "opencv2/gpu/device/limits.hpp" +#include "opencv2/gpu/device/dynamic_smem.hpp" namespace cv { namespace gpu { namespace device { @@ -53,8 +56,7 @@ namespace cv { namespace gpu { namespace device //////////////////////////////////////////////////////////////////////// // buildPointList - const int PIXELS_PER_THREAD = 16; - + template __global__ void buildPointList(const PtrStepSzb src, unsigned int* list) { __shared__ unsigned int s_queues[4][32 * PIXELS_PER_THREAD]; @@ -113,6 +115,8 @@ namespace cv { namespace gpu { namespace device int buildPointList_gpu(PtrStepSzb src, unsigned int* list) { + const int PIXELS_PER_THREAD = 16; + void* counterPtr; cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); @@ -121,9 +125,9 @@ namespace cv { namespace gpu { namespace device const dim3 block(32, 4); const dim3 grid(divUp(src.cols, block.x * PIXELS_PER_THREAD), divUp(src.rows, block.y)); - cudaSafeCall( cudaFuncSetCacheConfig(buildPointList, cudaFuncCachePreferShared) ); + cudaSafeCall( cudaFuncSetCacheConfig(buildPointList, cudaFuncCachePreferShared) ); - buildPointList<<>>(src, list); + buildPointList<<>>(src, list); cudaSafeCall( cudaGetLastError() ); cudaSafeCall( cudaDeviceSynchronize() ); @@ -167,7 +171,7 @@ namespace cv { namespace gpu { namespace device __global__ void linesAccumShared(const unsigned int* list, const int count, PtrStepi accum, const float irho, const float theta, const int numrho) { - extern __shared__ int smem[]; + int* smem = DynamicSharedMem(); for (int i = threadIdx.x; i < numrho + 1; i += blockDim.x) smem[i] = 0; @@ -410,7 +414,7 @@ namespace cv { namespace gpu { namespace device float3* circles, const int maxCircles, const float dp, const int minRadius, const int maxRadius, const int histSize, const int threshold) { - extern __shared__ int smem[]; + int* smem = DynamicSharedMem(); for (int i = threadIdx.x; i < histSize + 2; i += blockDim.x) smem[i] = 0; @@ -481,5 +485,1023 @@ namespace cv { namespace gpu { namespace device return totalCount; } + + //////////////////////////////////////////////////////////////////////// + // Generalized Hough + + template + __global__ void buildEdgePointList(const PtrStepSzb edges, const PtrStep dx, const PtrStep dy, unsigned int* coordList, float* thetaList) + { + __shared__ unsigned int s_coordLists[4][32 * PIXELS_PER_THREAD]; + __shared__ float s_thetaLists[4][32 * PIXELS_PER_THREAD]; + __shared__ int s_sizes[4]; + __shared__ int s_globStart[4]; + + const int x = blockIdx.x * blockDim.x * PIXELS_PER_THREAD + threadIdx.x; + const int y = blockIdx.y * blockDim.y + threadIdx.y; + + if (threadIdx.x == 0) + s_sizes[threadIdx.y] = 0; + __syncthreads(); + + if (y < edges.rows) + { + // fill the queue + const uchar* edgesRow = edges.ptr(y); + const T* dxRow = dx.ptr(y); + const T* dyRow = dy.ptr(y); + + for (int i = 0, xx = x; i < PIXELS_PER_THREAD && xx < edges.cols; ++i, xx += blockDim.x) + { + const T dxVal = dxRow[xx]; + const T dyVal = dyRow[xx]; + + if (edgesRow[xx] && (dxVal != 0 || dyVal != 0)) + { + const unsigned int coord = (y << 16) | xx; + + float theta = ::atan2f(dyVal, dxVal); + if (theta < 0) + theta += 2.0f * CV_PI_F; + + const int qidx = Emulation::smem::atomicAdd(&s_sizes[threadIdx.y], 1); + + s_coordLists[threadIdx.y][qidx] = coord; + s_thetaLists[threadIdx.y][qidx] = theta; + } + } + } + + __syncthreads(); + + // let one thread reserve the space required in the global list + if (threadIdx.x == 0 && threadIdx.y == 0) + { + // find how many items are stored in each list + int totalSize = 0; + for (int i = 0; i < blockDim.y; ++i) + { + s_globStart[i] = totalSize; + totalSize += s_sizes[i]; + } + + // calculate the offset in the global list + const int globalOffset = atomicAdd(&g_counter, totalSize); + for (int i = 0; i < blockDim.y; ++i) + s_globStart[i] += globalOffset; + } + + __syncthreads(); + + // copy local queues to global queue + const int qsize = s_sizes[threadIdx.y]; + int gidx = s_globStart[threadIdx.y] + threadIdx.x; + for(int i = threadIdx.x; i < qsize; i += blockDim.x, gidx += blockDim.x) + { + coordList[gidx] = s_coordLists[threadIdx.y][i]; + thetaList[gidx] = s_thetaLists[threadIdx.y][i]; + } + } + + template + int buildEdgePointList_gpu(PtrStepSzb edges, PtrStepSzb dx, PtrStepSzb dy, unsigned int* coordList, float* thetaList) + { + const int PIXELS_PER_THREAD = 8; + + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); + + const dim3 block(32, 4); + const dim3 grid(divUp(edges.cols, block.x * PIXELS_PER_THREAD), divUp(edges.rows, block.y)); + + cudaSafeCall( cudaFuncSetCacheConfig(buildEdgePointList, cudaFuncCachePreferShared) ); + + buildEdgePointList<<>>(edges, (PtrStepSz) dx, (PtrStepSz) dy, coordList, thetaList); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + return totalCount; + } + + template int buildEdgePointList_gpu(PtrStepSzb edges, PtrStepSzb dx, PtrStepSzb dy, unsigned int* coordList, float* thetaList); + template int buildEdgePointList_gpu(PtrStepSzb edges, PtrStepSzb dx, PtrStepSzb dy, unsigned int* coordList, float* thetaList); + template int buildEdgePointList_gpu(PtrStepSzb edges, PtrStepSzb dx, PtrStepSzb dy, unsigned int* coordList, float* thetaList); + + __global__ void buildRTable(const unsigned int* coordList, const float* thetaList, const int pointsCount, + PtrStep r_table, int* r_sizes, int maxSize, + const short2 templCenter, const float thetaScale) + { + const int tid = blockIdx.x * blockDim.x + threadIdx.x; + + if (tid >= pointsCount) + return; + + const unsigned int coord = coordList[tid]; + short2 p; + p.x = (coord & 0xFFFF); + p.y = (coord >> 16) & 0xFFFF; + + const float theta = thetaList[tid]; + const int n = __float2int_rn(theta * thetaScale); + + const int ind = ::atomicAdd(r_sizes + n, 1); + if (ind < maxSize) + r_table(n, ind) = p - templCenter; + } + + void buildRTable_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + PtrStepSz r_table, int* r_sizes, + short2 templCenter, int levels) + { + const dim3 block(256); + const dim3 grid(divUp(pointsCount, block.x)); + + const float thetaScale = levels / (2.0f * CV_PI_F); + + buildRTable<<>>(coordList, thetaList, pointsCount, r_table, r_sizes, r_table.cols, templCenter, thetaScale); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + } + + //////////////////////////////////////////////////////////////////////// + // GHT_Ballard_Pos + + __global__ void GHT_Ballard_Pos_calcHist(const unsigned int* coordList, const float* thetaList, const int pointsCount, + const PtrStep r_table, const int* r_sizes, + PtrStepSzi hist, + const float idp, const float thetaScale) + { + const int tid = blockIdx.x * blockDim.x + threadIdx.x; + + if (tid >= pointsCount) + return; + + const unsigned int coord = coordList[tid]; + short2 p; + p.x = (coord & 0xFFFF); + p.y = (coord >> 16) & 0xFFFF; + + const float theta = thetaList[tid]; + const int n = __float2int_rn(theta * thetaScale); + + const short2* r_row = r_table.ptr(n); + const int r_row_size = r_sizes[n]; + + for (int j = 0; j < r_row_size; ++j) + { + short2 c = p - r_row[j]; + + c.x = __float2int_rn(c.x * idp); + c.y = __float2int_rn(c.y * idp); + + if (c.x >= 0 && c.x < hist.cols - 2 && c.y >= 0 && c.y < hist.rows - 2) + ::atomicAdd(hist.ptr(c.y + 1) + c.x + 1, 1); + } + } + + void GHT_Ballard_Pos_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + PtrStepSz r_table, const int* r_sizes, + PtrStepSzi hist, + float dp, int levels) + { + const dim3 block(256); + const dim3 grid(divUp(pointsCount, block.x)); + + const float idp = 1.0f / dp; + const float thetaScale = levels / (2.0f * CV_PI_F); + + GHT_Ballard_Pos_calcHist<<>>(coordList, thetaList, pointsCount, r_table, r_sizes, hist, idp, thetaScale); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + } + + __global__ void GHT_Ballard_Pos_findPosInHist(const PtrStepSzi hist, float4* out, int3* votes, const int maxSize, const float dp, const int threshold) + { + const int x = blockIdx.x * blockDim.x + threadIdx.x; + const int y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x >= hist.cols - 2 || y >= hist.rows - 2) + return; + + const int curVotes = hist(y + 1, x + 1); + + if (curVotes > threshold && + curVotes > hist(y + 1, x) && + curVotes >= hist(y + 1, x + 2) && + curVotes > hist(y, x + 1) && + curVotes >= hist(y + 2, x + 1)) + { + const int ind = ::atomicAdd(&g_counter, 1); + + if (ind < maxSize) + { + out[ind] = make_float4(x * dp, y * dp, 1.0f, 0.0f); + votes[ind] = make_int3(curVotes, 0, 0); + } + } + } + + int GHT_Ballard_Pos_findPosInHist_gpu(PtrStepSzi hist, float4* out, int3* votes, int maxSize, float dp, int threshold) + { + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); + + const dim3 block(32, 8); + const dim3 grid(divUp(hist.cols - 2, block.x), divUp(hist.rows - 2, block.y)); + + cudaSafeCall( cudaFuncSetCacheConfig(GHT_Ballard_Pos_findPosInHist, cudaFuncCachePreferL1) ); + + GHT_Ballard_Pos_findPosInHist<<>>(hist, out, votes, maxSize, dp, threshold); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + totalCount = ::min(totalCount, maxSize); + + return totalCount; + } + + //////////////////////////////////////////////////////////////////////// + // GHT_Ballard_PosScale + + __global__ void GHT_Ballard_PosScale_calcHist(const unsigned int* coordList, const float* thetaList, + PtrStep r_table, const int* r_sizes, + PtrStepi hist, const int rows, const int cols, + const float minScale, const float scaleStep, const int scaleRange, + const float idp, const float thetaScale) + { + const unsigned int coord = coordList[blockIdx.x]; + float2 p; + p.x = (coord & 0xFFFF); + p.y = (coord >> 16) & 0xFFFF; + + const float theta = thetaList[blockIdx.x]; + const int n = __float2int_rn(theta * thetaScale); + + const short2* r_row = r_table.ptr(n); + const int r_row_size = r_sizes[n]; + + for (int j = 0; j < r_row_size; ++j) + { + const float2 d = saturate_cast(r_row[j]); + + for (int s = threadIdx.x; s < scaleRange; s += blockDim.x) + { + const float scale = minScale + s * scaleStep; + + float2 c = p - scale * d; + + c.x *= idp; + c.y *= idp; + + if (c.x >= 0 && c.x < cols && c.y >= 0 && c.y < rows) + ::atomicAdd(hist.ptr((s + 1) * (rows + 2) + __float2int_rn(c.y + 1)) + __float2int_rn(c.x + 1), 1); + } + } + } + + void GHT_Ballard_PosScale_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + PtrStepSz r_table, const int* r_sizes, + PtrStepi hist, int rows, int cols, + float minScale, float scaleStep, int scaleRange, + float dp, int levels) + { + const dim3 block(256); + const dim3 grid(pointsCount); + + const float idp = 1.0f / dp; + const float thetaScale = levels / (2.0f * CV_PI_F); + + GHT_Ballard_PosScale_calcHist<<>>(coordList, thetaList, + r_table, r_sizes, + hist, rows, cols, + minScale, scaleStep, scaleRange, + idp, thetaScale); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + } + + __global__ void GHT_Ballard_PosScale_findPosInHist(const PtrStepi hist, const int rows, const int cols, const int scaleRange, + float4* out, int3* votes, const int maxSize, + const float minScale, const float scaleStep, const float dp, const int threshold) + { + const int x = blockIdx.x * blockDim.x + threadIdx.x; + const int y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x >= cols || y >= rows) + return; + + for (int s = 0; s < scaleRange; ++s) + { + const float scale = minScale + s * scaleStep; + + const int prevScaleIdx = (s) * (rows + 2); + const int curScaleIdx = (s + 1) * (rows + 2); + const int nextScaleIdx = (s + 2) * (rows + 2); + + const int curVotes = hist(curScaleIdx + y + 1, x + 1); + + if (curVotes > threshold && + curVotes > hist(curScaleIdx + y + 1, x) && + curVotes >= hist(curScaleIdx + y + 1, x + 2) && + curVotes > hist(curScaleIdx + y, x + 1) && + curVotes >= hist(curScaleIdx + y + 2, x + 1) && + curVotes > hist(prevScaleIdx + y + 1, x + 1) && + curVotes >= hist(nextScaleIdx + y + 1, x + 1)) + { + const int ind = ::atomicAdd(&g_counter, 1); + + if (ind < maxSize) + { + out[ind] = make_float4(x * dp, y * dp, scale, 0.0f); + votes[ind] = make_int3(curVotes, curVotes, 0); + } + } + } + } + + int GHT_Ballard_PosScale_findPosInHist_gpu(PtrStepi hist, int rows, int cols, int scaleRange, float4* out, int3* votes, int maxSize, + float minScale, float scaleStep, float dp, int threshold) + { + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); + + const dim3 block(32, 8); + const dim3 grid(divUp(cols, block.x), divUp(rows, block.y)); + + cudaSafeCall( cudaFuncSetCacheConfig(GHT_Ballard_PosScale_findPosInHist, cudaFuncCachePreferL1) ); + + GHT_Ballard_PosScale_findPosInHist<<>>(hist, rows, cols, scaleRange, out, votes, maxSize, minScale, scaleStep, dp, threshold); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + totalCount = ::min(totalCount, maxSize); + + return totalCount; + } + + //////////////////////////////////////////////////////////////////////// + // GHT_Ballard_PosRotation + + __global__ void GHT_Ballard_PosRotation_calcHist(const unsigned int* coordList, const float* thetaList, + PtrStep r_table, const int* r_sizes, + PtrStepi hist, const int rows, const int cols, + const float minAngle, const float angleStep, const int angleRange, + const float idp, const float thetaScale) + { + const unsigned int coord = coordList[blockIdx.x]; + float2 p; + p.x = (coord & 0xFFFF); + p.y = (coord >> 16) & 0xFFFF; + + const float thetaVal = thetaList[blockIdx.x]; + + for (int a = threadIdx.x; a < angleRange; a += blockDim.x) + { + const float angle = (minAngle + a * angleStep) * (CV_PI_F / 180.0f); + float sinA, cosA; + sincosf(angle, &sinA, &cosA); + + float theta = thetaVal - angle; + if (theta < 0) + theta += 2.0f * CV_PI_F; + + const int n = __float2int_rn(theta * thetaScale); + + const short2* r_row = r_table.ptr(n); + const int r_row_size = r_sizes[n]; + + for (int j = 0; j < r_row_size; ++j) + { + const float2 d = saturate_cast(r_row[j]); + + const float2 dr = make_float2(d.x * cosA - d.y * sinA, d.x * sinA + d.y * cosA); + + float2 c = make_float2(p.x - dr.x, p.y - dr.y); + c.x *= idp; + c.y *= idp; + + if (c.x >= 0 && c.x < cols && c.y >= 0 && c.y < rows) + ::atomicAdd(hist.ptr((a + 1) * (rows + 2) + __float2int_rn(c.y + 1)) + __float2int_rn(c.x + 1), 1); + } + } + } + + void GHT_Ballard_PosRotation_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + PtrStepSz r_table, const int* r_sizes, + PtrStepi hist, int rows, int cols, + float minAngle, float angleStep, int angleRange, + float dp, int levels) + { + const dim3 block(256); + const dim3 grid(pointsCount); + + const float idp = 1.0f / dp; + const float thetaScale = levels / (2.0f * CV_PI_F); + + GHT_Ballard_PosRotation_calcHist<<>>(coordList, thetaList, + r_table, r_sizes, + hist, rows, cols, + minAngle, angleStep, angleRange, + idp, thetaScale); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + } + + __global__ void GHT_Ballard_PosRotation_findPosInHist(const PtrStepi hist, const int rows, const int cols, const int angleRange, + float4* out, int3* votes, const int maxSize, + const float minAngle, const float angleStep, const float dp, const int threshold) + { + const int x = blockIdx.x * blockDim.x + threadIdx.x; + const int y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x >= cols || y >= rows) + return; + + for (int a = 0; a < angleRange; ++a) + { + const float angle = minAngle + a * angleStep; + + const int prevAngleIdx = (a) * (rows + 2); + const int curAngleIdx = (a + 1) * (rows + 2); + const int nextAngleIdx = (a + 2) * (rows + 2); + + const int curVotes = hist(curAngleIdx + y + 1, x + 1); + + if (curVotes > threshold && + curVotes > hist(curAngleIdx + y + 1, x) && + curVotes >= hist(curAngleIdx + y + 1, x + 2) && + curVotes > hist(curAngleIdx + y, x + 1) && + curVotes >= hist(curAngleIdx + y + 2, x + 1) && + curVotes > hist(prevAngleIdx + y + 1, x + 1) && + curVotes >= hist(nextAngleIdx + y + 1, x + 1)) + { + const int ind = ::atomicAdd(&g_counter, 1); + + if (ind < maxSize) + { + out[ind] = make_float4(x * dp, y * dp, 1.0f, angle); + votes[ind] = make_int3(curVotes, 0, curVotes); + } + } + } + } + + int GHT_Ballard_PosRotation_findPosInHist_gpu(PtrStepi hist, int rows, int cols, int angleRange, float4* out, int3* votes, int maxSize, + float minAngle, float angleStep, float dp, int threshold) + { + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); + + const dim3 block(32, 8); + const dim3 grid(divUp(cols, block.x), divUp(rows, block.y)); + + cudaSafeCall( cudaFuncSetCacheConfig(GHT_Ballard_PosRotation_findPosInHist, cudaFuncCachePreferL1) ); + + GHT_Ballard_PosRotation_findPosInHist<<>>(hist, rows, cols, angleRange, out, votes, maxSize, minAngle, angleStep, dp, threshold); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + totalCount = ::min(totalCount, maxSize); + + return totalCount; + } + + //////////////////////////////////////////////////////////////////////// + // GHT_Guil_Full + + struct FeatureTable + { + uchar* p1_pos_data; + size_t p1_pos_step; + + uchar* p1_theta_data; + size_t p1_theta_step; + + uchar* p2_pos_data; + size_t p2_pos_step; + + uchar* d12_data; + size_t d12_step; + + uchar* r1_data; + size_t r1_step; + + uchar* r2_data; + size_t r2_step; + }; + + __constant__ FeatureTable c_templFeatures; + __constant__ FeatureTable c_imageFeatures; + + void GHT_Guil_Full_setTemplFeatures(PtrStepb p1_pos, PtrStepb p1_theta, PtrStepb p2_pos, PtrStepb d12, PtrStepb r1, PtrStepb r2) + { + FeatureTable tbl; + + tbl.p1_pos_data = p1_pos.data; + tbl.p1_pos_step = p1_pos.step; + + tbl.p1_theta_data = p1_theta.data; + tbl.p1_theta_step = p1_theta.step; + + tbl.p2_pos_data = p2_pos.data; + tbl.p2_pos_step = p2_pos.step; + + tbl.d12_data = d12.data; + tbl.d12_step = d12.step; + + tbl.r1_data = r1.data; + tbl.r1_step = r1.step; + + tbl.r2_data = r2.data; + tbl.r2_step = r2.step; + + cudaSafeCall( cudaMemcpyToSymbol(c_templFeatures, &tbl, sizeof(FeatureTable)) ); + } + void GHT_Guil_Full_setImageFeatures(PtrStepb p1_pos, PtrStepb p1_theta, PtrStepb p2_pos, PtrStepb d12, PtrStepb r1, PtrStepb r2) + { + FeatureTable tbl; + + tbl.p1_pos_data = p1_pos.data; + tbl.p1_pos_step = p1_pos.step; + + tbl.p1_theta_data = p1_theta.data; + tbl.p1_theta_step = p1_theta.step; + + tbl.p2_pos_data = p2_pos.data; + tbl.p2_pos_step = p2_pos.step; + + tbl.d12_data = d12.data; + tbl.d12_step = d12.step; + + tbl.r1_data = r1.data; + tbl.r1_step = r1.step; + + tbl.r2_data = r2.data; + tbl.r2_step = r2.step; + + cudaSafeCall( cudaMemcpyToSymbol(c_imageFeatures, &tbl, sizeof(FeatureTable)) ); + } + + struct TemplFeatureTable + { + static __device__ float2* p1_pos(int n) + { + return (float2*)(c_templFeatures.p1_pos_data + n * c_templFeatures.p1_pos_step); + } + static __device__ float* p1_theta(int n) + { + return (float*)(c_templFeatures.p1_theta_data + n * c_templFeatures.p1_theta_step); + } + static __device__ float2* p2_pos(int n) + { + return (float2*)(c_templFeatures.p2_pos_data + n * c_templFeatures.p2_pos_step); + } + + static __device__ float* d12(int n) + { + return (float*)(c_templFeatures.d12_data + n * c_templFeatures.d12_step); + } + + static __device__ float2* r1(int n) + { + return (float2*)(c_templFeatures.r1_data + n * c_templFeatures.r1_step); + } + static __device__ float2* r2(int n) + { + return (float2*)(c_templFeatures.r2_data + n * c_templFeatures.r2_step); + } + }; + struct ImageFeatureTable + { + static __device__ float2* p1_pos(int n) + { + return (float2*)(c_imageFeatures.p1_pos_data + n * c_imageFeatures.p1_pos_step); + } + static __device__ float* p1_theta(int n) + { + return (float*)(c_imageFeatures.p1_theta_data + n * c_imageFeatures.p1_theta_step); + } + static __device__ float2* p2_pos(int n) + { + return (float2*)(c_imageFeatures.p2_pos_data + n * c_imageFeatures.p2_pos_step); + } + + static __device__ float* d12(int n) + { + return (float*)(c_imageFeatures.d12_data + n * c_imageFeatures.d12_step); + } + + static __device__ float2* r1(int n) + { + return (float2*)(c_imageFeatures.r1_data + n * c_imageFeatures.r1_step); + } + static __device__ float2* r2(int n) + { + return (float2*)(c_imageFeatures.r2_data + n * c_imageFeatures.r2_step); + } + }; + + __device__ float clampAngle(float a) + { + float res = a; + + while (res > 2.0f * CV_PI_F) + res -= 2.0f * CV_PI_F; + while (res < 0.0f) + res += 2.0f * CV_PI_F; + + return res; + } + + __device__ bool angleEq(float a, float b, float eps) + { + return (::fabs(clampAngle(a - b)) <= eps); + } + + template + __global__ void GHT_Guil_Full_buildFeatureList(const unsigned int* coordList, const float* thetaList, const int pointsCount, + int* sizes, const int maxSize, + const float xi, const float angleEpsilon, const float alphaScale, + const float2 center, const float maxDist) + { + const float p1_theta = thetaList[blockIdx.x]; + const unsigned int coord1 = coordList[blockIdx.x]; + float2 p1_pos; + p1_pos.x = (coord1 & 0xFFFF); + p1_pos.y = (coord1 >> 16) & 0xFFFF; + + for (int i = threadIdx.x; i < pointsCount; i += blockDim.x) + { + const float p2_theta = thetaList[i]; + const unsigned int coord2 = coordList[i]; + float2 p2_pos; + p2_pos.x = (coord2 & 0xFFFF); + p2_pos.y = (coord2 >> 16) & 0xFFFF; + + if (angleEq(p1_theta - p2_theta, xi, angleEpsilon)) + { + const float2 d = p1_pos - p2_pos; + + float alpha12 = clampAngle(::atan2(d.y, d.x) - p1_theta); + float d12 = ::sqrtf(d.x * d.x + d.y * d.y); + + if (d12 > maxDist) + continue; + + float2 r1 = p1_pos - center; + float2 r2 = p2_pos - center; + + const int n = __float2int_rn(alpha12 * alphaScale); + + const int ind = ::atomicAdd(sizes + n, 1); + + if (ind < maxSize) + { + if (!isTempl) + { + FT::p1_pos(n)[ind] = p1_pos; + FT::p2_pos(n)[ind] = p2_pos; + } + + FT::p1_theta(n)[ind] = p1_theta; + + FT::d12(n)[ind] = d12; + + if (isTempl) + { + FT::r1(n)[ind] = r1; + FT::r2(n)[ind] = r2; + } + } + } + } + } + + template + void GHT_Guil_Full_buildFeatureList_caller(const unsigned int* coordList, const float* thetaList, int pointsCount, + int* sizes, int maxSize, + float xi, float angleEpsilon, int levels, + float2 center, float maxDist) + { + const dim3 block(256); + const dim3 grid(pointsCount); + + const float alphaScale = levels / (2.0f * CV_PI_F); + + GHT_Guil_Full_buildFeatureList<<>>(coordList, thetaList, pointsCount, + sizes, maxSize, + xi * (CV_PI_F / 180.0f), angleEpsilon * (CV_PI_F / 180.0f), alphaScale, + center, maxDist); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + thrust::device_ptr sizesPtr(sizes); + thrust::transform(sizesPtr, sizesPtr + levels + 1, sizesPtr, device::bind2nd(device::minimum(), maxSize)); + } + + void GHT_Guil_Full_buildTemplFeatureList_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + int* sizes, int maxSize, + float xi, float angleEpsilon, int levels, + float2 center, float maxDist) + { + GHT_Guil_Full_buildFeatureList_caller(coordList, thetaList, pointsCount, + sizes, maxSize, + xi, angleEpsilon, levels, + center, maxDist); + } + void GHT_Guil_Full_buildImageFeatureList_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + int* sizes, int maxSize, + float xi, float angleEpsilon, int levels, + float2 center, float maxDist) + { + GHT_Guil_Full_buildFeatureList_caller(coordList, thetaList, pointsCount, + sizes, maxSize, + xi, angleEpsilon, levels, + center, maxDist); + } + + __global__ void GHT_Guil_Full_calcOHist(const int* templSizes, const int* imageSizes, int* OHist, + const float minAngle, const float maxAngle, const float iAngleStep, const int angleRange) + { + extern __shared__ int s_OHist[]; + for (int i = threadIdx.x; i <= angleRange; i += blockDim.x) + s_OHist[i] = 0; + __syncthreads(); + + const int tIdx = blockIdx.x; + const int level = blockIdx.y; + + const int tSize = templSizes[level]; + + if (tIdx < tSize) + { + const int imSize = imageSizes[level]; + + const float t_p1_theta = TemplFeatureTable::p1_theta(level)[tIdx]; + + for (int i = threadIdx.x; i < imSize; i += blockDim.x) + { + const float im_p1_theta = ImageFeatureTable::p1_theta(level)[i]; + + const float angle = clampAngle(im_p1_theta - t_p1_theta); + + if (angle >= minAngle && angle <= maxAngle) + { + const int n = __float2int_rn((angle - minAngle) * iAngleStep); + Emulation::smem::atomicAdd(&s_OHist[n], 1); + } + } + } + __syncthreads(); + + for (int i = threadIdx.x; i <= angleRange; i += blockDim.x) + ::atomicAdd(OHist + i, s_OHist[i]); + } + + void GHT_Guil_Full_calcOHist_gpu(const int* templSizes, const int* imageSizes, int* OHist, + float minAngle, float maxAngle, float angleStep, int angleRange, + int levels, int tMaxSize) + { + const dim3 block(256); + const dim3 grid(tMaxSize, levels + 1); + + minAngle *= (CV_PI_F / 180.0f); + maxAngle *= (CV_PI_F / 180.0f); + angleStep *= (CV_PI_F / 180.0f); + + const size_t smemSize = (angleRange + 1) * sizeof(float); + + GHT_Guil_Full_calcOHist<<>>(templSizes, imageSizes, OHist, + minAngle, maxAngle, 1.0f / angleStep, angleRange); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + } + + __global__ void GHT_Guil_Full_calcSHist(const int* templSizes, const int* imageSizes, int* SHist, + const float angle, const float angleEpsilon, + const float minScale, const float maxScale, const float iScaleStep, const int scaleRange) + { + extern __shared__ int s_SHist[]; + for (int i = threadIdx.x; i <= scaleRange; i += blockDim.x) + s_SHist[i] = 0; + __syncthreads(); + + const int tIdx = blockIdx.x; + const int level = blockIdx.y; + + const int tSize = templSizes[level]; + + if (tIdx < tSize) + { + const int imSize = imageSizes[level]; + + const float t_p1_theta = TemplFeatureTable::p1_theta(level)[tIdx] + angle; + const float t_d12 = TemplFeatureTable::d12(level)[tIdx] + angle; + + for (int i = threadIdx.x; i < imSize; i += blockDim.x) + { + const float im_p1_theta = ImageFeatureTable::p1_theta(level)[i]; + const float im_d12 = ImageFeatureTable::d12(level)[i]; + + if (angleEq(im_p1_theta, t_p1_theta, angleEpsilon)) + { + const float scale = im_d12 / t_d12; + + if (scale >= minScale && scale <= maxScale) + { + const int s = __float2int_rn((scale - minScale) * iScaleStep); + Emulation::smem::atomicAdd(&s_SHist[s], 1); + } + } + } + } + __syncthreads(); + + for (int i = threadIdx.x; i <= scaleRange; i += blockDim.x) + ::atomicAdd(SHist + i, s_SHist[i]); + } + + void GHT_Guil_Full_calcSHist_gpu(const int* templSizes, const int* imageSizes, int* SHist, + float angle, float angleEpsilon, + float minScale, float maxScale, float iScaleStep, int scaleRange, + int levels, int tMaxSize) + { + const dim3 block(256); + const dim3 grid(tMaxSize, levels + 1); + + angle *= (CV_PI_F / 180.0f); + angleEpsilon *= (CV_PI_F / 180.0f); + + const size_t smemSize = (scaleRange + 1) * sizeof(float); + + GHT_Guil_Full_calcSHist<<>>(templSizes, imageSizes, SHist, + angle, angleEpsilon, + minScale, maxScale, iScaleStep, scaleRange); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + } + + __global__ void GHT_Guil_Full_calcPHist(const int* templSizes, const int* imageSizes, PtrStepSzi PHist, + const float angle, const float sinVal, const float cosVal, const float angleEpsilon, const float scale, + const float idp) + { + const int tIdx = blockIdx.x; + const int level = blockIdx.y; + + const int tSize = templSizes[level]; + + if (tIdx < tSize) + { + const int imSize = imageSizes[level]; + + const float t_p1_theta = TemplFeatureTable::p1_theta(level)[tIdx] + angle; + + float2 r1 = TemplFeatureTable::r1(level)[tIdx]; + float2 r2 = TemplFeatureTable::r2(level)[tIdx]; + + r1 = r1 * scale; + r2 = r2 * scale; + + r1 = make_float2(cosVal * r1.x - sinVal * r1.y, sinVal * r1.x + cosVal * r1.y); + r2 = make_float2(cosVal * r2.x - sinVal * r2.y, sinVal * r2.x + cosVal * r2.y); + + for (int i = threadIdx.x; i < imSize; i += blockDim.x) + { + const float im_p1_theta = ImageFeatureTable::p1_theta(level)[i]; + + const float2 im_p1_pos = ImageFeatureTable::p1_pos(level)[i]; + const float2 im_p2_pos = ImageFeatureTable::p2_pos(level)[i]; + + if (angleEq(im_p1_theta, t_p1_theta, angleEpsilon)) + { + float2 c1, c2; + + c1 = im_p1_pos - r1; + c1 = c1 * idp; + + c2 = im_p2_pos - r2; + c2 = c2 * idp; + + if (::fabs(c1.x - c2.x) > 1 || ::fabs(c1.y - c2.y) > 1) + continue; + + if (c1.y >= 0 && c1.y < PHist.rows - 2 && c1.x >= 0 && c1.x < PHist.cols - 2) + ::atomicAdd(PHist.ptr(__float2int_rn(c1.y) + 1) + __float2int_rn(c1.x) + 1, 1); + } + } + } + } + + void GHT_Guil_Full_calcPHist_gpu(const int* templSizes, const int* imageSizes, PtrStepSzi PHist, + float angle, float angleEpsilon, float scale, + float dp, + int levels, int tMaxSize) + { + const dim3 block(256); + const dim3 grid(tMaxSize, levels + 1); + + angle *= (CV_PI_F / 180.0f); + angleEpsilon *= (CV_PI_F / 180.0f); + + const float sinVal = ::sinf(angle); + const float cosVal = ::cosf(angle); + + cudaSafeCall( cudaFuncSetCacheConfig(GHT_Guil_Full_calcPHist, cudaFuncCachePreferL1) ); + + GHT_Guil_Full_calcPHist<<>>(templSizes, imageSizes, PHist, + angle, sinVal, cosVal, angleEpsilon, scale, + 1.0f / dp); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + } + + __global__ void GHT_Guil_Full_findPosInHist(const PtrStepSzi hist, float4* out, int3* votes, const int maxSize, + const float angle, const int angleVotes, const float scale, const int scaleVotes, + const float dp, const int threshold) + { + const int x = blockIdx.x * blockDim.x + threadIdx.x; + const int y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x >= hist.cols - 2 || y >= hist.rows - 2) + return; + + const int curVotes = hist(y + 1, x + 1); + + if (curVotes > threshold && + curVotes > hist(y + 1, x) && + curVotes >= hist(y + 1, x + 2) && + curVotes > hist(y, x + 1) && + curVotes >= hist(y + 2, x + 1)) + { + const int ind = ::atomicAdd(&g_counter, 1); + + if (ind < maxSize) + { + out[ind] = make_float4(x * dp, y * dp, scale, angle); + votes[ind] = make_int3(curVotes, scaleVotes, angleVotes); + } + } + } + + int GHT_Guil_Full_findPosInHist_gpu(PtrStepSzi hist, float4* out, int3* votes, int curSize, int maxSize, + float angle, int angleVotes, float scale, int scaleVotes, + float dp, int threshold) + { + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemcpy(counterPtr, &curSize, sizeof(int), cudaMemcpyHostToDevice) ); + + const dim3 block(32, 8); + const dim3 grid(divUp(hist.cols - 2, block.x), divUp(hist.rows - 2, block.y)); + + cudaSafeCall( cudaFuncSetCacheConfig(GHT_Guil_Full_findPosInHist, cudaFuncCachePreferL1) ); + + GHT_Guil_Full_findPosInHist<<>>(hist, out, votes, maxSize, + angle, angleVotes, scale, scaleVotes, + dp, threshold); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + totalCount = ::min(totalCount, maxSize); + + return totalCount; + } } }}} diff --git a/modules/gpu/src/hough.cpp b/modules/gpu/src/hough.cpp index 399de3684f..9cfcd920f5 100644 --- a/modules/gpu/src/hough.cpp +++ b/modules/gpu/src/hough.cpp @@ -42,6 +42,10 @@ #include "precomp.hpp" +using namespace std; +using namespace cv; +using namespace cv::gpu; + #if !defined (HAVE_CUDA) void cv::gpu::HoughLines(const GpuMat&, GpuMat&, float, float, int, bool, int) { throw_nogpu(); } @@ -52,6 +56,15 @@ void cv::gpu::HoughCircles(const GpuMat&, GpuMat&, int, float, float, int, int, void cv::gpu::HoughCircles(const GpuMat&, GpuMat&, HoughCirclesBuf&, int, float, float, int, int, int, int, int) { throw_nogpu(); } void cv::gpu::HoughCirclesDownload(const GpuMat&, OutputArray) { throw_nogpu(); } +Ptr cv::gpu::GeneralizedHough_GPU::create(int) { throw_nogpu(); return Ptr(); } +cv::gpu::GeneralizedHough_GPU::~GeneralizedHough_GPU() {} +void cv::gpu::GeneralizedHough_GPU::setTemplate(const GpuMat&, int, Point) { throw_nogpu(); } +void cv::gpu::GeneralizedHough_GPU::setTemplate(const GpuMat&, const GpuMat&, const GpuMat&, Point) { throw_nogpu(); } +void cv::gpu::GeneralizedHough_GPU::detect(const GpuMat&, GpuMat&, int) { throw_nogpu(); } +void cv::gpu::GeneralizedHough_GPU::detect(const GpuMat&, const GpuMat&, const GpuMat&, GpuMat&) { throw_nogpu(); } +void cv::gpu::GeneralizedHough_GPU::download(const GpuMat&, OutputArray, OutputArray) { throw_nogpu(); } +void cv::gpu::GeneralizedHough_GPU::release() {} + #else /* !defined (HAVE_CUDA) */ namespace cv { namespace gpu { namespace device @@ -59,20 +72,21 @@ namespace cv { namespace gpu { namespace device namespace hough { int buildPointList_gpu(PtrStepSzb src, unsigned int* list); - - void linesAccum_gpu(const unsigned int* list, int count, PtrStepSzi accum, float rho, float theta, size_t sharedMemPerBlock, bool has20); - int linesGetResult_gpu(PtrStepSzi accum, float2* out, int* votes, int maxSize, float rho, float theta, int threshold, bool doSort); - - void circlesAccumCenters_gpu(const unsigned int* list, int count, PtrStepi dx, PtrStepi dy, PtrStepSzi accum, int minRadius, int maxRadius, float idp); - int buildCentersList_gpu(PtrStepSzi accum, unsigned int* centers, int threshold); - int circlesAccumRadius_gpu(const unsigned int* centers, int centersCount, const unsigned int* list, int count, - float3* circles, int maxCircles, float dp, int minRadius, int maxRadius, int threshold, bool has20); } }}} ////////////////////////////////////////////////////////// // HoughLines +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + void linesAccum_gpu(const unsigned int* list, int count, PtrStepSzi accum, float rho, float theta, size_t sharedMemPerBlock, bool has20); + int linesGetResult_gpu(PtrStepSzi accum, float2* out, int* votes, int maxSize, float rho, float theta, int threshold, bool doSort); + } +}}} + void cv::gpu::HoughLines(const GpuMat& src, GpuMat& lines, float rho, float theta, int threshold, bool doSort, int maxLines) { HoughLinesBuf buf; @@ -144,6 +158,17 @@ void cv::gpu::HoughLinesDownload(const GpuMat& d_lines, OutputArray h_lines_, Ou ////////////////////////////////////////////////////////// // HoughCircles +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + void circlesAccumCenters_gpu(const unsigned int* list, int count, PtrStepi dx, PtrStepi dy, PtrStepSzi accum, int minRadius, int maxRadius, float idp); + int buildCentersList_gpu(PtrStepSzi accum, unsigned int* centers, int threshold); + int circlesAccumRadius_gpu(const unsigned int* centers, int centersCount, const unsigned int* list, int count, + float3* circles, int maxCircles, float dp, int minRadius, int maxRadius, int threshold, bool has20); + } +}}} + void cv::gpu::HoughCircles(const GpuMat& src, GpuMat& circles, int method, float dp, float minDist, int cannyThreshold, int votesThreshold, int minRadius, int maxRadius, int maxCircles) { HoughCirclesBuf buf; @@ -209,7 +234,7 @@ void cv::gpu::HoughCircles(const GpuMat& src, GpuMat& circles, HoughCirclesBuf& std::vector< std::vector > grid(gridWidth * gridHeight); - minDist *= minDist; + const float minDist2 = minDist * minDist; for (int i = 0; i < centersCount; ++i) { @@ -242,7 +267,7 @@ void cv::gpu::HoughCircles(const GpuMat& src, GpuMat& circles, HoughCirclesBuf& float dx = (float)(p.x - m[j].x); float dy = (float)(p.y - m[j].y); - if (dx * dx + dy * dy < minDist) + if (dx * dx + dy * dy < minDist2) { good = false; goto break_out; @@ -292,4 +317,1056 @@ void cv::gpu::HoughCirclesDownload(const GpuMat& d_circles, cv::OutputArray h_ci d_circles.download(h_circles); } +////////////////////////////////////////////////////////// +// GeneralizedHough + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + template + int buildEdgePointList_gpu(PtrStepSzb edges, PtrStepSzb dx, PtrStepSzb dy, unsigned int* coordList, float* thetaList); + void buildRTable_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + PtrStepSz r_table, int* r_sizes, + short2 templCenter, int levels); + + void GHT_Ballard_Pos_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + PtrStepSz r_table, const int* r_sizes, + PtrStepSzi hist, + float dp, int levels); + int GHT_Ballard_Pos_findPosInHist_gpu(PtrStepSzi hist, float4* out, int3* votes, int maxSize, float dp, int threshold); + + void GHT_Ballard_PosScale_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + PtrStepSz r_table, const int* r_sizes, + PtrStepi hist, int rows, int cols, + float minScale, float scaleStep, int scaleRange, + float dp, int levels); + int GHT_Ballard_PosScale_findPosInHist_gpu(PtrStepi hist, int rows, int cols, int scaleRange, float4* out, int3* votes, int maxSize, + float minScale, float scaleStep, float dp, int threshold); + + void GHT_Ballard_PosRotation_calcHist_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + PtrStepSz r_table, const int* r_sizes, + PtrStepi hist, int rows, int cols, + float minAngle, float angleStep, int angleRange, + float dp, int levels); + int GHT_Ballard_PosRotation_findPosInHist_gpu(PtrStepi hist, int rows, int cols, int angleRange, float4* out, int3* votes, int maxSize, + float minAngle, float angleStep, float dp, int threshold); + + void GHT_Guil_Full_setTemplFeatures(PtrStepb p1_pos, PtrStepb p1_theta, PtrStepb p2_pos, PtrStepb d12, PtrStepb r1, PtrStepb r2); + void GHT_Guil_Full_setImageFeatures(PtrStepb p1_pos, PtrStepb p1_theta, PtrStepb p2_pos, PtrStepb d12, PtrStepb r1, PtrStepb r2); + void GHT_Guil_Full_buildTemplFeatureList_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + int* sizes, int maxSize, + float xi, float angleEpsilon, int levels, + float2 center, float maxDist); + void GHT_Guil_Full_buildImageFeatureList_gpu(const unsigned int* coordList, const float* thetaList, int pointsCount, + int* sizes, int maxSize, + float xi, float angleEpsilon, int levels, + float2 center, float maxDist); + void GHT_Guil_Full_calcOHist_gpu(const int* templSizes, const int* imageSizes, int* OHist, + float minAngle, float maxAngle, float angleStep, int angleRange, + int levels, int tMaxSize); + void GHT_Guil_Full_calcSHist_gpu(const int* templSizes, const int* imageSizes, int* SHist, + float angle, float angleEpsilon, + float minScale, float maxScale, float iScaleStep, int scaleRange, + int levels, int tMaxSize); + void GHT_Guil_Full_calcPHist_gpu(const int* templSizes, const int* imageSizes, PtrStepSzi PHist, + float angle, float angleEpsilon, float scale, + float dp, + int levels, int tMaxSize); + int GHT_Guil_Full_findPosInHist_gpu(PtrStepSzi hist, float4* out, int3* votes, int curSize, int maxSize, + float angle, int angleVotes, float scale, int scaleVotes, + float dp, int threshold); + } +}}} + +namespace +{ + ///////////////////////////////////// + // Common + + template void releaseVector(vector& v) + { + vector empty; + empty.swap(v); + } + + class GHT_Pos : public GeneralizedHough_GPU + { + public: + GHT_Pos(); + + protected: + void setTemplateImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Point templCenter); + void detectImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, GpuMat& positions); + void releaseImpl(); + + virtual void processTempl() = 0; + virtual void processImage() = 0; + + void buildEdgePointList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy); + void filterMinDist(); + void convertTo(GpuMat& positions); + + int maxSize; + double minDist; + + Size templSize; + Point templCenter; + GpuMat templEdges; + GpuMat templDx; + GpuMat templDy; + + Size imageSize; + GpuMat imageEdges; + GpuMat imageDx; + GpuMat imageDy; + + GpuMat edgePointList; + + GpuMat outBuf; + int posCount; + + vector oldPosBuf; + vector oldVoteBuf; + vector newPosBuf; + vector newVoteBuf; + vector indexies; + }; + + GHT_Pos::GHT_Pos() + { + maxSize = 10000; + minDist = 1.0; + } + + void GHT_Pos::setTemplateImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Point templCenter_) + { + templSize = edges.size(); + templCenter = templCenter_; + + ensureSizeIsEnough(templSize, edges.type(), templEdges); + ensureSizeIsEnough(templSize, dx.type(), templDx); + ensureSizeIsEnough(templSize, dy.type(), templDy); + + edges.copyTo(templEdges); + dx.copyTo(templDx); + dy.copyTo(templDy); + + processTempl(); + } + + void GHT_Pos::detectImpl(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, GpuMat& positions) + { + imageSize = edges.size(); + + ensureSizeIsEnough(imageSize, edges.type(), imageEdges); + ensureSizeIsEnough(imageSize, dx.type(), imageDx); + ensureSizeIsEnough(imageSize, dy.type(), imageDy); + + edges.copyTo(imageEdges); + dx.copyTo(imageDx); + dy.copyTo(imageDy); + + posCount = 0; + + processImage(); + + if (posCount == 0) + positions.release(); + else + { + if (minDist > 1) + filterMinDist(); + convertTo(positions); + } + } + + void GHT_Pos::releaseImpl() + { + templSize = Size(); + templCenter = Point(-1, -1); + templEdges.release(); + templDx.release(); + templDy.release(); + + imageSize = Size(); + imageEdges.release(); + imageDx.release(); + imageDy.release(); + + edgePointList.release(); + + outBuf.release(); + posCount = 0; + + releaseVector(oldPosBuf); + releaseVector(oldVoteBuf); + releaseVector(newPosBuf); + releaseVector(newVoteBuf); + releaseVector(indexies); + } + + void GHT_Pos::buildEdgePointList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy) + { + using namespace cv::gpu::device::hough; + + typedef int (*func_t)(PtrStepSzb edges, PtrStepSzb dx, PtrStepSzb dy, unsigned int* coordList, float* thetaList); + static const func_t funcs[] = + { + 0, + 0, + 0, + buildEdgePointList_gpu, + buildEdgePointList_gpu, + buildEdgePointList_gpu, + 0 + }; + + CV_Assert(edges.type() == CV_8UC1); + CV_Assert(dx.size() == edges.size()); + CV_Assert(dy.type() == dx.type() && dy.size() == edges.size()); + + const func_t func = funcs[dx.depth()]; + CV_Assert(func != 0); + + edgePointList.cols = edgePointList.step / sizeof(int); + ensureSizeIsEnough(2, edges.size().area(), CV_32SC1, edgePointList); + + edgePointList.cols = func(edges, dx, dy, edgePointList.ptr(0), edgePointList.ptr(1)); + } + + #define votes_cmp_gt(l1, l2) (aux[l1].x > aux[l2].x) + static CV_IMPLEMENT_QSORT_EX( sortIndexies, int, votes_cmp_gt, const int3* ) + + void GHT_Pos::filterMinDist() + { + oldPosBuf.resize(posCount); + oldVoteBuf.resize(posCount); + + cudaSafeCall( cudaMemcpy(&oldPosBuf[0], outBuf.ptr(0), posCount * sizeof(float4), cudaMemcpyDeviceToHost) ); + cudaSafeCall( cudaMemcpy(&oldVoteBuf[0], outBuf.ptr(1), posCount * sizeof(int3), cudaMemcpyDeviceToHost) ); + + indexies.resize(posCount); + for (int i = 0; i < posCount; ++i) + indexies[i] = i; + sortIndexies(&indexies[0], posCount, &oldVoteBuf[0]); + + newPosBuf.clear(); + newVoteBuf.clear(); + newPosBuf.reserve(posCount); + newVoteBuf.reserve(posCount); + + const int cellSize = cvRound(minDist); + const int gridWidth = (imageSize.width + cellSize - 1) / cellSize; + const int gridHeight = (imageSize.height + cellSize - 1) / cellSize; + + vector< vector > grid(gridWidth * gridHeight); + + const double minDist2 = minDist * minDist; + + for (int i = 0; i < posCount; ++i) + { + const int ind = indexies[i]; + + Point2f p(oldPosBuf[ind].x, oldPosBuf[ind].y); + + bool good = true; + + const int xCell = static_cast(p.x / cellSize); + const int yCell = static_cast(p.y / cellSize); + + int x1 = xCell - 1; + int y1 = yCell - 1; + int x2 = xCell + 1; + int y2 = yCell + 1; + + // boundary check + x1 = std::max(0, x1); + y1 = std::max(0, y1); + x2 = std::min(gridWidth - 1, x2); + y2 = std::min(gridHeight - 1, y2); + + for (int yy = y1; yy <= y2; ++yy) + { + for (int xx = x1; xx <= x2; ++xx) + { + const vector& m = grid[yy * gridWidth + xx]; + + for(size_t j = 0; j < m.size(); ++j) + { + const Point2f d = p - m[j]; + + if (d.ddot(d) < minDist2) + { + good = false; + goto break_out; + } + } + } + } + + break_out: + + if(good) + { + grid[yCell * gridWidth + xCell].push_back(p); + + newPosBuf.push_back(oldPosBuf[ind]); + newVoteBuf.push_back(oldVoteBuf[ind]); + } + } + + posCount = static_cast(newPosBuf.size()); + cudaSafeCall( cudaMemcpy(outBuf.ptr(0), &newPosBuf[0], posCount * sizeof(float4), cudaMemcpyHostToDevice) ); + cudaSafeCall( cudaMemcpy(outBuf.ptr(1), &newVoteBuf[0], posCount * sizeof(int3), cudaMemcpyHostToDevice) ); + } + + void GHT_Pos::convertTo(GpuMat& positions) + { + ensureSizeIsEnough(2, posCount, CV_32FC4, positions); + GpuMat(2, posCount, CV_32FC4, outBuf.data, outBuf.step).copyTo(positions); + } + + ///////////////////////////////////// + // POSITION Ballard + + class GHT_Ballard_Pos : public GHT_Pos + { + public: + AlgorithmInfo* info() const; + + GHT_Ballard_Pos(); + + protected: + void releaseImpl(); + + void processTempl(); + void processImage(); + + virtual void calcHist(); + virtual void findPosInHist(); + + int levels; + int votesThreshold; + double dp; + + GpuMat r_table; + GpuMat r_sizes; + + GpuMat hist; + }; + + CV_INIT_ALGORITHM(GHT_Ballard_Pos, "GeneralizedHough_GPU.POSITION", + obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, + "Maximal size of inner buffers."); + obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, + "Minimum distance between the centers of the detected objects."); + obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, + "R-Table levels."); + obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, + "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); + obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, + "Inverse ratio of the accumulator resolution to the image resolution.")); + + GHT_Ballard_Pos::GHT_Ballard_Pos() + { + levels = 360; + votesThreshold = 100; + dp = 1.0; + } + + void GHT_Ballard_Pos::releaseImpl() + { + GHT_Pos::releaseImpl(); + + r_table.release(); + r_sizes.release(); + + hist.release(); + } + + void GHT_Ballard_Pos::processTempl() + { + using namespace cv::gpu::device::hough; + + CV_Assert(levels > 0); + + buildEdgePointList(templEdges, templDx, templDy); + + ensureSizeIsEnough(levels + 1, maxSize, CV_16SC2, r_table); + ensureSizeIsEnough(1, levels + 1, CV_32SC1, r_sizes); + r_sizes.setTo(Scalar::all(0)); + + if (edgePointList.cols > 0) + { + buildRTable_gpu(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, + r_table, r_sizes.ptr(), make_short2(templCenter.x, templCenter.y), levels); + min(r_sizes, maxSize, r_sizes); + } + } + + void GHT_Ballard_Pos::processImage() + { + calcHist(); + findPosInHist(); + } + + void GHT_Ballard_Pos::calcHist() + { + using namespace cv::gpu::device::hough; + + CV_Assert(levels > 0 && r_table.rows == (levels + 1) && r_sizes.cols == (levels + 1)); + CV_Assert(dp > 0.0); + + const double idp = 1.0 / dp; + + buildEdgePointList(imageEdges, imageDx, imageDy); + + ensureSizeIsEnough(cvCeil(imageSize.height * idp) + 2, cvCeil(imageSize.width * idp) + 2, CV_32SC1, hist); + hist.setTo(Scalar::all(0)); + + if (edgePointList.cols > 0) + { + GHT_Ballard_Pos_calcHist_gpu(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, + r_table, r_sizes.ptr(), + hist, + dp, levels); + } + } + + void GHT_Ballard_Pos::findPosInHist() + { + using namespace cv::gpu::device::hough; + + CV_Assert(votesThreshold > 0); + + ensureSizeIsEnough(2, maxSize, CV_32FC4, outBuf); + + posCount = GHT_Ballard_Pos_findPosInHist_gpu(hist, outBuf.ptr(0), outBuf.ptr(1), maxSize, dp, votesThreshold); + } + + ///////////////////////////////////// + // POSITION & SCALE + + class GHT_Ballard_PosScale : public GHT_Ballard_Pos + { + public: + AlgorithmInfo* info() const; + + GHT_Ballard_PosScale(); + + protected: + void calcHist(); + void findPosInHist(); + + double minScale; + double maxScale; + double scaleStep; + }; + + CV_INIT_ALGORITHM(GHT_Ballard_PosScale, "GeneralizedHough_GPU.POSITION_SCALE", + obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, + "Maximal size of inner buffers."); + obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, + "Minimum distance between the centers of the detected objects."); + obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, + "R-Table levels."); + obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, + "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); + obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, + "Inverse ratio of the accumulator resolution to the image resolution."); + obj.info()->addParam(obj, "minScale", obj.minScale, false, 0, 0, + "Minimal scale to detect."); + obj.info()->addParam(obj, "maxScale", obj.maxScale, false, 0, 0, + "Maximal scale to detect."); + obj.info()->addParam(obj, "scaleStep", obj.scaleStep, false, 0, 0, + "Scale step.")); + + GHT_Ballard_PosScale::GHT_Ballard_PosScale() + { + minScale = 0.5; + maxScale = 2.0; + scaleStep = 0.05; + } + + void GHT_Ballard_PosScale::calcHist() + { + using namespace cv::gpu::device::hough; + + CV_Assert(levels > 0 && r_table.rows == (levels + 1) && r_sizes.cols == (levels + 1)); + CV_Assert(dp > 0.0); + CV_Assert(minScale > 0.0 && minScale < maxScale); + CV_Assert(scaleStep > 0.0); + + const double idp = 1.0 / dp; + const int scaleRange = cvCeil((maxScale - minScale) / scaleStep); + const int rows = cvCeil(imageSize.height * idp); + const int cols = cvCeil(imageSize.width * idp); + + buildEdgePointList(imageEdges, imageDx, imageDy); + + ensureSizeIsEnough((scaleRange + 2) * (rows + 2), cols + 2, CV_32SC1, hist); + hist.setTo(Scalar::all(0)); + + if (edgePointList.cols > 0) + { + GHT_Ballard_PosScale_calcHist_gpu(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, + r_table, r_sizes.ptr(), + hist, rows, cols, + minScale, scaleStep, scaleRange, dp, levels); + } + } + + void GHT_Ballard_PosScale::findPosInHist() + { + using namespace cv::gpu::device::hough; + + CV_Assert(votesThreshold > 0); + + const double idp = 1.0 / dp; + const int scaleRange = cvCeil((maxScale - minScale) / scaleStep); + const int rows = cvCeil(imageSize.height * idp); + const int cols = cvCeil(imageSize.width * idp); + + ensureSizeIsEnough(2, maxSize, CV_32FC4, outBuf); + + posCount = GHT_Ballard_PosScale_findPosInHist_gpu(hist, rows, cols, scaleRange, outBuf.ptr(0), outBuf.ptr(1), maxSize, minScale, scaleStep, dp, votesThreshold); + } + + ///////////////////////////////////// + // POSITION & Rotation + + class GHT_Ballard_PosRotation : public GHT_Ballard_Pos + { + public: + AlgorithmInfo* info() const; + + GHT_Ballard_PosRotation(); + + protected: + void calcHist(); + void findPosInHist(); + + double minAngle; + double maxAngle; + double angleStep; + }; + + CV_INIT_ALGORITHM(GHT_Ballard_PosRotation, "GeneralizedHough_GPU.POSITION_ROTATION", + obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, + "Maximal size of inner buffers."); + obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, + "Minimum distance between the centers of the detected objects."); + obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, + "R-Table levels."); + obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, + "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); + obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, + "Inverse ratio of the accumulator resolution to the image resolution."); + obj.info()->addParam(obj, "minAngle", obj.minAngle, false, 0, 0, + "Minimal rotation angle to detect in degrees."); + obj.info()->addParam(obj, "maxAngle", obj.maxAngle, false, 0, 0, + "Maximal rotation angle to detect in degrees."); + obj.info()->addParam(obj, "angleStep", obj.angleStep, false, 0, 0, + "Angle step in degrees.")); + + GHT_Ballard_PosRotation::GHT_Ballard_PosRotation() + { + minAngle = 0.0; + maxAngle = 360.0; + angleStep = 1.0; + } + + void GHT_Ballard_PosRotation::calcHist() + { + using namespace cv::gpu::device::hough; + + CV_Assert(levels > 0 && r_table.rows == (levels + 1) && r_sizes.cols == (levels + 1)); + CV_Assert(dp > 0.0); + CV_Assert(minAngle >= 0.0 && minAngle < maxAngle && maxAngle <= 360.0); + CV_Assert(angleStep > 0.0 && angleStep < 360.0); + + const double idp = 1.0 / dp; + const int angleRange = cvCeil((maxAngle - minAngle) / angleStep); + const int rows = cvCeil(imageSize.height * idp); + const int cols = cvCeil(imageSize.width * idp); + + buildEdgePointList(imageEdges, imageDx, imageDy); + + ensureSizeIsEnough((angleRange + 2) * (rows + 2), cols + 2, CV_32SC1, hist); + hist.setTo(Scalar::all(0)); + + if (edgePointList.cols > 0) + { + GHT_Ballard_PosRotation_calcHist_gpu(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, + r_table, r_sizes.ptr(), + hist, rows, cols, + minAngle, angleStep, angleRange, dp, levels); + } + } + + void GHT_Ballard_PosRotation::findPosInHist() + { + using namespace cv::gpu::device::hough; + + CV_Assert(votesThreshold > 0); + + const double idp = 1.0 / dp; + const int angleRange = cvCeil((maxAngle - minAngle) / angleStep); + const int rows = cvCeil(imageSize.height * idp); + const int cols = cvCeil(imageSize.width * idp); + + ensureSizeIsEnough(2, maxSize, CV_32FC4, outBuf); + + posCount = GHT_Ballard_PosRotation_findPosInHist_gpu(hist, rows, cols, angleRange, outBuf.ptr(0), outBuf.ptr(1), maxSize, minAngle, angleStep, dp, votesThreshold); + } + + ///////////////////////////////////////// + // POSITION & SCALE & ROTATION + + double toRad(double a) + { + return a * CV_PI / 180.0; + } + + double clampAngle(double a) + { + double res = a; + + while (res > 360.0) + res -= 360.0; + while (res < 0) + res += 360.0; + + return res; + } + + bool angleEq(double a, double b, double eps = 1.0) + { + return (fabs(clampAngle(a - b)) <= eps); + } + + class GHT_Guil_Full : public GHT_Pos + { + public: + AlgorithmInfo* info() const; + + GHT_Guil_Full(); + + protected: + void releaseImpl(); + + void processTempl(); + void processImage(); + + struct Feature + { + GpuMat p1_pos; + GpuMat p1_theta; + GpuMat p2_pos; + + GpuMat d12; + + GpuMat r1; + GpuMat r2; + + GpuMat sizes; + int maxSize; + + void create(int levels, int maxCapacity, bool isTempl); + void release(); + }; + + typedef void (*set_func_t)(PtrStepb p1_pos, PtrStepb p1_theta, PtrStepb p2_pos, PtrStepb d12, PtrStepb r1, PtrStepb r2); + typedef void (*build_func_t)(const unsigned int* coordList, const float* thetaList, int pointsCount, + int* sizes, int maxSize, + float xi, float angleEpsilon, int levels, + float2 center, float maxDist); + + void buildFeatureList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Feature& features, + set_func_t set_func, build_func_t build_func, bool isTempl, Point2d center = Point2d()); + + void calcOrientation(); + void calcScale(double angle); + void calcPosition(double angle, int angleVotes, double scale, int scaleVotes); + + double xi; + int levels; + double angleEpsilon; + + double minAngle; + double maxAngle; + double angleStep; + int angleThresh; + + double minScale; + double maxScale; + double scaleStep; + int scaleThresh; + + double dp; + int posThresh; + + Feature templFeatures; + Feature imageFeatures; + + vector< pair > angles; + vector< pair > scales; + + GpuMat hist; + vector h_buf; + }; + + CV_INIT_ALGORITHM(GHT_Guil_Full, "GeneralizedHough_GPU.POSITION_SCALE_ROTATION", + obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, + "Minimum distance between the centers of the detected objects."); + obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, + "Maximal size of inner buffers."); + obj.info()->addParam(obj, "xi", obj.xi, false, 0, 0, + "Angle difference in degrees between two points in feature."); + obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, + "Feature table levels."); + obj.info()->addParam(obj, "angleEpsilon", obj.angleEpsilon, false, 0, 0, + "Maximal difference between angles that treated as equal."); + obj.info()->addParam(obj, "minAngle", obj.minAngle, false, 0, 0, + "Minimal rotation angle to detect in degrees."); + obj.info()->addParam(obj, "maxAngle", obj.maxAngle, false, 0, 0, + "Maximal rotation angle to detect in degrees."); + obj.info()->addParam(obj, "angleStep", obj.angleStep, false, 0, 0, + "Angle step in degrees."); + obj.info()->addParam(obj, "angleThresh", obj.angleThresh, false, 0, 0, + "Angle threshold."); + obj.info()->addParam(obj, "minScale", obj.minScale, false, 0, 0, + "Minimal scale to detect."); + obj.info()->addParam(obj, "maxScale", obj.maxScale, false, 0, 0, + "Maximal scale to detect."); + obj.info()->addParam(obj, "scaleStep", obj.scaleStep, false, 0, 0, + "Scale step."); + obj.info()->addParam(obj, "scaleThresh", obj.scaleThresh, false, 0, 0, + "Scale threshold."); + obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, + "Inverse ratio of the accumulator resolution to the image resolution."); + obj.info()->addParam(obj, "posThresh", obj.posThresh, false, 0, 0, + "Position threshold.")); + + GHT_Guil_Full::GHT_Guil_Full() + { + maxSize = 1000; + xi = 90.0; + levels = 360; + angleEpsilon = 1.0; + + minAngle = 0.0; + maxAngle = 360.0; + angleStep = 1.0; + angleThresh = 15000; + + minScale = 0.5; + maxScale = 2.0; + scaleStep = 0.05; + scaleThresh = 1000; + + dp = 1.0; + posThresh = 100; + } + + void GHT_Guil_Full::releaseImpl() + { + GHT_Pos::releaseImpl(); + + templFeatures.release(); + imageFeatures.release(); + + releaseVector(angles); + releaseVector(scales); + + hist.release(); + releaseVector(h_buf); + } + + void GHT_Guil_Full::processTempl() + { + using namespace cv::gpu::device::hough; + + buildFeatureList(templEdges, templDx, templDy, templFeatures, + GHT_Guil_Full_setTemplFeatures, GHT_Guil_Full_buildTemplFeatureList_gpu, + true, templCenter); + + h_buf.resize(templFeatures.sizes.cols); + cudaSafeCall( cudaMemcpy(&h_buf[0], templFeatures.sizes.data, h_buf.size() * sizeof(int), cudaMemcpyDeviceToHost) ); + templFeatures.maxSize = *max_element(h_buf.begin(), h_buf.end()); + } + + void GHT_Guil_Full::processImage() + { + using namespace cv::gpu::device::hough; + + CV_Assert(levels > 0); + CV_Assert(templFeatures.sizes.cols == levels + 1); + CV_Assert(minAngle >= 0.0 && minAngle < maxAngle && maxAngle <= 360.0); + CV_Assert(angleStep > 0.0 && angleStep < 360.0); + CV_Assert(angleThresh > 0); + CV_Assert(minScale > 0.0 && minScale < maxScale); + CV_Assert(scaleStep > 0.0); + CV_Assert(scaleThresh > 0); + CV_Assert(dp > 0.0); + CV_Assert(posThresh > 0); + + const double iAngleStep = 1.0 / angleStep; + const int angleRange = cvCeil((maxAngle - minAngle) * iAngleStep); + + const double iScaleStep = 1.0 / scaleStep; + const int scaleRange = cvCeil((maxScale - minScale) * iScaleStep); + + const double idp = 1.0 / dp; + const int histRows = cvCeil(imageSize.height * idp); + const int histCols = cvCeil(imageSize.width * idp); + + ensureSizeIsEnough(histRows + 2, std::max(angleRange + 1, std::max(scaleRange + 1, histCols + 2)), CV_32SC1, hist); + h_buf.resize(std::max(angleRange + 1, scaleRange + 1)); + + ensureSizeIsEnough(2, maxSize, CV_32FC4, outBuf); + + buildFeatureList(imageEdges, imageDx, imageDy, imageFeatures, + GHT_Guil_Full_setImageFeatures, GHT_Guil_Full_buildImageFeatureList_gpu, + false); + + calcOrientation(); + + for (size_t i = 0; i < angles.size(); ++i) + { + const double angle = angles[i].first; + const int angleVotes = angles[i].second; + + calcScale(angle); + + for (size_t j = 0; j < scales.size(); ++j) + { + const double scale = scales[j].first; + const int scaleVotes = scales[j].second; + + calcPosition(angle, angleVotes, scale, scaleVotes); + } + } + } + + void GHT_Guil_Full::Feature::create(int levels, int maxCapacity, bool isTempl) + { + if (!isTempl) + { + ensureSizeIsEnough(levels + 1, maxCapacity, CV_32FC2, p1_pos); + ensureSizeIsEnough(levels + 1, maxCapacity, CV_32FC2, p2_pos); + } + + ensureSizeIsEnough(levels + 1, maxCapacity, CV_32FC1, p1_theta); + + ensureSizeIsEnough(levels + 1, maxCapacity, CV_32FC1, d12); + + if (isTempl) + { + ensureSizeIsEnough(levels + 1, maxCapacity, CV_32FC2, r1); + ensureSizeIsEnough(levels + 1, maxCapacity, CV_32FC2, r2); + } + + ensureSizeIsEnough(1, levels + 1, CV_32SC1, sizes); + sizes.setTo(Scalar::all(0)); + + maxSize = 0; + } + + void GHT_Guil_Full::Feature::release() + { + p1_pos.release(); + p1_theta.release(); + p2_pos.release(); + + d12.release(); + + r1.release(); + r2.release(); + + sizes.release(); + + maxSize = 0; + } + + void GHT_Guil_Full::buildFeatureList(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Feature& features, + set_func_t set_func, build_func_t build_func, bool isTempl, Point2d center) + { + CV_Assert(levels > 0); + + const double maxDist = sqrt((double) templSize.width * templSize.width + templSize.height * templSize.height) * maxScale; + + features.create(levels, maxSize, isTempl); + set_func(features.p1_pos, features.p1_theta, features.p2_pos, features.d12, features.r1, features.r2); + + buildEdgePointList(edges, dx, dy); + + if (edgePointList.cols > 0) + { + build_func(edgePointList.ptr(0), edgePointList.ptr(1), edgePointList.cols, + features.sizes.ptr(), maxSize, xi, angleEpsilon, levels, make_float2(center.x, center.y), maxDist); + } + } + + void GHT_Guil_Full::calcOrientation() + { + using namespace cv::gpu::device::hough; + + const double iAngleStep = 1.0 / angleStep; + const int angleRange = cvCeil((maxAngle - minAngle) * iAngleStep); + + hist.setTo(Scalar::all(0)); + GHT_Guil_Full_calcOHist_gpu(templFeatures.sizes.ptr(), imageFeatures.sizes.ptr(0), + hist.ptr(), minAngle, maxAngle, angleStep, angleRange, levels, templFeatures.maxSize); + cudaSafeCall( cudaMemcpy(&h_buf[0], hist.data, h_buf.size() * sizeof(int), cudaMemcpyDeviceToHost) ); + + angles.clear(); + + for (int n = 0; n < angleRange; ++n) + { + if (h_buf[n] >= angleThresh) + { + const double angle = minAngle + n * angleStep; + angles.push_back(make_pair(angle, h_buf[n])); + } + } + } + + void GHT_Guil_Full::calcScale(double angle) + { + using namespace cv::gpu::device::hough; + + const double iScaleStep = 1.0 / scaleStep; + const int scaleRange = cvCeil((maxScale - minScale) * iScaleStep); + + hist.setTo(Scalar::all(0)); + GHT_Guil_Full_calcSHist_gpu(templFeatures.sizes.ptr(), imageFeatures.sizes.ptr(0), + hist.ptr(), angle, angleEpsilon, minScale, maxScale, iScaleStep, scaleRange, levels, templFeatures.maxSize); + cudaSafeCall( cudaMemcpy(&h_buf[0], hist.data, h_buf.size() * sizeof(int), cudaMemcpyDeviceToHost) ); + + scales.clear(); + + for (int s = 0; s < scaleRange; ++s) + { + if (h_buf[s] >= scaleThresh) + { + const double scale = minScale + s * scaleStep; + scales.push_back(make_pair(scale, h_buf[s])); + } + } + } + + void GHT_Guil_Full::calcPosition(double angle, int angleVotes, double scale, int scaleVotes) + { + using namespace cv::gpu::device::hough; + + hist.setTo(Scalar::all(0)); + GHT_Guil_Full_calcPHist_gpu(templFeatures.sizes.ptr(), imageFeatures.sizes.ptr(0), + hist, angle, angleEpsilon, scale, dp, levels, templFeatures.maxSize); + + posCount = GHT_Guil_Full_findPosInHist_gpu(hist, outBuf.ptr(0), outBuf.ptr(1), + posCount, maxSize, angle, angleVotes, scale, scaleVotes, dp, posThresh); + } +} + +Ptr cv::gpu::GeneralizedHough_GPU::create(int method) +{ + switch (method) + { + case GHT_POSITION: + CV_Assert( !GHT_Ballard_Pos_info_auto.name().empty() ); + return new GHT_Ballard_Pos(); + + case (GHT_POSITION | GHT_SCALE): + CV_Assert( !GHT_Ballard_PosScale_info_auto.name().empty() ); + return new GHT_Ballard_PosScale(); + + case (GHT_POSITION | GHT_ROTATION): + CV_Assert( !GHT_Ballard_PosRotation_info_auto.name().empty() ); + return new GHT_Ballard_PosRotation(); + + case (GHT_POSITION | GHT_SCALE | GHT_ROTATION): + CV_Assert( !GHT_Guil_Full_info_auto.name().empty() ); + return new GHT_Guil_Full(); + } + + CV_Error(CV_StsBadArg, "Unsupported method"); + return Ptr(); +} + +cv::gpu::GeneralizedHough_GPU::~GeneralizedHough_GPU() +{ +} + +void cv::gpu::GeneralizedHough_GPU::setTemplate(const GpuMat& templ, int cannyThreshold, Point templCenter) +{ + CV_Assert(templ.type() == CV_8UC1); + CV_Assert(cannyThreshold > 0); + + ensureSizeIsEnough(templ.size(), CV_8UC1, edges_); + Canny(templ, cannyBuf_, edges_, cannyThreshold / 2, cannyThreshold); + + if (templCenter == Point(-1, -1)) + templCenter = Point(templ.cols / 2, templ.rows / 2); + + setTemplateImpl(edges_, cannyBuf_.dx, cannyBuf_.dy, templCenter); +} + +void cv::gpu::GeneralizedHough_GPU::setTemplate(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, Point templCenter) +{ + if (templCenter == Point(-1, -1)) + templCenter = Point(edges.cols / 2, edges.rows / 2); + + setTemplateImpl(edges, dx, dy, templCenter); +} + +void cv::gpu::GeneralizedHough_GPU::detect(const GpuMat& image, GpuMat& positions, int cannyThreshold) +{ + CV_Assert(image.type() == CV_8UC1); + CV_Assert(cannyThreshold > 0); + + ensureSizeIsEnough(image.size(), CV_8UC1, edges_); + Canny(image, cannyBuf_, edges_, cannyThreshold / 2, cannyThreshold); + + detectImpl(edges_, cannyBuf_.dx, cannyBuf_.dy, positions); +} + +void cv::gpu::GeneralizedHough_GPU::detect(const GpuMat& edges, const GpuMat& dx, const GpuMat& dy, GpuMat& positions) +{ + detectImpl(edges, dx, dy, positions); +} + +void cv::gpu::GeneralizedHough_GPU::download(const GpuMat& d_positions, OutputArray h_positions_, OutputArray h_votes_) +{ + if (d_positions.empty()) + { + h_positions_.release(); + if (h_votes_.needed()) + h_votes_.release(); + return; + } + + CV_Assert(d_positions.rows == 2 && d_positions.type() == CV_32FC4); + + h_positions_.create(1, d_positions.cols, CV_32FC4); + Mat h_positions = h_positions_.getMat(); + d_positions.row(0).download(h_positions); + + if (h_votes_.needed()) + { + h_votes_.create(1, d_positions.cols, CV_32SC3); + Mat h_votes = h_votes_.getMat(); + GpuMat d_votes(1, d_positions.cols, CV_32SC3, const_cast(d_positions.ptr(1))); + d_votes.download(h_votes); + } +} + +void cv::gpu::GeneralizedHough_GPU::release() +{ + edges_.release(); + cannyBuf_.release(); + releaseImpl(); +} + #endif /* !defined (HAVE_CUDA) */ diff --git a/modules/gpu/test/test_hough.cpp b/modules/gpu/test/test_hough.cpp new file mode 100644 index 0000000000..e6cb4fa852 --- /dev/null +++ b/modules/gpu/test/test_hough.cpp @@ -0,0 +1,256 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA + +namespace { + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// HoughLines + +PARAM_TEST_CASE(HoughLines, cv::gpu::DeviceInfo, cv::Size, UseRoi) +{ + static void generateLines(cv::Mat& img) + { + img.setTo(cv::Scalar::all(0)); + + cv::line(img, cv::Point(20, 0), cv::Point(20, img.rows), cv::Scalar::all(255)); + cv::line(img, cv::Point(0, 50), cv::Point(img.cols, 50), cv::Scalar::all(255)); + cv::line(img, cv::Point(0, 0), cv::Point(img.cols, img.rows), cv::Scalar::all(255)); + cv::line(img, cv::Point(img.cols, 0), cv::Point(0, img.rows), cv::Scalar::all(255)); + } + + static void drawLines(cv::Mat& dst, const std::vector& lines) + { + dst.setTo(cv::Scalar::all(0)); + + for (size_t i = 0; i < lines.size(); ++i) + { + float rho = lines[i][0], theta = lines[i][1]; + cv::Point pt1, pt2; + double a = std::cos(theta), b = std::sin(theta); + double x0 = a*rho, y0 = b*rho; + pt1.x = cvRound(x0 + 1000*(-b)); + pt1.y = cvRound(y0 + 1000*(a)); + pt2.x = cvRound(x0 - 1000*(-b)); + pt2.y = cvRound(y0 - 1000*(a)); + cv::line(dst, pt1, pt2, cv::Scalar::all(255)); + } + } +}; + +TEST_P(HoughLines, Accuracy) +{ + const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); + cv::gpu::setDevice(devInfo.deviceID()); + const cv::Size size = GET_PARAM(1); + const bool useRoi = GET_PARAM(2); + + const float rho = 1.0f; + const float theta = 1.5f * CV_PI / 180.0f; + const int threshold = 100; + + cv::Mat src(size, CV_8UC1); + generateLines(src); + + cv::gpu::GpuMat d_lines; + cv::gpu::HoughLines(loadMat(src, useRoi), d_lines, rho, theta, threshold); + + std::vector lines; + cv::gpu::HoughLinesDownload(d_lines, lines); + + cv::Mat dst(size, CV_8UC1); + drawLines(dst, lines); + + ASSERT_MAT_NEAR(src, dst, 0.0); +} + +INSTANTIATE_TEST_CASE_P(GPU_ImgProc, HoughLines, testing::Combine( + ALL_DEVICES, + DIFFERENT_SIZES, + WHOLE_SUBMAT)); + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// HoughCircles + +PARAM_TEST_CASE(HoughCircles, cv::gpu::DeviceInfo, cv::Size, UseRoi) +{ + static void drawCircles(cv::Mat& dst, const std::vector& circles, bool fill) + { + dst.setTo(cv::Scalar::all(0)); + + for (size_t i = 0; i < circles.size(); ++i) + cv::circle(dst, cv::Point2f(circles[i][0], circles[i][1]), (int)circles[i][2], cv::Scalar::all(255), fill ? -1 : 1); + } +}; + +TEST_P(HoughCircles, Accuracy) +{ + const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); + cv::gpu::setDevice(devInfo.deviceID()); + const cv::Size size = GET_PARAM(1); + const bool useRoi = GET_PARAM(2); + + const float dp = 2.0f; + const float minDist = 10.0f; + const int minRadius = 10; + const int maxRadius = 20; + const int cannyThreshold = 100; + const int votesThreshold = 20; + + std::vector circles_gold(4); + circles_gold[0] = cv::Vec3i(20, 20, minRadius); + circles_gold[1] = cv::Vec3i(90, 87, minRadius + 3); + circles_gold[2] = cv::Vec3i(30, 70, minRadius + 8); + circles_gold[3] = cv::Vec3i(80, 10, maxRadius); + + cv::Mat src(size, CV_8UC1); + drawCircles(src, circles_gold, true); + + cv::gpu::GpuMat d_circles; + cv::gpu::HoughCircles(loadMat(src, useRoi), d_circles, CV_HOUGH_GRADIENT, dp, minDist, cannyThreshold, votesThreshold, minRadius, maxRadius); + + std::vector circles; + cv::gpu::HoughCirclesDownload(d_circles, circles); + + ASSERT_FALSE(circles.empty()); + + for (size_t i = 0; i < circles.size(); ++i) + { + cv::Vec3f cur = circles[i]; + + bool found = false; + + for (size_t j = 0; j < circles_gold.size(); ++j) + { + cv::Vec3f gold = circles_gold[j]; + + if (std::fabs(cur[0] - gold[0]) < minDist && std::fabs(cur[1] - gold[1]) < minDist && std::fabs(cur[2] - gold[2]) < minDist) + { + found = true; + break; + } + } + + ASSERT_TRUE(found); + } +} + +INSTANTIATE_TEST_CASE_P(GPU_ImgProc, HoughCircles, testing::Combine( + ALL_DEVICES, + DIFFERENT_SIZES, + WHOLE_SUBMAT)); + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// GeneralizedHough + +PARAM_TEST_CASE(GeneralizedHough, cv::gpu::DeviceInfo, UseRoi) +{ +}; + +TEST_P(GeneralizedHough, POSITION) +{ + const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); + cv::gpu::setDevice(devInfo.deviceID()); + const bool useRoi = GET_PARAM(1); + + cv::Mat templ = readImage("../cv/shared/templ.png", cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(templ.empty()); + + cv::Point templCenter(templ.cols / 2, templ.rows / 2); + + const size_t gold_count = 3; + cv::Point pos_gold[gold_count]; + pos_gold[0] = cv::Point(templCenter.x + 10, templCenter.y + 10); + pos_gold[1] = cv::Point(2 * templCenter.x + 40, templCenter.y + 10); + pos_gold[2] = cv::Point(2 * templCenter.x + 40, 2 * templCenter.y + 40); + + cv::Mat image(templ.rows * 3, templ.cols * 3, CV_8UC1, cv::Scalar::all(0)); + for (size_t i = 0; i < gold_count; ++i) + { + cv::Rect rec(pos_gold[i].x - templCenter.x, pos_gold[i].y - templCenter.y, templ.cols, templ.rows); + cv::Mat imageROI = image(rec); + templ.copyTo(imageROI); + } + + cv::Ptr hough = cv::gpu::GeneralizedHough_GPU::create(cv::GHT_POSITION); + hough->set("votesThreshold", 200); + + hough->setTemplate(loadMat(templ, useRoi)); + + cv::gpu::GpuMat d_pos; + hough->detect(loadMat(image, useRoi), d_pos); + + std::vector pos; + hough->download(d_pos, pos); + + ASSERT_EQ(gold_count, pos.size()); + + for (size_t i = 0; i < gold_count; ++i) + { + cv::Point gold = pos_gold[i]; + + bool found = false; + + for (size_t j = 0; j < pos.size(); ++j) + { + cv::Point2f p(pos[j][0], pos[j][1]); + + if (::fabs(p.x - gold.x) < 2 && ::fabs(p.y - gold.y) < 2) + { + found = true; + break; + } + } + + ASSERT_TRUE(found); + } +} + +INSTANTIATE_TEST_CASE_P(GPU_ImgProc, GeneralizedHough, testing::Combine( + ALL_DEVICES, + WHOLE_SUBMAT)); + +} // namespace + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_imgproc.cpp b/modules/gpu/test/test_imgproc.cpp index b723ded22e..13c8a1ca8e 100644 --- a/modules/gpu/test/test_imgproc.cpp +++ b/modules/gpu/test/test_imgproc.cpp @@ -1126,142 +1126,6 @@ INSTANTIATE_TEST_CASE_P(GPU_ImgProc, CornerMinEigen, testing::Combine( testing::Values(BlockSize(3), BlockSize(5), BlockSize(7)), testing::Values(ApertureSize(0), ApertureSize(3), ApertureSize(5), ApertureSize(7)))); -/////////////////////////////////////////////////////////////////////////////////////////////////////// -// HoughLines - -PARAM_TEST_CASE(HoughLines, cv::gpu::DeviceInfo, cv::Size, UseRoi) -{ - static void generateLines(cv::Mat& img) - { - img.setTo(cv::Scalar::all(0)); - - cv::line(img, cv::Point(20, 0), cv::Point(20, img.rows), cv::Scalar::all(255)); - cv::line(img, cv::Point(0, 50), cv::Point(img.cols, 50), cv::Scalar::all(255)); - cv::line(img, cv::Point(0, 0), cv::Point(img.cols, img.rows), cv::Scalar::all(255)); - cv::line(img, cv::Point(img.cols, 0), cv::Point(0, img.rows), cv::Scalar::all(255)); - } - - static void drawLines(cv::Mat& dst, const std::vector& lines) - { - dst.setTo(cv::Scalar::all(0)); - - for (size_t i = 0; i < lines.size(); ++i) - { - float rho = lines[i][0], theta = lines[i][1]; - cv::Point pt1, pt2; - double a = std::cos(theta), b = std::sin(theta); - double x0 = a*rho, y0 = b*rho; - pt1.x = cvRound(x0 + 1000*(-b)); - pt1.y = cvRound(y0 + 1000*(a)); - pt2.x = cvRound(x0 - 1000*(-b)); - pt2.y = cvRound(y0 - 1000*(a)); - cv::line(dst, pt1, pt2, cv::Scalar::all(255)); - } - } -}; - -TEST_P(HoughLines, Accuracy) -{ - const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - const cv::Size size = GET_PARAM(1); - const bool useRoi = GET_PARAM(2); - - const float rho = 1.0f; - const float theta = 1.5f * CV_PI / 180.0f; - const int threshold = 100; - - cv::Mat src(size, CV_8UC1); - generateLines(src); - - cv::gpu::GpuMat d_lines; - cv::gpu::HoughLines(loadMat(src, useRoi), d_lines, rho, theta, threshold); - - std::vector lines; - cv::gpu::HoughLinesDownload(d_lines, lines); - - cv::Mat dst(size, CV_8UC1); - drawLines(dst, lines); - - ASSERT_MAT_NEAR(src, dst, 0.0); -} - -INSTANTIATE_TEST_CASE_P(GPU_ImgProc, HoughLines, testing::Combine( - ALL_DEVICES, - DIFFERENT_SIZES, - WHOLE_SUBMAT)); - -/////////////////////////////////////////////////////////////////////////////////////////////////////// -// HoughCircles - -PARAM_TEST_CASE(HoughCircles, cv::gpu::DeviceInfo, cv::Size, UseRoi) -{ - static void drawCircles(cv::Mat& dst, const std::vector& circles, bool fill) - { - dst.setTo(cv::Scalar::all(0)); - - for (size_t i = 0; i < circles.size(); ++i) - cv::circle(dst, cv::Point2f(circles[i][0], circles[i][1]), (int)circles[i][2], cv::Scalar::all(255), fill ? -1 : 1); - } -}; - -TEST_P(HoughCircles, Accuracy) -{ - const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - const cv::Size size = GET_PARAM(1); - const bool useRoi = GET_PARAM(2); - - const float dp = 2.0f; - const float minDist = 10.0f; - const int minRadius = 10; - const int maxRadius = 20; - const int cannyThreshold = 100; - const int votesThreshold = 20; - - std::vector circles_gold(4); - circles_gold[0] = cv::Vec3i(20, 20, minRadius); - circles_gold[1] = cv::Vec3i(90, 87, minRadius + 3); - circles_gold[2] = cv::Vec3i(30, 70, minRadius + 8); - circles_gold[3] = cv::Vec3i(80, 10, maxRadius); - - cv::Mat src(size, CV_8UC1); - drawCircles(src, circles_gold, true); - - cv::gpu::GpuMat d_circles; - cv::gpu::HoughCircles(loadMat(src, useRoi), d_circles, CV_HOUGH_GRADIENT, dp, minDist, cannyThreshold, votesThreshold, minRadius, maxRadius); - - std::vector circles; - cv::gpu::HoughCirclesDownload(d_circles, circles); - - ASSERT_FALSE(circles.empty()); - - for (size_t i = 0; i < circles.size(); ++i) - { - cv::Vec3f cur = circles[i]; - - bool found = false; - - for (size_t j = 0; j < circles_gold.size(); ++j) - { - cv::Vec3f gold = circles_gold[j]; - - if (std::fabs(cur[0] - gold[0]) < minDist && std::fabs(cur[1] - gold[1]) < minDist && std::fabs(cur[2] - gold[2]) < minDist) - { - found = true; - break; - } - } - - ASSERT_TRUE(found); - } -} - -INSTANTIATE_TEST_CASE_P(GPU_ImgProc, HoughCircles, testing::Combine( - ALL_DEVICES, - DIFFERENT_SIZES, - WHOLE_SUBMAT)); - } // namespace #endif // HAVE_CUDA diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index d0031bf5d2..63f5218140 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -489,6 +489,42 @@ CV_EXPORTS_W void HoughCircles( InputArray image, OutputArray circles, double param1=100, double param2=100, int minRadius=0, int maxRadius=0 ); +enum +{ + GHT_POSITION = 0, + GHT_SCALE = 1, + GHT_ROTATION = 2 +}; + +//! finds arbitrary template in the grayscale image using Generalized Hough Transform +//! Ballard, D.H. (1981). Generalizing the Hough transform to detect arbitrary shapes. Pattern Recognition 13 (2): 111-122. +//! Guil, N., González-Linares, J.M. and Zapata, E.L. (1999). Bidimensional shape detection using an invariant approach. Pattern Recognition 32 (6): 1025-1038. +class CV_EXPORTS GeneralizedHough : public Algorithm +{ +public: + static Ptr create(int method); + + virtual ~GeneralizedHough(); + + //! set template to search + void setTemplate(InputArray templ, int cannyThreshold = 100, Point templCenter = Point(-1, -1)); + void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter = Point(-1, -1)); + + //! find template on image + void detect(InputArray image, OutputArray positions, OutputArray votes = cv::noArray(), int cannyThreshold = 100); + void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes = cv::noArray()); + + void release(); + +protected: + virtual void setTemplateImpl(const Mat& edges, const Mat& dx, const Mat& dy, Point templCenter) = 0; + virtual void detectImpl(const Mat& edges, const Mat& dx, const Mat& dy, OutputArray positions, OutputArray votes) = 0; + virtual void releaseImpl() = 0; + +private: + Mat edges_, dx_, dy_; +}; + //! erodes the image (applies the local minimum operator) CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1,-1), int iterations=1, diff --git a/modules/imgproc/src/generalized_hough.cpp b/modules/imgproc/src/generalized_hough.cpp new file mode 100644 index 0000000000..4895b55e9d --- /dev/null +++ b/modules/imgproc/src/generalized_hough.cpp @@ -0,0 +1,1293 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +using namespace std; +using namespace cv; + +namespace +{ + ///////////////////////////////////// + // Common + + template void releaseVector(vector& v) + { + vector empty; + empty.swap(v); + } + + double toRad(double a) + { + return a * CV_PI / 180.0; + } + + bool notNull(float v) + { + return fabs(v) > numeric_limits::epsilon(); + } + bool notNull(double v) + { + return fabs(v) > numeric_limits::epsilon(); + } + + class GHT_Pos : public GeneralizedHough + { + public: + GHT_Pos(); + + protected: + void setTemplateImpl(const Mat& edges, const Mat& dx, const Mat& dy, Point templCenter); + void detectImpl(const Mat& edges, const Mat& dx, const Mat& dy, OutputArray positions, OutputArray votes); + void releaseImpl(); + + virtual void processTempl() = 0; + virtual void processImage() = 0; + + void filterMinDist(); + void convertTo(OutputArray positions, OutputArray votes); + + double minDist; + + Size templSize; + Point templCenter; + Mat templEdges; + Mat templDx; + Mat templDy; + + Size imageSize; + Mat imageEdges; + Mat imageDx; + Mat imageDy; + + vector posOutBuf; + vector voteOutBuf; + }; + + GHT_Pos::GHT_Pos() + { + minDist = 1.0; + } + + void GHT_Pos::setTemplateImpl(const Mat& edges, const Mat& dx, const Mat& dy, Point templCenter_) + { + templSize = edges.size(); + templCenter = templCenter_; + edges.copyTo(templEdges); + dx.copyTo(templDx); + dy.copyTo(templDy); + + processTempl(); + } + + void GHT_Pos::detectImpl(const Mat& edges, const Mat& dx, const Mat& dy, OutputArray positions, OutputArray votes) + { + imageSize = edges.size(); + edges.copyTo(imageEdges); + dx.copyTo(imageDx); + dy.copyTo(imageDy); + + posOutBuf.clear(); + voteOutBuf.clear(); + + processImage(); + + if (!posOutBuf.empty()) + { + if (minDist > 1) + filterMinDist(); + convertTo(positions, votes); + } + else + { + positions.release(); + if (votes.needed()) + votes.release(); + } + } + + void GHT_Pos::releaseImpl() + { + templSize = Size(); + templCenter = Point(-1, -1); + templEdges.release(); + templDx.release(); + templDy.release(); + + imageSize = Size(); + imageEdges.release(); + imageDx.release(); + imageDy.release(); + + releaseVector(posOutBuf); + releaseVector(voteOutBuf); + } + + #define votes_cmp_gt(l1, l2) (aux[l1][0] > aux[l2][0]) + static CV_IMPLEMENT_QSORT_EX( sortIndexies, size_t, votes_cmp_gt, const Vec3i* ) + + void GHT_Pos::filterMinDist() + { + size_t oldSize = posOutBuf.size(); + const bool hasVotes = !voteOutBuf.empty(); + + CV_Assert(!hasVotes || voteOutBuf.size() == oldSize); + + vector oldPosBuf(posOutBuf); + vector oldVoteBuf(voteOutBuf); + + vector indexies(oldSize); + for (size_t i = 0; i < oldSize; ++i) + indexies[i] = i; + sortIndexies(&indexies[0], oldSize, &oldVoteBuf[0]); + + posOutBuf.clear(); + voteOutBuf.clear(); + + const int cellSize = cvRound(minDist); + const int gridWidth = (imageSize.width + cellSize - 1) / cellSize; + const int gridHeight = (imageSize.height + cellSize - 1) / cellSize; + + vector< vector > grid(gridWidth * gridHeight); + + const double minDist2 = minDist * minDist; + + for (size_t i = 0; i < oldSize; ++i) + { + const size_t ind = indexies[i]; + + Point2f p(oldPosBuf[ind][0], oldPosBuf[ind][1]); + + bool good = true; + + const int xCell = static_cast(p.x / cellSize); + const int yCell = static_cast(p.y / cellSize); + + int x1 = xCell - 1; + int y1 = yCell - 1; + int x2 = xCell + 1; + int y2 = yCell + 1; + + // boundary check + x1 = std::max(0, x1); + y1 = std::max(0, y1); + x2 = std::min(gridWidth - 1, x2); + y2 = std::min(gridHeight - 1, y2); + + for (int yy = y1; yy <= y2; ++yy) + { + for (int xx = x1; xx <= x2; ++xx) + { + const vector& m = grid[yy * gridWidth + xx]; + + for(size_t j = 0; j < m.size(); ++j) + { + const Point2f d = p - m[j]; + + if (d.ddot(d) < minDist2) + { + good = false; + goto break_out; + } + } + } + } + + break_out: + + if(good) + { + grid[yCell * gridWidth + xCell].push_back(p); + + posOutBuf.push_back(oldPosBuf[ind]); + if (hasVotes) + voteOutBuf.push_back(oldVoteBuf[ind]); + } + } + } + + void GHT_Pos::convertTo(OutputArray _positions, OutputArray _votes) + { + const int total = static_cast(posOutBuf.size()); + const bool hasVotes = !voteOutBuf.empty(); + + CV_Assert(!hasVotes || voteOutBuf.size() == posOutBuf.size()); + + _positions.create(1, total, CV_32FC4); + Mat positions = _positions.getMat(); + Mat(1, total, CV_32FC4, &posOutBuf[0]).copyTo(positions); + + if (_votes.needed()) + { + if (!hasVotes) + _votes.release(); + else + { + _votes.create(1, total, CV_32SC3); + Mat votes = _votes.getMat(); + Mat(1, total, CV_32SC3, &voteOutBuf[0]).copyTo(votes); + } + } + } + + ///////////////////////////////////// + // POSITION Ballard + + class GHT_Ballard_Pos : public GHT_Pos + { + public: + AlgorithmInfo* info() const; + + GHT_Ballard_Pos(); + + protected: + void releaseImpl(); + + void processTempl(); + void processImage(); + + virtual void calcHist(); + virtual void findPosInHist(); + + int levels; + int votesThreshold; + double dp; + + vector< vector > r_table; + Mat hist; + }; + + CV_INIT_ALGORITHM(GHT_Ballard_Pos, "GeneralizedHough.POSITION", + obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, + "Minimum distance between the centers of the detected objects."); + obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, + "R-Table levels."); + obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, + "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); + obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, + "Inverse ratio of the accumulator resolution to the image resolution.")); + + GHT_Ballard_Pos::GHT_Ballard_Pos() + { + levels = 360; + votesThreshold = 100; + dp = 1.0; + } + + void GHT_Ballard_Pos::releaseImpl() + { + GHT_Pos::releaseImpl(); + + releaseVector(r_table); + hist.release(); + } + + void GHT_Ballard_Pos::processTempl() + { + CV_Assert(templEdges.type() == CV_8UC1); + CV_Assert(templDx.type() == CV_32FC1 && templDx.size() == templSize); + CV_Assert(templDy.type() == templDx.type() && templDy.size() == templSize); + CV_Assert(levels > 0); + + const double thetaScale = levels / 360.0; + + r_table.resize(levels + 1); + for_each(r_table.begin(), r_table.end(), mem_fun_ref(&vector::clear)); + + for (int y = 0; y < templSize.height; ++y) + { + const uchar* edgesRow = templEdges.ptr(y); + const float* dxRow = templDx.ptr(y); + const float* dyRow = templDy.ptr(y); + + for (int x = 0; x < templSize.width; ++x) + { + const Point p(x, y); + + if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) + { + const float theta = fastAtan2(dyRow[x], dxRow[x]); + const int n = cvRound(theta * thetaScale); + r_table[n].push_back(p - templCenter); + } + } + } + } + + void GHT_Ballard_Pos::processImage() + { + calcHist(); + findPosInHist(); + } + + void GHT_Ballard_Pos::calcHist() + { + CV_Assert(imageEdges.type() == CV_8UC1); + CV_Assert(imageDx.type() == CV_32FC1 && imageDx.size() == imageSize); + CV_Assert(imageDy.type() == imageDx.type() && imageDy.size() == imageSize); + CV_Assert(levels > 0 && r_table.size() == static_cast(levels + 1)); + CV_Assert(dp > 0.0); + + const double thetaScale = levels / 360.0; + const double idp = 1.0 / dp; + + hist.create(cvCeil(imageSize.height * idp) + 2, cvCeil(imageSize.width * idp) + 2, CV_32SC1); + hist.setTo(0); + + const int rows = hist.rows - 2; + const int cols = hist.cols - 2; + + for (int y = 0; y < imageSize.height; ++y) + { + const uchar* edgesRow = imageEdges.ptr(y); + const float* dxRow = imageDx.ptr(y); + const float* dyRow = imageDy.ptr(y); + + for (int x = 0; x < imageSize.width; ++x) + { + const Point p(x, y); + + if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) + { + const float theta = fastAtan2(dyRow[x], dxRow[x]); + const int n = cvRound(theta * thetaScale); + + const vector& r_row = r_table[n]; + + for (size_t j = 0; j < r_row.size(); ++j) + { + Point c = p - r_row[j]; + + c.x = cvRound(c.x * idp); + c.y = cvRound(c.y * idp); + + if (c.x >= 0 && c.x < cols && c.y >= 0 && c.y < rows) + ++hist.at(c.y + 1, c.x + 1); + } + } + } + } + } + + void GHT_Ballard_Pos::findPosInHist() + { + CV_Assert(votesThreshold > 0); + + const int histRows = hist.rows - 2; + const int histCols = hist.cols - 2; + + for(int y = 0; y < histRows; ++y) + { + const int* prevRow = hist.ptr(y); + const int* curRow = hist.ptr(y + 1); + const int* nextRow = hist.ptr(y + 2); + + for(int x = 0; x < histCols; ++x) + { + const int votes = curRow[x + 1]; + + if (votes > votesThreshold && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1]) + { + posOutBuf.push_back(Vec4f(static_cast(x * dp), static_cast(y * dp), 1.0f, 0.0f)); + voteOutBuf.push_back(Vec3i(votes, 0, 0)); + } + } + } + } + + ///////////////////////////////////// + // POSITION & SCALE + + class GHT_Ballard_PosScale : public GHT_Ballard_Pos + { + public: + AlgorithmInfo* info() const; + + GHT_Ballard_PosScale(); + + protected: + void calcHist(); + void findPosInHist(); + + double minScale; + double maxScale; + double scaleStep; + + class Worker; + friend class Worker; + }; + + CV_INIT_ALGORITHM(GHT_Ballard_PosScale, "GeneralizedHough.POSITION_SCALE", + obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, + "Minimum distance between the centers of the detected objects."); + obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, + "R-Table levels."); + obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, + "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); + obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, + "Inverse ratio of the accumulator resolution to the image resolution."); + obj.info()->addParam(obj, "minScale", obj.minScale, false, 0, 0, + "Minimal scale to detect."); + obj.info()->addParam(obj, "maxScale", obj.maxScale, false, 0, 0, + "Maximal scale to detect."); + obj.info()->addParam(obj, "scaleStep", obj.scaleStep, false, 0, 0, + "Scale step.")); + + GHT_Ballard_PosScale::GHT_Ballard_PosScale() + { + minScale = 0.5; + maxScale = 2.0; + scaleStep = 0.05; + } + + class GHT_Ballard_PosScale::Worker : public ParallelLoopBody + { + public: + explicit Worker(GHT_Ballard_PosScale* base_) : base(base_) {} + + void operator ()(const Range& range) const; + + private: + GHT_Ballard_PosScale* base; + }; + + void GHT_Ballard_PosScale::Worker::operator ()(const Range& range) const + { + const double thetaScale = base->levels / 360.0; + const double idp = 1.0 / base->dp; + + for (int s = range.start; s < range.end; ++s) + { + const double scale = base->minScale + s * base->scaleStep; + + Mat curHist(base->hist.size[1], base->hist.size[2], CV_32SC1, base->hist.ptr(s + 1), base->hist.step[1]); + + for (int y = 0; y < base->imageSize.height; ++y) + { + const uchar* edgesRow = base->imageEdges.ptr(y); + const float* dxRow = base->imageDx.ptr(y); + const float* dyRow = base->imageDy.ptr(y); + + for (int x = 0; x < base->imageSize.width; ++x) + { + const Point2d p(x, y); + + if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) + { + const float theta = fastAtan2(dyRow[x], dxRow[x]); + const int n = cvRound(theta * thetaScale); + + const vector& r_row = base->r_table[n]; + + for (size_t j = 0; j < r_row.size(); ++j) + { + Point2d d = r_row[j]; + Point2d c = p - d * scale; + + c.x *= idp; + c.y *= idp; + + if (c.x >= 0 && c.x < base->hist.size[2] - 2 && c.y >= 0 && c.y < base->hist.size[1] - 2) + ++curHist.at(cvRound(c.y + 1), cvRound(c.x + 1)); + } + } + } + } + } + } + + void GHT_Ballard_PosScale::calcHist() + { + CV_Assert(imageEdges.type() == CV_8UC1); + CV_Assert(imageDx.type() == CV_32FC1 && imageDx.size() == imageSize); + CV_Assert(imageDy.type() == imageDx.type() && imageDy.size() == imageSize); + CV_Assert(levels > 0 && r_table.size() == static_cast(levels + 1)); + CV_Assert(dp > 0.0); + CV_Assert(minScale > 0.0 && minScale < maxScale); + CV_Assert(scaleStep > 0.0); + + const double idp = 1.0 / dp; + const int scaleRange = cvCeil((maxScale - minScale) / scaleStep); + + const int sizes[] = {scaleRange + 2, cvCeil(imageSize.height * idp) + 2, cvCeil(imageSize.width * idp) + 2}; + hist.create(3, sizes, CV_32SC1); + hist.setTo(0); + + parallel_for_(Range(0, scaleRange), Worker(this)); + } + + void GHT_Ballard_PosScale::findPosInHist() + { + CV_Assert(votesThreshold > 0); + + const int scaleRange = hist.size[0] - 2; + const int histRows = hist.size[1] - 2; + const int histCols = hist.size[2] - 2; + + for (int s = 0; s < scaleRange; ++s) + { + const float scale = static_cast(minScale + s * scaleStep); + + const Mat prevHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(s), hist.step[1]); + const Mat curHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(s + 1), hist.step[1]); + const Mat nextHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(s + 2), hist.step[1]); + + for(int y = 0; y < histRows; ++y) + { + const int* prevHistRow = prevHist.ptr(y + 1); + const int* prevRow = curHist.ptr(y); + const int* curRow = curHist.ptr(y + 1); + const int* nextRow = curHist.ptr(y + 2); + const int* nextHistRow = nextHist.ptr(y + 1); + + for(int x = 0; x < histCols; ++x) + { + const int votes = curRow[x + 1]; + + if (votes > votesThreshold && + votes > curRow[x] && + votes >= curRow[x + 2] && + votes > prevRow[x + 1] && + votes >= nextRow[x + 1] && + votes > prevHistRow[x + 1] && + votes >= nextHistRow[x + 1]) + { + posOutBuf.push_back(Vec4f(static_cast(x * dp), static_cast(y * dp), scale, 0.0f)); + voteOutBuf.push_back(Vec3i(votes, votes, 0)); + } + } + } + } + } + + ///////////////////////////////////// + // POSITION & ROTATION + + class GHT_Ballard_PosRotation : public GHT_Ballard_Pos + { + public: + AlgorithmInfo* info() const; + + GHT_Ballard_PosRotation(); + + protected: + void calcHist(); + void findPosInHist(); + + double minAngle; + double maxAngle; + double angleStep; + + class Worker; + friend class Worker; + }; + + CV_INIT_ALGORITHM(GHT_Ballard_PosRotation, "GeneralizedHough.POSITION_ROTATION", + obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, + "Minimum distance between the centers of the detected objects."); + obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, + "R-Table levels."); + obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, + "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); + obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, + "Inverse ratio of the accumulator resolution to the image resolution."); + obj.info()->addParam(obj, "minAngle", obj.minAngle, false, 0, 0, + "Minimal rotation angle to detect in degrees."); + obj.info()->addParam(obj, "maxAngle", obj.maxAngle, false, 0, 0, + "Maximal rotation angle to detect in degrees."); + obj.info()->addParam(obj, "angleStep", obj.angleStep, false, 0, 0, + "Angle step in degrees.")); + + GHT_Ballard_PosRotation::GHT_Ballard_PosRotation() + { + minAngle = 0.0; + maxAngle = 360.0; + angleStep = 1.0; + } + + class GHT_Ballard_PosRotation::Worker : public ParallelLoopBody + { + public: + explicit Worker(GHT_Ballard_PosRotation* base_) : base(base_) {} + + void operator ()(const Range& range) const; + + private: + GHT_Ballard_PosRotation* base; + }; + + void GHT_Ballard_PosRotation::Worker::operator ()(const Range& range) const + { + const double thetaScale = base->levels / 360.0; + const double idp = 1.0 / base->dp; + + for (int a = range.start; a < range.end; ++a) + { + const double angle = base->minAngle + a * base->angleStep; + + const double sinA = ::sin(toRad(angle)); + const double cosA = ::cos(toRad(angle)); + + Mat curHist(base->hist.size[1], base->hist.size[2], CV_32SC1, base->hist.ptr(a + 1), base->hist.step[1]); + + for (int y = 0; y < base->imageSize.height; ++y) + { + const uchar* edgesRow = base->imageEdges.ptr(y); + const float* dxRow = base->imageDx.ptr(y); + const float* dyRow = base->imageDy.ptr(y); + + for (int x = 0; x < base->imageSize.width; ++x) + { + const Point2d p(x, y); + + if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) + { + double theta = fastAtan2(dyRow[x], dxRow[x]) - angle; + if (theta < 0) + theta += 360.0; + const int n = cvRound(theta * thetaScale); + + const vector& r_row = base->r_table[n]; + + for (size_t j = 0; j < r_row.size(); ++j) + { + Point2d d = r_row[j]; + Point2d c = p - Point2d(d.x * cosA - d.y * sinA, d.x * sinA + d.y * cosA); + + c.x *= idp; + c.y *= idp; + + if (c.x >= 0 && c.x < base->hist.size[2] - 2 && c.y >= 0 && c.y < base->hist.size[1] - 2) + ++curHist.at(cvRound(c.y + 1), cvRound(c.x + 1)); + } + } + } + } + } + } + + void GHT_Ballard_PosRotation::calcHist() + { + CV_Assert(imageEdges.type() == CV_8UC1); + CV_Assert(imageDx.type() == CV_32FC1 && imageDx.size() == imageSize); + CV_Assert(imageDy.type() == imageDx.type() && imageDy.size() == imageSize); + CV_Assert(levels > 0 && r_table.size() == static_cast(levels + 1)); + CV_Assert(dp > 0.0); + CV_Assert(minAngle >= 0.0 && minAngle < maxAngle && maxAngle <= 360.0); + CV_Assert(angleStep > 0.0 && angleStep < 360.0); + + const double idp = 1.0 / dp; + const int angleRange = cvCeil((maxAngle - minAngle) / angleStep); + + const int sizes[] = {angleRange + 2, cvCeil(imageSize.height * idp) + 2, cvCeil(imageSize.width * idp) + 2}; + hist.create(3, sizes, CV_32SC1); + hist.setTo(0); + + parallel_for_(Range(0, angleRange), Worker(this)); + } + + void GHT_Ballard_PosRotation::findPosInHist() + { + CV_Assert(votesThreshold > 0); + + const int angleRange = hist.size[0] - 2; + const int histRows = hist.size[1] - 2; + const int histCols = hist.size[2] - 2; + + for (int a = 0; a < angleRange; ++a) + { + const float angle = static_cast(minAngle + a * angleStep); + + const Mat prevHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(a), hist.step[1]); + const Mat curHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(a + 1), hist.step[1]); + const Mat nextHist(histRows + 2, histCols + 2, CV_32SC1, hist.ptr(a + 2), hist.step[1]); + + for(int y = 0; y < histRows; ++y) + { + const int* prevHistRow = prevHist.ptr(y + 1); + const int* prevRow = curHist.ptr(y); + const int* curRow = curHist.ptr(y + 1); + const int* nextRow = curHist.ptr(y + 2); + const int* nextHistRow = nextHist.ptr(y + 1); + + for(int x = 0; x < histCols; ++x) + { + const int votes = curRow[x + 1]; + + if (votes > votesThreshold && + votes > curRow[x] && + votes >= curRow[x + 2] && + votes > prevRow[x + 1] && + votes >= nextRow[x + 1] && + votes > prevHistRow[x + 1] && + votes >= nextHistRow[x + 1]) + { + posOutBuf.push_back(Vec4f(static_cast(x * dp), static_cast(y * dp), 1.0f, angle)); + voteOutBuf.push_back(Vec3i(votes, 0, votes)); + } + } + } + } + } + + ///////////////////////////////////////// + // POSITION & SCALE & ROTATION + + double clampAngle(double a) + { + double res = a; + + while (res > 360.0) + res -= 360.0; + while (res < 0) + res += 360.0; + + return res; + } + + bool angleEq(double a, double b, double eps = 1.0) + { + return (fabs(clampAngle(a - b)) <= eps); + } + + class GHT_Guil_Full : public GHT_Pos + { + public: + AlgorithmInfo* info() const; + + GHT_Guil_Full(); + + protected: + void releaseImpl(); + + void processTempl(); + void processImage(); + + struct ContourPoint + { + Point2d pos; + double theta; + }; + + struct Feature + { + ContourPoint p1; + ContourPoint p2; + + double alpha12; + double d12; + + Point2d r1; + Point2d r2; + }; + + void buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, vector< vector >& features, Point2d center = Point2d()); + void getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, vector& points); + + void calcOrientation(); + void calcScale(double angle); + void calcPosition(double angle, int angleVotes, double scale, int scaleVotes); + + int maxSize; + double xi; + int levels; + double angleEpsilon; + + double minAngle; + double maxAngle; + double angleStep; + int angleThresh; + + double minScale; + double maxScale; + double scaleStep; + int scaleThresh; + + double dp; + int posThresh; + + vector< vector > templFeatures; + vector< vector > imageFeatures; + + vector< pair > angles; + vector< pair > scales; + }; + + CV_INIT_ALGORITHM(GHT_Guil_Full, "GeneralizedHough.POSITION_SCALE_ROTATION", + obj.info()->addParam(obj, "minDist", obj.minDist, false, 0, 0, + "Minimum distance between the centers of the detected objects."); + obj.info()->addParam(obj, "maxSize", obj.maxSize, false, 0, 0, + "Maximal size of inner buffers."); + obj.info()->addParam(obj, "xi", obj.xi, false, 0, 0, + "Angle difference in degrees between two points in feature."); + obj.info()->addParam(obj, "levels", obj.levels, false, 0, 0, + "Feature table levels."); + obj.info()->addParam(obj, "angleEpsilon", obj.angleEpsilon, false, 0, 0, + "Maximal difference between angles that treated as equal."); + obj.info()->addParam(obj, "minAngle", obj.minAngle, false, 0, 0, + "Minimal rotation angle to detect in degrees."); + obj.info()->addParam(obj, "maxAngle", obj.maxAngle, false, 0, 0, + "Maximal rotation angle to detect in degrees."); + obj.info()->addParam(obj, "angleStep", obj.angleStep, false, 0, 0, + "Angle step in degrees."); + obj.info()->addParam(obj, "angleThresh", obj.angleThresh, false, 0, 0, + "Angle threshold."); + obj.info()->addParam(obj, "minScale", obj.minScale, false, 0, 0, + "Minimal scale to detect."); + obj.info()->addParam(obj, "maxScale", obj.maxScale, false, 0, 0, + "Maximal scale to detect."); + obj.info()->addParam(obj, "scaleStep", obj.scaleStep, false, 0, 0, + "Scale step."); + obj.info()->addParam(obj, "scaleThresh", obj.scaleThresh, false, 0, 0, + "Scale threshold."); + obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, + "Inverse ratio of the accumulator resolution to the image resolution."); + obj.info()->addParam(obj, "posThresh", obj.posThresh, false, 0, 0, + "Position threshold.")); + + GHT_Guil_Full::GHT_Guil_Full() + { + maxSize = 1000; + xi = 90.0; + levels = 360; + angleEpsilon = 1.0; + + minAngle = 0.0; + maxAngle = 360.0; + angleStep = 1.0; + angleThresh = 15000; + + minScale = 0.5; + maxScale = 2.0; + scaleStep = 0.05; + scaleThresh = 1000; + + dp = 1.0; + posThresh = 100; + } + + void GHT_Guil_Full::releaseImpl() + { + GHT_Pos::releaseImpl(); + + releaseVector(templFeatures); + releaseVector(imageFeatures); + + releaseVector(angles); + releaseVector(scales); + } + + void GHT_Guil_Full::processTempl() + { + buildFeatureList(templEdges, templDx, templDy, templFeatures, templCenter); + } + + void GHT_Guil_Full::processImage() + { + buildFeatureList(imageEdges, imageDx, imageDy, imageFeatures); + + calcOrientation(); + + for (size_t i = 0; i < angles.size(); ++i) + { + const double angle = angles[i].first; + const int angleVotes = angles[i].second; + + calcScale(angle); + + for (size_t j = 0; j < scales.size(); ++j) + { + const double scale = scales[j].first; + const int scaleVotes = scales[j].second; + + calcPosition(angle, angleVotes, scale, scaleVotes); + } + } + } + + void GHT_Guil_Full::buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, vector< vector >& features, Point2d center) + { + CV_Assert(levels > 0); + + const double maxDist = sqrt((double) templSize.width * templSize.width + templSize.height * templSize.height) * maxScale; + + const double alphaScale = levels / 360.0; + + vector points; + getContourPoints(edges, dx, dy, points); + + features.resize(levels + 1); + for_each(features.begin(), features.end(), mem_fun_ref(&vector::clear)); + for_each(features.begin(), features.end(), bind2nd(mem_fun_ref(&vector::reserve), maxSize)); + + for (size_t i = 0; i < points.size(); ++i) + { + ContourPoint p1 = points[i]; + + for (size_t j = 0; j < points.size(); ++j) + { + ContourPoint p2 = points[j]; + + if (angleEq(p1.theta - p2.theta, xi, angleEpsilon)) + { + const Point2d d = p1.pos - p2.pos; + + Feature f; + + f.p1 = p1; + f.p2 = p2; + + f.alpha12 = clampAngle(fastAtan2(d.y, d.x) - p1.theta); + f.d12 = norm(d); + + if (f.d12 > maxDist) + continue; + + f.r1 = p1.pos - center; + f.r2 = p2.pos - center; + + const int n = cvRound(f.alpha12 * alphaScale); + + if (features[n].size() < static_cast(maxSize)) + features[n].push_back(f); + } + } + } + } + + void GHT_Guil_Full::getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, vector& points) + { + CV_Assert(edges.type() == CV_8UC1); + CV_Assert(dx.type() == CV_32FC1 && dx.size == edges.size); + CV_Assert(dy.type() == dx.type() && dy.size == edges.size); + + points.clear(); + points.reserve(edges.size().area()); + + for (int y = 0; y < edges.rows; ++y) + { + const uchar* edgesRow = edges.ptr(y); + const float* dxRow = dx.ptr(y); + const float* dyRow = dy.ptr(y); + + for (int x = 0; x < edges.cols; ++x) + { + if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) + { + ContourPoint p; + + p.pos = Point2d(x, y); + p.theta = fastAtan2(dyRow[x], dxRow[x]); + + points.push_back(p); + } + } + } + } + + void GHT_Guil_Full::calcOrientation() + { + CV_Assert(levels > 0); + CV_Assert(templFeatures.size() == static_cast(levels + 1)); + CV_Assert(imageFeatures.size() == templFeatures.size()); + CV_Assert(minAngle >= 0.0 && minAngle < maxAngle && maxAngle <= 360.0); + CV_Assert(angleStep > 0.0 && angleStep < 360.0); + CV_Assert(angleThresh > 0); + + const double iAngleStep = 1.0 / angleStep; + const int angleRange = cvCeil((maxAngle - minAngle) * iAngleStep); + + vector OHist(angleRange + 1, 0); + for (int i = 0; i <= levels; ++i) + { + const vector& templRow = templFeatures[i]; + const vector& imageRow = imageFeatures[i]; + + for (size_t j = 0; j < templRow.size(); ++j) + { + Feature templF = templRow[j]; + + for (size_t k = 0; k < imageRow.size(); ++k) + { + Feature imF = imageRow[k]; + + const double angle = clampAngle(imF.p1.theta - templF.p1.theta); + if (angle >= minAngle && angle <= maxAngle) + { + const int n = cvRound((angle - minAngle) * iAngleStep); + ++OHist[n]; + } + } + } + } + + angles.clear(); + + for (int n = 0; n < angleRange; ++n) + { + if (OHist[n] >= angleThresh) + { + const double angle = minAngle + n * angleStep; + angles.push_back(make_pair(angle, OHist[n])); + } + } + } + + void GHT_Guil_Full::calcScale(double angle) + { + CV_Assert(levels > 0); + CV_Assert(templFeatures.size() == static_cast(levels + 1)); + CV_Assert(imageFeatures.size() == templFeatures.size()); + CV_Assert(minScale > 0.0 && minScale < maxScale); + CV_Assert(scaleStep > 0.0); + CV_Assert(scaleThresh > 0); + + const double iScaleStep = 1.0 / scaleStep; + const int scaleRange = cvCeil((maxScale - minScale) * iScaleStep); + + vector SHist(scaleRange + 1, 0); + + for (int i = 0; i <= levels; ++i) + { + const vector& templRow = templFeatures[i]; + const vector& imageRow = imageFeatures[i]; + + for (size_t j = 0; j < templRow.size(); ++j) + { + Feature templF = templRow[j]; + + templF.p1.theta += angle; + + for (size_t k = 0; k < imageRow.size(); ++k) + { + Feature imF = imageRow[k]; + + if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon)) + { + const double scale = imF.d12 / templF.d12; + if (scale >= minScale && scale <= maxScale) + { + const int s = cvRound((scale - minScale) * iScaleStep); + ++SHist[s]; + } + } + } + } + } + + scales.clear(); + + for (int s = 0; s < scaleRange; ++s) + { + if (SHist[s] >= scaleThresh) + { + const double scale = minScale + s * scaleStep; + scales.push_back(make_pair(scale, SHist[s])); + } + } + } + + void GHT_Guil_Full::calcPosition(double angle, int angleVotes, double scale, int scaleVotes) + { + CV_Assert(levels > 0); + CV_Assert(templFeatures.size() == static_cast(levels + 1)); + CV_Assert(imageFeatures.size() == templFeatures.size()); + CV_Assert(dp > 0.0); + CV_Assert(posThresh > 0); + + const double sinVal = sin(toRad(angle)); + const double cosVal = cos(toRad(angle)); + const double idp = 1.0 / dp; + + const int histRows = cvCeil(imageSize.height * idp); + const int histCols = cvCeil(imageSize.width * idp); + + Mat DHist(histRows + 2, histCols + 2, CV_32SC1, Scalar::all(0)); + + for (int i = 0; i <= levels; ++i) + { + const vector& templRow = templFeatures[i]; + const vector& imageRow = imageFeatures[i]; + + for (size_t j = 0; j < templRow.size(); ++j) + { + Feature templF = templRow[j]; + + templF.p1.theta += angle; + + templF.r1 *= scale; + templF.r2 *= scale; + + templF.r1 = Point2d(cosVal * templF.r1.x - sinVal * templF.r1.y, sinVal * templF.r1.x + cosVal * templF.r1.y); + templF.r2 = Point2d(cosVal * templF.r2.x - sinVal * templF.r2.y, sinVal * templF.r2.x + cosVal * templF.r2.y); + + for (size_t k = 0; k < imageRow.size(); ++k) + { + Feature imF = imageRow[k]; + + if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon)) + { + Point2d c1, c2; + + c1 = imF.p1.pos - templF.r1; + c1 *= idp; + + c2 = imF.p2.pos - templF.r2; + c2 *= idp; + + if (fabs(c1.x - c2.x) > 1 || fabs(c1.y - c2.y) > 1) + continue; + + if (c1.y >= 0 && c1.y < histRows && c1.x >= 0 && c1.x < histCols) + ++DHist.at(cvRound(c1.y) + 1, cvRound(c1.x) + 1); + } + } + } + } + + for(int y = 0; y < histRows; ++y) + { + const int* prevRow = DHist.ptr(y); + const int* curRow = DHist.ptr(y + 1); + const int* nextRow = DHist.ptr(y + 2); + + for(int x = 0; x < histCols; ++x) + { + const int votes = curRow[x + 1]; + + if (votes > posThresh && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1]) + { + posOutBuf.push_back(Vec4f(static_cast(x * dp), static_cast(y * dp), static_cast(scale), static_cast(angle))); + voteOutBuf.push_back(Vec3i(votes, scaleVotes, angleVotes)); + } + } + } + } +} + +Ptr cv::GeneralizedHough::create(int method) +{ + switch (method) + { + case GHT_POSITION: + CV_Assert( !GHT_Ballard_Pos_info_auto.name().empty() ); + return new GHT_Ballard_Pos(); + + case (GHT_POSITION | GHT_SCALE): + CV_Assert( !GHT_Ballard_PosScale_info_auto.name().empty() ); + return new GHT_Ballard_PosScale(); + + case (GHT_POSITION | GHT_ROTATION): + CV_Assert( !GHT_Ballard_PosRotation_info_auto.name().empty() ); + return new GHT_Ballard_PosRotation(); + + case (GHT_POSITION | GHT_SCALE | GHT_ROTATION): + CV_Assert( !GHT_Guil_Full_info_auto.name().empty() ); + return new GHT_Guil_Full(); + } + + CV_Error(CV_StsBadArg, "Unsupported method"); + return Ptr(); +} + +cv::GeneralizedHough::~GeneralizedHough() +{ +} + +void cv::GeneralizedHough::setTemplate(InputArray _templ, int cannyThreshold, Point templCenter) +{ + Mat templ = _templ.getMat(); + + CV_Assert(templ.type() == CV_8UC1); + CV_Assert(cannyThreshold > 0); + + Canny(templ, edges_, cannyThreshold / 2, cannyThreshold); + Sobel(templ, dx_, CV_32F, 1, 0); + Sobel(templ, dy_, CV_32F, 0, 1); + + if (templCenter == Point(-1, -1)) + templCenter = Point(templ.cols / 2, templ.rows / 2); + + setTemplateImpl(edges_, dx_, dy_, templCenter); +} + +void cv::GeneralizedHough::setTemplate(InputArray _edges, InputArray _dx, InputArray _dy, Point templCenter) +{ + Mat edges = _edges.getMat(); + Mat dx = _dx.getMat(); + Mat dy = _dy.getMat(); + + if (templCenter == Point(-1, -1)) + templCenter = Point(edges.cols / 2, edges.rows / 2); + + setTemplateImpl(edges, dx, dy, templCenter); +} + +void cv::GeneralizedHough::detect(InputArray _image, OutputArray positions, OutputArray votes, int cannyThreshold) +{ + Mat image = _image.getMat(); + + CV_Assert(image.type() == CV_8UC1); + CV_Assert(cannyThreshold > 0); + + Canny(image, edges_, cannyThreshold / 2, cannyThreshold); + Sobel(image, dx_, CV_32F, 1, 0); + Sobel(image, dy_, CV_32F, 0, 1); + + detectImpl(edges_, dx_, dy_, positions, votes); +} + +void cv::GeneralizedHough::detect(InputArray _edges, InputArray _dx, InputArray _dy, OutputArray positions, OutputArray votes) +{ + cv::Mat edges = _edges.getMat(); + cv::Mat dx = _dx.getMat(); + cv::Mat dy = _dy.getMat(); + + detectImpl(edges, dx, dy, positions, votes); +} + +void cv::GeneralizedHough::release() +{ + edges_.release(); + dx_.release(); + dy_.release(); + releaseImpl(); +} diff --git a/samples/cpp/generalized_hough.cpp b/samples/cpp/generalized_hough.cpp new file mode 100644 index 0000000000..c41e7903c8 --- /dev/null +++ b/samples/cpp/generalized_hough.cpp @@ -0,0 +1,209 @@ +#include +#include +#include + +#include "opencv2/core/core.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/gpu/gpu.hpp" +#include "opencv2/highgui/highgui.hpp" +#include "opencv2/contrib/contrib.hpp" + +using namespace std; +using namespace cv; +using namespace cv::gpu; + +static Mat loadImage(const string& name) +{ + Mat image = imread(name, IMREAD_GRAYSCALE); + if (image.empty()) + { + cerr << "Can't load image - " << name << endl; + exit(-1); + } + return image; +} + +int main(int argc, const char* argv[]) +{ + CommandLineParser cmd(argc, argv, + "{ image i | pic1.png | input image }" + "{ template t | templ.png | template image }" + "{ scale s | | estimate scale }" + "{ rotation r | | estimate rotation }" + "{ gpu | | use gpu version }" + "{ minDist | 100 | minimum distance between the centers of the detected objects }" + "{ levels | 360 | R-Table levels }" + "{ votesThreshold | 30 | the accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected }" + "{ angleThresh | 10000 | angle votes treshold }" + "{ scaleThresh | 1000 | scale votes treshold }" + "{ posThresh | 100 | position votes threshold }" + "{ dp | 2 | inverse ratio of the accumulator resolution to the image resolution }" + "{ minScale | 0.5 | minimal scale to detect }" + "{ maxScale | 2 | maximal scale to detect }" + "{ scaleStep | 0.05 | scale step }" + "{ minAngle | 0 | minimal rotation angle to detect in degrees }" + "{ maxAngle | 360 | maximal rotation angle to detect in degrees }" + "{ angleStep | 1 | angle step in degrees }" + "{ maxSize | 1000 | maximal size of inner buffers }" + "{ help h ? | | print help message }" + ); + + cmd.about("This program demonstrates arbitary object finding with the Generalized Hough transform."); + + if (cmd.has("help")) + { + cmd.printMessage(); + return 0; + } + + const string templName = cmd.get("template"); + const string imageName = cmd.get("image"); + const bool estimateScale = cmd.has("scale"); + const bool estimateRotation = cmd.has("rotation"); + const bool useGpu = cmd.has("gpu"); + const double minDist = cmd.get("minDist"); + const int levels = cmd.get("levels"); + const int votesThreshold = cmd.get("votesThreshold"); + const int angleThresh = cmd.get("angleThresh"); + const int scaleThresh = cmd.get("scaleThresh"); + const int posThresh = cmd.get("posThresh"); + const double dp = cmd.get("dp"); + const double minScale = cmd.get("minScale"); + const double maxScale = cmd.get("maxScale"); + const double scaleStep = cmd.get("scaleStep"); + const double minAngle = cmd.get("minAngle"); + const double maxAngle = cmd.get("maxAngle"); + const double angleStep = cmd.get("angleStep"); + const int maxSize = cmd.get("maxSize"); + + if (!cmd.check()) + { + cmd.printErrors(); + return -1; + } + + Mat templ = loadImage(templName); + Mat image = loadImage(imageName); + + int method = GHT_POSITION; + if (estimateScale) + method += GHT_SCALE; + if (estimateRotation) + method += GHT_ROTATION; + + vector position; + cv::TickMeter tm; + + if (useGpu) + { + GpuMat d_templ(templ); + GpuMat d_image(image); + GpuMat d_position; + + Ptr d_hough = GeneralizedHough_GPU::create(method); + d_hough->set("minDist", minDist); + d_hough->set("levels", levels); + d_hough->set("dp", dp); + d_hough->set("maxSize", maxSize); + if (estimateScale && estimateRotation) + { + d_hough->set("angleThresh", angleThresh); + d_hough->set("scaleThresh", scaleThresh); + d_hough->set("posThresh", posThresh); + } + else + { + d_hough->set("votesThreshold", votesThreshold); + } + if (estimateScale) + { + d_hough->set("minScale", minScale); + d_hough->set("maxScale", maxScale); + d_hough->set("scaleStep", scaleStep); + } + if (estimateRotation) + { + d_hough->set("minAngle", minAngle); + d_hough->set("maxAngle", maxAngle); + d_hough->set("angleStep", angleStep); + } + + d_hough->setTemplate(d_templ); + + tm.start(); + + d_hough->detect(d_image, d_position); + d_hough->download(d_position, position); + + tm.stop(); + } + else + { + Ptr hough = GeneralizedHough::create(method); + hough->set("minDist", minDist); + hough->set("levels", levels); + hough->set("dp", dp); + if (estimateScale && estimateRotation) + { + hough->set("angleThresh", angleThresh); + hough->set("scaleThresh", scaleThresh); + hough->set("posThresh", posThresh); + hough->set("maxSize", maxSize); + } + else + { + hough->set("votesThreshold", votesThreshold); + } + if (estimateScale) + { + hough->set("minScale", minScale); + hough->set("maxScale", maxScale); + hough->set("scaleStep", scaleStep); + } + if (estimateRotation) + { + hough->set("minAngle", minAngle); + hough->set("maxAngle", maxAngle); + hough->set("angleStep", angleStep); + } + + hough->setTemplate(templ); + + tm.start(); + + hough->detect(image, position); + + tm.stop(); + } + + cout << "Found : " << position.size() << " objects" << endl; + cout << "Detection time : " << tm.getTimeMilli() << " ms" << endl; + + Mat out; + cvtColor(image, out, COLOR_GRAY2BGR); + + for (size_t i = 0; i < position.size(); ++i) + { + Point2f pos(position[i][0], position[i][1]); + float scale = position[i][2]; + float angle = position[i][3]; + + RotatedRect rect; + rect.center = pos; + rect.size = Size2f(templ.cols * scale, templ.rows * scale); + rect.angle = angle; + + Point2f pts[4]; + rect.points(pts); + + line(out, pts[0], pts[1], Scalar(0, 0, 255), 3); + line(out, pts[1], pts[2], Scalar(0, 0, 255), 3); + line(out, pts[2], pts[3], Scalar(0, 0, 255), 3); + line(out, pts[3], pts[0], Scalar(0, 0, 255), 3); + } + + imshow("out", out); + waitKey(); + + return 0; +} diff --git a/samples/cpp/templ.png b/samples/cpp/templ.png new file mode 100644 index 0000000000000000000000000000000000000000..31f6995cf4f5fc3406bb3c019c55cfa960d79898 GIT binary patch literal 1635 zcmV-p2AuhcP)Wn#00001b5ch_0Itp) z=>Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FZT01FZU(%pXi00007bV*G`2iyo8 z2oe#Wz|@NX00rYoL_t(|+U;E}Pcuyvoh2;ER%}H{ir7tRYHL6sYHBKKYT-8^FbGP5 zDanSUh?0Qj4`3^5K$L`vA`lG%QLzAOrv*AOuJYA>;9w=(9NKCNd$WX{I(!1=XC05U?3JD>n7qstz0g1sm4gy=-33^@UljuAqF9v9v#cp zZnv2dA_d)`vTd8_x}NU#1VgB*N+y#DQ4}SaUe?9M1<3gEfj&P!C)3MX=LMiB0`HrU z*=zA>+GEtc2_na}6+@bD0h_b?XlCF*;raO-qBdi5l}1^$#6I%vMlp$zXSg8`S}@2 zhY=xyZC)Wl1lzm#L`JXIi%!Vn<0HHo0l8d`G@DKG{{HS;JC^g? zKiJ>jM;kqa<#M^m<8Lbok!2YU4rql!fyrdm%9ORey^XvR5cPUJczW`ivTC&&zWRiY zlCF%9lamvC@&RGncAyqXpoO91I3$zFz{>%lX&Nz2lRY6=sfS0@SSk!UZ$@tyZf=zkJ5FZIaDq@tHG#q9|lCnfS^&NLew!aU9eUrVUNG1 z;#&3w9X0_Nv{c*#rdTXOlo{_TdL4jpjgX_GBYfI%M~NF-gTVk_cFeauxJF2+RKjP? zAxhk)svzp3^|(Aw(jm=ov2M2;{sJ=~z9{j$z$}x=5XW)g!I;`9o)J>1RN%ch+bOt> zRIKBto+%5b82RF-`?vh$I)D#$c6P{gIt@k$NQs6*p+HR2B!6dd{%6za^xx4BKf72M zPw+NY&mj4V5R4~ygUPUtpLGa?J)Ag)g>MbPmTo-o-8whyv zK@bgD06HR&hCoLI(-7#0fEofF5llk>!88Q;9jta$0~3PI?`p7m?K3bT*wS5%pcf{) ziBTY6OZWFTLlSu{8l7Lhl~yPXfsP1PWkE*-BLpoJL0Tu!5&jwyiV#Bn9AUu?*aC?9 z0zt3P1~?OfaW}yTsn_f9o+zSD$e$M!c6WF20Hr7zBFi#4J39jl99RvF`Fu{xAIXj)Wz|vMj1;+D45wvl?gPamTge=hO^=P$P zP0h-_TCH-8io^+7U^E&Dy=JLYD)i~;DOR5(O2~3G*6ntMDU)-#9Bs8)iTFYYLY9eX zwOUkF)l|Ruyf%dVtje0rW-R2ecn5v$35i2%M2Lt~62dBKWLc*5dYyJU9oOr=K?p}; hL`p|oCa3_u_zx@}rJQFau%`e3002ovPDHLkV1f?`;?)2E literal 0 HcmV?d00001 From 7d4432df27d1ff8f0f09cae90a7d613d5122baf2 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 10 Sep 2012 17:07:21 +0400 Subject: [PATCH 43/97] getWidth/getHeight optimized. --- modules/androidcamera/src/camera_activity.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/androidcamera/src/camera_activity.cpp b/modules/androidcamera/src/camera_activity.cpp index 8ce53a816c..fe4b284d38 100644 --- a/modules/androidcamera/src/camera_activity.cpp +++ b/modules/androidcamera/src/camera_activity.cpp @@ -61,7 +61,7 @@ private: std::string CameraWrapperConnector::pathLibFolder; -bool CameraWrapperConnector::isConnectedToLib=false; +bool CameraWrapperConnector::isConnectedToLib = false; InitCameraConnectC CameraWrapperConnector::pInitCameraC = 0; CloseCameraConnectC CameraWrapperConnector::pCloseCameraC = 0; GetCameraPropertyC CameraWrapperConnector::pGetPropertyC = 0; @@ -421,18 +421,24 @@ void CameraActivity::applyProperties() frameWidth = -1; frameHeight = -1; CameraWrapperConnector::applyProperties(&camera); + frameWidth = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH); + frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT); } int CameraActivity::getFrameWidth() { LOGD("CameraActivity::getFrameWidth()"); - return getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH); + if (frameWidth <= 0) + frameWidth = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH); + return frameWidth; } int CameraActivity::getFrameHeight() { LOGD("CameraActivity::getFrameHeight()"); - return frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT); + if (frameHeight <= 0) + frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT); + return frameHeight; } void CameraActivity::setPathLibFolder(const char* path) From 200a81746e6567f5adc570455880a957e49930df Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Mon, 10 Sep 2012 18:14:32 +0400 Subject: [PATCH 44/97] Improve Eigen search Should now automatically detect it during Android build --- cmake/OpenCVFindLibsPerf.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVFindLibsPerf.cmake b/cmake/OpenCVFindLibsPerf.cmake index 840d0c0834..5ab16ada2d 100644 --- a/cmake/OpenCVFindLibsPerf.cmake +++ b/cmake/OpenCVFindLibsPerf.cmake @@ -29,7 +29,8 @@ if(WITH_EIGEN) find_path(EIGEN_INCLUDE_PATH "Eigen/Core" PATHS /usr/local /opt /usr ENV ProgramFiles ENV ProgramW6432 PATH_SUFFIXES include/eigen3 include/eigen2 Eigen/include/eigen3 Eigen/include/eigen2 - DOC "The path to Eigen3/Eigen2 headers") + DOC "The path to Eigen3/Eigen2 headers" + CMAKE_FIND_ROOT_PATH_BOTH) if(EIGEN_INCLUDE_PATH) ocv_include_directories(${EIGEN_INCLUDE_PATH}) From 36f912261b373b194b9fa9e9a2e65c958bed2f39 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Mon, 10 Sep 2012 19:17:50 +0400 Subject: [PATCH 45/97] Move gpu sample to gpu folder --- samples/{cpp => gpu}/generalized_hough.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename samples/{cpp => gpu}/generalized_hough.cpp (100%) diff --git a/samples/cpp/generalized_hough.cpp b/samples/gpu/generalized_hough.cpp similarity index 100% rename from samples/cpp/generalized_hough.cpp rename to samples/gpu/generalized_hough.cpp From a201233b59ca1b227bd245fd7409800dd1ee8ef7 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Mon, 10 Sep 2012 19:44:59 +0400 Subject: [PATCH 46/97] Fix warning in Android camera code --- modules/androidcamera/src/camera_activity.cpp | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/modules/androidcamera/src/camera_activity.cpp b/modules/androidcamera/src/camera_activity.cpp index fe4b284d38..2cd3e6e493 100644 --- a/modules/androidcamera/src/camera_activity.cpp +++ b/modules/androidcamera/src/camera_activity.cpp @@ -169,8 +169,8 @@ CameraActivity::ErrorCode CameraWrapperConnector::connectToLib() string folderPath = getPathLibFolder(); if (folderPath.empty()) { - LOGD("Trying to find native camera in default OpenCV packages"); - folderPath = getDefaultPathLibFolder(); + LOGD("Trying to find native camera in default OpenCV packages"); + folderPath = getDefaultPathLibFolder(); } LOGD("CameraWrapperConnector::connectToLib: folderPath=%s", folderPath.c_str()); @@ -270,21 +270,21 @@ std::string CameraWrapperConnector::getDefaultPathLibFolder() const string packageList[] = {"tegra3", "armv7a_neon", "armv7a", "armv5", "x86"}; for (size_t i = 0; i < 5; i++) { - char path[128]; - sprintf(path, "/data/data/org.opencv.lib_v%d%d_%s/lib/", CV_MAJOR_VERSION, CV_MINOR_VERSION, packageList[i].c_str()); - LOGD("Trying package \"%s\" (\"%s\")", packageList[i], path); + char path[128]; + sprintf(path, "/data/data/org.opencv.lib_v%d%d_%s/lib/", CV_MAJOR_VERSION, CV_MINOR_VERSION, packageList[i].c_str()); + LOGD("Trying package \"%s\" (\"%s\")", packageList[i].c_str(), path); - DIR* dir = opendir(path); - if (!dir) - { - LOGD("Package not found"); - continue; - } - else - { - closedir(dir); - return path; - } + DIR* dir = opendir(path); + if (!dir) + { + LOGD("Package not found"); + continue; + } + else + { + closedir(dir); + return path; + } } return string(); @@ -301,18 +301,18 @@ std::string CameraWrapperConnector::getPathLibFolder() LOGD("Library name: %s", dl_info.dli_fname); LOGD("Library base address: %p", dl_info.dli_fbase); - const char* libName=dl_info.dli_fname; - while( ((*libName)=='/') || ((*libName)=='.') ) - libName++; + const char* libName=dl_info.dli_fname; + while( ((*libName)=='/') || ((*libName)=='.') ) + libName++; char lineBuf[2048]; FILE* file = fopen("/proc/self/smaps", "rt"); if(file) { - while (fgets(lineBuf, sizeof lineBuf, file) != NULL) - { - //verify that line ends with library name + while (fgets(lineBuf, sizeof lineBuf, file) != NULL) + { + //verify that line ends with library name int lineLength = strlen(lineBuf); int libNameLength = strlen(libName); @@ -325,7 +325,7 @@ std::string CameraWrapperConnector::getPathLibFolder() if (0 != strncmp(lineBuf + lineLength - libNameLength, libName, libNameLength)) { - //the line does not contain the library name + //the line does not contain the library name continue; } @@ -344,18 +344,18 @@ std::string CameraWrapperConnector::getPathLibFolder() fclose(file); return pathBegin; - } - fclose(file); - LOGE("Could not find library path"); + } + fclose(file); + LOGE("Could not find library path"); } else { - LOGE("Could not read /proc/self/smaps"); + LOGE("Could not read /proc/self/smaps"); } } else { - LOGE("Could not get library name and base address"); + LOGE("Could not get library name and base address"); } return string(); @@ -429,7 +429,7 @@ int CameraActivity::getFrameWidth() { LOGD("CameraActivity::getFrameWidth()"); if (frameWidth <= 0) - frameWidth = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH); + frameWidth = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH); return frameWidth; } @@ -437,7 +437,7 @@ int CameraActivity::getFrameHeight() { LOGD("CameraActivity::getFrameHeight()"); if (frameHeight <= 0) - frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT); + frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT); return frameHeight; } From dd1091bbe142610e43fff121dffc72b0e5d28d67 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 10 Sep 2012 19:55:03 +0400 Subject: [PATCH 47/97] a reference function for remap was stabilized --- modules/imgproc/test/test_imgwarp_strict.cpp | 767 ++++++++----------- 1 file changed, 320 insertions(+), 447 deletions(-) diff --git a/modules/imgproc/test/test_imgwarp_strict.cpp b/modules/imgproc/test/test_imgwarp_strict.cpp index 9209c6973d..59ec2582a5 100644 --- a/modules/imgproc/test/test_imgwarp_strict.cpp +++ b/modules/imgproc/test/test_imgwarp_strict.cpp @@ -46,19 +46,23 @@ #include using namespace cv; -using namespace std; -void __wrap_printf_func(const char* fmt, ...) +namespace internal { - va_list args; - va_start(args, fmt); - char buffer[256]; - vsprintf (buffer, fmt, args); - cvtest::TS::ptr()->printf(cvtest::TS::SUMMARY, buffer); - va_end(args); + void __wrap_printf_func(const char* fmt, ...) + { + va_list args; + va_start(args, fmt); + char buffer[256]; + vsprintf (buffer, fmt, args); + cvtest::TS::ptr()->printf(cvtest::TS::SUMMARY, buffer); + va_end(args); + } + + #define PRINT_TO_LOG __wrap_printf_func } -#define PRINT_TO_LOG __wrap_printf_func +using internal::PRINT_TO_LOG; #define SHOW_IMAGE #undef SHOW_IMAGE @@ -70,23 +74,19 @@ class CV_ImageWarpBaseTest : public cvtest::BaseTest { public: - enum - { - cell_size = 10 - }; + enum { cell_size = 10 }; CV_ImageWarpBaseTest(); - - virtual void run(int); - virtual ~CV_ImageWarpBaseTest(); - + + virtual void run(int); protected: virtual void generate_test_data(); virtual void run_func() = 0; virtual void run_reference_func() = 0; - virtual void validate_results() const = 0; + virtual void validate_results() const; + virtual void prepare_test_data_for_reference_func(); Size randSize(RNG& rng) const; @@ -95,11 +95,12 @@ protected: int interpolation; Mat src; Mat dst; + Mat reference_dst; }; CV_ImageWarpBaseTest::CV_ImageWarpBaseTest() : BaseTest(), interpolation(-1), - src(), dst() + src(), dst(), reference_dst() { test_case_count = 40; ts->set_failed_test_info(cvtest::TS::OK); @@ -126,58 +127,11 @@ const char* CV_ImageWarpBaseTest::interpolation_to_string(int inter) const return "Unsupported/Unkown interpolation type"; } -void interpolateLinear(float x, float* coeffs) -{ - coeffs[0] = 1.f - x; - coeffs[1] = x; -} - -void interpolateCubic(float x, float* coeffs) -{ - const float A = -0.75f; - - coeffs[0] = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A; - coeffs[1] = ((A + 2)*x - (A + 3))*x*x + 1; - coeffs[2] = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1; - coeffs[3] = 1.f - coeffs[0] - coeffs[1] - coeffs[2]; -} - -void interpolateLanczos4(float x, float* coeffs) -{ - static const double s45 = 0.70710678118654752440084436210485; - static const double cs[][2]= - {{1, 0}, {-s45, -s45}, {0, 1}, {s45, -s45}, {-1, 0}, {s45, s45}, {0, -1}, {-s45, s45}}; - - if( x < FLT_EPSILON ) - { - for( int i = 0; i < 8; i++ ) - coeffs[i] = 0; - coeffs[3] = 1; - return; - } - - float sum = 0; - double y0=-(x+3)*CV_PI*0.25, s0 = sin(y0), c0=cos(y0); - for(int i = 0; i < 8; i++ ) - { - double y = -(x+3-i)*CV_PI*0.25; - coeffs[i] = (float)((cs[i][0]*s0 + cs[i][1]*c0)/(y*y)); - sum += coeffs[i]; - } - - sum = 1.f/sum; - for(int i = 0; i < 8; i++ ) - coeffs[i] *= sum; -} - -typedef void (*interpolate_method)(float x, float* coeffs); -interpolate_method inter_array[] = { &interpolateLinear, &interpolateCubic, &interpolateLanczos4 }; - Size CV_ImageWarpBaseTest::randSize(RNG& rng) const { Size size; - size.width = saturate_cast(std::exp(rng.uniform(0.0, 7.0))); - size.height = saturate_cast(std::exp(rng.uniform(0.0, 7.0))); + size.width = saturate_cast(std::exp(rng.uniform(1.0f, 7.0f))); + size.height = saturate_cast(std::exp(rng.uniform(1.0f, 7.0f))); return size; } @@ -186,18 +140,17 @@ void CV_ImageWarpBaseTest::generate_test_data() { RNG& rng = ts->get_rng(); - Size ssize = randSize(rng); + // generating the src matrix structure + Size ssize = randSize(rng), dsize; - int depth = CV_8S; + int depth = rng.uniform(0, CV_64F); while (depth == CV_8S || depth == CV_32S) depth = rng.uniform(0, CV_64F); int cn = rng.uniform(1, 4); while (cn == 2) cn = rng.uniform(1, 4); - interpolation = rng.uniform(0, CV_INTER_LANCZOS4 + 1); - interpolation = INTER_NEAREST; - + src.create(ssize, CV_MAKE_TYPE(depth, cn)); // generating the src matrix @@ -217,6 +170,41 @@ void CV_ImageWarpBaseTest::generate_test_data() for (x = cell_size; x < src.cols; x += cell_size) line(src, Point2i(x, 0), Point2i(x, src.rows), Scalar::all(0), 1); } + + // generating an interpolation type + interpolation = rng.uniform(0, CV_INTER_LANCZOS4 + 1); + + // generating the dst matrix structure + double scale_x = 2, scale_y = 2; + if (interpolation == INTER_AREA) + { + bool area_fast = rng.uniform(0., 1.) > 0.5; + if (area_fast) + { + scale_x = rng.uniform(2, 5); + scale_y = rng.uniform(2, 5); + } + else + { + scale_x = rng.uniform(1.0, 3.0); + scale_y = rng.uniform(1.0, 3.0); + } + } + else + { + scale_x = rng.uniform(0.4, 4.0); + scale_y = rng.uniform(0.4, 4.0); + } + CV_Assert(scale_x > 0.0f && scale_y > 0.0f); + + dsize.width = saturate_cast((ssize.width + scale_x - 1) / scale_x); + dsize.height = saturate_cast((ssize.height + scale_y - 1) / scale_y); + + dst = Mat::zeros(dsize, src.type()); + reference_dst = Mat::zeros(dst.size(), CV_MAKE_TYPE(CV_32F, dst.channels())); + + if (interpolation == INTER_AREA && (scale_x < 1.0 || scale_y < 1.0)) + interpolation = INTER_LINEAR; } void CV_ImageWarpBaseTest::run(int) @@ -236,6 +224,90 @@ void CV_ImageWarpBaseTest::run(int) ts->set_gtest_status(); } +void CV_ImageWarpBaseTest::validate_results() const +{ + Mat _dst; + dst.convertTo(_dst, reference_dst.depth()); + + Size dsize = dst.size(), ssize = src.size(); + int cn = _dst.channels(); + dsize.width *= cn; + float t = 1.0f; + if (interpolation == INTER_CUBIC) + t = 1.0f; + else if (interpolation == INTER_LANCZOS4) + t = 1.0f; + else if (interpolation == INTER_NEAREST) + t = 1.0f; + else if (interpolation == INTER_AREA) + t = 2.0f; + + for (int dy = 0; dy < dsize.height; ++dy) + { + const float* rD = reference_dst.ptr(dy); + const float* D = _dst.ptr(dy); + + for (int dx = 0; dx < dsize.width; ++dx) + if (fabs(rD[dx] - D[dx]) > t && +// fabs(rD[dx] - D[dx]) < 250.0f && + rD[dx] <= 255.0f && D[dx] <= 255.0f && rD[dx] >= 0.0f && D[dx] >= 0.0f) + { + PRINT_TO_LOG("\nNorm of the difference: %lf\n", norm(reference_dst, _dst, NORM_INF)); + PRINT_TO_LOG("Error in (dx, dy): (%d, %d)\n", dx / cn + 1, dy + 1); + PRINT_TO_LOG("Tuple (rD, D): (%f, %f)\n", rD[dx], D[dx]); + PRINT_TO_LOG("Dsize: (%d, %d)\n", dsize.width / cn, dsize.height); + PRINT_TO_LOG("Ssize: (%d, %d)\n", src.cols, src.rows); + + float scale_x = static_cast(ssize.width) / dsize.width, + scale_y = static_cast(ssize.height) / dsize.height; + PRINT_TO_LOG("Interpolation: %s\n", interpolation_to_string(interpolation == INTER_AREA && + fabs(scale_x - cvRound(scale_x)) < FLT_EPSILON && + fabs(scale_y - cvRound(scale_y)) < FLT_EPSILON ? INTER_LANCZOS4 + 1 : interpolation)); + PRINT_TO_LOG("Scale (x, y): (%lf, %lf)\n", scale_x, scale_y); + PRINT_TO_LOG("Elemsize: %d\n", src.elemSize1()); + PRINT_TO_LOG("Channels: %d\n", cn); + +#ifdef SHOW_IMAGE + const std::string w1("OpenCV impl (run func)"), w2("Reference func"), w3("Src image"), w4("Diff"); + namedWindow(w1, CV_WINDOW_KEEPRATIO); + namedWindow(w2, CV_WINDOW_KEEPRATIO); + namedWindow(w3, CV_WINDOW_KEEPRATIO); + namedWindow(w4, CV_WINDOW_KEEPRATIO); + + Mat diff; + absdiff(reference_dst, _dst, diff); + + imshow(w1, dst); + imshow(w2, reference_dst); + imshow(w3, src); + imshow(w4, diff); + + waitKey(); +#endif + + const int radius = 3; + int rmin = MAX(dy - radius, 0), rmax = MIN(dy + radius, dsize.height); + int cmin = MAX(dx / cn - radius, 0), cmax = MIN(dx / cn + radius, dsize.width); + + std::cout << "opencv result:\n" << dst(Range(rmin, rmax), Range(cmin, cmax)) << std::endl; + std::cout << "reference result:\n" << reference_dst(Range(rmin, rmax), Range(cmin, cmax)) << std::endl; + + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + return; + } + } +} + +void CV_ImageWarpBaseTest::prepare_test_data_for_reference_func() +{ + if (src.depth() != CV_32F) + { + Mat tmp; + src.convertTo(tmp, CV_32F); + src = tmp; + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////////// // Resize //////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -252,13 +324,11 @@ protected: virtual void run_func(); virtual void run_reference_func(); - virtual void validate_results() const; - + private: double scale_x; double scale_y; bool area_fast; - Mat reference_dst; void resize_generic(); void resize_area(); @@ -270,8 +340,8 @@ private: }; CV_Resize_Test::CV_Resize_Test() : - CV_ImageWarpBaseTest(), scale_x(), scale_y(), - area_fast(false), reference_dst() + CV_ImageWarpBaseTest(), scale_x(), + scale_y(), area_fast(false) { } @@ -279,43 +349,63 @@ CV_Resize_Test::~CV_Resize_Test() { } +namespace internal +{ + void interpolateLinear(float x, float* coeffs) + { + coeffs[0] = 1.f - x; + coeffs[1] = x; + } + + void interpolateCubic(float x, float* coeffs) + { + const float A = -0.75f; + + coeffs[0] = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A; + coeffs[1] = ((A + 2)*x - (A + 3))*x*x + 1; + coeffs[2] = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1; + coeffs[3] = 1.f - coeffs[0] - coeffs[1] - coeffs[2]; + } + + void interpolateLanczos4(float x, float* coeffs) + { + static const double s45 = 0.70710678118654752440084436210485; + static const double cs[][2]= + {{1, 0}, {-s45, -s45}, {0, 1}, {s45, -s45}, {-1, 0}, {s45, s45}, {0, -1}, {-s45, s45}}; + + if( x < FLT_EPSILON ) + { + for( int i = 0; i < 8; i++ ) + coeffs[i] = 0; + coeffs[3] = 1; + return; + } + + float sum = 0; + double y0=-(x+3)*CV_PI*0.25, s0 = sin(y0), c0=cos(y0); + for(int i = 0; i < 8; i++ ) + { + double y = -(x+3-i)*CV_PI*0.25; + coeffs[i] = (float)((cs[i][0]*s0 + cs[i][1]*c0)/(y*y)); + sum += coeffs[i]; + } + + sum = 1.f/sum; + for(int i = 0; i < 8; i++ ) + coeffs[i] *= sum; + } + + typedef void (*interpolate_method)(float x, float* coeffs); + interpolate_method inter_array[] = { &interpolateLinear, &interpolateCubic, &interpolateLanczos4 }; +} + void CV_Resize_Test::generate_test_data() { CV_ImageWarpBaseTest::generate_test_data(); - RNG& rng = ts->get_rng(); - Size dsize, ssize = src.size(); - if (interpolation == INTER_AREA) - { - area_fast = rng.uniform(0., 1.) > 0.5; - if (area_fast) - { - scale_x = rng.uniform(2, 5); - scale_y = rng.uniform(2, 5); - } - else - { - scale_x = rng.uniform(1.0, 3.0); - scale_y = rng.uniform(1.0, 3.0); - } - } - else - { - scale_x = rng.uniform(0.4, 4.0); - scale_y = rng.uniform(0.4, 4.0); - } - dsize.width = saturate_cast((ssize.width + scale_x - 1) / scale_x); - dsize.height = saturate_cast((ssize.height + scale_y - 1) / scale_y); - - dst = Mat::zeros(dsize, src.type()); - reference_dst = Mat::zeros(dst.size(), CV_MAKE_TYPE(CV_32F, dst.channels())); - scale_x = src.cols / static_cast(dst.cols); scale_y = src.rows / static_cast(dst.rows); - - if (interpolation == INTER_AREA && (scale_x < 1.0 || scale_y < 1.0)) - interpolation = INTER_LINEAR; - + area_fast = interpolation == INTER_AREA && fabs(scale_x - cvRound(scale_x)) < FLT_EPSILON && fabs(scale_y - cvRound(scale_y)) < FLT_EPSILON; @@ -333,12 +423,8 @@ void CV_Resize_Test::run_func() void CV_Resize_Test::run_reference_func() { - if (src.depth() != CV_32F) - { - Mat tmp; - src.convertTo(tmp, CV_32F); - src = tmp; - } + CV_ImageWarpBaseTest::prepare_test_data_for_reference_func(); + if (interpolation == INTER_AREA) resize_area(); else @@ -347,7 +433,7 @@ void CV_Resize_Test::run_reference_func() double CV_Resize_Test::getWeight(double a, double b, int x) { - float w = std::min(x + 1., b) - std::max(x + 0., a); + float w = std::min(x + 1, b) - std::max(x, a); CV_Assert(w >= 0); return w; } @@ -427,7 +513,7 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d } else if (interpolation == INTER_LINEAR || interpolation == INTER_CUBIC || interpolation == INTER_LANCZOS4) { - interpolate_method inter_func = inter_array[interpolation - (interpolation == INTER_LANCZOS4 ? 2 : 1)]; + internal::interpolate_method inter_func = internal::inter_array[interpolation - (interpolation == INTER_LANCZOS4 ? 2 : 1)]; int elemsize = _src.elemSize(); int ofs = 0, ksize = 2; @@ -435,8 +521,6 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d ofs = 1, ksize = 4; else if (interpolation == INTER_LANCZOS4) ofs = 3, ksize = 8; - cv::AutoBuffer _w(ksize); - float* w = _w; Mat _extended_src_row(1, _src.cols + ksize * 2, _src.type()); uchar* srow = _src.data + dy * _src.step; @@ -455,6 +539,7 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d float *xyD = yD + dx * cn; const float* xyS = _extended_src_row.ptr(0) + (isx + ksize - ofs) * cn; + float w[ksize]; inter_func(fsx, w); for (int r = 0; r < cn; ++r) @@ -512,80 +597,6 @@ void CV_Resize_Test::resize_generic() transpose(reference_dst, reference_dst); } -void CV_Resize_Test::validate_results() const -{ - Mat _dst = dst; - if (dst.depth() != reference_dst.depth()) - { - Mat tmp; - dst.convertTo(tmp, reference_dst.depth()); - _dst = tmp; - } - - Size dsize = dst.size(); - int cn = _dst.channels(); - dsize.width *= cn; - float t = 1.0f; - if (interpolation == INTER_CUBIC) - t = 1.0f; - else if (interpolation == INTER_LANCZOS4) - t = 1.0f; - else if (interpolation == INTER_NEAREST) - t = 1.0f; - else if (interpolation == INTER_AREA) - t = 2.0f; - - for (int dy = 0; dy < dsize.height; ++dy) - { - const float* rD = reference_dst.ptr(dy); - const float* D = _dst.ptr(dy); - - for (int dx = 0; dx < dsize.width; ++dx) - if (fabs(rD[dx] - D[dx]) > t) - { - PRINT_TO_LOG("\nNorm of the difference: %lf\n", norm(reference_dst, _dst, NORM_INF)); - PRINT_TO_LOG("Error in (dx, dy): (%d, %d)\n", dx / cn + 1, dy + 1); - PRINT_TO_LOG("Tuple (rD, D): (%f, %f)\n", rD[dx], D[dx]); - PRINT_TO_LOG("Dsize: (%d, %d)\n", dsize.width / cn, dsize.height); - PRINT_TO_LOG("Ssize: (%d, %d)\n", src.cols, src.rows); - PRINT_TO_LOG("Interpolation: %s\n", - interpolation_to_string(area_fast ? INTER_LANCZOS4 + 1 : interpolation)); - PRINT_TO_LOG("Scale (x, y): (%lf, %lf)\n", scale_x, scale_y); - PRINT_TO_LOG("Elemsize: %d\n", src.elemSize()); - PRINT_TO_LOG("Channels: %d\n", cn); - -#ifdef SHOW_IMAGE - const std::string w1("Resize (run func)"), w2("Reference Resize"), w3("Src image"), w4("Diff"); - namedWindow(w1, CV_WINDOW_KEEPRATIO); - namedWindow(w2, CV_WINDOW_KEEPRATIO); - namedWindow(w3, CV_WINDOW_KEEPRATIO); - namedWindow(w4, CV_WINDOW_KEEPRATIO); - - Mat diff; - absdiff(reference_dst, _dst, diff); - - imshow(w1, dst); - imshow(w2, reference_dst); - imshow(w3, src); - imshow(w4, diff); - - waitKey(); -#endif - /* - const int radius = 3; - int rmin = MAX(dy - radius, 0), rmax = MIN(dy + radius, dsize.height); - int cmin = MAX(dx - radius, 0), cmax = MIN(dx + radius, dsize.width); - - cout << "opencv result:\n" << dst(Range(rmin, rmax), Range(cmin, cmax)) << endl; - cout << "reference result:\n" << reference_dst(Range(rmin, rmax), Range(cmin, cmax)) << endl; - */ - - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } -} - //////////////////////////////////////////////////////////////////////////////////////////////////////// // remap //////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -607,31 +618,25 @@ protected: virtual void run_func(); virtual void run_reference_func(); - virtual void validate_results() const; Mat mapx, mapy; int borderType; Scalar borderValue; remap_func funcs[2]; - - Mat dilate_src; - Mat erode_src; - Mat dilate_dst; - Mat erode_dst; - + private: void remap_nearest(const Mat&, Mat&); void remap_generic(const Mat&, Mat&); void convert_maps(); const char* borderType_to_string() const; + virtual void validate_results() const; }; CV_Remap_Test::CV_Remap_Test() : CV_ImageWarpBaseTest(), mapx(), mapy(), - borderType(), borderValue(), dilate_src(), erode_src(), - dilate_dst(), erode_dst() + borderType(-1), borderValue() { funcs[0] = &CV_Remap_Test::remap_nearest; funcs[1] = &CV_Remap_Test::remap_generic; @@ -641,17 +646,6 @@ CV_Remap_Test::~CV_Remap_Test() { } -const char* CV_Remap_Test::borderType_to_string() const -{ - if (borderType == BORDER_CONSTANT) - return "BORDER_CONSTANT"; - if (borderType == BORDER_REPLICATE) - return "BORDER_REPLICATE"; - if (borderType == BORDER_REFLECT) - return "BORDER_REFLECT"; - return "Unsupported/Unkown border type"; -} - void CV_Remap_Test::generate_test_data() { CV_ImageWarpBaseTest::generate_test_data(); @@ -660,31 +654,14 @@ void CV_Remap_Test::generate_test_data() borderType = rng.uniform(1, BORDER_WRAP); borderValue = Scalar::all(rng.uniform(0, 255)); - Size dsize = randSize(rng); - dst.create(dsize, src.type()); - - // generating the mapx, mapy matrix + // generating the mapx, mapy matrices static const int mapx_types[] = { CV_16SC2, CV_32FC1, CV_32FC2 }; mapx.create(dst.size(), mapx_types[rng.uniform(0, sizeof(mapx_types) / sizeof(int))]); - mapy.release(); + mapy = Mat(); const int n = std::min(std::min(src.cols, src.rows) / 10 + 1, 2); float _n = 0; //static_cast(-n); - -// mapy.release(); -// mapx.create(dst.size(), CV_32FC2); -// for (int y = 0; y < dsize.height; ++y) -// { -// float* yM = mapx.ptr(y); -// for (int x = 0; x < dsize.width; ++x) -// { -// float* xyM = yM + (x << 1); -// xyM[0] = x; -// xyM[1] = y; -// } -// } -// return; - + switch (mapx.type()) { case CV_16SC2: @@ -707,7 +684,7 @@ void CV_Remap_Test::generate_test_data() { MatIterator_ begin_y = mapy.begin(), end_y = mapy.end(); for ( ; begin_y != end_y; ++begin_y) - begin_y[0] = (ushort)rng.uniform(0, 1024); + begin_y[0] = rng.uniform(0, 1024); } break; @@ -715,7 +692,7 @@ void CV_Remap_Test::generate_test_data() { MatIterator_ begin_y = mapy.begin(), end_y = mapy.end(); for ( ; begin_y != end_y; ++begin_y) - begin_y[0] = (short)rng.uniform(0, 1024); + begin_y[0] = rng.uniform(0, 1024); } break; } @@ -750,6 +727,10 @@ void CV_Remap_Test::generate_test_data() } } break; + + default: + assert(0); + break; } } @@ -760,41 +741,33 @@ void CV_Remap_Test::run_func() void CV_Remap_Test::convert_maps() { - if (mapx.type() == mapy.type() && mapx.type() == CV_32FC1) - { - Mat maps[] = { mapx, mapy }; - Mat dst_map; - merge(maps, sizeof(maps) / sizeof(Mat), dst_map); - mapx = dst_map; - } - else if (interpolation == INTER_NEAREST && mapx.type() == CV_16SC2) - { - Mat tmp_mapx; - mapx.convertTo(tmp_mapx, CV_32F); - mapx = tmp_mapx; - mapy.release(); - return; - } - else if (mapx.type() != CV_32FC2) - { - Mat out_mapy; - convertMaps(mapx.clone(), mapy, mapx, out_mapy, CV_32FC2, interpolation == INTER_NEAREST); - } + if (mapx.type() != CV_16SC2) + convertMaps(mapx.clone(), mapy.clone(), mapx, mapy, CV_16SC2, interpolation == INTER_NEAREST); + else if (interpolation != INTER_NEAREST) + if (mapy.type() != CV_16UC1) + mapy.clone().convertTo(mapy, CV_16UC1); + + if (interpolation == INTER_NEAREST) + mapy = Mat(); + CV_Assert((interpolation == INTER_NEAREST && !mapy.data || mapy.type() == CV_16UC1 || + mapy.type() == CV_16SC1) && mapx.type() == CV_16SC2); +} - mapy.release(); +const char* CV_Remap_Test::borderType_to_string() const +{ + if (borderType == BORDER_CONSTANT) + return "BORDER_CONSTANT"; + if (borderType == BORDER_REPLICATE) + return "BORDER_REPLICATE"; + if (borderType == BORDER_REFLECT) + return "BORDER_REFLECT"; + return "Unsupported/Unkown border type"; } void CV_Remap_Test::prepare_test_data_for_reference_func() { + CV_ImageWarpBaseTest::prepare_test_data_for_reference_func(); convert_maps(); - - if (src.depth() != CV_32F) - { - Mat _src; - src.convertTo(_src, CV_32F); - src = _src; - } - /* const int ksize = 3; Mat kernel = getStructuringElement(CV_MOP_ERODE, Size(ksize, ksize)); @@ -812,12 +785,6 @@ void CV_Remap_Test::prepare_test_data_for_reference_func() src.copyTo(dilate_src, dst_mask); dst_mask.release(); */ - - dilate_src = src; - erode_src = src; - - dilate_dst = Mat::zeros(dst.size(), dilate_src.type()); - erode_dst = Mat::zeros(dst.size(), erode_src.type()); } void CV_Remap_Test::run_reference_func() @@ -826,17 +793,15 @@ void CV_Remap_Test::run_reference_func() if (interpolation == INTER_AREA) interpolation = INTER_LINEAR; - CV_Assert(interpolation != INTER_AREA); - + int index = interpolation == INTER_NEAREST ? 0 : 1; - (this->*funcs[index])(erode_src, erode_dst); - (this->*funcs[index])(dilate_src, dilate_dst); + (this->*funcs[index])(src, reference_dst); } void CV_Remap_Test::remap_nearest(const Mat& _src, Mat& _dst) { CV_Assert(_src.depth() == CV_32F && _dst.type() == _src.type()); - CV_Assert(mapx.type() == CV_32FC2); + CV_Assert(mapx.type() == CV_16SC2 && !mapy.data); Size ssize = _src.size(), dsize = _dst.size(); CV_Assert(ssize.area() > 0 && dsize.area() > 0); @@ -844,36 +809,36 @@ void CV_Remap_Test::remap_nearest(const Mat& _src, Mat& _dst) for (int dy = 0; dy < dsize.height; ++dy) { - const float* yM = mapx.ptr(dy); + const short* yM = mapx.ptr(dy); float* yD = _dst.ptr(dy); for (int dx = 0; dx < dsize.width; ++dx) { float* xyD = yD + cn * dx; - int sx = cvRound(yM[dx * 2]), sy = cvRound(yM[dx * 2 + 1]); + int sx = yM[dx * 2], sy = yM[dx * 2 + 1]; if (sx >= 0 && sx < ssize.width && sy >= 0 && sy < ssize.height) { - const float *S = _src.ptr(sy) + sx * cn; + const float *xyS = _src.ptr(sy) + sx * cn; for (int r = 0; r < cn; ++r) - xyD[r] = S[r]; + xyD[r] = xyS[r]; } else if (borderType != BORDER_TRANSPARENT) { if (borderType == BORDER_CONSTANT) for (int r = 0; r < cn; ++r) - xyD[r] = (float)borderValue[r]; + xyD[r] = borderValue[r]; else { sx = borderInterpolate(sx, ssize.width, borderType); sy = borderInterpolate(sy, ssize.height, borderType); CV_Assert(sx >= 0 && sy >= 0 && sx < ssize.width && sy < ssize.height); - const float *S = _src.ptr(sy) + sx * cn; + const float *xyS = _src.ptr(sy) + sx * cn; for (int r = 0; r < cn; ++r) - xyD[r] = S[r]; + xyD[r] = xyS[r]; } } } @@ -882,50 +847,58 @@ void CV_Remap_Test::remap_nearest(const Mat& _src, Mat& _dst) void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) { - int ksize = 2; - if (interpolation == INTER_CUBIC) + CV_Assert(mapx.type() == CV_16SC2 && mapy.type() == CV_16UC1); + + int ksize; + if (interpolation == INTER_LINEAR) + ksize = 2; + else if (interpolation == INTER_CUBIC) ksize = 4; else if (interpolation == INTER_LANCZOS4) ksize = 8; + else + assert(0); int ofs = (ksize / 2) - 1; CV_Assert(_src.depth() == CV_32F && _dst.type() == _src.type()); - CV_Assert(mapx.type() == CV_32FC2); - Size ssize = _src.size(), dsize = _dst.size(); - int cn = _src.channels(), width1 = std::max(ssize.width - ksize / 2, 0), height1 = std::max(ssize.height - ksize / 2, 0); + int cn = _src.channels(), width1 = std::max(ssize.width - ksize + 1, 0), + height1 = std::max(ssize.height - ksize + 1, 0); float ix[8], w[16]; - interpolate_method inter_func = inter_array[interpolation - (interpolation == INTER_LANCZOS4 ? 2 : 1)]; + internal::interpolate_method inter_func = internal::inter_array[interpolation - (interpolation == INTER_LANCZOS4 ? 2 : 1)]; for (int dy = 0; dy < dsize.height; ++dy) { - const float* yM = mapx.ptr(dy); + const short* yMx = mapx.ptr(dy); + const ushort* yMy = mapy.ptr(dy); + float* yD = _dst.ptr(dy); for (int dx = 0; dx < dsize.width; ++dx) { float* xyD = yD + dx * cn; - float sx = yM[dx * 2], sy = yM[dx * 2 + 1]; + float sx = yMx[dx * 2], sy = yMx[dx * 2 + 1]; int isx = cvFloor(sx), isy = cvFloor(sy); - float fsx = sx - isx, fsy = sy - isy; - inter_func(fsx, w); - inter_func(fsy, w + ksize); + inter_func((yMy[dx] & (INTER_TAB_SIZE - 1)) / static_cast(INTER_TAB_SIZE), w); + inter_func(((yMy[dx] >> INTER_BITS) & (INTER_TAB_SIZE - 1)) / static_cast(INTER_TAB_SIZE), w + ksize); + + isx -= ofs; + isy -= ofs; - if (isx >= ofs && isx < width1 && isy >= ofs && isy < height1) + if (isx >= 0 && isx < width1 && isy >= 0 && isy < height1) { for (int r = 0; r < cn; ++r) { for (int y = 0; y < ksize; ++y) { - const float* xyS = _src.ptr(isy + y - ofs) + isx * cn; + const float* xyS = _src.ptr(isy + y) + isx * cn; ix[y] = 0; for (int i = 0; i < ksize; ++i) ix[y] += w[i] * xyS[i * cn + r]; } - xyD[r] = 0; for (int i = 0; i < ksize; ++i) xyD[r] += w[ksize + i] * ix[i]; @@ -933,70 +906,32 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) } else if (borderType != BORDER_TRANSPARENT) { - if (borderType == BORDER_CONSTANT && - (isx >= ssize.width || isx + ksize <= 0 || - isy >= ssize.height || isy + ksize <= 0)) - for (int r = 0; r < cn; ++r) - xyD[r] = (float)borderValue[r]; - else + int ar_x[8], ar_y[8]; + + for (int k = 0; k < ksize; k++) { - int ar_x[8], ar_y[8]; - - for(int k = 0; k < ksize; k++ ) - { - ar_x[k] = borderInterpolate(isx + k - ofs, ssize.width, borderType) * cn; - ar_y[k] = borderInterpolate(isy + k - ofs, ssize.height, borderType); - - CV_Assert(ar_x[k] < ssize.width * cn && ar_y[k] < ssize.height); - } + ar_x[k] = borderInterpolate(isx + k, ssize.width, borderType) * cn; + ar_y[k] = borderInterpolate(isy + k, ssize.height, borderType); + } - for (int r = 0; r < cn; r++) + for (int r = 0; r < cn; r++) + { + xyD[r] = 0; + for (int i = 0; i < ksize; ++i) { -// if (interpolation == INTER_LINEAR) + ix[i] = 0; + if (ar_y[i] >= 0) { - xyD[r] = 0; - for (int i = 0; i < ksize; ++i) - { - ix[i] = 0; - if (ar_y[i] >= 0) - { - const float* yS = _src.ptr(ar_y[i]); - for (int j = 0; j < ksize; ++j) - if (ar_x[j] >= 0) - { - CV_Assert(ar_x[j] < ssize.width * cn); - ix[i] += yS[ar_x[j] + r] * w[j]; - } - else - ix[i] += borderValue[r] * w[j]; - } - else - for (int j = 0; j < ksize; ++j) - ix[i] += borderValue[r] * w[j]; - } - for (int i = 0; i < ksize; ++i) - xyD[r] += w[ksize + i] * ix[i]; + const float* yS = _src.ptr(ar_y[i]); + for (int j = 0; j < ksize; ++j) + ix[i] += (ar_x[j] >= 0 ? yS[ar_x[j] + r] : borderValue[r]) * w[j]; } -// else -// { -// int ONE = 1; -// if (src.elemSize() == 4) -// ONE <<= 15; -// -// float cv = borderValue[r], sum = cv * ONE; -// for (int i = 0; i < ksize; ++i) -// { -// int yi = ar_y[i]; -// if (yi < 0) -// continue; -// const float* S1 = _src.ptr(ar_y[i]); -// for (int j = 0; j < ksize; ++j) -// if( ar_x[j] >= 0 ) -// sum += (S1[ar_x[j] + r] - cv) * w[j]; -// } -// xyD[r] = sum; -// } + else + for (int j = 0; j < ksize; ++j) + ix[i] += borderValue[r] * w[j]; } + for (int i = 0; i < ksize; ++i) + xyD[r] += w[ksize + i] * ix[i]; } } } @@ -1005,78 +940,12 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) void CV_Remap_Test::validate_results() const { - Mat _dst; - dst.convertTo(_dst, CV_32F); - - Size dsize = _dst.size(), ssize = src.size(); - dsize.width *= _dst.channels(); - - CV_Assert(_dst.size() == erode_dst.size() && dilate_dst.size() == _dst.size()); - CV_Assert(dilate_dst.type() == _dst.type() && _dst.type() == erode_dst.type()); - - for (int y = 0; y < dsize.height; ++y) + CV_ImageWarpBaseTest::validate_results(); + if (cvtest::TS::ptr()->get_err_code() == cvtest::TS::FAIL_BAD_ACCURACY) { - const float* D = _dst.ptr(y); - const float* dD = dilate_dst.ptr(y); - const float* eD = erode_dst.ptr(y); - dD = eD; - - float t = 1.0f; - for (int x = 0; x < dsize.width; ++x) - if ( !(eD[x] - t <= D[x] && D[x] <= dD[x] + t) ) - { - PRINT_TO_LOG("\nnorm(erode_dst, dst): %lf\n", norm(erode_dst, _dst, NORM_INF)); - PRINT_TO_LOG("norm(dst, dilate_dst): %lf\n", norm(_dst, dilate_dst, NORM_INF)); - PRINT_TO_LOG("(dx, dy): (%d, %d)\n", x / _dst.channels() + 1, 1 + y); - PRINT_TO_LOG("Values = (%f, %f, %f)\n", eD[x], D[x], dD[x]); - PRINT_TO_LOG("Interpolation: %s\n", - interpolation_to_string((interpolation | CV_WARP_INVERSE_MAP) ^ CV_WARP_INVERSE_MAP)); - PRINT_TO_LOG("Ssize: (%d, %d)\n", ssize.width, ssize.height); - PRINT_TO_LOG("Dsize: (%d, %d)\n", _dst.cols, dsize.height); - PRINT_TO_LOG("BorderType: %s\n", borderType_to_string()); - PRINT_TO_LOG("BorderValue: (%f, %f, %f, %f)\n", - borderValue[0], borderValue[1], borderValue[2], borderValue[3]); - -#ifdef _SHOW_IMAGE - - std::string w1("Dst"), w2("Erode dst"), w3("Dilate dst"), w4("Diff erode"), w5("Diff dilate"); - - cv::namedWindow(w1, CV_WINDOW_AUTOSIZE); - cv::namedWindow(w2, CV_WINDOW_AUTOSIZE); - cv::namedWindow(w3, CV_WINDOW_AUTOSIZE); - cv::namedWindow(w4, CV_WINDOW_AUTOSIZE); - cv::namedWindow(w5, CV_WINDOW_AUTOSIZE); - - Mat diff_dilate, diff_erode; - absdiff(_dst, erode_dst, diff_erode); - absdiff(_dst, dilate_dst, diff_dilate); - - cv::imshow(w1, dst / 255.); - cv::imshow(w2, erode_dst / 255.); - cv::imshow(w3, dilate_dst / 255.); - cv::imshow(w4, erode_dst / 255.); - cv::imshow(w5, dilate_dst / 255.); - - cv::waitKey(); - -#endif - - /* - const int radius = 3; - int rmin = MAX(y - radius, 0), rmax = MIN(y + radius, dsize.height); - int cmin = MAX(x - radius, 0), cmax = MIN(x + radius, dsize.width); - - cout << "src:\n" << src(Range(rmin, rmax), Range(cmin, cmax)) << endl; - cout << "opencv result:\n" << dst(Range(rmin, rmax), Range(cmin, cmax)) << endl << std::endl; - cout << "erode src:\n" << erode_src(Range(rmin, rmax), Range(cmin, cmax)) << endl; - cout << "erode result:\n" << dilate_dst(Range(rmin, rmax), Range(cmin, cmax)) << endl << std::endl; - cout << "dilate src:\n" << dilate_src(Range(rmin, rmax), Range(cmin, cmax)) << endl; - cout << "dilate result:\n" << dilate_dst(Range(rmin, rmax), Range(cmin, cmax)) << endl << std::endl; - */ - - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } + PRINT_TO_LOG("BorderType: %s\n", borderType_to_string()); + PRINT_TO_LOG("BorderValue: (%f, %f, %f, %f)\n", + borderValue[0], borderValue[1], borderValue[2], borderValue[3]); } } @@ -1094,6 +963,7 @@ public: protected: virtual void generate_test_data(); + virtual void prepare_test_data_for_reference_func(); virtual void run_func(); virtual void run_reference_func(); @@ -1115,14 +985,6 @@ CV_WarpAffine_Test::~CV_WarpAffine_Test() void CV_WarpAffine_Test::generate_test_data() { CV_Remap_Test::generate_test_data(); - CV_Remap_Test::prepare_test_data_for_reference_func(); - - if (src.depth() != CV_32F) - { - Mat tmp; - src.convertTo(tmp, CV_32F); - src = tmp; - } RNG& rng = ts->get_rng(); @@ -1150,19 +1012,24 @@ void CV_WarpAffine_Test::run_func() cv::warpAffine(src, dst, M, dst.size(), interpolation, borderType, borderValue); } +void CV_WarpAffine_Test::prepare_test_data_for_reference_func() +{ + CV_ImageWarpBaseTest::prepare_test_data_for_reference_func(); +} + void CV_WarpAffine_Test::run_reference_func() { - CV_Remap_Test::prepare_test_data_for_reference_func(); + prepare_test_data_for_reference_func(); - warpAffine(erode_src, erode_dst); - warpAffine(dilate_src, dilate_dst); + warpAffine(src, reference_dst); } void CV_WarpAffine_Test::warpAffine(const Mat& _src, Mat& _dst) { Size dsize = _dst.size(); - CV_Assert(_src.size().area() > 0 && dsize.area() > 0); + CV_Assert(_src.size().area() > 0); + CV_Assert(dsize.area() > 0); CV_Assert(_src.type() == _dst.type()); Mat tM; @@ -1175,6 +1042,8 @@ void CV_WarpAffine_Test::warpAffine(const Mat& _src, Mat& _dst) mapx.create(dsize, CV_16SC2); if (inter != INTER_NEAREST) mapy.create(dsize, CV_16SC1); + else + mapy = Mat(); if (!(interpolation & CV_WARP_INVERSE_MAP)) invertAffineTransform(tM.clone(), tM); @@ -1204,6 +1073,7 @@ void CV_WarpAffine_Test::warpAffine(const Mat& _src, Mat& _dst) } } + CV_Assert(mapx.type() == CV_16SC2 && (inter == INTER_NEAREST && !mapy.data || mapy.type() == CV_16SC1)); cv::remap(_src, _dst, mapx, mapy, inter, borderType, borderValue); } @@ -1245,11 +1115,11 @@ void CV_WarpPerspective_Test::generate_test_data() // generating the M 3x3 matrix RNG& rng = ts->get_rng(); - Point2f sp[] = { Point(0, 0), Point(src.cols, 0), Point(0, src.rows), Point(src.cols, src.rows) }; - Point2f dp[] = { Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), - Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), - Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), - Point(rng.uniform(0, src.cols), rng.uniform(0, src.rows)) }; + Point2f sp[] = { Point2f(0, 0), Point2f(src.cols, 0), Point2f(0, src.rows), Point2f(src.cols, src.rows) }; + Point2f dp[] = { Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), + Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), + Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), + Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)) }; M = getPerspectiveTransform(sp, dp); static const int depths[] = { CV_32F, CV_64F }; @@ -1264,17 +1134,17 @@ void CV_WarpPerspective_Test::run_func() void CV_WarpPerspective_Test::run_reference_func() { - CV_Remap_Test::prepare_test_data_for_reference_func(); + prepare_test_data_for_reference_func(); - warpPerspective(erode_src, erode_dst); - warpPerspective(dilate_src, dilate_dst); + warpPerspective(src, reference_dst); } void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) { Size ssize = _src.size(), dsize = _dst.size(); - CV_Assert(ssize.area() > 0 && dsize.area() > 0); + CV_Assert(ssize.area() > 0); + CV_Assert(dsize.area() > 0); CV_Assert(_src.type() == _dst.type()); if (M.depth() != CV_64F) @@ -1298,6 +1168,8 @@ void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) mapx.create(dsize, CV_16SC2); if (inter != INTER_NEAREST) mapy.create(dsize, CV_16SC1); + else + mapy = Mat(); double* tM = M.ptr(0); for (int dy = 0; dy < dsize.height; ++dy) @@ -1327,6 +1199,7 @@ void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) } } + CV_Assert(mapx.type() == CV_16SC2 && (inter == INTER_NEAREST && !mapy.data || mapy.type() == CV_16SC1)); cv::remap(_src, _dst, mapx, mapy, inter, borderType, borderValue); } @@ -1335,6 +1208,6 @@ void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) //////////////////////////////////////////////////////////////////////////////////////////////////////// TEST(Imgproc_Resize_Test, accuracy) { CV_Resize_Test test; test.safe_run(); } -// TEST(Imgproc_Remap_Test, accuracy) { CV_Remap_Test test; test.safe_run(); } +TEST(Imgproc_Remap_Test, accuracy) { CV_Remap_Test test; test.safe_run(); } TEST(Imgproc_WarpAffine_Test, accuracy) { CV_WarpAffine_Test test; test.safe_run(); } TEST(Imgproc_WarpPerspective_Test, accuracy) { CV_WarpPerspective_Test test; test.safe_run(); } From b1b5e392e60c5370d37af9f28ac34117e6d011d9 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Mon, 10 Sep 2012 20:33:35 +0400 Subject: [PATCH 48/97] Fix Android build warnings in new imgproc tests --- modules/imgproc/test/test_imgwarp_strict.cpp | 189 ++++++++++--------- 1 file changed, 95 insertions(+), 94 deletions(-) diff --git a/modules/imgproc/test/test_imgwarp_strict.cpp b/modules/imgproc/test/test_imgwarp_strict.cpp index 59ec2582a5..a5f40df5bd 100644 --- a/modules/imgproc/test/test_imgwarp_strict.cpp +++ b/modules/imgproc/test/test_imgwarp_strict.cpp @@ -58,7 +58,7 @@ namespace internal cvtest::TS::ptr()->printf(cvtest::TS::SUMMARY, buffer); va_end(args); } - + #define PRINT_TO_LOG __wrap_printf_func } @@ -78,7 +78,7 @@ public: CV_ImageWarpBaseTest(); virtual ~CV_ImageWarpBaseTest(); - + virtual void run(int); protected: virtual void generate_test_data(); @@ -89,7 +89,7 @@ protected: virtual void prepare_test_data_for_reference_func(); Size randSize(RNG& rng) const; - + const char* interpolation_to_string(int inter_type) const; int interpolation; @@ -150,7 +150,7 @@ void CV_ImageWarpBaseTest::generate_test_data() int cn = rng.uniform(1, 4); while (cn == 2) cn = rng.uniform(1, 4); - + src.create(ssize, CV_MAKE_TYPE(depth, cn)); // generating the src matrix @@ -170,10 +170,10 @@ void CV_ImageWarpBaseTest::generate_test_data() for (x = cell_size; x < src.cols; x += cell_size) line(src, Point2i(x, 0), Point2i(x, src.rows), Scalar::all(0), 1); } - + // generating an interpolation type interpolation = rng.uniform(0, CV_INTER_LANCZOS4 + 1); - + // generating the dst matrix structure double scale_x = 2, scale_y = 2; if (interpolation == INTER_AREA) @@ -196,13 +196,13 @@ void CV_ImageWarpBaseTest::generate_test_data() scale_y = rng.uniform(0.4, 4.0); } CV_Assert(scale_x > 0.0f && scale_y > 0.0f); - + dsize.width = saturate_cast((ssize.width + scale_x - 1) / scale_x); dsize.height = saturate_cast((ssize.height + scale_y - 1) / scale_y); - + dst = Mat::zeros(dsize, src.type()); reference_dst = Mat::zeros(dst.size(), CV_MAKE_TYPE(CV_32F, dst.channels())); - + if (interpolation == INTER_AREA && (scale_x < 1.0 || scale_y < 1.0)) interpolation = INTER_LINEAR; } @@ -228,7 +228,7 @@ void CV_ImageWarpBaseTest::validate_results() const { Mat _dst; dst.convertTo(_dst, reference_dst.depth()); - + Size dsize = dst.size(), ssize = src.size(); int cn = _dst.channels(); dsize.width *= cn; @@ -241,12 +241,12 @@ void CV_ImageWarpBaseTest::validate_results() const t = 1.0f; else if (interpolation == INTER_AREA) t = 2.0f; - + for (int dy = 0; dy < dsize.height; ++dy) { const float* rD = reference_dst.ptr(dy); const float* D = _dst.ptr(dy); - + for (int dx = 0; dx < dsize.width; ++dx) if (fabs(rD[dx] - D[dx]) > t && // fabs(rD[dx] - D[dx]) < 250.0f && @@ -257,7 +257,7 @@ void CV_ImageWarpBaseTest::validate_results() const PRINT_TO_LOG("Tuple (rD, D): (%f, %f)\n", rD[dx], D[dx]); PRINT_TO_LOG("Dsize: (%d, %d)\n", dsize.width / cn, dsize.height); PRINT_TO_LOG("Ssize: (%d, %d)\n", src.cols, src.rows); - + float scale_x = static_cast(ssize.width) / dsize.width, scale_y = static_cast(ssize.height) / dsize.height; PRINT_TO_LOG("Interpolation: %s\n", interpolation_to_string(interpolation == INTER_AREA && @@ -266,32 +266,32 @@ void CV_ImageWarpBaseTest::validate_results() const PRINT_TO_LOG("Scale (x, y): (%lf, %lf)\n", scale_x, scale_y); PRINT_TO_LOG("Elemsize: %d\n", src.elemSize1()); PRINT_TO_LOG("Channels: %d\n", cn); - + #ifdef SHOW_IMAGE const std::string w1("OpenCV impl (run func)"), w2("Reference func"), w3("Src image"), w4("Diff"); namedWindow(w1, CV_WINDOW_KEEPRATIO); namedWindow(w2, CV_WINDOW_KEEPRATIO); namedWindow(w3, CV_WINDOW_KEEPRATIO); namedWindow(w4, CV_WINDOW_KEEPRATIO); - + Mat diff; absdiff(reference_dst, _dst, diff); - + imshow(w1, dst); imshow(w2, reference_dst); imshow(w3, src); imshow(w4, diff); - + waitKey(); #endif - + const int radius = 3; int rmin = MAX(dy - radius, 0), rmax = MIN(dy + radius, dsize.height); int cmin = MAX(dx / cn - radius, 0), cmax = MIN(dx / cn + radius, dsize.width); - + std::cout << "opencv result:\n" << dst(Range(rmin, rmax), Range(cmin, cmax)) << std::endl; std::cout << "reference result:\n" << reference_dst(Range(rmin, rmax), Range(cmin, cmax)) << std::endl; - + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } @@ -324,7 +324,7 @@ protected: virtual void run_func(); virtual void run_reference_func(); - + private: double scale_x; double scale_y; @@ -333,7 +333,7 @@ private: void resize_generic(); void resize_area(); double getWeight(double a, double b, int x); - + typedef std::vector > dim; void generate_buffer(double scale, dim& _dim); void resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _dim); @@ -356,23 +356,23 @@ namespace internal coeffs[0] = 1.f - x; coeffs[1] = x; } - + void interpolateCubic(float x, float* coeffs) { const float A = -0.75f; - + coeffs[0] = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A; coeffs[1] = ((A + 2)*x - (A + 3))*x*x + 1; coeffs[2] = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1; coeffs[3] = 1.f - coeffs[0] - coeffs[1] - coeffs[2]; } - + void interpolateLanczos4(float x, float* coeffs) { static const double s45 = 0.70710678118654752440084436210485; static const double cs[][2]= {{1, 0}, {-s45, -s45}, {0, 1}, {s45, -s45}, {-1, 0}, {s45, s45}, {0, -1}, {-s45, s45}}; - + if( x < FLT_EPSILON ) { for( int i = 0; i < 8; i++ ) @@ -380,7 +380,7 @@ namespace internal coeffs[3] = 1; return; } - + float sum = 0; double y0=-(x+3)*CV_PI*0.25, s0 = sin(y0), c0=cos(y0); for(int i = 0; i < 8; i++ ) @@ -389,12 +389,12 @@ namespace internal coeffs[i] = (float)((cs[i][0]*s0 + cs[i][1]*c0)/(y*y)); sum += coeffs[i]; } - + sum = 1.f/sum; for(int i = 0; i < 8; i++ ) coeffs[i] *= sum; } - + typedef void (*interpolate_method)(float x, float* coeffs); interpolate_method inter_array[] = { &interpolateLinear, &interpolateCubic, &interpolateLanczos4 }; } @@ -402,10 +402,10 @@ namespace internal void CV_Resize_Test::generate_test_data() { CV_ImageWarpBaseTest::generate_test_data(); - + scale_x = src.cols / static_cast(dst.cols); scale_y = src.rows / static_cast(dst.rows); - + area_fast = interpolation == INTER_AREA && fabs(scale_x - cvRound(scale_x)) < FLT_EPSILON && fabs(scale_y - cvRound(scale_y)) < FLT_EPSILON; @@ -424,7 +424,7 @@ void CV_Resize_Test::run_func() void CV_Resize_Test::run_reference_func() { CV_ImageWarpBaseTest::prepare_test_data_for_reference_func(); - + if (interpolation == INTER_AREA) resize_area(); else @@ -441,28 +441,28 @@ double CV_Resize_Test::getWeight(double a, double b, int x) void CV_Resize_Test::resize_area() { Size ssize = src.size(), dsize = reference_dst.size(); - CV_Assert(ssize.area() > 0 && dsize.area() > 0); + CV_Assert(ssize.area() > 0 && dsize.area() > 0); int cn = src.channels(); - CV_Assert(scale_x >= 1.0 && scale_y >= 1.0); - + CV_Assert(scale_x >= 1.0 && scale_y >= 1.0); + double fsy0 = 0, fsy1 = scale_y; for (int dy = 0; dy < dsize.height; ++dy) { float* yD = reference_dst.ptr(dy); int isy0 = cvFloor(fsy0), isy1 = std::min(cvFloor(fsy1), ssize.height - 1); CV_Assert(isy1 <= ssize.height && isy0 < ssize.height); - + float fsx0 = 0, fsx1 = scale_x; for (int dx = 0; dx < dsize.width; ++dx) { float* xyD = yD + cn * dx; int isx0 = cvFloor(fsx0), isx1 = std::min(ssize.width - 1, cvFloor(fsx1)); - + CV_Assert(isx1 <= ssize.width); CV_Assert(isx0 < ssize.width); - + // for each pixel of dst for (int r = 0; r < cn; ++r) { @@ -480,7 +480,7 @@ void CV_Resize_Test::resize_area() area += w; } } - + CV_Assert(area != 0); // norming pixel xyD[r] /= area; @@ -494,19 +494,19 @@ void CV_Resize_Test::resize_area() // for interpolation type : INTER_LINEAR, INTER_LINEAR, INTER_CUBIC, INTER_LANCZOS4 void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _dim) { - Size dsize = _dst.size(); + Size dsize = _dst.size(); int cn = _dst.channels(); float* yD = _dst.ptr(dy); - + if (interpolation == INTER_NEAREST) { const float* yS = _src.ptr(dy); for (int dx = 0; dx < dsize.width; ++dx) { int isx = _dim[dx].first; - const float* xyS = yS + isx * cn; - float* xyD = yD + dx * cn; - + const float* xyS = yS + isx * cn; + float* xyD = yD + dx * cn; + for (int r = 0; r < cn; ++r) xyD[r] = xyS[r]; } @@ -515,13 +515,13 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d { internal::interpolate_method inter_func = internal::inter_array[interpolation - (interpolation == INTER_LANCZOS4 ? 2 : 1)]; int elemsize = _src.elemSize(); - + int ofs = 0, ksize = 2; if (interpolation == INTER_CUBIC) ofs = 1, ksize = 4; else if (interpolation == INTER_LANCZOS4) ofs = 3, ksize = 8; - + Mat _extended_src_row(1, _src.cols + ksize * 2, _src.type()); uchar* srow = _src.data + dy * _src.step; memcpy(_extended_src_row.data + elemsize * ksize, srow, _src.step); @@ -530,7 +530,7 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d memcpy(_extended_src_row.data + k * elemsize, srow, elemsize); memcpy(_extended_src_row.data + (ksize + k) * elemsize + _src.step, srow + _src.step - elemsize, elemsize); } - + for (int dx = 0; dx < dsize.width; ++dx) { int isx = _dim[dx].first; @@ -559,7 +559,7 @@ void CV_Resize_Test::generate_buffer(double scale, dim& _dim) { int length = _dim.size(); for (int dx = 0; dx < length; ++dx) - { + { double fsx = scale * (dx + 0.5f) - 0.5f; int isx = cvFloor(fsx); _dim[dx] = std::make_pair(isx, fsx - isx); @@ -570,12 +570,12 @@ void CV_Resize_Test::resize_generic() { Size dsize = reference_dst.size(), ssize = src.size(); CV_Assert(dsize.area() > 0 && ssize.area() > 0); - + dim dims[] = { dim(dsize.width), dim(dsize.height) }; if (interpolation == INTER_NEAREST) { for (int dx = 0; dx < dsize.width; ++dx) - dims[0][dx].first = std::min(cvFloor(dx * scale_x), ssize.width - 1); + dims[0][dx].first = std::min(cvFloor(dx * scale_x), ssize.width - 1); for (int dy = 0; dy < dsize.height; ++dy) dims[1][dy].first = std::min(cvFloor(dy * scale_y), ssize.height - 1); } @@ -584,14 +584,14 @@ void CV_Resize_Test::resize_generic() generate_buffer(scale_x, dims[0]); generate_buffer(scale_y, dims[1]); } - + Mat tmp(ssize.height, dsize.width, reference_dst.type()); for (int dy = 0; dy < tmp.rows; ++dy) resize_1d(src, tmp, dy, dims[0]); transpose(tmp, tmp); transpose(reference_dst, reference_dst); - + for (int dy = 0; dy < tmp.rows; ++dy) resize_1d(tmp, reference_dst, dy, dims[1]); transpose(reference_dst, reference_dst); @@ -624,7 +624,7 @@ protected: Scalar borderValue; remap_func funcs[2]; - + private: void remap_nearest(const Mat&, Mat&); void remap_generic(const Mat&, Mat&); @@ -661,7 +661,7 @@ void CV_Remap_Test::generate_test_data() const int n = std::min(std::min(src.cols, src.rows) / 10 + 1, 2); float _n = 0; //static_cast(-n); - + switch (mapx.type()) { case CV_16SC2: @@ -727,7 +727,7 @@ void CV_Remap_Test::generate_test_data() } } break; - + default: assert(0); break; @@ -746,10 +746,10 @@ void CV_Remap_Test::convert_maps() else if (interpolation != INTER_NEAREST) if (mapy.type() != CV_16UC1) mapy.clone().convertTo(mapy, CV_16UC1); - + if (interpolation == INTER_NEAREST) mapy = Mat(); - CV_Assert((interpolation == INTER_NEAREST && !mapy.data || mapy.type() == CV_16UC1 || + CV_Assert(( (interpolation == INTER_NEAREST && !mapy.data) || mapy.type() == CV_16UC1 || mapy.type() == CV_16SC1) && mapx.type() == CV_16SC2); } @@ -793,7 +793,7 @@ void CV_Remap_Test::run_reference_func() if (interpolation == INTER_AREA) interpolation = INTER_LINEAR; - + int index = interpolation == INTER_NEAREST ? 0 : 1; (this->*funcs[index])(src, reference_dst); } @@ -811,7 +811,7 @@ void CV_Remap_Test::remap_nearest(const Mat& _src, Mat& _dst) { const short* yM = mapx.ptr(dy); float* yD = _dst.ptr(dy); - + for (int dx = 0; dx < dsize.width; ++dx) { float* xyD = yD + cn * dx; @@ -848,7 +848,7 @@ void CV_Remap_Test::remap_nearest(const Mat& _src, Mat& _dst) void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) { CV_Assert(mapx.type() == CV_16SC2 && mapy.type() == CV_16UC1); - + int ksize; if (interpolation == INTER_LINEAR) ksize = 2; @@ -857,9 +857,10 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) else if (interpolation == INTER_LANCZOS4) ksize = 8; else - assert(0); + ksize = 0; + assert(ksize); int ofs = (ksize / 2) - 1; - + CV_Assert(_src.depth() == CV_32F && _dst.type() == _src.type()); Size ssize = _src.size(), dsize = _dst.size(); int cn = _src.channels(), width1 = std::max(ssize.width - ksize + 1, 0), @@ -874,7 +875,7 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) const ushort* yMy = mapy.ptr(dy); float* yD = _dst.ptr(dy); - + for (int dx = 0; dx < dsize.width; ++dx) { float* xyD = yD + dx * cn; @@ -883,7 +884,7 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) inter_func((yMy[dx] & (INTER_TAB_SIZE - 1)) / static_cast(INTER_TAB_SIZE), w); inter_func(((yMy[dx] >> INTER_BITS) & (INTER_TAB_SIZE - 1)) / static_cast(INTER_TAB_SIZE), w + ksize); - + isx -= ofs; isy -= ofs; @@ -907,7 +908,7 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) else if (borderType != BORDER_TRANSPARENT) { int ar_x[8], ar_y[8]; - + for (int k = 0; k < ksize; k++) { ar_x[k] = borderInterpolate(isx + k, ssize.width, borderType) * cn; @@ -1001,7 +1002,7 @@ void CV_WarpAffine_Test::generate_test_data() M.convertTo(tmp, depth); M = tmp; } - + // warp_matrix is inverse if (rng.uniform(0., 1.) > 0) interpolation |= CV_WARP_INVERSE_MAP; @@ -1034,7 +1035,7 @@ void CV_WarpAffine_Test::warpAffine(const Mat& _src, Mat& _dst) Mat tM; M.convertTo(tM, CV_64F); - + int inter = interpolation & INTER_MAX; if (inter == INTER_AREA) inter = INTER_LINEAR; @@ -1044,36 +1045,36 @@ void CV_WarpAffine_Test::warpAffine(const Mat& _src, Mat& _dst) mapy.create(dsize, CV_16SC1); else mapy = Mat(); - + if (!(interpolation & CV_WARP_INVERSE_MAP)) invertAffineTransform(tM.clone(), tM); - + const int AB_BITS = MAX(10, (int)INTER_BITS); - const int AB_SCALE = 1 << AB_BITS; + const int AB_SCALE = 1 << AB_BITS; int round_delta = (inter == INTER_NEAREST) ? AB_SCALE / 2 : (AB_SCALE / INTER_TAB_SIZE / 2); - + const double* data_tM = tM.ptr(0); for (int dy = 0; dy < dsize.height; ++dy) { short* yM = mapx.ptr(dy); for (int dx = 0; dx < dsize.width; ++dx, yM += 2) - { - int v1 = saturate_cast(saturate_cast(data_tM[0] * dx * AB_SCALE) + - saturate_cast((data_tM[1] * dy + data_tM[2]) * AB_SCALE) + round_delta), - v2 = saturate_cast(saturate_cast(data_tM[3] * dx * AB_SCALE) + + { + int v1 = saturate_cast(saturate_cast(data_tM[0] * dx * AB_SCALE) + + saturate_cast((data_tM[1] * dy + data_tM[2]) * AB_SCALE) + round_delta), + v2 = saturate_cast(saturate_cast(data_tM[3] * dx * AB_SCALE) + saturate_cast((data_tM[4] * dy + data_tM[5]) * AB_SCALE) + round_delta); v1 >>= AB_BITS - INTER_BITS; v2 >>= AB_BITS - INTER_BITS; yM[0] = saturate_cast(v1 >> INTER_BITS); yM[1] = saturate_cast(v2 >> INTER_BITS); - + if (inter != INTER_NEAREST) mapy.ptr(dy)[dx] = ((v2 & (INTER_TAB_SIZE - 1)) * INTER_TAB_SIZE + (v1 & (INTER_TAB_SIZE - 1))); } } - - CV_Assert(mapx.type() == CV_16SC2 && (inter == INTER_NEAREST && !mapy.data || mapy.type() == CV_16SC1)); + + CV_Assert(mapx.type() == CV_16SC2 && ((inter == INTER_NEAREST && !mapy.data) || mapy.type() == CV_16SC1)); cv::remap(_src, _dst, mapx, mapy, inter, borderType, borderValue); } @@ -1147,24 +1148,24 @@ void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) CV_Assert(dsize.area() > 0); CV_Assert(_src.type() == _dst.type()); - if (M.depth() != CV_64F) - { - Mat tmp; - M.convertTo(tmp, CV_64F); - M = tmp; - } - + if (M.depth() != CV_64F) + { + Mat tmp; + M.convertTo(tmp, CV_64F); + M = tmp; + } + if (!(interpolation & CV_WARP_INVERSE_MAP)) { Mat tmp; invert(M, tmp); M = tmp; } - + int inter = interpolation & INTER_MAX; if (inter == INTER_AREA) inter = INTER_LINEAR; - + mapx.create(dsize, CV_16SC2); if (inter != INTER_NEAREST) mapy.create(dsize, CV_16SC1); @@ -1175,31 +1176,31 @@ void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) for (int dy = 0; dy < dsize.height; ++dy) { short* yMx = mapx.ptr(dy); - + for (int dx = 0; dx < dsize.width; ++dx, yMx += 2) { double den = tM[6] * dx + tM[7] * dy + tM[8]; den = den ? 1.0 / den : 0.0; - + if (inter == INTER_NEAREST) { yMx[0] = saturate_cast((tM[0] * dx + tM[1] * dy + tM[2]) * den); yMx[1] = saturate_cast((tM[3] * dx + tM[4] * dy + tM[5]) * den); continue; } - + den *= INTER_TAB_SIZE; int v0 = saturate_cast((tM[0] * dx + tM[1] * dy + tM[2]) * den); int v1 = saturate_cast((tM[3] * dx + tM[4] * dy + tM[5]) * den); - + yMx[0] = saturate_cast(v0 >> INTER_BITS); yMx[1] = saturate_cast(v1 >> INTER_BITS); - mapy.ptr(dy)[dx] = saturate_cast((v1 & (INTER_TAB_SIZE - 1)) * + mapy.ptr(dy)[dx] = saturate_cast((v1 & (INTER_TAB_SIZE - 1)) * INTER_TAB_SIZE + (v0 & (INTER_TAB_SIZE - 1))); } } - - CV_Assert(mapx.type() == CV_16SC2 && (inter == INTER_NEAREST && !mapy.data || mapy.type() == CV_16SC1)); + + CV_Assert(mapx.type() == CV_16SC2 && ((inter == INTER_NEAREST && !mapy.data) || mapy.type() == CV_16SC1)); cv::remap(_src, _dst, mapx, mapy, inter, borderType, borderValue); } From fbd9bfba47cf3fc4613ab9703877613294b92758 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 10 Sep 2012 21:37:44 +0400 Subject: [PATCH 49/97] fixed some warnings and errors on windows --- modules/imgproc/test/test_imgwarp_strict.cpp | 242 +++++++++---------- 1 file changed, 120 insertions(+), 122 deletions(-) diff --git a/modules/imgproc/test/test_imgwarp_strict.cpp b/modules/imgproc/test/test_imgwarp_strict.cpp index a5f40df5bd..a8142095b1 100644 --- a/modules/imgproc/test/test_imgwarp_strict.cpp +++ b/modules/imgproc/test/test_imgwarp_strict.cpp @@ -58,7 +58,7 @@ namespace internal cvtest::TS::ptr()->printf(cvtest::TS::SUMMARY, buffer); va_end(args); } - + #define PRINT_TO_LOG __wrap_printf_func } @@ -78,7 +78,7 @@ public: CV_ImageWarpBaseTest(); virtual ~CV_ImageWarpBaseTest(); - + virtual void run(int); protected: virtual void generate_test_data(); @@ -89,7 +89,7 @@ protected: virtual void prepare_test_data_for_reference_func(); Size randSize(RNG& rng) const; - + const char* interpolation_to_string(int inter_type) const; int interpolation; @@ -130,8 +130,8 @@ const char* CV_ImageWarpBaseTest::interpolation_to_string(int inter) const Size CV_ImageWarpBaseTest::randSize(RNG& rng) const { Size size; - size.width = saturate_cast(std::exp(rng.uniform(1.0f, 7.0f))); - size.height = saturate_cast(std::exp(rng.uniform(1.0f, 7.0f))); + size.width = static_cast(std::exp(rng.uniform(1.0f, 7.0f))); + size.height = static_cast(std::exp(rng.uniform(1.0f, 7.0f))); return size; } @@ -150,7 +150,7 @@ void CV_ImageWarpBaseTest::generate_test_data() int cn = rng.uniform(1, 4); while (cn == 2) cn = rng.uniform(1, 4); - + src.create(ssize, CV_MAKE_TYPE(depth, cn)); // generating the src matrix @@ -170,10 +170,10 @@ void CV_ImageWarpBaseTest::generate_test_data() for (x = cell_size; x < src.cols; x += cell_size) line(src, Point2i(x, 0), Point2i(x, src.rows), Scalar::all(0), 1); } - + // generating an interpolation type interpolation = rng.uniform(0, CV_INTER_LANCZOS4 + 1); - + // generating the dst matrix structure double scale_x = 2, scale_y = 2; if (interpolation == INTER_AREA) @@ -196,13 +196,13 @@ void CV_ImageWarpBaseTest::generate_test_data() scale_y = rng.uniform(0.4, 4.0); } CV_Assert(scale_x > 0.0f && scale_y > 0.0f); - + dsize.width = saturate_cast((ssize.width + scale_x - 1) / scale_x); dsize.height = saturate_cast((ssize.height + scale_y - 1) / scale_y); - + dst = Mat::zeros(dsize, src.type()); reference_dst = Mat::zeros(dst.size(), CV_MAKE_TYPE(CV_32F, dst.channels())); - + if (interpolation == INTER_AREA && (scale_x < 1.0 || scale_y < 1.0)) interpolation = INTER_LINEAR; } @@ -228,7 +228,7 @@ void CV_ImageWarpBaseTest::validate_results() const { Mat _dst; dst.convertTo(_dst, reference_dst.depth()); - + Size dsize = dst.size(), ssize = src.size(); int cn = _dst.channels(); dsize.width *= cn; @@ -241,12 +241,12 @@ void CV_ImageWarpBaseTest::validate_results() const t = 1.0f; else if (interpolation == INTER_AREA) t = 2.0f; - + for (int dy = 0; dy < dsize.height; ++dy) { const float* rD = reference_dst.ptr(dy); const float* D = _dst.ptr(dy); - + for (int dx = 0; dx < dsize.width; ++dx) if (fabs(rD[dx] - D[dx]) > t && // fabs(rD[dx] - D[dx]) < 250.0f && @@ -257,7 +257,7 @@ void CV_ImageWarpBaseTest::validate_results() const PRINT_TO_LOG("Tuple (rD, D): (%f, %f)\n", rD[dx], D[dx]); PRINT_TO_LOG("Dsize: (%d, %d)\n", dsize.width / cn, dsize.height); PRINT_TO_LOG("Ssize: (%d, %d)\n", src.cols, src.rows); - + float scale_x = static_cast(ssize.width) / dsize.width, scale_y = static_cast(ssize.height) / dsize.height; PRINT_TO_LOG("Interpolation: %s\n", interpolation_to_string(interpolation == INTER_AREA && @@ -266,32 +266,32 @@ void CV_ImageWarpBaseTest::validate_results() const PRINT_TO_LOG("Scale (x, y): (%lf, %lf)\n", scale_x, scale_y); PRINT_TO_LOG("Elemsize: %d\n", src.elemSize1()); PRINT_TO_LOG("Channels: %d\n", cn); - + #ifdef SHOW_IMAGE const std::string w1("OpenCV impl (run func)"), w2("Reference func"), w3("Src image"), w4("Diff"); namedWindow(w1, CV_WINDOW_KEEPRATIO); namedWindow(w2, CV_WINDOW_KEEPRATIO); namedWindow(w3, CV_WINDOW_KEEPRATIO); namedWindow(w4, CV_WINDOW_KEEPRATIO); - + Mat diff; absdiff(reference_dst, _dst, diff); - + imshow(w1, dst); imshow(w2, reference_dst); imshow(w3, src); imshow(w4, diff); - + waitKey(); #endif - + const int radius = 3; int rmin = MAX(dy - radius, 0), rmax = MIN(dy + radius, dsize.height); int cmin = MAX(dx / cn - radius, 0), cmax = MIN(dx / cn + radius, dsize.width); - + std::cout << "opencv result:\n" << dst(Range(rmin, rmax), Range(cmin, cmax)) << std::endl; std::cout << "reference result:\n" << reference_dst(Range(rmin, rmax), Range(cmin, cmax)) << std::endl; - + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } @@ -324,7 +324,7 @@ protected: virtual void run_func(); virtual void run_reference_func(); - + private: double scale_x; double scale_y; @@ -333,7 +333,7 @@ private: void resize_generic(); void resize_area(); double getWeight(double a, double b, int x); - + typedef std::vector > dim; void generate_buffer(double scale, dim& _dim); void resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _dim); @@ -356,23 +356,23 @@ namespace internal coeffs[0] = 1.f - x; coeffs[1] = x; } - + void interpolateCubic(float x, float* coeffs) { const float A = -0.75f; - + coeffs[0] = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A; coeffs[1] = ((A + 2)*x - (A + 3))*x*x + 1; coeffs[2] = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1; coeffs[3] = 1.f - coeffs[0] - coeffs[1] - coeffs[2]; } - + void interpolateLanczos4(float x, float* coeffs) { static const double s45 = 0.70710678118654752440084436210485; static const double cs[][2]= {{1, 0}, {-s45, -s45}, {0, 1}, {s45, -s45}, {-1, 0}, {s45, s45}, {0, -1}, {-s45, s45}}; - + if( x < FLT_EPSILON ) { for( int i = 0; i < 8; i++ ) @@ -380,7 +380,7 @@ namespace internal coeffs[3] = 1; return; } - + float sum = 0; double y0=-(x+3)*CV_PI*0.25, s0 = sin(y0), c0=cos(y0); for(int i = 0; i < 8; i++ ) @@ -389,12 +389,12 @@ namespace internal coeffs[i] = (float)((cs[i][0]*s0 + cs[i][1]*c0)/(y*y)); sum += coeffs[i]; } - + sum = 1.f/sum; for(int i = 0; i < 8; i++ ) coeffs[i] *= sum; } - + typedef void (*interpolate_method)(float x, float* coeffs); interpolate_method inter_array[] = { &interpolateLinear, &interpolateCubic, &interpolateLanczos4 }; } @@ -402,10 +402,10 @@ namespace internal void CV_Resize_Test::generate_test_data() { CV_ImageWarpBaseTest::generate_test_data(); - + scale_x = src.cols / static_cast(dst.cols); scale_y = src.rows / static_cast(dst.rows); - + area_fast = interpolation == INTER_AREA && fabs(scale_x - cvRound(scale_x)) < FLT_EPSILON && fabs(scale_y - cvRound(scale_y)) < FLT_EPSILON; @@ -424,7 +424,7 @@ void CV_Resize_Test::run_func() void CV_Resize_Test::run_reference_func() { CV_ImageWarpBaseTest::prepare_test_data_for_reference_func(); - + if (interpolation == INTER_AREA) resize_area(); else @@ -433,7 +433,7 @@ void CV_Resize_Test::run_reference_func() double CV_Resize_Test::getWeight(double a, double b, int x) { - float w = std::min(x + 1, b) - std::max(x, a); + float w = std::min(static_cast(x + 1), b) - std::max(static_cast(x), a); CV_Assert(w >= 0); return w; } @@ -441,28 +441,28 @@ double CV_Resize_Test::getWeight(double a, double b, int x) void CV_Resize_Test::resize_area() { Size ssize = src.size(), dsize = reference_dst.size(); - CV_Assert(ssize.area() > 0 && dsize.area() > 0); + CV_Assert(ssize.area() > 0 && dsize.area() > 0); int cn = src.channels(); - CV_Assert(scale_x >= 1.0 && scale_y >= 1.0); - + CV_Assert(scale_x >= 1.0 && scale_y >= 1.0); + double fsy0 = 0, fsy1 = scale_y; for (int dy = 0; dy < dsize.height; ++dy) { float* yD = reference_dst.ptr(dy); int isy0 = cvFloor(fsy0), isy1 = std::min(cvFloor(fsy1), ssize.height - 1); CV_Assert(isy1 <= ssize.height && isy0 < ssize.height); - - float fsx0 = 0, fsx1 = scale_x; + + double fsx0 = 0, fsx1 = scale_x; for (int dx = 0; dx < dsize.width; ++dx) { float* xyD = yD + cn * dx; int isx0 = cvFloor(fsx0), isx1 = std::min(ssize.width - 1, cvFloor(fsx1)); - + CV_Assert(isx1 <= ssize.width); CV_Assert(isx0 < ssize.width); - + // for each pixel of dst for (int r = 0; r < cn; ++r) { @@ -476,37 +476,37 @@ void CV_Resize_Test::resize_area() double wy = getWeight(fsy0, fsy1, sy); double wx = getWeight(fsx0, fsx1, sx); double w = wx * wy; - xyD[r] += yS[sx * cn + r] * w; + xyD[r] += static_cast(yS[sx * cn + r] * w); area += w; } } - + CV_Assert(area != 0); // norming pixel - xyD[r] /= area; + xyD[r] = static_cast(xyD[r] / area); } - fsx1 = std::min((fsx0 = fsx1) + scale_x, ssize.width); + fsx1 = std::min((fsx0 = fsx1) + scale_x, static_cast(ssize.width)); } - fsy1 = std::min((fsy0 = fsy1) + scale_y, ssize.height); + fsy1 = std::min((fsy0 = fsy1) + scale_y, static_cast(ssize.height)); } } // for interpolation type : INTER_LINEAR, INTER_LINEAR, INTER_CUBIC, INTER_LANCZOS4 void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _dim) { - Size dsize = _dst.size(); + Size dsize = _dst.size(); int cn = _dst.channels(); float* yD = _dst.ptr(dy); - + if (interpolation == INTER_NEAREST) { const float* yS = _src.ptr(dy); for (int dx = 0; dx < dsize.width; ++dx) { int isx = _dim[dx].first; - const float* xyS = yS + isx * cn; - float* xyD = yD + dx * cn; - + const float* xyS = yS + isx * cn; + float* xyD = yD + dx * cn; + for (int r = 0; r < cn; ++r) xyD[r] = xyS[r]; } @@ -515,13 +515,13 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d { internal::interpolate_method inter_func = internal::inter_array[interpolation - (interpolation == INTER_LANCZOS4 ? 2 : 1)]; int elemsize = _src.elemSize(); - + int ofs = 0, ksize = 2; if (interpolation == INTER_CUBIC) ofs = 1, ksize = 4; else if (interpolation == INTER_LANCZOS4) ofs = 3, ksize = 8; - + Mat _extended_src_row(1, _src.cols + ksize * 2, _src.type()); uchar* srow = _src.data + dy * _src.step; memcpy(_extended_src_row.data + elemsize * ksize, srow, _src.step); @@ -530,7 +530,7 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d memcpy(_extended_src_row.data + k * elemsize, srow, elemsize); memcpy(_extended_src_row.data + (ksize + k) * elemsize + _src.step, srow + _src.step - elemsize, elemsize); } - + for (int dx = 0; dx < dsize.width; ++dx) { int isx = _dim[dx].first; @@ -539,8 +539,8 @@ void CV_Resize_Test::resize_1d(const Mat& _src, Mat& _dst, int dy, const dim& _d float *xyD = yD + dx * cn; const float* xyS = _extended_src_row.ptr(0) + (isx + ksize - ofs) * cn; - float w[ksize]; - inter_func(fsx, w); + float w[8]; + inter_func(static_cast(fsx), w); for (int r = 0; r < cn; ++r) { @@ -559,7 +559,7 @@ void CV_Resize_Test::generate_buffer(double scale, dim& _dim) { int length = _dim.size(); for (int dx = 0; dx < length; ++dx) - { + { double fsx = scale * (dx + 0.5f) - 0.5f; int isx = cvFloor(fsx); _dim[dx] = std::make_pair(isx, fsx - isx); @@ -570,12 +570,12 @@ void CV_Resize_Test::resize_generic() { Size dsize = reference_dst.size(), ssize = src.size(); CV_Assert(dsize.area() > 0 && ssize.area() > 0); - + dim dims[] = { dim(dsize.width), dim(dsize.height) }; if (interpolation == INTER_NEAREST) { for (int dx = 0; dx < dsize.width; ++dx) - dims[0][dx].first = std::min(cvFloor(dx * scale_x), ssize.width - 1); + dims[0][dx].first = std::min(cvFloor(dx * scale_x), ssize.width - 1); for (int dy = 0; dy < dsize.height; ++dy) dims[1][dy].first = std::min(cvFloor(dy * scale_y), ssize.height - 1); } @@ -584,14 +584,14 @@ void CV_Resize_Test::resize_generic() generate_buffer(scale_x, dims[0]); generate_buffer(scale_y, dims[1]); } - + Mat tmp(ssize.height, dsize.width, reference_dst.type()); for (int dy = 0; dy < tmp.rows; ++dy) resize_1d(src, tmp, dy, dims[0]); transpose(tmp, tmp); transpose(reference_dst, reference_dst); - + for (int dy = 0; dy < tmp.rows; ++dy) resize_1d(tmp, reference_dst, dy, dims[1]); transpose(reference_dst, reference_dst); @@ -624,7 +624,7 @@ protected: Scalar borderValue; remap_func funcs[2]; - + private: void remap_nearest(const Mat&, Mat&); void remap_generic(const Mat&, Mat&); @@ -661,7 +661,7 @@ void CV_Remap_Test::generate_test_data() const int n = std::min(std::min(src.cols, src.rows) / 10 + 1, 2); float _n = 0; //static_cast(-n); - + switch (mapx.type()) { case CV_16SC2: @@ -669,8 +669,8 @@ void CV_Remap_Test::generate_test_data() MatIterator_ begin_x = mapx.begin(), end_x = mapx.end(); for ( ; begin_x != end_x; ++begin_x) { - begin_x[0] = rng.uniform(static_cast(_n), std::max(src.cols + n - 1, 0)); - begin_x[1] = rng.uniform(static_cast(_n), std::max(src.rows + n - 1, 0)); + begin_x[0] = static_cast(rng.uniform(static_cast(_n), std::max(src.cols + n - 1, 0))); + begin_x[1] = static_cast(rng.uniform(static_cast(_n), std::max(src.rows + n - 1, 0))); } if (interpolation != INTER_NEAREST) @@ -684,7 +684,7 @@ void CV_Remap_Test::generate_test_data() { MatIterator_ begin_y = mapy.begin(), end_y = mapy.end(); for ( ; begin_y != end_y; ++begin_y) - begin_y[0] = rng.uniform(0, 1024); + begin_y[0] = static_cast(rng.uniform(0, 1024)); } break; @@ -692,7 +692,7 @@ void CV_Remap_Test::generate_test_data() { MatIterator_ begin_y = mapy.begin(), end_y = mapy.end(); for ( ; begin_y != end_y; ++begin_y) - begin_y[0] = rng.uniform(0, 1024); + begin_y[0] = static_cast(rng.uniform(0, 1024)); } break; } @@ -727,7 +727,7 @@ void CV_Remap_Test::generate_test_data() } } break; - + default: assert(0); break; @@ -746,10 +746,10 @@ void CV_Remap_Test::convert_maps() else if (interpolation != INTER_NEAREST) if (mapy.type() != CV_16UC1) mapy.clone().convertTo(mapy, CV_16UC1); - + if (interpolation == INTER_NEAREST) mapy = Mat(); - CV_Assert(( (interpolation == INTER_NEAREST && !mapy.data) || mapy.type() == CV_16UC1 || + CV_Assert(((interpolation == INTER_NEAREST && !mapy.data) || mapy.type() == CV_16UC1 || mapy.type() == CV_16SC1) && mapx.type() == CV_16SC2); } @@ -793,7 +793,7 @@ void CV_Remap_Test::run_reference_func() if (interpolation == INTER_AREA) interpolation = INTER_LINEAR; - + int index = interpolation == INTER_NEAREST ? 0 : 1; (this->*funcs[index])(src, reference_dst); } @@ -811,7 +811,7 @@ void CV_Remap_Test::remap_nearest(const Mat& _src, Mat& _dst) { const short* yM = mapx.ptr(dy); float* yD = _dst.ptr(dy); - + for (int dx = 0; dx < dsize.width; ++dx) { float* xyD = yD + cn * dx; @@ -828,7 +828,7 @@ void CV_Remap_Test::remap_nearest(const Mat& _src, Mat& _dst) { if (borderType == BORDER_CONSTANT) for (int r = 0; r < cn; ++r) - xyD[r] = borderValue[r]; + xyD[r] = saturate_cast(borderValue[r]); else { sx = borderInterpolate(sx, ssize.width, borderType); @@ -848,19 +848,16 @@ void CV_Remap_Test::remap_nearest(const Mat& _src, Mat& _dst) void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) { CV_Assert(mapx.type() == CV_16SC2 && mapy.type() == CV_16UC1); - - int ksize; - if (interpolation == INTER_LINEAR) - ksize = 2; - else if (interpolation == INTER_CUBIC) + + int ksize = 2; + if (interpolation == INTER_CUBIC) ksize = 4; else if (interpolation == INTER_LANCZOS4) ksize = 8; - else - ksize = 0; - assert(ksize); + else if (interpolation != INTER_LINEAR) + assert(0); int ofs = (ksize / 2) - 1; - + CV_Assert(_src.depth() == CV_32F && _dst.type() == _src.type()); Size ssize = _src.size(), dsize = _dst.size(); int cn = _src.channels(), width1 = std::max(ssize.width - ksize + 1, 0), @@ -875,7 +872,7 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) const ushort* yMy = mapy.ptr(dy); float* yD = _dst.ptr(dy); - + for (int dx = 0; dx < dsize.width; ++dx) { float* xyD = yD + dx * cn; @@ -884,7 +881,7 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) inter_func((yMy[dx] & (INTER_TAB_SIZE - 1)) / static_cast(INTER_TAB_SIZE), w); inter_func(((yMy[dx] >> INTER_BITS) & (INTER_TAB_SIZE - 1)) / static_cast(INTER_TAB_SIZE), w + ksize); - + isx -= ofs; isy -= ofs; @@ -908,7 +905,7 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) else if (borderType != BORDER_TRANSPARENT) { int ar_x[8], ar_y[8]; - + for (int k = 0; k < ksize; k++) { ar_x[k] = borderInterpolate(isx + k, ssize.width, borderType) * cn; @@ -925,14 +922,14 @@ void CV_Remap_Test::remap_generic(const Mat& _src, Mat& _dst) { const float* yS = _src.ptr(ar_y[i]); for (int j = 0; j < ksize; ++j) - ix[i] += (ar_x[j] >= 0 ? yS[ar_x[j] + r] : borderValue[r]) * w[j]; + ix[i] += saturate_cast((ar_x[j] >= 0 ? yS[ar_x[j] + r] : borderValue[r]) * w[j]); } else for (int j = 0; j < ksize; ++j) - ix[i] += borderValue[r] * w[j]; + ix[i] += saturate_cast(borderValue[r] * w[j]); } for (int i = 0; i < ksize; ++i) - xyD[r] += w[ksize + i] * ix[i]; + xyD[r] += saturate_cast(w[ksize + i] * ix[i]); } } } @@ -1002,7 +999,7 @@ void CV_WarpAffine_Test::generate_test_data() M.convertTo(tmp, depth); M = tmp; } - + // warp_matrix is inverse if (rng.uniform(0., 1.) > 0) interpolation |= CV_WARP_INVERSE_MAP; @@ -1035,7 +1032,7 @@ void CV_WarpAffine_Test::warpAffine(const Mat& _src, Mat& _dst) Mat tM; M.convertTo(tM, CV_64F); - + int inter = interpolation & INTER_MAX; if (inter == INTER_AREA) inter = INTER_LINEAR; @@ -1045,35 +1042,35 @@ void CV_WarpAffine_Test::warpAffine(const Mat& _src, Mat& _dst) mapy.create(dsize, CV_16SC1); else mapy = Mat(); - + if (!(interpolation & CV_WARP_INVERSE_MAP)) invertAffineTransform(tM.clone(), tM); - + const int AB_BITS = MAX(10, (int)INTER_BITS); - const int AB_SCALE = 1 << AB_BITS; + const int AB_SCALE = 1 << AB_BITS; int round_delta = (inter == INTER_NEAREST) ? AB_SCALE / 2 : (AB_SCALE / INTER_TAB_SIZE / 2); - + const double* data_tM = tM.ptr(0); for (int dy = 0; dy < dsize.height; ++dy) { short* yM = mapx.ptr(dy); for (int dx = 0; dx < dsize.width; ++dx, yM += 2) - { - int v1 = saturate_cast(saturate_cast(data_tM[0] * dx * AB_SCALE) + - saturate_cast((data_tM[1] * dy + data_tM[2]) * AB_SCALE) + round_delta), - v2 = saturate_cast(saturate_cast(data_tM[3] * dx * AB_SCALE) + + { + int v1 = saturate_cast(saturate_cast(data_tM[0] * dx * AB_SCALE) + + saturate_cast((data_tM[1] * dy + data_tM[2]) * AB_SCALE) + round_delta), + v2 = saturate_cast(saturate_cast(data_tM[3] * dx * AB_SCALE) + saturate_cast((data_tM[4] * dy + data_tM[5]) * AB_SCALE) + round_delta); v1 >>= AB_BITS - INTER_BITS; v2 >>= AB_BITS - INTER_BITS; yM[0] = saturate_cast(v1 >> INTER_BITS); yM[1] = saturate_cast(v2 >> INTER_BITS); - + if (inter != INTER_NEAREST) mapy.ptr(dy)[dx] = ((v2 & (INTER_TAB_SIZE - 1)) * INTER_TAB_SIZE + (v1 & (INTER_TAB_SIZE - 1))); } } - + CV_Assert(mapx.type() == CV_16SC2 && ((inter == INTER_NEAREST && !mapy.data) || mapy.type() == CV_16SC1)); cv::remap(_src, _dst, mapx, mapy, inter, borderType, borderValue); } @@ -1099,7 +1096,7 @@ protected: private: void warpPerspective(const Mat&, Mat&); }; - + CV_WarpPerspective_Test::CV_WarpPerspective_Test() : CV_WarpAffine_Test() { @@ -1116,11 +1113,12 @@ void CV_WarpPerspective_Test::generate_test_data() // generating the M 3x3 matrix RNG& rng = ts->get_rng(); - Point2f sp[] = { Point2f(0, 0), Point2f(src.cols, 0), Point2f(0, src.rows), Point2f(src.cols, src.rows) }; - Point2f dp[] = { Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), - Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), - Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)), - Point2f(rng.uniform(0, src.cols), rng.uniform(0, src.rows)) }; + float cols = static_cast(src.cols), rows = static_cast(src.rows); + Point2f sp[] = { Point2f(0.0f, 0.0f), Point2f(cols, 0.0f), Point2f(0.0f, rows), Point2f(cols, rows) }; + Point2f dp[] = { Point2f(rng.uniform(0.0f, cols), rng.uniform(0.0f, rows)), + Point2f(rng.uniform(0.0f, cols), rng.uniform(0.0f, rows)), + Point2f(rng.uniform(0.0f, cols), rng.uniform(0.0f, rows)), + Point2f(rng.uniform(0.0f, cols), rng.uniform(0.0f, rows)) }; M = getPerspectiveTransform(sp, dp); static const int depths[] = { CV_32F, CV_64F }; @@ -1148,24 +1146,24 @@ void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) CV_Assert(dsize.area() > 0); CV_Assert(_src.type() == _dst.type()); - if (M.depth() != CV_64F) - { - Mat tmp; - M.convertTo(tmp, CV_64F); - M = tmp; - } - + if (M.depth() != CV_64F) + { + Mat tmp; + M.convertTo(tmp, CV_64F); + M = tmp; + } + if (!(interpolation & CV_WARP_INVERSE_MAP)) { Mat tmp; invert(M, tmp); M = tmp; } - + int inter = interpolation & INTER_MAX; if (inter == INTER_AREA) inter = INTER_LINEAR; - + mapx.create(dsize, CV_16SC2); if (inter != INTER_NEAREST) mapy.create(dsize, CV_16SC1); @@ -1176,30 +1174,30 @@ void CV_WarpPerspective_Test::warpPerspective(const Mat& _src, Mat& _dst) for (int dy = 0; dy < dsize.height; ++dy) { short* yMx = mapx.ptr(dy); - + for (int dx = 0; dx < dsize.width; ++dx, yMx += 2) { double den = tM[6] * dx + tM[7] * dy + tM[8]; den = den ? 1.0 / den : 0.0; - + if (inter == INTER_NEAREST) { yMx[0] = saturate_cast((tM[0] * dx + tM[1] * dy + tM[2]) * den); yMx[1] = saturate_cast((tM[3] * dx + tM[4] * dy + tM[5]) * den); continue; } - + den *= INTER_TAB_SIZE; int v0 = saturate_cast((tM[0] * dx + tM[1] * dy + tM[2]) * den); int v1 = saturate_cast((tM[3] * dx + tM[4] * dy + tM[5]) * den); - + yMx[0] = saturate_cast(v0 >> INTER_BITS); yMx[1] = saturate_cast(v1 >> INTER_BITS); - mapy.ptr(dy)[dx] = saturate_cast((v1 & (INTER_TAB_SIZE - 1)) * + mapy.ptr(dy)[dx] = saturate_cast((v1 & (INTER_TAB_SIZE - 1)) * INTER_TAB_SIZE + (v0 & (INTER_TAB_SIZE - 1))); } } - + CV_Assert(mapx.type() == CV_16SC2 && ((inter == INTER_NEAREST && !mapy.data) || mapy.type() == CV_16SC1)); cv::remap(_src, _dst, mapx, mapy, inter, borderType, borderValue); } From b3408a9b3a0d899ef52543c99e3d7a79fd34db3c Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 11 Sep 2012 13:56:25 +0400 Subject: [PATCH 50/97] fixed bug #2186 (thanks to Joao Soares for the patch) --- modules/imgproc/src/approx.cpp | 8 ++++++-- modules/imgproc/src/generalized_hough.cpp | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/imgproc/src/approx.cpp b/modules/imgproc/src/approx.cpp index bd0e3dabd3..773c9e74b2 100644 --- a/modules/imgproc/src/approx.cpp +++ b/modules/imgproc/src/approx.cpp @@ -642,13 +642,17 @@ icvApproxPolyDP( CvSeq* src_contour, int header_size, new_count = count = dst_contour->total; for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ ) { - double dx, dy, dist; + double dx, dy, dist, successive_inner_product; CV_READ_SEQ_ELEM( end_pt, reader ); dx = end_pt.x - start_pt.x; dy = end_pt.y - start_pt.y; dist = fabs((pt.x - start_pt.x)*dy - (pt.y - start_pt.y)*dx); - if( dist * dist <= 0.5*eps*(dx*dx + dy*dy) && dx != 0 && dy != 0 ) + successive_inner_product = (pt.x - start_pt.x) * (end_pt.x - pt.x) + + (pt.y - start_pt.y) * (end_pt.y - pt.y); + + if( dist * dist <= 0.5*eps*(dx*dx + dy*dy) && dx != 0 && dy != 0 && + successive_inner_product >= 0 ) { new_count--; *((PT*)reader2.ptr) = start_pt = end_pt; diff --git a/modules/imgproc/src/generalized_hough.cpp b/modules/imgproc/src/generalized_hough.cpp index 4895b55e9d..e07a5f0b38 100644 --- a/modules/imgproc/src/generalized_hough.cpp +++ b/modules/imgproc/src/generalized_hough.cpp @@ -64,10 +64,10 @@ namespace { return fabs(v) > numeric_limits::epsilon(); } - bool notNull(double v) + /*bool notNull(double v) { return fabs(v) > numeric_limits::epsilon(); - } + }*/ class GHT_Pos : public GeneralizedHough { From bb9365104872885fe0530059e68c8f7f5ace87d7 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 11 Sep 2012 14:05:25 +0400 Subject: [PATCH 51/97] added support for bi-level PNG's (patch #2301; thanks to Costantino Grana) --- .../include/opencv2/highgui/highgui.hpp | 1 + .../include/opencv2/highgui/highgui_c.h | 1 + modules/highgui/src/grfmt_png.cpp | 20 +++++++++++++------ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/highgui/include/opencv2/highgui/highgui.hpp b/modules/highgui/include/opencv2/highgui/highgui.hpp index 5c5d24b7f7..2cb1afa2c9 100644 --- a/modules/highgui/include/opencv2/highgui/highgui.hpp +++ b/modules/highgui/include/opencv2/highgui/highgui.hpp @@ -176,6 +176,7 @@ enum IMWRITE_JPEG_QUALITY =1, IMWRITE_PNG_COMPRESSION =16, IMWRITE_PNG_STRATEGY =17, + IMWRITE_PNG_BILEVEL =18, IMWRITE_PNG_STRATEGY_DEFAULT =0, IMWRITE_PNG_STRATEGY_FILTERED =1, IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY =2, diff --git a/modules/highgui/include/opencv2/highgui/highgui_c.h b/modules/highgui/include/opencv2/highgui/highgui_c.h index ddd3003afc..c82d2087b7 100644 --- a/modules/highgui/include/opencv2/highgui/highgui_c.h +++ b/modules/highgui/include/opencv2/highgui/highgui_c.h @@ -217,6 +217,7 @@ enum CV_IMWRITE_JPEG_QUALITY =1, CV_IMWRITE_PNG_COMPRESSION =16, CV_IMWRITE_PNG_STRATEGY =17, + CV_IMWRITE_PNG_BILEVEL =18, CV_IMWRITE_PNG_STRATEGY_DEFAULT =0, CV_IMWRITE_PNG_STRATEGY_FILTERED =1, CV_IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY =2, diff --git a/modules/highgui/src/grfmt_png.cpp b/modules/highgui/src/grfmt_png.cpp index 8f9df830c6..6df6c49885 100644 --- a/modules/highgui/src/grfmt_png.cpp +++ b/modules/highgui/src/grfmt_png.cpp @@ -357,28 +357,33 @@ bool PngEncoder::write( const Mat& img, const vector& params ) png_init_io( png_ptr, f ); } - int compression_level = 0; - int compression_strategy = Z_RLE; + int compression_level = -1; // Invalid value to allow setting 0-9 as valid + int compression_strategy = Z_RLE; // Default strategy + bool isBilevel = false; for( size_t i = 0; i < params.size(); i += 2 ) { if( params[i] == CV_IMWRITE_PNG_COMPRESSION ) { compression_level = params[i+1]; - compression_level = MIN(MAX(compression_level, 0), MAX_MEM_LEVEL); + compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION); } if( params[i] == CV_IMWRITE_PNG_STRATEGY ) { compression_strategy = params[i+1]; compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED); } + if( params[i] == CV_IMWRITE_PNG_BILEVEL ) + { + isBilevel = params[i+1] != 0; + } } if( m_buf || f ) { - if( compression_level > 0 ) + if( compression_level >= 0 ) { - png_set_compression_mem_level( png_ptr, compression_level ); + png_set_compression_level( png_ptr, compression_level ); } else { @@ -389,7 +394,7 @@ bool PngEncoder::write( const Mat& img, const vector& params ) } png_set_compression_strategy(png_ptr, compression_strategy); - png_set_IHDR( png_ptr, info_ptr, width, height, depth == CV_8U ? 8 : 16, + png_set_IHDR( png_ptr, info_ptr, width, height, depth == CV_8U ? isBilevel?1:8 : 16, channels == 1 ? PNG_COLOR_TYPE_GRAY : channels == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, @@ -397,6 +402,9 @@ bool PngEncoder::write( const Mat& img, const vector& params ) png_write_info( png_ptr, info_ptr ); + if (isBilevel) + png_set_packing(png_ptr); + png_set_bgr( png_ptr ); if( !isBigEndian() ) png_set_swap( png_ptr ); From bbeffcc11560b72416779e16b2a4bc3a6e4011ef Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 11 Sep 2012 14:13:37 +0400 Subject: [PATCH 52/97] fixed the case learningRate==0 in BackgroundSubtractorMOG2 (patch #2221; thanks to Will Lucas) --- modules/video/src/bgfg_gaussmix2.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/modules/video/src/bgfg_gaussmix2.cpp b/modules/video/src/bgfg_gaussmix2.cpp index 47fc1192b3..023130f29f 100644 --- a/modules/video/src/bgfg_gaussmix2.cpp +++ b/modules/video/src/bgfg_gaussmix2.cpp @@ -562,18 +562,15 @@ void BackgroundSubtractorMOG2::operator()(InputArray _image, OutputArray _fgmask learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./min( 2*nframes, history ); CV_Assert(learningRate >= 0); - if (learningRate > 0) - { - parallel_for(BlockedRange(0, image.rows), - MOG2Invoker(image, fgmask, - (GMM*)bgmodel.data, - (float*)(bgmodel.data + sizeof(GMM)*nmixtures*image.rows*image.cols), - bgmodelUsedModes.data, nmixtures, (float)learningRate, - (float)varThreshold, - backgroundRatio, varThresholdGen, - fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau, - bShadowDetection, nShadowDetection)); - } + parallel_for(BlockedRange(0, image.rows), + MOG2Invoker(image, fgmask, + (GMM*)bgmodel.data, + (float*)(bgmodel.data + sizeof(GMM)*nmixtures*image.rows*image.cols), + bgmodelUsedModes.data, nmixtures, (float)learningRate, + (float)varThreshold, + backgroundRatio, varThresholdGen, + fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau, + bShadowDetection, nShadowDetection)); } void BackgroundSubtractorMOG2::getBackgroundImage(OutputArray backgroundImage) const From 9ea5b6bb44e3ef6a6ce6c09d82b2a7e2a5a59ae3 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 11 Sep 2012 14:25:34 +0400 Subject: [PATCH 53/97] fixed possible access violation in HSV2RGB (patch #2020) --- modules/imgproc/src/color.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/imgproc/src/color.cpp b/modules/imgproc/src/color.cpp index 8739e42d07..0dc95d17e0 100644 --- a/modules/imgproc/src/color.cpp +++ b/modules/imgproc/src/color.cpp @@ -934,6 +934,11 @@ struct HSV2RGB_f do h -= 6; while( h >= 6 ); sector = cvFloor(h); h -= sector; + if( (unsigned)sector >= 6u ) + { + sector = 0; + h = 0.f; + } tab[0] = v; tab[1] = v*(1.f - s); From f53d94c64505dc86611aa7bd93cb8eac6beb48a6 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 11 Sep 2012 14:32:31 +0400 Subject: [PATCH 54/97] fixed OpenCL detection in Linux (patch #2261) --- cmake/OpenCVDetectOpenCL.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/OpenCVDetectOpenCL.cmake b/cmake/OpenCVDetectOpenCL.cmake index b62072939e..67baaece6e 100644 --- a/cmake/OpenCVDetectOpenCL.cmake +++ b/cmake/OpenCVDetectOpenCL.cmake @@ -56,7 +56,7 @@ else() if(OPENCL_LIB_SEARCH_PATH) find_library(OPENCL_LIBRARY NAMES OpenCL PATHS ${OPENCL_LIB_SEARCH_PATH} NO_DEFAULT_PATH) else() - find_library(OPENCL_LIBRARY NAMES OpenCL PATHS ${OPENCL_LIB_SEARCH_PATH} NO_DEFAULT_PATH) + find_library(OPENCL_LIBRARY NAMES OpenCL) endif() include(FindPackageHandleStandardArgs) From 9956c42804cf7f9e930db69aae243bfa4a9a7e87 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 11 Sep 2012 14:49:56 +0400 Subject: [PATCH 55/97] fixed iterations>1 case in morphological operations (bug #2348; thanks to Andrei Zaharescu for the fix) --- modules/imgproc/src/morph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/src/morph.cpp b/modules/imgproc/src/morph.cpp index 801a2cdff1..761c6331be 100644 --- a/modules/imgproc/src/morph.cpp +++ b/modules/imgproc/src/morph.cpp @@ -1166,8 +1166,8 @@ static void morphOp( int op, InputArray _src, OutputArray _dst, { anchor = Point(anchor.x*iterations, anchor.y*iterations); kernel = getStructuringElement(MORPH_RECT, - Size(ksize.width + iterations*(ksize.width-1), - ksize.height + iterations*(ksize.height-1)), + Size(ksize.width + (iterations-1)*(ksize.width-1), + ksize.height + (iterations-1)*(ksize.height-1)), anchor); iterations = 1; } From e975259c068f8ab2cc5137211d9a9bb90daab065 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 11 Sep 2012 14:59:52 +0400 Subject: [PATCH 56/97] several fixes in gpu module fixed iterations>1 case in morphological operations fixed possible access violation in HSV2RGB fixed the case learningRate==0 in BackgroundSubtractorMOG2 --- modules/gpu/src/bgfg_mog.cpp | 3 +-- modules/gpu/src/filtering.cpp | 6 ++++-- modules/gpu/src/opencv2/gpu/device/detail/color_detail.hpp | 6 ++++++ modules/imgproc/src/generalized_hough.cpp | 4 ---- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/gpu/src/bgfg_mog.cpp b/modules/gpu/src/bgfg_mog.cpp index 94668e817f..67cac76526 100644 --- a/modules/gpu/src/bgfg_mog.cpp +++ b/modules/gpu/src/bgfg_mog.cpp @@ -251,8 +251,7 @@ void cv::gpu::MOG2_GPU::operator()(const GpuMat& frame, GpuMat& fgmask, float le learningRate = learningRate >= 0.0f && nframes_ > 1 ? learningRate : 1.0f / std::min(2 * nframes_, history); CV_Assert(learningRate >= 0.0f); - if (learningRate > 0.0f) - mog2_gpu(frame, frame.channels(), fgmask, bgmodelUsedModes_, weight_, variance_, mean_, learningRate, -learningRate * fCT, bShadowDetection, StreamAccessor::getStream(stream)); + mog2_gpu(frame, frame.channels(), fgmask, bgmodelUsedModes_, weight_, variance_, mean_, learningRate, -learningRate * fCT, bShadowDetection, StreamAccessor::getStream(stream)); } void cv::gpu::MOG2_GPU::getBackgroundImage(GpuMat& backgroundImage, Stream& stream) const diff --git a/modules/gpu/src/filtering.cpp b/modules/gpu/src/filtering.cpp index 1e9fbc5f10..d90a0d8210 100644 --- a/modules/gpu/src/filtering.cpp +++ b/modules/gpu/src/filtering.cpp @@ -576,8 +576,10 @@ namespace else if (iterations > 1 && countNonZero(_kernel) == _kernel.rows * _kernel.cols) { anchor = Point(anchor.x * iterations, anchor.y * iterations); - kernel = getStructuringElement(MORPH_RECT, Size(ksize.width + iterations * (ksize.width - 1), - ksize.height + iterations * (ksize.height - 1)), anchor); + kernel = getStructuringElement(MORPH_RECT, + Size(ksize.width + (iterations - 1) * (ksize.width - 1), + ksize.height + (iterations - 1) * (ksize.height - 1)), + anchor); iterations = 1; } else diff --git a/modules/gpu/src/opencv2/gpu/device/detail/color_detail.hpp b/modules/gpu/src/opencv2/gpu/device/detail/color_detail.hpp index 900958f929..61f9f2c976 100644 --- a/modules/gpu/src/opencv2/gpu/device/detail/color_detail.hpp +++ b/modules/gpu/src/opencv2/gpu/device/detail/color_detail.hpp @@ -1140,6 +1140,12 @@ namespace cv { namespace gpu { namespace device int sector = __float2int_rd(h); h -= sector; + if ( (unsigned)sector >= 6u ) + { + sector = 0; + h = 0.f; + } + float tab[4]; tab[0] = v; tab[1] = v * (1.f - s); diff --git a/modules/imgproc/src/generalized_hough.cpp b/modules/imgproc/src/generalized_hough.cpp index e07a5f0b38..a42de39828 100644 --- a/modules/imgproc/src/generalized_hough.cpp +++ b/modules/imgproc/src/generalized_hough.cpp @@ -64,10 +64,6 @@ namespace { return fabs(v) > numeric_limits::epsilon(); } - /*bool notNull(double v) - { - return fabs(v) > numeric_limits::epsilon(); - }*/ class GHT_Pos : public GeneralizedHough { From 84087a8566f08a92b6d27accf99780e27c805405 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 11 Sep 2012 16:47:25 +0400 Subject: [PATCH 57/97] fixed crash in Python's SURF wrapper (bug #2325) --- .../include/opencv2/features2d/features2d.hpp | 2 +- modules/features2d/src/features2d_init.cpp | 5 ++++ modules/nonfree/test/test_features2d.cpp | 7 +++++ modules/python/src2/cv2.cpp | 1 + modules/python/src2/gen2.py | 29 +++++++++++++------ 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index 774b01c18a..375c861c17 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -264,7 +264,7 @@ public: bool useProvidedKeypoints=false ) const = 0; // Create feature detector and descriptor extractor by name. - static Ptr create( const string& name ); + CV_WRAP static Ptr create( const string& name ); }; /*! diff --git a/modules/features2d/src/features2d_init.cpp b/modules/features2d/src/features2d_init.cpp index fa11b809cb..af3bcd0a5d 100644 --- a/modules/features2d/src/features2d_init.cpp +++ b/modules/features2d/src/features2d_init.cpp @@ -44,6 +44,11 @@ using namespace cv; +Ptr Feature2D::create( const string& feature2DType ) +{ + return Algorithm::create("Feature2D." + feature2DType); +} + /////////////////////// AlgorithmInfo for various detector & descriptors //////////////////////////// /* NOTE!!! diff --git a/modules/nonfree/test/test_features2d.cpp b/modules/nonfree/test/test_features2d.cpp index 34258e082d..eb8f44b8d3 100644 --- a/modules/nonfree/test/test_features2d.cpp +++ b/modules/nonfree/test/test_features2d.cpp @@ -1079,3 +1079,10 @@ TEST(Features2d_BruteForceDescriptorMatcher_knnMatch, regression) } } } + +/*TEST(Features2d_DescriptorExtractorParamTest, regression) +{ + Ptr s = DescriptorExtractor::create("SURF"); + ASSERT_STREQ(s->paramHelp("extended").c_str(), ""); +} +*/ \ No newline at end of file diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index a3fa64d74e..79a226320a 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -121,6 +121,7 @@ typedef vector > vector_vector_DMatch; typedef Ptr Ptr_Algorithm; typedef Ptr Ptr_FeatureDetector; typedef Ptr Ptr_DescriptorExtractor; +typedef Ptr Ptr_Feature2D; typedef Ptr Ptr_DescriptorMatcher; typedef SimpleBlobDetector::Params SimpleBlobDetector_Params; diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index b239f085bc..0687826bee 100644 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -6,6 +6,11 @@ gen_template_check_self = Template(""" if(!PyObject_TypeCheck(self, &pyopencv $cname* _self_ = ${amp}((pyopencv_${name}_t*)self)->v; """) +gen_template_check_self_algo = Template(""" if(!PyObject_TypeCheck(self, &pyopencv_${name}_Type)) + return failmsgp("Incorrect type of self (must be '${name}' or its derivative)"); + $cname* _self_ = dynamic_cast<$cname*>(${amp}((pyopencv_${name}_t*)self)->v.obj); +""") + gen_template_call_constructor = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new if(self) ERRWRAP2(self->v = new $cname""") @@ -70,7 +75,7 @@ gen_template_type_decl = Template(""" struct pyopencv_${name}_t { PyObject_HEAD - Ptr<${cname}> v; + Ptr<${cname1}> v; }; static PyTypeObject pyopencv_${name}_Type = @@ -83,14 +88,14 @@ static PyTypeObject pyopencv_${name}_Type = static void pyopencv_${name}_dealloc(PyObject* self) { - ((pyopencv_${name}_t*)self)->v = NULL; + ((pyopencv_${name}_t*)self)->v.release(); PyObject_Del(self); } static PyObject* pyopencv_from(const Ptr<${cname}>& r) { pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); - new (&(m->v)) Ptr<$cname>(); // init Ptr with placement new + new (&(m->v)) Ptr<$cname1>(); // init Ptr with placement new m->v = r; return (PyObject*)m; } @@ -207,6 +212,7 @@ class ClassInfo(object): self.name = self.wname = normalize_class_name(name) self.ismap = False self.issimple = False + self.isalgorithm = False self.methods = {} self.props = [] self.consts = {} @@ -222,6 +228,8 @@ class ClassInfo(object): #return sys.exit(-1) if self.bases and self.bases[0].startswith("cv::"): self.bases[0] = self.bases[0][4:] + if self.bases and self.bases[0] == "Algorithm": + self.isalgorithm = True for m in decl[2]: if m.startswith("="): self.wname = m[1:] @@ -510,7 +518,10 @@ class FuncInfo(object): amp = "" if selfinfo.issimple: amp = "&" - code += gen_template_check_self.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp) + if selfinfo.isalgorithm: + code += gen_template_check_self_algo.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp) + else: + code += gen_template_check_self.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp) fullname = selfinfo.wname + "." + fullname all_code_variants = [] @@ -675,6 +686,8 @@ class PythonWrapperGenerator(object): % (classinfo.name, classinfo.cname) sys.exit(-1) self.classes[classinfo.name] = classinfo + if classinfo.bases and not classinfo.isalgorithm: + classinfo.isalgorithm = self.classes[classinfo.bases[0]].isalgorithm def add_const(self, name, decl): constinfo = ConstInfo(name, decl[1]) @@ -770,8 +783,9 @@ class PythonWrapperGenerator(object): templ = gen_template_simple_type_decl else: templ = gen_template_type_decl - self.code_types.write(templ.substitute(name=name, wname=classinfo.wname, cname=classinfo.cname)) - + self.code_types.write(templ.substitute(name=name, wname=classinfo.wname, cname=classinfo.cname, + cname1=("cv::Algorithm" if classinfo.isalgorithm else classinfo.cname))) + # register classes in the same order as they have been declared. # this way, base classes will be registered in Python before their derivatives. classlist1 = [(classinfo.decl_idx, name, classinfo) for name, classinfo in classlist] @@ -813,6 +827,3 @@ if __name__ == "__main__": srcfiles = sys.argv[2:] generator = PythonWrapperGenerator() generator.gen(srcfiles, dstdir) - - - From a69959485400d07fe092dd768f9657bec4bf92fc Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 11 Sep 2012 16:10:15 +0400 Subject: [PATCH 58/97] Issue # 2344 OpenCV Manager doesn't tell which pack is used fixed. --- android/service/engine/res/layout/info.xml | 4 +- .../engine/manager/ManagerActivity.java | 121 ++++++++++++------ .../engine/manager/PackageListAdapter.java | 34 +++++ 3 files changed, 115 insertions(+), 44 deletions(-) create mode 100644 android/service/engine/src/org/opencv/engine/manager/PackageListAdapter.java diff --git a/android/service/engine/res/layout/info.xml b/android/service/engine/res/layout/info.xml index 2e2a247273..cd2e874a02 100644 --- a/android/service/engine/res/layout/info.xml +++ b/android/service/engine/res/layout/info.xml @@ -1,7 +1,7 @@ diff --git a/android/service/engine/src/org/opencv/engine/manager/ManagerActivity.java b/android/service/engine/src/org/opencv/engine/manager/ManagerActivity.java index bdf57adda3..d31680559c 100644 --- a/android/service/engine/src/org/opencv/engine/manager/ManagerActivity.java +++ b/android/service/engine/src/org/opencv/engine/manager/ManagerActivity.java @@ -47,11 +47,11 @@ public class ManagerActivity extends Activity mMarket = new MarketConnector(this); - mInstalledPacksAdapter = new SimpleAdapter( + mInstalledPacksAdapter = new PackageListAdapter( this, mListViewItems, R.layout.info, - new String[] {"Name", "Version", "Hardware"}, + new String[] {"Name", "Version", "Hardware", "Activity"}, new int[] {R.id.InfoName,R.id.InfoVersion, R.id.InfoHardware} ); @@ -158,7 +158,7 @@ public class ManagerActivity extends Activity public void onItemClick(AdapterView arg0, View arg1, int arg2, long id) { - mInstalledPackageView.setTag(new Integer((int)id)); + mInstalledPackageView.setTag(Integer.valueOf((int)id)); mActionDialog.show(); } }); @@ -188,6 +188,7 @@ public class ManagerActivity extends Activity @Override protected void onResume() { super.onResume(); + Log.d(TAG, "Filling package list on resume"); FillPackageList(); } @@ -195,15 +196,18 @@ public class ManagerActivity extends Activity protected ListView mInstalledPackageView; protected Button mUpdateEngineButton; protected PackageInfo[] mInstalledPackageInfo; - protected static final ArrayList> mListViewItems = new ArrayList>(); + protected final ArrayList> mListViewItems = new ArrayList>(); + protected static final String TAG = "OpenCV_Manager/Activity"; protected MarketConnector mMarket; - AlertDialog mActionDialog; + protected AlertDialog mActionDialog; + protected HashMap mActivePackageMap = new HashMap(); protected BroadcastReceiver mPackageChangeReciever = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d("OpenCV Manager/Reciever", "Bradcast message " + intent.getAction() + " reciever"); + Log.d("OpenCV Manager/Reciever", "Filling package list on broadcast message"); FillPackageList(); } }; @@ -224,50 +228,83 @@ public class ManagerActivity extends Activity EngineVersionView.setText("not avaliable"); e.printStackTrace(); } + + try { + String path = EngineService.getLibPathByVersion("2.4"); + Log.d(TAG, "2.4 -> " + path); + mActivePackageMap.put("24", path); + path = EngineService.getLibPathByVersion("2.5"); + Log.d(TAG, "2.5 -> " + path); + mActivePackageMap.put("25", path); + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + Log.d(TAG, "Filling package list on service connection"); + FillPackageList(); + unbindService(mServiceConnection); } }; protected void FillPackageList() { - mInstalledPackageInfo = mMarket.GetInstalledOpenCVPackages(); - mListViewItems.clear(); - - for (int i = 0; i < mInstalledPackageInfo.length; i++) - { - // Convert to Items for package list view - HashMap temp = new HashMap(); - temp.put("Name", mMarket.GetApplicationName(mInstalledPackageInfo[i].applicationInfo)); + synchronized (mListViewItems) { + mInstalledPackageInfo = mMarket.GetInstalledOpenCVPackages(); + mListViewItems.clear(); + + for (int i = 0; i < mInstalledPackageInfo.length; i++) + { + // Convert to Items for package list view + HashMap temp = new HashMap(); + String PublicName = mMarket.GetApplicationName(mInstalledPackageInfo[i].applicationInfo); + + int idx = 0; + String OpenCVersion = "unknown"; + String HardwareName = ""; + StringTokenizer tokenizer = new StringTokenizer(mInstalledPackageInfo[i].packageName, "_"); + while (tokenizer.hasMoreTokens()) + { + if (idx == 1) + { + // version of OpenCV + OpenCVersion = tokenizer.nextToken().substring(1); + } + else if (idx >= 2) + { + // hardware options + HardwareName += tokenizer.nextToken() + " "; + } + else + { + tokenizer.nextToken(); + } + idx++; + } + + String ActivePackagePath; + ActivePackagePath = mActivePackageMap.get(OpenCVersion); + Log.d(TAG, OpenCVersion + " -> " + ActivePackagePath); + + if (null != ActivePackagePath && ActivePackagePath.indexOf(mInstalledPackageInfo[i].packageName) >= 0) + { + temp.put("Activity", "y"); + PublicName += " (Active)"; + } + else + { + temp.put("Activity", "n"); + } - int idx = 0; - String OpenCVersion = "unknown"; - String HardwareName = ""; - StringTokenizer tokenizer = new StringTokenizer(mInstalledPackageInfo[i].packageName, "_"); - while (tokenizer.hasMoreTokens()) - { - if (idx == 1) - { - // version of OpenCV - OpenCVersion = tokenizer.nextToken().substring(1); - } - else if (idx >= 2) - { - // hardware options - HardwareName += tokenizer.nextToken() + " "; - } - else - { - tokenizer.nextToken(); - } - idx++; - } - - temp.put("Version", NormalizeVersion(OpenCVersion, mInstalledPackageInfo[i].versionName)); - temp.put("Hardware", HardwareName); - mListViewItems.add(temp); - } - - mInstalledPacksAdapter.notifyDataSetChanged(); + temp.put("Name", PublicName); + temp.put("Version", NormalizeVersion(OpenCVersion, mInstalledPackageInfo[i].versionName)); + temp.put("Hardware", HardwareName); + mListViewItems.add(temp); + } + + mInstalledPacksAdapter.notifyDataSetChanged(); + } } protected String NormalizeVersion(String OpenCVersion, String PackageVersion) diff --git a/android/service/engine/src/org/opencv/engine/manager/PackageListAdapter.java b/android/service/engine/src/org/opencv/engine/manager/PackageListAdapter.java new file mode 100644 index 0000000000..cbc0acad63 --- /dev/null +++ b/android/service/engine/src/org/opencv/engine/manager/PackageListAdapter.java @@ -0,0 +1,34 @@ +package org.opencv.engine.manager; + +import java.util.List; +import java.util.Map; + +import android.content.Context; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SimpleAdapter; + +public class PackageListAdapter extends SimpleAdapter { + + public PackageListAdapter(Context context, + List> data, int resource, String[] from, + int[] to) { + super(context, data, resource, from, to); + // TODO Auto-generated constructor stub + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = super.getView(position, convertView, parent); + @SuppressWarnings("unchecked") + Map item = (Map)getItem(position); + Log.d("PackageListAdapter", item.get("Activity")); + if (item.get("Activity") != "n") + { + view.setBackgroundColor(0x50ffffff); + } + + return view; + } +} From f22a3af4831b54c586b293e4d2d9d09c2292f78f Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 11 Sep 2012 17:04:01 +0400 Subject: [PATCH 59/97] Issue #2345 Adapt OpenCV Manager UI for different screen resolution partially fixed. Layout for small resolution added. Layout for small display resolution added. --- .../service/engine/res/layout-small/info.xml | 61 +++++++++ .../service/engine/res/layout-small/main.xml | 120 ++++++++++++++++++ android/service/engine/res/layout/main.xml | 3 +- .../engine/manager/ManagerActivity.java | 2 +- .../engine/manager/PackageListAdapter.java | 6 +- 5 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 android/service/engine/res/layout-small/info.xml create mode 100644 android/service/engine/res/layout-small/main.xml diff --git a/android/service/engine/res/layout-small/info.xml b/android/service/engine/res/layout-small/info.xml new file mode 100644 index 0000000000..cd2e874a02 --- /dev/null +++ b/android/service/engine/res/layout-small/info.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/service/engine/res/layout-small/main.xml b/android/service/engine/res/layout-small/main.xml new file mode 100644 index 0000000000..6f8611f50c --- /dev/null +++ b/android/service/engine/res/layout-small/main.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +