Commit 23e83f8f authored by Vadim Pisarevsky's avatar Vadim Pisarevsky

--no commit message

--no commit message
parent e4b91918
if(BUILD_SHARED_LIBS)
add_definitions(-DGTEST_CREATE_SHARED_LIBRARY=1)
endif()
define_opencv_module(ts opencv_core)
The new OpenCV test engine is based
on the Google C++ Testing Framework (GTest).
Below is the original GTest README.
-----------------------------------
Google C++ Testing Framework
============================
http://code.google.com/p/googletest/
Overview
--------
Google's framework for writing C++ tests on a variety of platforms
(Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Based on the
xUnit architecture. Supports automatic test discovery, a rich set of
assertions, user-defined assertions, death tests, fatal and non-fatal
failures, various options for running the tests, and XML test report
generation.
Please see the project page above for more information as well as the
mailing list for questions, discussions, and development. There is
also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please
join us!
Requirements for End Users
--------------------------
Google Test is designed to have fairly minimal requirements to build
and use with your projects, but there are some. Currently, we support
Linux, Windows, Mac OS X, and Cygwin. We will also make our best
effort to support other platforms (e.g. Solaris, AIX, and z/OS).
However, since core members of the Google Test project have no access
to these platforms, Google Test may have outstanding issues there. If
you notice any problems on your platform, please notify
googletestframework@googlegroups.com. Patches for fixing them are
even more welcome!
### Linux Requirements ###
These are the base requirements to build and use Google Test from a source
package (as described below):
* GNU-compatible Make or gmake
* POSIX-standard shell
* POSIX(-2) Regular Expressions (regex.h)
* A C++98-standard-compliant compiler
### Windows Requirements ###
* Microsoft Visual C++ 7.1 or newer
### Cygwin Requirements ###
* Cygwin 1.5.25-14 or newer
### Mac OS X Requirements ###
* Mac OS X 10.4 Tiger or newer
* Developer Tools Installed
Also, you'll need CMake 2.6.4 or higher if you want to build the
samples using the provided CMake script, regardless of the platform.
Requirements for Contributors
-----------------------------
We welcome patches. If you plan to contribute a patch, you need to
build Google Test and its own tests from an SVN checkout (described
below), which has further requirements:
* Python version 2.3 or newer (for running some of the tests and
re-generating certain source files from templates)
* CMake 2.6.4 or newer
Getting the Source
------------------
There are two primary ways of getting Google Test's source code: you
can download a stable source release in your preferred archive format,
or directly check out the source from our Subversion (SVN) repositary.
The SVN checkout requires a few extra steps and some extra software
packages on your system, but lets you track the latest development and
make patches much more easily, so we highly encourage it.
### Source Package ###
Google Test is released in versioned source packages which can be
downloaded from the download page [1]. Several different archive
formats are provided, but the only difference is the tools used to
manipulate them, and the size of the resulting file. Download
whichever you are most comfortable with.
[1] http://code.google.com/p/googletest/downloads/list
Once the package is downloaded, expand it using whichever tools you
prefer for that type. This will result in a new directory with the
name "gtest-X.Y.Z" which contains all of the source code. Here are
some examples on Linux:
tar -xvzf gtest-X.Y.Z.tar.gz
tar -xvjf gtest-X.Y.Z.tar.bz2
unzip gtest-X.Y.Z.zip
### SVN Checkout ###
To check out the main branch (also known as the "trunk") of Google
Test, run the following Subversion command:
svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn
Setting up the Build
--------------------
To build Google Test and your tests that use it, you need to tell your
build system where to find its headers and source files. The exact
way to do it depends on which build system you use, and is usually
straightforward.
### Generic Build Instructions ###
Suppose you put Google Test in directory ${GTEST_DIR}. To build it,
create a library build target (or a project as called by Visual Studio
and Xcode) to compile
${GTEST_DIR}/src/gtest-all.cc
with
${GTEST_DIR}/include and ${GTEST_DIR}
in the header search path. Assuming a Linux-like system and gcc,
something like the following will do:
g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -c ${GTEST_DIR}/src/gtest-all.cc
ar -rv libgtest.a gtest-all.o
Next, you should compile your test source file with
${GTEST_DIR}/include in the header search path, and link it with gtest
and any other necessary libraries:
g++ -I${GTEST_DIR}/include path/to/your_test.cc libgtest.a -o your_test
As an example, the make/ directory contains a Makefile that you can
use to build Google Test on systems where GNU make is available
(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google
Test's own tests. Instead, it just builds the Google Test library and
a sample test. You can use it as a starting point for your own build
script.
If the default settings are correct for your environment, the
following commands should succeed:
cd ${GTEST_DIR}/make
make
./sample1_unittest
If you see errors, try to tweak the contents of make/Makefile to make
them go away. There are instructions in make/Makefile on how to do
it.
### Using CMake ###
Google Test comes with a CMake build script (CMakeLists.txt) that can
be used on a wide range of platforms ("C" stands for cross-platofrm.).
If you don't have CMake installed already, you can download it for
free from http://www.cmake.org/.
CMake works by generating native makefiles or build projects that can
be used in the compiler environment of your choice. The typical
workflow starts with:
mkdir mybuild # Create a directory to hold the build output.
cd mybuild
cmake ${GTEST_DIR} # Generate native build scripts.
If you want to build Google Test's samples, you should replace the
last command with
cmake -Dbuild_gtest_samples=ON ${GTEST_DIR}
If you are on a *nix system, you should now see a Makefile in the
current directory. Just type 'make' to build gtest.
If you use Windows and have Vistual Studio installed, a gtest.sln file
and several .vcproj files will be created. You can then build them
using Visual Studio.
On Mac OS X with Xcode installed, a .xcodeproj file will be generated.
### Legacy Build Scripts ###
Before settling on CMake, we have been providing hand-maintained build
projects/scripts for Visual Studio, Xcode, and Autotools. While we
continue to provide them for convenience, they are not actively
maintained any more. We highly recommend that you follow the
instructions in the previous two sections to integrate Google Test
with your existing build system.
If you still need to use the legacy build scripts, here's how:
The msvc\ folder contains two solutions with Visual C++ projects.
Open the gtest.sln or gtest-md.sln file using Visual Studio, and you
are ready to build Google Test the same way you build any Visual
Studio project. Files that have names ending with -md use DLL
versions of Microsoft runtime libraries (the /MD or the /MDd compiler
option). Files without that suffix use static versions of the runtime
libraries (the /MT or the /MTd option). Please note that one must use
the same option to compile both gtest and the test code. If you use
Visual Studio 2005 or above, we recommend the -md version as /MD is
the default for new projects in these versions of Visual Studio.
On Mac OS X, open the gtest.xcodeproj in the xcode/ folder using
Xcode. Build the "gtest" target. The universal binary framework will
end up in your selected build directory (selected in the Xcode
"Preferences..." -> "Building" pane and defaults to xcode/build).
Alternatively, at the command line, enter:
xcodebuild
This will build the "Release" configuration of gtest.framework in your
default build location. See the "xcodebuild" man page for more
information about building different configurations and building in
different locations.
Tweaking Google Test
--------------------
Google Test can be used in diverse environments. The default
configuration may not work (or may not work well) out of the box in
some environments. However, you can easily tweak Google Test by
defining control macros on the compiler command line. Generally,
these macros are named like GTEST_XYZ and you define them to either 1
or 0 to enable or disable a certain feature.
We list the most frequently used macros below. For a complete list,
see file include/gtest/internal/gtest-port.h.
### Choosing a TR1 Tuple Library ###
Some Google Test features require the C++ Technical Report 1 (TR1)
tuple library, which is not yet available with all compilers. The
good news is that Google Test implements a subset of TR1 tuple that's
enough for its own need, and will automatically use this when the
compiler doesn't provide TR1 tuple.
Usually you don't need to care about which tuple library Google Test
uses. However, if your project already uses TR1 tuple, you need to
tell Google Test to use the same TR1 tuple library the rest of your
project uses, or the two tuple implementations will clash. To do
that, add
-DGTEST_USE_OWN_TR1_TUPLE=0
to the compiler flags while compiling Google Test and your tests. If
you want to force Google Test to use its own tuple library, just add
-DGTEST_USE_OWN_TR1_TUPLE=1
to the compiler flags instead.
If you don't want Google Test to use tuple at all, add
-DGTEST_HAS_TR1_TUPLE=0
and all features using tuple will be disabled.
### Multi-threaded Tests ###
Google Test is thread-safe where the pthread library is available.
After #include <gtest/gtest.h>, you can check the GTEST_IS_THREADSAFE
macro to see whether this is the case (yes if the macro is #defined to
1, no if it's undefined.).
If Google Test doesn't correctly detect whether pthread is available
in your environment, you can force it with
-DGTEST_HAS_PTHREAD=1
or
-DGTEST_HAS_PTHREAD=0
When Google Test uses pthread, you may need to add flags to your
compiler and/or linker to select the pthread library, or you'll get
link errors. If you use the CMake script or the deprecated Autotools
script, this is taken care of for you. If you use your own build
script, you'll need to read your compiler and linker's manual to
figure out what flags to add.
### As a Shared Library (DLL) ###
Google Test is compact, so most users can build and link it as a
static library for the simplicity. You can choose to use Google Test
as a shared library (known as a DLL on Windows) if you prefer.
To compile gtest as a shared library, add
-DGTEST_CREATE_SHARED_LIBRARY=1
to the compiler flags. You'll also need to tell the linker to produce
a shared library instead - consult your linker's manual for how to do
it.
To compile your tests that use the gtest shared library, add
-DGTEST_LINKED_AS_SHARED_LIBRARY=1
to the compiler flags.
### Avoiding Macro Name Clashes ###
In C++, macros don't obey namespaces. Therefore two libraries that
both define a macro of the same name will clash if you #include both
definitions. In case a Google Test macro clashes with another
library, you can force Google Test to rename its macro to avoid the
conflict.
Specifically, if both Google Test and some other code define macro
FOO, you can add
-DGTEST_DONT_DEFINE_FOO=1
to the compiler flags to tell Google Test to change the macro's name
from FOO to GTEST_FOO. Currently FOO can be FAIL, SUCCEED, or TEST.
For example, with -DGTEST_DONT_DEFINE_TEST=1, you'll need to write
GTEST_TEST(SomeTest, DoesThis) { ... }
instead of
TEST(SomeTest, DoesThis) { ... }
in order to define a test.
Upgrating from an Earlier Version
---------------------------------
We strive to keep Google Test releases backward compatible.
Sometimes, though, we have to make some breaking changes for the
users' long-term benefits. This section describes what you'll need to
do if you are upgrading from an earlier version of Google Test.
### Upgrading from 1.3.0 or Earlier ###
You may need to explicitly enable or disable Google Test's own TR1
tuple library. See the instructions in section "Choosing a TR1 Tuple
Library".
### Upgrading from 1.4.0 or Earlier ###
The Autotools build script (configure + make) is no longer officially
supportted. You are encouraged to migrate to your own build system or
use CMake. If you still need to use Autotools, you can find
instructions in the README file from Google Test 1.4.0.
On platforms where the pthread library is available, Google Test uses
it in order to be thread-safe. See the "Multi-threaded Tests" section
for what this means to your build script.
If you use Microsoft Visual C++ 7.1 with exceptions disabled, Google
Test will no longer compile. This should affect very few people, as a
large portion of STL (including <string>) doesn't compile in this mode
anyway. We decided to stop supporting it in order to greatly simplify
Google Test's implementation.
Developing Google Test
----------------------
This section discusses how to make your own changes to Google Test.
### Testing Google Test Itself ###
To make sure your changes work as intended and don't break existing
functionality, you'll want to compile and run Google Test's own tests.
For that you can use CMake:
mkdir mybuild
cd mybuild
cmake -Dbuild_all_gtest_tests=ON ${GTEST_DIR}
Make sure you have Python installed, as some of Google Test's tests
are written in Python. If the cmake command complains about not being
able to find Python ("Could NOT find PythonInterp (missing:
PYTHON_EXECUTABLE)"), try telling it explicitly where your Python
executable can be found:
cmake -DPYTHON_EXECUTABLE=path/to/python -Dbuild_all_gtest_tests=ON \
${GTEST_DIR}
Next, you can build Google Test and all of its own tests. On *nix,
this is usually done by 'make'. To run the tests, do
make test
All tests should pass.
### Regenerating Source Files ###
Some of Google Test's source files are generated from templates (not
in the C++ sense) using a script. A template file is named FOO.pump,
where FOO is the name of the file it will generate. For example, the
file include/gtest/internal/gtest-type-util.h.pump is used to generate
gtest-type-util.h in the same directory.
Normally you don't need to worry about regenerating the source files,
unless you need to modify them. In that case, you should modify the
corresponding .pump files instead and run the pump.py Python script to
regenerate them. You can find pump.py in the scripts/ directory.
Read the Pump manual [2] for how to use it.
[2] http://code.google.com/p/googletest/wiki/PumpManual
### Contributing a Patch ###
We welcome patches. Please read the Google Test developer's guide [3]
for how you can contribute. In particular, make sure you have signed
the Contributor License Agreement, or we won't be able to accept the
patch.
[3] http://code.google.com/p/googletest/wiki/GoogleTestDevGuide
Happy testing!
#ifndef __OPENCV_GTESTCV_HPP__
#define __OPENCV_GTESTCV_HPP__
#include "opencv2/ts/ts_gtest.h"
#include "opencv2/core/core.hpp"
namespace cvtest
{
using std::vector;
using std::string;
using cv::RNG;
using cv::Mat;
using cv::Scalar;
using cv::Size;
using cv::Point;
using cv::Rect;
class CV_EXPORTS TS;
enum
{
TYPE_MASK_8U = 1 << CV_8U,
TYPE_MASK_8S = 1 << CV_8S,
TYPE_MASK_16U = 1 << CV_16U,
TYPE_MASK_16S = 1 << CV_16S,
TYPE_MASK_32S = 1 << CV_32S,
TYPE_MASK_32F = 1 << CV_32F,
TYPE_MASK_64F = 1 << CV_64F,
TYPE_MASK_ALL = (TYPE_MASK_64F<<1)-1,
TYPE_MASK_ALL_BUT_8S = TYPE_MASK_ALL & ~TYPE_MASK_8S,
TYPE_MASK_FLT = TYPE_MASK_32F + TYPE_MASK_64F
};
CV_EXPORTS int64 readSeed(const char* str);
CV_EXPORTS void randUni( RNG& rng, Mat& a, const Scalar& param1, const Scalar& param2 );
inline unsigned randInt( RNG& rng )
{
return (unsigned)rng;
}
inline double randReal( RNG& rng )
{
return (double)rng;
}
CV_EXPORTS const char* getTypeName( int type );
CV_EXPORTS int typeByName( const char* type_name );
CV_EXPORTS string vec2str(const string& sep, const int* v, size_t nelems);
inline int clipInt( int val, int min_val, int max_val )
{
if( val < min_val )
val = min_val;
if( val > max_val )
val = max_val;
return val;
}
CV_EXPORTS double getMinVal(int depth);
CV_EXPORTS double getMaxVal(int depth);
CV_EXPORTS Size randomSize(RNG& rng, double maxSizeLog);
CV_EXPORTS void randomSize(RNG& rng, int minDims, int maxDims, double maxSizeLog, vector<int>& sz);
CV_EXPORTS int randomType(RNG& rng, int typeMask, int minChannels, int maxChannels);
CV_EXPORTS Mat randomMat(RNG& rng, Size size, int type, double minVal, double maxVal, bool useRoi);
CV_EXPORTS Mat randomMat(RNG& rng, const vector<int>& size, int type, double minVal, double maxVal, bool useRoi);
CV_EXPORTS void add(const Mat& a, double alpha, const Mat& b, double beta,
Scalar gamma, Mat& c, int ctype, bool calcAbs=false);
CV_EXPORTS void multiply(const Mat& a, const Mat& b, Mat& c, double alpha=1);
CV_EXPORTS void divide(const Mat& a, const Mat& b, Mat& c, double alpha=1);
CV_EXPORTS void convert(const Mat& src, Mat& dst, int dtype, double alpha=1, double beta=0);
CV_EXPORTS void copy(const Mat& src, Mat& dst, const Mat& mask=Mat(), bool invertMask=false);
CV_EXPORTS void set(Mat& dst, const Scalar& gamma, const Mat& mask=Mat());
// working with multi-channel arrays
CV_EXPORTS void extract( const Mat& a, Mat& plane, int coi );
CV_EXPORTS void insert( const Mat& plane, Mat& a, int coi );
// checks that the array does not have NaNs and/or Infs and all the elements are
// within [min_val,max_val). idx is the index of the first "bad" element.
CV_EXPORTS int check( const Mat& data, double min_val, double max_val, vector<int>* idx );
// modifies values that are close to zero
CV_EXPORTS void patchZeros( Mat& mat, double level );
CV_EXPORTS void transpose(const Mat& src, Mat& dst);
CV_EXPORTS void erode(const Mat& src, Mat& dst, const Mat& _kernel, Point anchor=Point(-1,-1),
int borderType=IPL_BORDER_CONSTANT, const Scalar& borderValue=Scalar());
CV_EXPORTS void dilate(const Mat& src, Mat& dst, const Mat& _kernel, Point anchor=Point(-1,-1),
int borderType=IPL_BORDER_CONSTANT, const Scalar& borderValue=Scalar());
CV_EXPORTS void filter2D(const Mat& src, Mat& dst, int ddepth, const Mat& kernel,
Point anchor, double delta, int borderType,
const Scalar& borderValue=Scalar());
CV_EXPORTS void copyMakeBorder(const Mat& src, Mat& dst, int top, int bottom, int left, int right,
int borderType, const Scalar& borderValue=Scalar());
CV_EXPORTS Mat calcSobelKernel2D( int dx, int dy, int apertureSize, int origin=0 );
CV_EXPORTS Mat calcLaplaceKernel2D( int aperture_size );
CV_EXPORTS void initUndistortMap( const Mat& a, const Mat& k, Size sz, Mat& mapx, Mat& mapy );
CV_EXPORTS void minMaxLoc(const Mat& src, double* minval, double* maxval,
vector<int>* minloc, vector<int>* maxloc, const Mat& mask=Mat());
CV_EXPORTS double norm(const Mat& src, int normType, const Mat& mask=Mat());
CV_EXPORTS double norm(const Mat& src1, const Mat& src2, int normType, const Mat& mask=Mat());
CV_EXPORTS Scalar mean(const Mat& src, const Mat& mask=Mat());
CV_EXPORTS bool cmpUlps(const Mat& data, const Mat& refdata, int expMaxDiff, double* realMaxDiff, vector<int>* idx);
// compares two arrays. max_diff is the maximum actual difference,
// success_err_level is maximum allowed difference, idx is the index of the first
// element for which difference is >success_err_level
// (or index of element with the maximum difference)
CV_EXPORTS int cmpEps( const Mat& data, const Mat& refdata, double* max_diff,
double success_err_level, vector<int>* idx,
bool element_wise_relative_error );
// a wrapper for the previous function. in case of error prints the message to log file.
CV_EXPORTS int cmpEps2( TS* ts, const Mat& data, const Mat& refdata, double success_err_level,
bool element_wise_relative_error, const char* desc );
CV_EXPORTS int cmpEps2_64f( TS* ts, const double* val, const double* refval, int len,
double eps, const char* param_name );
CV_EXPORTS void logicOp(const Mat& src1, const Mat& src2, Mat& dst, char c);
CV_EXPORTS void logicOp(const Mat& src, const Scalar& s, Mat& dst, char c);
CV_EXPORTS void min(const Mat& src1, const Mat& src2, Mat& dst);
CV_EXPORTS void min(const Mat& src, double s, Mat& dst);
CV_EXPORTS void max(const Mat& src1, const Mat& src2, Mat& dst);
CV_EXPORTS void max(const Mat& src, double s, Mat& dst);
CV_EXPORTS void compare(const Mat& src1, const Mat& src2, Mat& dst, int cmpop);
CV_EXPORTS void compare(const Mat& src, double s, Mat& dst, int cmpop);
CV_EXPORTS void gemm(const Mat& src1, const Mat& src2, double alpha,
const Mat& src3, double beta, Mat& dst, int flags);
CV_EXPORTS void transform( const Mat& src, Mat& dst, const Mat& transmat, const Mat& shift );
CV_EXPORTS double crossCorr(const Mat& src1, const Mat& src2);
struct CV_EXPORTS MatInfo
{
MatInfo(const Mat& _m) : m(&_m) {}
const Mat* m;
};
CV_EXPORTS std::ostream& operator << (std::ostream& out, const MatInfo& m);
struct CV_EXPORTS MatComparator
{
public:
MatComparator(double maxdiff, int context);
::testing::AssertionResult operator()(const char* expr1, const char* expr2,
const Mat& m1, const Mat& m2);
double maxdiff;
double realmaxdiff;
vector<int> loc0;
int context;
};
class BaseTest;
class TS;
class CV_EXPORTS BaseTest
{
public:
// constructor(s) and destructor
BaseTest();
virtual ~BaseTest();
// the main procedure of the test
virtual void run( int start_from );
// the wrapper for run that cares of exceptions
virtual void safe_run( int start_from=0 );
const string& get_name() const { return name; }
// returns true if and only if the different test cases do not depend on each other
// (so that test system could get right to a problematic test case)
virtual bool can_do_fast_forward();
// deallocates all the memory.
// called by init() (before initialization) and by the destructor
virtual void clear();
protected:
int test_case_count; // the total number of test cases
// read test params
virtual int read_params( CvFileStorage* fs );
// returns the number of tests or -1 if it is unknown a-priori
virtual int get_test_case_count();
// prepares data for the next test case. rng seed is updated by the function
virtual int prepare_test_case( int test_case_idx );
// checks if the test output is valid and accurate
virtual int validate_test_results( int test_case_idx );
// calls the tested function. the method is called from run_test_case()
virtual void run_func(); // runs tested func(s)
// updates progress bar
virtual int update_progress( int progress, int test_case_idx, int count, double dt );
// finds test parameter
const CvFileNode* find_param( CvFileStorage* fs, const char* param_name );
// name of the test (it is possible to locate a test by its name)
string name;
// pointer to the system that includes the test
TS* ts;
};
/*****************************************************************************************\
* Information about a failed test *
\*****************************************************************************************/
struct TestInfo
{
TestInfo();
// pointer to the test
BaseTest* test;
// failure code (CV_FAIL*)
int code;
// seed value right before the data for the failed test case is prepared.
uint64 rng_seed;
// seed value right before running the test
uint64 rng_seed0;
// index of test case, can be then passed to BaseTest::proceed_to_test_case()
int test_case_idx;
};
/*****************************************************************************************\
* Base Class for test system *
\*****************************************************************************************/
// common parameters:
struct CV_EXPORTS TSParams
{
TSParams();
// RNG seed, passed to and updated by every test executed.
uint64 rng_seed;
// whether to use IPP, MKL etc. or not
bool use_optimized;
// extensivity of the tests, scale factor for test_case_count
double test_case_count_scale;
};
class CV_EXPORTS TS
{
public:
// constructor(s) and destructor
TS();
virtual ~TS();
enum
{
NUL=0,
SUMMARY_IDX=0,
SUMMARY=1 << SUMMARY_IDX,
LOG_IDX=1,
LOG=1 << LOG_IDX,
CSV_IDX=2,
CSV=1 << CSV_IDX,
CONSOLE_IDX=3,
CONSOLE=1 << CONSOLE_IDX,
MAX_IDX=4
};
static TS* ptr();
// initialize test system before running the first test
virtual void init( const string& modulename );
// low-level printing functions that are used by individual tests and by the system itself
virtual void printf( int streams, const char* fmt, ... );
virtual void vprintf( int streams, const char* fmt, va_list arglist );
// updates the context: current test, test case, rng state
virtual void update_context( BaseTest* test, int test_case_idx, bool update_ts_context );
const TestInfo* get_current_test_info() { return &current_test_info; }
// sets information about a failed test
virtual void set_failed_test_info( int fail_code );
virtual void set_gtest_status();
// test error codes
enum
{
// everything is Ok
OK=0,
// generic error: stub value to be used
// temporarily if the error's cause is unknown
FAIL_GENERIC=-1,
// the test is missing some essential data to proceed further
FAIL_MISSING_TEST_DATA=-2,
// the tested function raised an error via cxcore error handler
FAIL_ERROR_IN_CALLED_FUNC=-3,
// an exception has been raised;
// for memory and arithmetic exception
// there are two specialized codes (see below...)
FAIL_EXCEPTION=-4,
// a memory exception
// (access violation, access to missed page, stack overflow etc.)
FAIL_MEMORY_EXCEPTION=-5,
// arithmetic exception (overflow, division by zero etc.)
FAIL_ARITHM_EXCEPTION=-6,
// the tested function corrupted memory (no exception have been raised)
FAIL_MEMORY_CORRUPTION_BEGIN=-7,
FAIL_MEMORY_CORRUPTION_END=-8,
// the tested function (or test ifself) do not deallocate some memory
FAIL_MEMORY_LEAK=-9,
// the tested function returned invalid object, e.g. matrix, containing NaNs,
// structure with NULL or out-of-range fields (while it should not)
FAIL_INVALID_OUTPUT=-10,
// the tested function returned valid object, but it does not match to
// the original (or produced by the test) object
FAIL_MISMATCH=-11,
// the tested function returned valid object (a single number or numerical array),
// but it differs too much from the original (or produced by the test) object
FAIL_BAD_ACCURACY=-12,
// the tested function hung. Sometimes, can be determined by unexpectedly long
// processing time (in this case there should be possibility to interrupt such a function
FAIL_HANG=-13,
// unexpected responce on passing bad arguments to the tested function
// (the function crashed, proceed succesfully (while it should not), or returned
// error code that is different from what is expected)
FAIL_BAD_ARG_CHECK=-14,
// the test data (in whole or for the particular test case) is invalid
FAIL_INVALID_TEST_DATA=-15,
// the test has been skipped because it is not in the selected subset of the tests to run,
// because it has been run already within the same run with the same parameters, or because
// of some other reason and this is not considered as an error.
// Normally TS::run() (or overrided method in the derived class) takes care of what
// needs to be run, so this code should not occur.
SKIPPED=1
};
// get file storage
CvFileStorage* get_file_storage();
// get RNG to generate random input data for a test
RNG& get_rng() { return rng; }
// returns the current error code
int get_err_code() { return current_test_info.code; }
// returns the test extensivity scale
double get_test_case_count_scale() { return params.test_case_count_scale; }
const string& get_data_path() const { return data_path; }
// returns textual description of failure code
static string str_from_code( int code );
protected:
// these are allocated within a test to try keep them valid in case of stack corruption
RNG rng;
// information about the current test
TestInfo current_test_info;
// the path to data files used by tests
string data_path;
TSParams params;
std::string output_buf[MAX_IDX];
};
/*****************************************************************************************\
* Subclass of BaseTest for testing functions that process dense arrays *
\*****************************************************************************************/
class CV_EXPORTS ArrayTest : public BaseTest
{
public:
// constructor(s) and destructor
ArrayTest();
virtual ~ArrayTest();
virtual void clear();
protected:
virtual int read_params( CvFileStorage* fs );
virtual int prepare_test_case( int test_case_idx );
virtual int validate_test_results( int test_case_idx );
virtual void prepare_to_validation( int test_case_idx );
virtual void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
virtual void fill_array( int test_case_idx, int i, int j, Mat& arr );
virtual void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high );
virtual double get_success_error_level( int test_case_idx, int i, int j );
bool cvmat_allowed;
bool iplimage_allowed;
bool optional_mask;
bool element_wise_relative_error;
int min_log_array_size;
int max_log_array_size;
enum { INPUT, INPUT_OUTPUT, OUTPUT, REF_INPUT_OUTPUT, REF_OUTPUT, TEMP, MASK, MAX_ARR };
vector<vector<void*> > test_array;
vector<vector<Mat> > test_mat;
float buf[4];
};
class CV_EXPORTS BadArgTest : public BaseTest
{
public:
// constructor(s) and destructor
BadArgTest();
virtual ~BadArgTest();
protected:
virtual int run_test_case( int expected_code, const string& descr );
virtual void run_func(void) = 0;
int test_case_idx;
int progress;
double t, freq;
template<class F>
int run_test_case( int expected_code, const string& _descr, F f)
{
double new_t = (double)cv::getTickCount(), dt;
if( test_case_idx < 0 )
{
test_case_idx = 0;
progress = 0;
dt = 0;
}
else
{
dt = (new_t - t)/(freq*1000);
t = new_t;
}
progress = update_progress(progress, test_case_idx, 0, dt);
int errcount = 0;
bool thrown = false;
const char* descr = _descr.c_str() ? _descr.c_str() : "";
try
{
f();
}
catch(const cv::Exception& e)
{
thrown = true;
if( e.code != expected_code )
{
ts->printf(TS::LOG, "%s (test case #%d): the error code %d is different from the expected %d\n",
descr, test_case_idx, e.code, expected_code);
errcount = 1;
}
}
catch(...)
{
thrown = true;
ts->printf(TS::LOG, "%s (test case #%d): unknown exception was thrown (the function has likely crashed)\n",
descr, test_case_idx);
errcount = 1;
}
if(!thrown)
{
ts->printf(TS::LOG, "%s (test case #%d): no expected exception was thrown\n",
descr, test_case_idx);
errcount = 1;
}
test_case_idx++;
return errcount;
}
};
struct CV_EXPORTS DefaultRngAuto
{
const uint64 old_state;
DefaultRngAuto() : old_state(cv::theRNG().state) { cv::theRNG().state = (uint64)-1; }
~DefaultRngAuto() { cv::theRNG().state = old_state; }
DefaultRngAuto& operator=(const DefaultRngAuto&);
};
}
// fills c with zeros
CV_EXPORTS void cvTsZero( CvMat* c, const CvMat* mask=0 );
// copies a to b (whole matrix or only the selected region)
CV_EXPORTS void cvTsCopy( const CvMat* a, CvMat* b, const CvMat* mask=0 );
// converts one array to another
CV_EXPORTS void cvTsConvert( const CvMat* src, CvMat* dst );
CV_EXPORTS void cvTsGEMM( const CvMat* a, const CvMat* b, double alpha,
const CvMat* c, double beta, CvMat* d, int flags );
#define CV_TEST_MAIN(resourcesubdir) \
int main(int argc, char **argv) \
{ \
cvtest::TS::ptr()->init(resourcesubdir); \
::testing::InitGoogleTest(&argc, argv); \
return RUN_ALL_TESTS(); \
}
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
#include "opencv2/ts/ts.hpp"
#include "opencv2/core/core_c.h"
/*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"
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
#include <io.h>
#include <windows.h>
#ifdef _MSC_VER
#include <eh.h>
#endif
#else
#include <unistd.h>
#endif
namespace cvtest
{
/*****************************************************************************************\
* Exception and memory handlers *
\*****************************************************************************************/
// a few platform-dependent declarations
#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
#ifdef _MSC_VER
static void setSEHTranslator( unsigned int /*u*/, EXCEPTION_POINTERS* pExp )
{
int code = TS::FAIL_EXCEPTION;
switch( pExp->ExceptionRecord->ExceptionCode )
{
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_FLT_STACK_CHECK:
case EXCEPTION_STACK_OVERFLOW:
case EXCEPTION_IN_PAGE_ERROR:
code = TS::FAIL_MEMORY_EXCEPTION;
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_UNDERFLOW:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
case EXCEPTION_INT_OVERFLOW:
code = TS::FAIL_ARITHM_EXCEPTION;
break;
case EXCEPTION_BREAKPOINT:
case EXCEPTION_ILLEGAL_INSTRUCTION:
case EXCEPTION_INVALID_DISPOSITION:
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
case EXCEPTION_PRIV_INSTRUCTION:
case EXCEPTION_SINGLE_STEP:
code = TS::FAIL_EXCEPTION;
}
throw code;
}
#endif
#else
#include <signal.h>
#include <setjmp.h>
static const int tsSigId[] = { SIGSEGV, SIGBUS, SIGFPE, SIGILL, SIGABRT, -1 };
static jmp_buf tsJmpMark;
void signalHandler( int sig_code )
{
int code = TS::FAIL_EXCEPTION;
switch( sig_code )
{
case SIGFPE:
code = TS::FAIL_ARITHM_EXCEPTION;
break;
case SIGSEGV:
case SIGBUS:
code = TS::FAIL_ARITHM_EXCEPTION;
break;
case SIGILL:
code = TS::FAIL_EXCEPTION;
}
longjmp( tsJmpMark, code );
}
#endif
// reads 16-digit hexadecimal number (i.e. 64-bit integer)
int64 readSeed( const char* str )
{
int64 val = 0;
if( str && strlen(str) == 16 )
{
for( int i = 0; str[i]; i++ )
{
int c = tolower(str[i]);
if( !isxdigit(c) )
return 0;
val = val * 16 +
(str[i] < 'a' ? str[i] - '0' : str[i] - 'a' + 10);
}
}
return val;
}
/*****************************************************************************************\
* Base Class for Tests *
\*****************************************************************************************/
BaseTest::BaseTest()
{
ts = TS::ptr();
test_case_count = -1;
}
BaseTest::~BaseTest()
{
clear();
}
void BaseTest::clear()
{
}
const CvFileNode* BaseTest::find_param( CvFileStorage* fs, const char* param_name )
{
CvFileNode* node = cvGetFileNodeByName(fs, 0, get_name().c_str());
return node ? cvGetFileNodeByName( fs, node, param_name ) : 0;
}
int BaseTest::read_params( CvFileStorage* )
{
return 0;
}
bool BaseTest::can_do_fast_forward()
{
return true;
}
void BaseTest::safe_run( int start_from )
{
read_params( ts->get_file_storage() );
ts->update_context( 0, -1, true );
ts->update_context( this, -1, true );
if( !::testing::GTEST_FLAG(catch_exceptions) )
run( start_from );
else
{
try
{
#if !defined WIN32 && !defined _WIN32
int _code = setjmp( tsJmpMark );
if( !_code )
run( start_from );
else
throw _code;
#else
run( start_from );
#endif
}
catch (const cv::Exception& exc)
{
const char* errorStr = cvErrorStr(exc.code);
char buf[1 << 16];
sprintf( buf, "OpenCV Error: %s (%s) in %s, file %s, line %d",
errorStr, exc.err.c_str(), exc.func.size() > 0 ?
exc.func.c_str() : "unknown function", exc.file.c_str(), exc.line );
ts->printf(TS::LOG, "%s\n", buf);
ts->set_failed_test_info( TS::FAIL_ERROR_IN_CALLED_FUNC );
}
catch (...)
{
ts->set_failed_test_info( TS::FAIL_EXCEPTION );
}
}
ts->set_gtest_status();
}
void BaseTest::run( int start_from )
{
int test_case_idx, count = get_test_case_count();
int64 t_start = cvGetTickCount();
double freq = cv::getTickFrequency();
bool ff = can_do_fast_forward();
int progress = 0, code;
int64 t1 = t_start;
for( test_case_idx = ff && start_from >= 0 ? start_from : 0;
count < 0 || test_case_idx < count; test_case_idx++ )
{
ts->update_context( this, test_case_idx, ff );
progress = update_progress( progress, test_case_idx, count, (double)(t1 - t_start)/(freq*1000) );
code = prepare_test_case( test_case_idx );
if( code < 0 || ts->get_err_code() < 0 )
return;
if( code == 0 )
continue;
run_func();
if( ts->get_err_code() < 0 )
return;
if( validate_test_results( test_case_idx ) < 0 || ts->get_err_code() < 0 )
return;
}
}
void BaseTest::run_func(void)
{
assert(0);
}
int BaseTest::get_test_case_count(void)
{
return test_case_count;
}
int BaseTest::prepare_test_case( int )
{
return 0;
}
int BaseTest::validate_test_results( int )
{
return 0;
}
int BaseTest::update_progress( int progress, int test_case_idx, int count, double dt )
{
int width = 60 - (int)get_name().size();
if( count > 0 )
{
int t = cvRound( ((double)test_case_idx * width)/count );
if( t > progress )
{
ts->printf( TS::CONSOLE, "." );
progress = t;
}
}
else if( cvRound(dt) > progress )
{
ts->printf( TS::CONSOLE, "." );
progress = cvRound(dt);
}
return progress;
}
BadArgTest::BadArgTest()
{
progress = -1;
test_case_idx = -1;
freq = cv::getTickFrequency();
}
BadArgTest::~BadArgTest(void)
{
}
int BadArgTest::run_test_case( int expected_code, const string& _descr )
{
double new_t = (double)cv::getTickCount(), dt;
if( test_case_idx < 0 )
{
test_case_idx = 0;
progress = 0;
dt = 0;
}
else
{
dt = (new_t - t)/(freq*1000);
t = new_t;
}
progress = update_progress(progress, test_case_idx, 0, dt);
int errcount = 0;
bool thrown = false;
const char* descr = _descr.c_str() ? _descr.c_str() : "";
try
{
run_func();
}
catch(const cv::Exception& e)
{
thrown = true;
if( e.code != expected_code )
{
ts->printf(TS::LOG, "%s (test case #%d): the error code %d is different from the expected %d\n",
descr, test_case_idx, e.code, expected_code);
errcount = 1;
}
}
catch(...)
{
thrown = true;
ts->printf(TS::LOG, "%s (test case #%d): unknown exception was thrown (the function has likely crashed)\n",
descr, test_case_idx);
errcount = 1;
}
if(!thrown)
{
ts->printf(TS::LOG, "%s (test case #%d): no expected exception was thrown\n",
descr, test_case_idx);
errcount = 1;
}
test_case_idx++;
return errcount;
}
/*****************************************************************************************\
* Base Class for Test System *
\*****************************************************************************************/
/******************************** Constructors/Destructors ******************************/
TSParams::TSParams()
{
rng_seed = (uint64)-1;
use_optimized = true;
test_case_count_scale = 1;
}
TestInfo::TestInfo()
{
test = 0;
code = 0;
rng_seed = rng_seed0 = 0;
test_case_idx = -1;
}
TS::TS()
{
} // ctor
TS::~TS()
{
} // dtor
string TS::str_from_code( int code )
{
switch( code )
{
case OK: return "Ok";
case FAIL_GENERIC: return "Generic/Unknown";
case FAIL_MISSING_TEST_DATA: return "No test data";
case FAIL_INVALID_TEST_DATA: return "Invalid test data";
case FAIL_ERROR_IN_CALLED_FUNC: return "cvError invoked";
case FAIL_EXCEPTION: return "Hardware/OS exception";
case FAIL_MEMORY_EXCEPTION: return "Invalid memory access";
case FAIL_ARITHM_EXCEPTION: return "Arithmetic exception";
case FAIL_MEMORY_CORRUPTION_BEGIN: return "Corrupted memblock (beginning)";
case FAIL_MEMORY_CORRUPTION_END: return "Corrupted memblock (end)";
case FAIL_MEMORY_LEAK: return "Memory leak";
case FAIL_INVALID_OUTPUT: return "Invalid function output";
case FAIL_MISMATCH: return "Unexpected output";
case FAIL_BAD_ACCURACY: return "Bad accuracy";
case FAIL_HANG: return "Infinite loop(?)";
case FAIL_BAD_ARG_CHECK: return "Incorrect handling of bad arguments";
default:
;
}
return "Generic/Unknown";
}
/************************************** Running tests **********************************/
void TS::init( const string& modulename )
{
char* datapath_dir = getenv("OPENCV_TEST_DATA_PATH");
if( datapath_dir )
{
char buf[1024];
size_t l = strlen(datapath_dir);
bool haveSlash = l > 0 && (datapath_dir[l-1] == '/' || datapath_dir[l-1] == '\\');
sprintf( buf, "%s%s%s/", datapath_dir, haveSlash ? "" : "/", modulename.c_str() );
data_path = string(buf);
}
if( ::testing::GTEST_FLAG(catch_exceptions) )
{
cvSetErrMode( CV_ErrModeParent );
cvRedirectError( cvStdErrReport );
#if defined WIN32 || defined _WIN32
#ifdef _MSC_VER
_set_se_translator( SEHTranslator );
#endif
#else
for( int i = 0; tsSigId[i] >= 0; i++ )
signal( tsSigId[i], signalHandler );
#endif
}
else
{
cvSetErrMode( CV_ErrModeLeaf );
cvRedirectError( cvGuiBoxReport );
#if defined WIN32 || defined _WIN32
#ifdef _MSC_VER
_set_se_translator( 0 );
#endif
#else
for( int i = 0; tsSigId[i] >= 0; i++ )
signal( tsSigId[i], SIG_DFL );
#endif
}
if( params.use_optimized == 0 )
cv::setUseOptimized(false);
rng = RNG(params.rng_seed);
}
void TS::set_gtest_status()
{
int code = get_err_code();
if( code >= 0 )
return SUCCEED();
char seedstr[32];
sprintf(seedstr, "%08x%08x", (unsigned)(current_test_info.rng_seed>>32),
(unsigned)(current_test_info.rng_seed));
string logs = "";
if( !output_buf[SUMMARY_IDX].empty() )
logs += "\n-----------------------------------\n\tSUM: " + output_buf[SUMMARY_IDX];
if( !output_buf[LOG_IDX].empty() )
logs += "\n-----------------------------------\n\tLOG: " + output_buf[LOG_IDX];
if( !output_buf[CONSOLE_IDX].empty() )
logs += "\n-----------------------------------\n\tCONSOLE: " + output_buf[CONSOLE_IDX];
logs += "\n-----------------------------------\n";
FAIL() << "\n\tfailure reason: " << str_from_code(code) <<
"\n\ttest case #" << current_test_info.test_case_idx <<
"\n\tseed: " << seedstr << logs;
}
CvFileStorage* TS::get_file_storage() { return 0; }
void TS::update_context( BaseTest* test, int test_case_idx, bool update_ts_context )
{
if( current_test_info.test != test )
{
for( int i = 0; i <= CONSOLE_IDX; i++ )
output_buf[i] = string();
rng = RNG(params.rng_seed);
current_test_info.rng_seed0 = current_test_info.rng_seed = rng.state;
}
current_test_info.test = test;
current_test_info.test_case_idx = test_case_idx;
current_test_info.code = 0;
cvSetErrStatus( CV_StsOk );
if( update_ts_context )
current_test_info.rng_seed = rng.state;
}
void TS::set_failed_test_info( int fail_code )
{
if( current_test_info.code >= 0 )
current_test_info.code = fail_code;
}
#if defined _MSC_VER && _MSC_VER < 1400
#undef vsnprintf
#define vsnprintf _vsnprintf
#endif
void TS::vprintf( int streams, const char* fmt, va_list l )
{
char str[1 << 14];
vsnprintf( str, sizeof(str)-1, fmt, l );
for( int i = 0; i < MAX_IDX; i++ )
if( (streams & (1 << i)) )
{
output_buf[i] += std::string(str);
// in the new GTest-based framework we do not use
// any output files (except for the automatically generated xml report).
// if a test fails, all the buffers are printed, so we do not want to duplicate the information and
// thus only add the new information to a single buffer and return from the function.
break;
}
}
void TS::printf( int streams, const char* fmt, ... )
{
if( streams )
{
va_list l;
va_start( l, fmt );
vprintf( streams, fmt, l );
va_end( l );
}
}
TS ts;
TS* TS::ptr() { return &ts; }
}
/* End of file. */
/*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"
namespace cvtest
{
static const int default_test_case_count = 500;
static const int default_max_log_array_size = 9;
ArrayTest::ArrayTest()
{
test_case_count = default_test_case_count;
iplimage_allowed = true;
cvmat_allowed = true;
optional_mask = false;
min_log_array_size = 0;
max_log_array_size = default_max_log_array_size;
element_wise_relative_error = true;
test_array.resize(MAX_ARR);
}
ArrayTest::~ArrayTest()
{
clear();
}
void ArrayTest::clear()
{
for( size_t i = 0; i < test_array.size(); i++ )
{
for( size_t j = 0; j < test_array[i].size(); j++ )
cvRelease( &test_array[i][j] );
}
BaseTest::clear();
}
int ArrayTest::read_params( CvFileStorage* fs )
{
int code = BaseTest::read_params( fs );
if( code < 0 )
return code;
min_log_array_size = cvReadInt( find_param( fs, "min_log_array_size" ), min_log_array_size );
max_log_array_size = cvReadInt( find_param( fs, "max_log_array_size" ), max_log_array_size );
test_case_count = cvReadInt( find_param( fs, "test_case_count" ), test_case_count );
test_case_count = cvRound( test_case_count*ts->get_test_case_count_scale() );
min_log_array_size = clipInt( min_log_array_size, 0, 20 );
max_log_array_size = clipInt( max_log_array_size, min_log_array_size, 20 );
test_case_count = clipInt( test_case_count, 0, 100000 );
return code;
}
void ArrayTest::get_test_array_types_and_sizes( int /*test_case_idx*/, vector<vector<Size> >& sizes, vector<vector<int> >& types )
{
RNG& rng = ts->get_rng();
Size size;
double val;
size_t i, j;
val = randReal(rng) * (max_log_array_size - min_log_array_size) + min_log_array_size;
size.width = cvRound( exp(val*CV_LOG2) );
val = randReal(rng) * (max_log_array_size - min_log_array_size) + min_log_array_size;
size.height = cvRound( exp(val*CV_LOG2) );
for( i = 0; i < test_array.size(); i++ )
{
size_t sizei = test_array[i].size();
for( j = 0; j < sizei; j++ )
{
sizes[i][j] = size;
types[i][j] = CV_8UC1;
}
}
}
static const int icvTsTypeToDepth[] =
{
IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U, IPL_DEPTH_16S,
IPL_DEPTH_32S, IPL_DEPTH_32F, IPL_DEPTH_64F
};
int ArrayTest::prepare_test_case( int test_case_idx )
{
int code = 1;
size_t max_arr = test_array.size();
vector<vector<Size> > sizes(max_arr);
vector<vector<Size> > whole_sizes(max_arr);
vector<vector<int> > types(max_arr);
size_t i, j;
RNG& rng = ts->get_rng();
bool is_image = false;
for( i = 0; i < max_arr; i++ )
{
size_t sizei = std::max(test_array[i].size(), (size_t)1);
sizes[i].resize(sizei);
types[i].resize(sizei);
whole_sizes[i].resize(sizei);
}
get_test_array_types_and_sizes( test_case_idx, sizes, types );
for( i = 0; i < max_arr; i++ )
{
size_t sizei = test_array[i].size();
for( j = 0; j < sizei; j++ )
{
unsigned t = randInt(rng);
bool create_mask = true, use_roi = false;
CvSize size = sizes[i][j], whole_size = size;
CvRect roi = {0,0,0,0};
is_image = !cvmat_allowed ? true : iplimage_allowed ? (t & 1) != 0 : false;
create_mask = (t & 6) == 0; // ~ each of 3 tests will use mask
use_roi = (t & 8) != 0;
if( use_roi )
{
whole_size.width += randInt(rng) % 10;
whole_size.height += randInt(rng) % 10;
}
cvRelease( &test_array[i][j] );
if( size.width > 0 && size.height > 0 &&
types[i][j] >= 0 && (i != MASK || create_mask) )
{
if( use_roi )
{
roi.width = size.width;
roi.height = size.height;
if( whole_size.width > size.width )
roi.x = randInt(rng) % (whole_size.width - size.width);
if( whole_size.height > size.height )
roi.y = randInt(rng) % (whole_size.height - size.height);
}
if( is_image )
{
test_array[i][j] = cvCreateImage( whole_size,
icvTsTypeToDepth[CV_MAT_DEPTH(types[i][j])], CV_MAT_CN(types[i][j]) );
if( use_roi )
cvSetImageROI( (IplImage*)test_array[i][j], roi );
}
else
{
test_array[i][j] = cvCreateMat( whole_size.height, whole_size.width, types[i][j] );
if( use_roi )
{
CvMat submat, *mat = (CvMat*)test_array[i][j];
cvGetSubRect( test_array[i][j], &submat, roi );
submat.refcount = mat->refcount;
*mat = submat;
}
}
}
}
}
test_mat.resize(test_array.size());
for( i = 0; i < max_arr; i++ )
{
size_t sizei = test_array[i].size();
test_mat[i].resize(sizei);
for( j = 0; j < sizei; j++ )
{
CvArr* arr = test_array[i][j];
test_mat[i][j] = cv::cvarrToMat(arr);
if( !test_mat[i][j].empty() )
fill_array( test_case_idx, i, j, test_mat[i][j] );
}
}
return code;
}
void ArrayTest::get_minmax_bounds( int i, int /*j*/, int type, Scalar& low, Scalar& high )
{
double l, u;
int depth = CV_MAT_DEPTH(type);
if( i == MASK )
{
l = -2;
u = 2;
}
else if( depth < CV_32S )
{
l = getMinVal(type);
u = getMaxVal(type);
}
else
{
u = depth == CV_32S ? 1000000 : 1000.;
l = -u;
}
low = Scalar::all(l);
high = Scalar::all(u);
}
void ArrayTest::fill_array( int /*test_case_idx*/, int i, int j, Mat& arr )
{
if( i == REF_INPUT_OUTPUT )
cvtest::copy( test_mat[INPUT_OUTPUT][j], arr, Mat() );
else if( i == INPUT || i == INPUT_OUTPUT || i == MASK )
{
Scalar low, high;
get_minmax_bounds( i, j, arr.type(), low, high );
randUni( ts->get_rng(), arr, low, high );
}
}
double ArrayTest::get_success_error_level( int /*test_case_idx*/, int i, int j )
{
int elem_depth = CV_MAT_DEPTH(cvGetElemType(test_array[i][j]));
assert( i == OUTPUT || i == INPUT_OUTPUT );
return elem_depth < CV_32F ? 0 : elem_depth == CV_32F ? FLT_EPSILON*100: DBL_EPSILON*5000;
}
void ArrayTest::prepare_to_validation( int /*test_case_idx*/ )
{
assert(0);
}
int ArrayTest::validate_test_results( int test_case_idx )
{
static const char* arr_names[] = { "input", "input/output", "output",
"ref input/output", "ref output",
"temporary", "mask" };
size_t i, j;
prepare_to_validation( test_case_idx );
for( i = 0; i < 2; i++ )
{
int i0 = i == 0 ? OUTPUT : INPUT_OUTPUT;
int i1 = i == 0 ? REF_OUTPUT : REF_INPUT_OUTPUT;
size_t sizei = test_array[i0].size();
assert( sizei == test_array[i1].size() );
for( j = 0; j < sizei; j++ )
{
double err_level;
vector<int> idx;
double max_diff = 0;
int code;
char msg[100];
if( !test_array[i1][j] )
continue;
err_level = get_success_error_level( test_case_idx, i0, j );
code = cmpEps( test_mat[i0][j], test_mat[i1][j], &max_diff, err_level, &idx, element_wise_relative_error );
switch( code )
{
case -1:
sprintf( msg, "Too big difference (=%g)", max_diff );
code = TS::FAIL_BAD_ACCURACY;
break;
case -2:
strcpy( msg, "Invalid output" );
code = TS::FAIL_INVALID_OUTPUT;
break;
case -3:
strcpy( msg, "Invalid output in the reference array" );
code = TS::FAIL_INVALID_OUTPUT;
break;
default:
continue;
}
string idxstr = vec2str(", ", &idx[0], idx.size());
ts->printf( TS::LOG, "%s in %s array %d at (%s)", msg, arr_names[i0], j, idxstr.c_str() );
for( i0 = 0; i0 < (int)test_array.size(); i0++ )
{
size_t sizei0 = test_array[i0].size();
if( i0 == REF_INPUT_OUTPUT || i0 == OUTPUT || i0 == TEMP )
continue;
for( i1 = 0; i1 < (int)sizei0; i1++ )
{
const Mat& arr = test_mat[i0][i1];
if( !arr.empty() )
{
string sizestr = vec2str(", ", &arr.size[0], arr.dims);
ts->printf( TS::LOG, "%s array %d type=%sC%d, size=(%s)\n",
arr_names[i0], i1, getTypeName(arr.depth()),
arr.channels(), sizestr.c_str() );
}
}
}
ts->set_failed_test_info( code );
return code;
}
}
return 0;
}
}
/* End of file. */
#include "precomp.hpp"
#include <float.h>
#include <limits.h>
using namespace cv;
namespace cvtest
{
const char* getTypeName( int type )
{
static const char* type_names[] = { "8u", "8s", "16u", "16s", "32s", "32f", "64f", "ptr" };
return type_names[CV_MAT_DEPTH(type)];
}
int typeByName( const char* name )
{
int i;
for( i = 0; i < CV_DEPTH_MAX; i++ )
if( strcmp(name, getTypeName(i)) == 0 )
return i;
return -1;
}
string vec2str( const string& sep, const int* v, size_t nelems )
{
char buf[32];
string result = "";
for( size_t i = 0; i < nelems; i++ )
{
sprintf(buf, "%d", v[i]);
result += string(buf);
if( i < nelems - 1 )
result += sep;
}
return result;
}
Size randomSize(RNG& rng, double maxSizeLog)
{
double width_log = rng.uniform(0., maxSizeLog);
double height_log = rng.uniform(0., maxSizeLog - width_log);
if( (unsigned)rng % 2 != 0 )
std::swap(width_log, height_log);
Size sz;
sz.width = cvRound(exp(width_log));
sz.height = cvRound(exp(height_log));
return sz;
}
void randomSize(RNG& rng, int minDims, int maxDims, double maxSizeLog, vector<int>& sz)
{
int i, dims = rng.uniform(minDims, maxDims+1);
sz.resize(dims);
for( i = 0; i < dims; i++ )
{
double v = rng.uniform(0., maxSizeLog);
maxSizeLog -= v;
sz[i] = cvRound(exp(v));
}
for( i = 0; i < dims; i++ )
{
int j = rng.uniform(0, dims);
int k = rng.uniform(0, dims);
std::swap(sz[j], sz[k]);
}
}
int randomType(RNG& rng, int typeMask, int minChannels, int maxChannels)
{
int channels = rng.uniform(minChannels, maxChannels+1);
int depth = 0;
CV_Assert((typeMask & TYPE_MASK_ALL) != 0);
for(;;)
{
depth = rng.uniform(CV_8U, CV_64F+1);
if( ((1 << depth) & typeMask) != 0 )
break;
}
return CV_MAKETYPE(depth, channels);
}
double getMinVal(int depth)
{
depth = CV_MAT_DEPTH(depth);
double val = depth == CV_8U ? 0 : depth == CV_8S ? SCHAR_MIN : depth == CV_16U ? 0 :
depth == CV_16S ? SHRT_MIN : depth == CV_32S ? INT_MIN :
depth == CV_32F ? -FLT_MAX : depth == CV_64F ? -DBL_MAX : -1;
CV_Assert(val != -1);
return val;
}
double getMaxVal(int depth)
{
depth = CV_MAT_DEPTH(depth);
double val = depth == CV_8U ? UCHAR_MAX : depth == CV_8S ? SCHAR_MAX : depth == CV_16U ? USHRT_MAX :
depth == CV_16S ? SHRT_MAX : depth == CV_32S ? INT_MAX :
depth == CV_32F ? FLT_MAX : depth == CV_64F ? DBL_MAX : -1;
CV_Assert(val != -1);
return val;
}
Mat randomMat(RNG& rng, Size size, int type, double minVal, double maxVal, bool useRoi)
{
Size size0 = size;
if( useRoi )
{
size0.width += std::max(rng.uniform(0, 10) - 5, 0);
size0.height += std::max(rng.uniform(0, 10) - 5, 0);
}
Mat m(size0, type);
rng.fill(m, RNG::UNIFORM, Scalar::all(minVal), Scalar::all(maxVal));
if( size0 == size )
return m;
return m(Rect((size0.width-size.width)/2, (size0.height-size.height)/2, size.width, size.height));
}
Mat randomMat(RNG& rng, const vector<int>& size, int type, double minVal, double maxVal, bool useRoi)
{
int i, dims = (int)size.size();
vector<int> size0(dims);
vector<Range> r(dims);
bool eqsize = true;
for( i = 0; i < dims; i++ )
{
size0[i] = size[i];
r[i] = Range::all();
if( useRoi )
{
size0[i] += std::max(rng.uniform(0, 5) - 2, 0);
r[i] = Range((size0[i] - size[i])/2, (size0[i] - size[i])/2 + size[i]);
}
eqsize = eqsize && size[i] == size0[i];
}
Mat m(dims, &size0[0], type);
rng.fill(m, RNG::UNIFORM, Scalar::all(minVal), Scalar::all(maxVal));
if( eqsize )
return m;
return m(&r[0]);
}
void add(const Mat& _a, double alpha, const Mat& _b, double beta,
Scalar gamma, Mat& c, int ctype, bool calcAbs)
{
Mat a = _a, b = _b;
if( a.empty() || alpha == 0 )
{
// both alpha and beta can be 0, but at least one of a and b must be non-empty array,
// otherwise we do not know the size of the output (and may be type of the output, when ctype<0)
CV_Assert( !a.empty() || !b.empty() );
if( !b.empty() )
{
a = b;
alpha = beta;
b = Mat();
beta = 0;
}
}
if( b.empty() || beta == 0 )
{
b = Mat();
beta = 0;
}
else
CV_Assert(a.size == b.size);
if( ctype < 0 )
ctype = a.depth();
ctype = CV_MAKETYPE(CV_MAT_DEPTH(ctype), a.channels());
c.create(a.dims, &a.size[0], ctype);
const Mat *arrays[] = {&a, &b, &c, 0};
Mat planes[3], buf[3];
NAryMatIterator it(arrays, planes);
int i, nplanes = it.nplanes, cn=a.channels();
size_t total = planes[0].total(), maxsize = std::min((size_t)12*12*std::max(12/cn, 1), total);
CV_Assert(planes[0].rows == 1);
buf[0].create(1, (int)maxsize, CV_64FC(cn));
if(!b.empty())
buf[1].create(1, maxsize, CV_64FC(cn));
buf[2].create(1, maxsize, CV_64FC(cn));
scalarToRawData(gamma, buf[2].data, CV_64FC(cn), (int)(maxsize*cn));
for( i = 0; i < nplanes; i++, ++it)
{
for( size_t j = 0; j < total; j += maxsize )
{
size_t j2 = std::min(j + maxsize, total);
Mat apart0 = planes[0].colRange((int)j, (int)j2);
Mat cpart0 = planes[2].colRange((int)j, (int)j2);
Mat apart = buf[0].colRange(0, (int)(j2 - j));
apart0.convertTo(apart, apart.type(), alpha);
size_t k, n = (j2 - j)*cn;
double* aptr = (double*)apart.data;
const double* gptr = (const double*)buf[2].data;
if( b.empty() )
{
for( k = 0; k < n; k++ )
aptr[k] += gptr[k];
}
else
{
Mat bpart0 = planes[1].colRange((int)j, (int)j2);
Mat bpart = buf[1].colRange(0, (int)(j2 - j));
bpart0.convertTo(bpart, bpart.type(), beta);
const double* bptr = (const double*)bpart.data;
for( k = 0; k < n; k++ )
aptr[k] += bptr[k] + gptr[k];
}
if( calcAbs )
for( k = 0; k < n; k++ )
aptr[k] = fabs(aptr[k]);
apart.convertTo(cpart0, cpart0.type(), 1, 0);
}
}
}
template<typename _Tp1, typename _Tp2> inline void
convert_(const _Tp1* src, _Tp2* dst, size_t total, double alpha, double beta)
{
size_t i;
if( alpha == 1 && beta == 0 )
for( i = 0; i < total; i++ )
dst[i] = saturate_cast<_Tp2>(src[i]);
else if( beta == 0 )
for( i = 0; i < total; i++ )
dst[i] = saturate_cast<_Tp2>(src[i]*alpha);
else
for( i = 0; i < total; i++ )
dst[i] = saturate_cast<_Tp2>(src[i]*alpha + beta);
}
template<typename _Tp> inline void
convertTo(const _Tp* src, void* dst, int dtype, size_t total, double alpha, double beta)
{
switch( CV_MAT_DEPTH(dtype) )
{
case CV_8U:
convert_(src, (uchar*)dst, total, alpha, beta);
break;
case CV_8S:
convert_(src, (schar*)dst, total, alpha, beta);
break;
case CV_16U:
convert_(src, (ushort*)dst, total, alpha, beta);
break;
case CV_16S:
convert_(src, (short*)dst, total, alpha, beta);
break;
case CV_32S:
convert_(src, (int*)dst, total, alpha, beta);
break;
case CV_32F:
convert_(src, (float*)dst, total, alpha, beta);
break;
case CV_64F:
convert_(src, (double*)dst, total, alpha, beta);
break;
default:
CV_Assert(0);
}
}
void convert(const Mat& src, Mat& dst, int dtype, double alpha, double beta)
{
dtype = CV_MAKETYPE(CV_MAT_DEPTH(dtype), src.channels());
dst.create(src.dims, &src.size[0], dtype);
if( alpha == 0 )
{
set( dst, Scalar::all(beta) );
return;
}
if( dtype == src.type() && alpha == 1 && beta == 0 )
{
copy( src, dst );
return;
}
const Mat *arrays[]={&src, &dst, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total()*planes[0].channels();
int i, nplanes = it.nplanes;
for( i = 0; i < nplanes; i++, ++it)
{
const uchar* sptr = planes[0].data;
uchar* dptr = planes[1].data;
switch( src.depth() )
{
case CV_8U:
convertTo((const uchar*)sptr, dptr, dtype, total, alpha, beta);
break;
case CV_8S:
convertTo((const schar*)sptr, dptr, dtype, total, alpha, beta);
break;
case CV_16U:
convertTo((const ushort*)sptr, dptr, dtype, total, alpha, beta);
break;
case CV_16S:
convertTo((const short*)sptr, dptr, dtype, total, alpha, beta);
break;
case CV_32S:
convertTo((const int*)sptr, dptr, dtype, total, alpha, beta);
break;
case CV_32F:
convertTo((const float*)sptr, dptr, dtype, total, alpha, beta);
break;
case CV_64F:
convertTo((const double*)sptr, dptr, dtype, total, alpha, beta);
break;
}
}
}
void copy(const Mat& src, Mat& dst, const Mat& mask, bool invertMask)
{
dst.create(src.dims, &src.size[0], src.type());
if(mask.empty())
{
const Mat* arrays[] = {&src, &dst, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
int i, nplanes = it.nplanes;
size_t planeSize = planes[0].total()*src.elemSize();
for( i = 0; i < nplanes; i++, ++it )
memcpy(planes[1].data, planes[0].data, planeSize);
return;
}
CV_Assert( src.size == mask.size && mask.type() == CV_8U );
const Mat *arrays[]={&src, &dst, &mask, 0};
Mat planes[3];
NAryMatIterator it(arrays, planes);
size_t j, k, elemSize = src.elemSize(), total = planes[0].total();
int i, nplanes = it.nplanes;
for( i = 0; i < nplanes; i++, ++it)
{
const uchar* sptr = planes[0].data;
uchar* dptr = planes[1].data;
const uchar* mptr = planes[2].data;
for( j = 0; j < total; j++, sptr += elemSize, dptr += elemSize )
{
if( (mptr[j] != 0) ^ invertMask )
for( k = 0; k < elemSize; k++ )
dptr[k] = sptr[k];
}
}
}
void set(Mat& dst, const Scalar& gamma, const Mat& mask)
{
double buf[12];
scalarToRawData(gamma, &buf, dst.type(), dst.channels());
const uchar* gptr = (const uchar*)&buf[0];
if(mask.empty())
{
const Mat* arrays[] = {&dst, 0};
Mat plane;
NAryMatIterator it(arrays, &plane);
int i, nplanes = it.nplanes;
size_t j, k, elemSize = dst.elemSize(), planeSize = plane.total()*elemSize;
for( k = 1; k < elemSize; k++ )
if( gptr[k] != gptr[0] )
break;
bool uniform = k >= elemSize;
for( i = 0; i < nplanes; i++, ++it )
{
uchar* dptr = plane.data;
if( uniform )
memset( dptr, gptr[0], planeSize );
else if( i == 0 )
{
for( j = 0; j < planeSize; j += elemSize, dptr += elemSize )
for( k = 0; k < elemSize; k++ )
dptr[k] = gptr[k];
}
else
memcpy(dptr, dst.data, planeSize);
}
return;
}
CV_Assert( dst.size == mask.size && mask.type() == CV_8U );
const Mat *arrays[]={&dst, &mask, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t j, k, elemSize = dst.elemSize(), total = planes[0].total();
int i, nplanes = it.nplanes;
for( i = 0; i < nplanes; i++, ++it)
{
uchar* dptr = planes[0].data;
const uchar* mptr = planes[1].data;
for( j = 0; j < total; j++, dptr += elemSize )
{
if( mptr[j] )
for( k = 0; k < elemSize; k++ )
dptr[k] = gptr[k];
}
}
}
void insert(const Mat& src, Mat& dst, int coi)
{
CV_Assert( dst.size == src.size && src.depth() == dst.depth() &&
0 <= coi && coi < dst.channels() );
const Mat* arrays[] = {&src, &dst, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
int i, nplanes = it.nplanes;
size_t j, k, size0 = src.elemSize(), size1 = dst.elemSize(), total = planes[0].total();
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr = planes[0].data;
uchar* dptr = planes[1].data + coi*size0;
for( j = 0; j < total; j++, sptr += size0, dptr += size1 )
{
for( k = 0; k < size0; k++ )
dptr[k] = sptr[k];
}
}
}
void extract(const Mat& src, Mat& dst, int coi)
{
dst.create( src.dims, &src.size[0], src.depth() );
CV_Assert( 0 <= coi && coi < src.channels() );
const Mat* arrays[] = {&src, &dst, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
int i, nplanes = it.nplanes;
size_t j, k, size0 = src.elemSize(), size1 = dst.elemSize(), total = planes[0].total();
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr = planes[0].data + coi*size1;
uchar* dptr = planes[1].data;
for( j = 0; j < total; j++, sptr += size0, dptr += size1 )
{
for( k = 0; k < size1; k++ )
dptr[k] = sptr[k];
}
}
}
void transpose(const Mat& src, Mat& dst)
{
CV_Assert(src.dims == 2);
dst.create(src.cols, src.rows, src.type());
int i, j, k, esz = (int)src.elemSize();
for( i = 0; i < dst.rows; i++ )
{
const uchar* sptr = src.ptr(0) + i*esz;
uchar* dptr = dst.ptr(i);
for( j = 0; j < dst.cols; j++, sptr += src.step[0], dptr += esz )
{
for( k = 0; k < esz; k++ )
dptr[k] = sptr[k];
}
}
}
template<typename _Tp> static void
randUniInt_(RNG& rng, _Tp* data, size_t total, int cn, const Scalar& scale, const Scalar& delta)
{
for( size_t i = 0; i < total; i += cn )
for( int k = 0; k < cn; k++ )
{
int val = cvFloor( randInt(rng)*scale[k] + delta[k] );
data[i + k] = saturate_cast<_Tp>(val);
}
}
template<typename _Tp> static void
randUniFlt_(RNG& rng, _Tp* data, size_t total, int cn, const Scalar& scale, const Scalar& delta)
{
for( size_t i = 0; i < total; i += cn )
for( int k = 0; k < cn; k++ )
{
double val = randReal(rng)*scale[k] + delta[k];
data[i + k] = saturate_cast<_Tp>(val);
}
}
void randUni( RNG& rng, Mat& a, const Scalar& param0, const Scalar& param1 )
{
Scalar scale = param0;
Scalar delta = param1;
double C = a.depth() < CV_32F ? 1./(65536.*65536.) : 1.;
for( int k = 0; k < 4; k++ )
{
double s = scale.val[k] - delta.val[k];
if( s >= 0 )
scale.val[k] = s;
else
{
delta.val[k] = scale.val[k];
scale.val[k] = -s;
}
scale.val[k] *= C;
}
const Mat *arrays[]={&a, 0};
Mat plane;
NAryMatIterator it(arrays, &plane);
int i, nplanes = it.nplanes, depth = a.depth(), cn = a.channels();
size_t total = plane.total()*cn;
for( i = 0; i < nplanes; i++, ++it )
{
switch( depth )
{
case CV_8U:
randUniInt_(rng, plane.ptr<uchar>(), total, cn, scale, delta);
break;
case CV_8S:
randUniInt_(rng, plane.ptr<schar>(), total, cn, scale, delta);
break;
case CV_16U:
randUniInt_(rng, plane.ptr<ushort>(), total, cn, scale, delta);
break;
case CV_16S:
randUniInt_(rng, plane.ptr<short>(), total, cn, scale, delta);
break;
case CV_32S:
randUniInt_(rng, plane.ptr<int>(), total, cn, scale, delta);
break;
case CV_32F:
randUniFlt_(rng, plane.ptr<float>(), total, cn, scale, delta);
break;
case CV_64F:
randUniFlt_(rng, plane.ptr<double>(), total, cn, scale, delta);
break;
default:
CV_Assert(0);
}
}
}
template<typename _Tp> static void
erode_(const Mat& src, Mat& dst, const vector<int>& ofsvec)
{
int width = dst.cols*src.channels(), n = (int)ofsvec.size();
const int* ofs = &ofsvec[0];
for( int y = 0; y < dst.rows; y++ )
{
const _Tp* sptr = src.ptr<_Tp>(y);
_Tp* dptr = dst.ptr<_Tp>(y);
for( int x = 0; x < width; x++ )
{
_Tp result = sptr[x + ofs[0]];
for( int i = 1; i < n; i++ )
result = std::min(result, sptr[x + ofs[i]]);
dptr[x] = result;
}
}
}
template<typename _Tp> static void
dilate_(const Mat& src, Mat& dst, const vector<int>& ofsvec)
{
int width = dst.cols*src.channels(), n = (int)ofsvec.size();
const int* ofs = &ofsvec[0];
for( int y = 0; y < dst.rows; y++ )
{
const _Tp* sptr = src.ptr<_Tp>(y);
_Tp* dptr = dst.ptr<_Tp>(y);
for( int x = 0; x < width; x++ )
{
_Tp result = sptr[x + ofs[0]];
for( int i = 1; i < n; i++ )
result = std::max(result, sptr[x + ofs[i]]);
dptr[x] = result;
}
}
}
void erode(const Mat& _src, Mat& dst, const Mat& _kernel, Point anchor,
int borderType, const Scalar& _borderValue)
{
//if( _src.type() == CV_16UC3 && _src.size() == Size(1, 2) )
// putchar('*');
Mat kernel = _kernel, src;
Scalar borderValue = _borderValue;
if( kernel.empty() )
kernel = Mat::ones(3, 3, CV_8U);
else
{
CV_Assert( kernel.type() == CV_8U );
}
if( anchor == Point(-1,-1) )
anchor = Point(kernel.cols/2, kernel.rows/2);
if( borderType == IPL_BORDER_CONSTANT )
borderValue = getMaxVal(src.depth());
copyMakeBorder(_src, src, anchor.y, kernel.rows - anchor.y - 1,
anchor.x, kernel.cols - anchor.x - 1,
borderType, borderValue);
dst.create( _src.size(), src.type() );
vector<int> ofs;
int step = (int)(src.step/src.elemSize1()), cn = src.channels();
for( int i = 0; i < kernel.rows; i++ )
for( int j = 0; j < kernel.cols; j++ )
if( kernel.at<uchar>(i, j) != 0 )
ofs.push_back(i*step + j*cn);
if( ofs.empty() )
ofs.push_back(anchor.y*step + anchor.x*cn);
switch( src.depth() )
{
case CV_8U:
erode_<uchar>(src, dst, ofs);
break;
case CV_8S:
erode_<schar>(src, dst, ofs);
break;
case CV_16U:
erode_<ushort>(src, dst, ofs);
break;
case CV_16S:
erode_<short>(src, dst, ofs);
break;
case CV_32S:
erode_<int>(src, dst, ofs);
break;
case CV_32F:
erode_<float>(src, dst, ofs);
break;
case CV_64F:
erode_<double>(src, dst, ofs);
break;
default:
CV_Assert(0);
}
}
void dilate(const Mat& _src, Mat& dst, const Mat& _kernel, Point anchor,
int borderType, const Scalar& _borderValue)
{
Mat kernel = _kernel, src;
Scalar borderValue = _borderValue;
if( kernel.empty() )
kernel = Mat::ones(3, 3, CV_8U);
else
{
CV_Assert( kernel.type() == CV_8U );
}
if( anchor == Point(-1,-1) )
anchor = Point(kernel.cols/2, kernel.rows/2);
if( borderType == IPL_BORDER_CONSTANT )
borderValue = getMinVal(src.depth());
copyMakeBorder(_src, src, anchor.y, kernel.rows - anchor.y - 1,
anchor.x, kernel.cols - anchor.x - 1,
borderType, borderValue);
dst.create( _src.size(), src.type() );
vector<int> ofs;
int step = (int)(src.step/src.elemSize1()), cn = src.channels();
for( int i = 0; i < kernel.rows; i++ )
for( int j = 0; j < kernel.cols; j++ )
if( kernel.at<uchar>(i, j) != 0 )
ofs.push_back(i*step + j*cn);
if( ofs.empty() )
ofs.push_back(anchor.y*step + anchor.x*cn);
switch( src.depth() )
{
case CV_8U:
dilate_<uchar>(src, dst, ofs);
break;
case CV_8S:
dilate_<schar>(src, dst, ofs);
break;
case CV_16U:
dilate_<ushort>(src, dst, ofs);
break;
case CV_16S:
dilate_<short>(src, dst, ofs);
break;
case CV_32S:
dilate_<int>(src, dst, ofs);
break;
case CV_32F:
dilate_<float>(src, dst, ofs);
break;
case CV_64F:
dilate_<double>(src, dst, ofs);
break;
default:
CV_Assert(0);
}
}
template<typename _Tp> static void
filter2D_(const Mat& src, Mat& dst, const vector<int>& ofsvec, const vector<double>& coeffvec)
{
const int* ofs = &ofsvec[0];
const double* coeff = &coeffvec[0];
int width = dst.cols*dst.channels(), ncoeffs = (int)ofsvec.size();
for( int y = 0; y < dst.rows; y++ )
{
const _Tp* sptr = src.ptr<_Tp>(y);
double* dptr = dst.ptr<double>(y);
for( int x = 0; x < width; x++ )
{
double s = 0;
for( int i = 0; i < ncoeffs; i++ )
s += sptr[x + ofs[i]]*coeff[i];
dptr[x] = s;
}
}
}
void filter2D(const Mat& _src, Mat& dst, int ddepth, const Mat& kernel,
Point anchor, double delta, int borderType, const Scalar& _borderValue)
{
Mat src, _dst;
Scalar borderValue = _borderValue;
CV_Assert( kernel.type() == CV_32F || kernel.type() == CV_64F );
if( anchor == Point(-1,-1) )
anchor = Point(kernel.cols/2, kernel.rows/2);
if( borderType == IPL_BORDER_CONSTANT )
borderValue = getMinVal(src.depth());
copyMakeBorder(_src, src, anchor.y, kernel.rows - anchor.y - 1,
anchor.x, kernel.cols - anchor.x - 1,
borderType, borderValue);
_dst.create( _src.size(), CV_MAKETYPE(CV_64F, src.channels()) );
vector<int> ofs;
vector<double> coeff(kernel.rows*kernel.cols);
Mat cmat(kernel.rows, kernel.cols, CV_64F, &coeff[0]);
convert(kernel, cmat, cmat.type());
int step = (int)(src.step/src.elemSize1()), cn = src.channels();
for( int i = 0; i < kernel.rows; i++ )
for( int j = 0; j < kernel.cols; j++ )
ofs.push_back(i*step + j*cn);
switch( src.depth() )
{
case CV_8U:
filter2D_<uchar>(src, _dst, ofs, coeff);
break;
case CV_8S:
filter2D_<schar>(src, _dst, ofs, coeff);
break;
case CV_16U:
filter2D_<ushort>(src, _dst, ofs, coeff);
break;
case CV_16S:
filter2D_<short>(src, _dst, ofs, coeff);
break;
case CV_32S:
filter2D_<int>(src, _dst, ofs, coeff);
break;
case CV_32F:
filter2D_<float>(src, _dst, ofs, coeff);
break;
case CV_64F:
filter2D_<double>(src, _dst, ofs, coeff);
break;
default:
CV_Assert(0);
}
convert(_dst, dst, ddepth, 1, delta);
}
static int borderInterpolate( int p, int len, int borderType )
{
if( (unsigned)p < (unsigned)len )
;
else if( borderType == IPL_BORDER_REPLICATE )
p = p < 0 ? 0 : len - 1;
else if( borderType == IPL_BORDER_REFLECT || borderType == IPL_BORDER_REFLECT_101 )
{
int delta = borderType == IPL_BORDER_REFLECT_101;
if( len == 1 )
return 0;
do
{
if( p < 0 )
p = -p - 1 + delta;
else
p = len - 1 - (p - len) - delta;
}
while( (unsigned)p >= (unsigned)len );
}
else if( borderType == IPL_BORDER_WRAP )
{
if( p < 0 )
p -= ((p-len+1)/len)*len;
if( p >= len )
p %= len;
}
else if( borderType == IPL_BORDER_CONSTANT )
p = -1;
else
CV_Error( CV_StsBadArg, "Unknown/unsupported border type" );
return p;
}
void copyMakeBorder(const Mat& src, Mat& dst, int top, int bottom, int left, int right,
int borderType, const Scalar& borderValue)
{
dst.create(src.rows + top + bottom, src.cols + left + right, src.type());
int i, j, k, esz = (int)src.elemSize();
int width = src.cols*esz, width1 = dst.cols*esz;
if( borderType == IPL_BORDER_CONSTANT )
{
vector<uchar> valvec((src.cols + left + right)*esz);
uchar* val = &valvec[0];
scalarToRawData(borderValue, val, src.type(), (src.cols + left + right)*src.channels());
left *= esz;
right *= esz;
for( i = 0; i < src.rows; i++ )
{
const uchar* sptr = src.ptr(i);
uchar* dptr = dst.ptr(i + top) + left;
for( j = 0; j < left; j++ )
dptr[j - left] = val[j];
if( dptr != sptr )
for( j = 0; j < width; j++ )
dptr[j] = sptr[j];
for( j = 0; j < right; j++ )
dptr[j + width] = val[j];
}
for( i = 0; i < top; i++ )
{
uchar* dptr = dst.ptr(i);
for( j = 0; j < width1; j++ )
dptr[j] = val[j];
}
for( i = 0; i < bottom; i++ )
{
uchar* dptr = dst.ptr(i + top + src.rows);
for( j = 0; j < width1; j++ )
dptr[j] = val[j];
}
}
else
{
vector<int> tabvec((left + right)*esz);
int* ltab = &tabvec[0];
int* rtab = &tabvec[left*esz];
for( i = 0; i < left; i++ )
{
j = borderInterpolate(i - left, src.cols, borderType)*esz;
for( k = 0; k < esz; k++ )
ltab[i*esz + k] = j + k;
}
for( i = 0; i < right; i++ )
{
j = borderInterpolate(src.cols + i, src.cols, borderType)*esz;
for( k = 0; k < esz; k++ )
rtab[i*esz + k] = j + k;
}
left *= esz;
right *= esz;
for( i = 0; i < src.rows; i++ )
{
const uchar* sptr = src.ptr(i);
uchar* dptr = dst.ptr(i + top);
for( j = 0; j < left; j++ )
dptr[j] = sptr[ltab[j]];
if( dptr + left != sptr )
{
for( j = 0; j < width; j++ )
dptr[j + left] = sptr[j];
}
for( j = 0; j < right; j++ )
dptr[j + left + width] = sptr[rtab[j]];
}
for( i = 0; i < top; i++ )
{
j = borderInterpolate(i - top, src.rows, borderType);
const uchar* sptr = dst.ptr(j + top);
uchar* dptr = dst.ptr(i);
for( k = 0; k < width1; k++ )
dptr[k] = sptr[k];
}
for( i = 0; i < bottom; i++ )
{
j = borderInterpolate(i + src.rows, src.rows, borderType);
const uchar* sptr = dst.ptr(j + top);
uchar* dptr = dst.ptr(i + top + src.rows);
for( k = 0; k < width1; k++ )
dptr[k] = sptr[k];
}
}
}
template<typename _Tp> static void
minMaxLoc_(const _Tp* src, size_t total, size_t startidx,
double* _minval, double* _maxval,
size_t* _minpos, size_t* _maxpos,
const uchar* mask)
{
_Tp maxval = saturate_cast<_Tp>(*_maxval), minval = saturate_cast<_Tp>(*_minval);
size_t minpos = *_minpos, maxpos = *_maxpos;
if( !mask )
{
for( size_t i = 0; i < total; i++ )
{
_Tp val = src[i];
if( minval > val )
{
minval = val;
minpos = startidx + i;
}
if( maxval < val )
{
maxval = val;
maxpos = startidx + i;
}
}
}
else
{
for( size_t i = 0; i < total; i++ )
{
_Tp val = src[i];
if( minval > val && mask[i] )
{
minval = val;
minpos = startidx + i;
}
if( maxval < val && mask[i] )
{
maxval = val;
maxpos = startidx + i;
}
}
}
*_maxval = maxval;
*_minval = minval;
*_maxpos = maxpos;
*_minpos = minpos;
}
static void setpos( const Mat& mtx, vector<int>& pos, size_t idx )
{
pos.resize(mtx.dims);
if( idx > 0 )
{
idx--;
for( int i = mtx.dims-1; i >= 0; i-- )
{
int sz = mtx.size[i]*(i == mtx.dims-1 ? mtx.channels() : 1);
pos[i] = idx % sz;
idx /= sz;
}
}
else
{
for( int i = mtx.dims-1; i >= 0; i-- )
pos[i] = -1;
}
}
void minMaxLoc(const Mat& src, double* _minval, double* _maxval,
vector<int>* _minloc, vector<int>* _maxloc,
const Mat& mask)
{
CV_Assert( src.channels() == 1 );
const Mat *arrays[]={&src, &mask, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t startidx = 1, total = planes[0].total();
int i, nplanes = it.nplanes, depth = src.depth();
double maxval = depth < CV_32F ? INT_MIN : depth == CV_32F ? -FLT_MAX : -DBL_MAX;
double minval = depth < CV_32F ? INT_MAX : depth == CV_32F ? FLT_MAX : DBL_MAX;
size_t maxidx = 0, minidx = 0;
for( i = 0; i < nplanes; i++, ++it, startidx += total )
{
const uchar* sptr = planes[0].data;
const uchar* mptr = planes[1].data;
switch( depth )
{
case CV_8U:
minMaxLoc_((const uchar*)sptr, total, startidx,
&minval, &maxval, &minidx, &maxidx, mptr);
break;
case CV_8S:
minMaxLoc_((const schar*)sptr, total, startidx,
&minval, &maxval, &minidx, &maxidx, mptr);
break;
case CV_16U:
minMaxLoc_((const ushort*)sptr, total, startidx,
&minval, &maxval, &minidx, &maxidx, mptr);
break;
case CV_16S:
minMaxLoc_((const short*)sptr, total, startidx,
&minval, &maxval, &minidx, &maxidx, mptr);
break;
case CV_32S:
minMaxLoc_((const int*)sptr, total, startidx,
&minval, &maxval, &minidx, &maxidx, mptr);
break;
case CV_32F:
minMaxLoc_((const float*)sptr, total, startidx,
&minval, &maxval, &minidx, &maxidx, mptr);
break;
case CV_64F:
minMaxLoc_((const double*)sptr, total, startidx,
&minval, &maxval, &minidx, &maxidx, mptr);
break;
default:
CV_Assert(0);
}
}
if( minidx == 0 )
minval = maxval = 0;
if( _maxval )
*_maxval = maxval;
if( _minval )
*_minval = minval;
if( _maxloc )
setpos( src, *_maxloc, maxidx );
if( _minloc )
setpos( src, *_minloc, minidx );
}
template<typename _Tp> static double
norm_(const _Tp* src, size_t total, int cn, int normType, double startval, const uchar* mask)
{
size_t i;
double result = startval;
if( !mask )
total *= cn;
if( normType == NORM_INF )
{
if( !mask )
for( i = 0; i < total; i++ )
result = std::max(result, (double)std::abs(src[i]));
else
for( int c = 0; c < cn; c++ )
{
for( i = 0; i < total; i++ )
if( mask[i] )
result = std::max(result, (double)std::abs(src[i*cn + c]));
}
}
else if( normType == NORM_L1 )
{
if( !mask )
for( i = 0; i < total; i++ )
result += std::abs(src[i]);
else
for( int c = 0; c < cn; c++ )
{
for( i = 0; i < total; i++ )
if( mask[i] )
result += std::abs(src[i*cn + c]);
}
}
else
{
if( !mask )
for( i = 0; i < total; i++ )
{
double v = src[i];
result += v*v;
}
else
for( int c = 0; c < cn; c++ )
{
for( i = 0; i < total; i++ )
if( mask[i] )
{
double v = src[i*cn + c];
result += v*v;
}
}
}
return result;
}
template<typename _Tp> static double
norm_(const _Tp* src1, const _Tp* src2, size_t total, int cn, int normType, double startval, const uchar* mask)
{
size_t i;
double result = startval;
if( !mask )
total *= cn;
if( normType == NORM_INF )
{
if( !mask )
for( i = 0; i < total; i++ )
result = std::max(result, (double)std::abs(src1[i] - src2[i]));
else
for( int c = 0; c < cn; c++ )
{
for( i = 0; i < total; i++ )
if( mask[i] )
result = std::max(result, (double)std::abs(src1[i*cn + c] - src2[i*cn + c]));
}
}
else if( normType == NORM_L1 )
{
if( !mask )
for( i = 0; i < total; i++ )
result += std::abs(src1[i] - src2[i]);
else
for( int c = 0; c < cn; c++ )
{
for( i = 0; i < total; i++ )
if( mask[i] )
result += std::abs(src1[i*cn + c] - src2[i*cn + c]);
}
}
else
{
if( !mask )
for( i = 0; i < total; i++ )
{
double v = src1[i] - src2[i];
result += v*v;
}
else
for( int c = 0; c < cn; c++ )
{
for( i = 0; i < total; i++ )
if( mask[i] )
{
double v = src1[i*cn + c] - src2[i*cn + c];
result += v*v;
}
}
}
return result;
}
double norm(const Mat& src, int normType, const Mat& mask)
{
CV_Assert( mask.empty() || (src.size == mask.size && mask.type() == CV_8U) );
CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 );
const Mat *arrays[]={&src, &mask, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total();
int i, nplanes = it.nplanes, depth = src.depth();
int cn = planes[0].channels();
double result = 0;
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr = planes[0].data;
const uchar* mptr = planes[1].data;
switch( depth )
{
case CV_8U:
result = norm_((const uchar*)sptr, total, cn, normType, result, mptr);
break;
case CV_8S:
result = norm_((const schar*)sptr, total, cn, normType, result, mptr);
break;
case CV_16U:
result = norm_((const ushort*)sptr, total, cn, normType, result, mptr);
break;
case CV_16S:
result = norm_((const short*)sptr, total, cn, normType, result, mptr);
break;
case CV_32S:
result = norm_((const int*)sptr, total, cn, normType, result, mptr);
break;
case CV_32F:
result = norm_((const float*)sptr, total, cn, normType, result, mptr);
break;
case CV_64F:
result = norm_((const double*)sptr, total, cn, normType, result, mptr);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
};
}
if( normType == NORM_L2 )
result = sqrt(result);
return result;
}
double norm(const Mat& src1, const Mat& src2, int normType, const Mat& mask)
{
CV_Assert( src1.type() == src2.type() && src1.size == src2.size );
CV_Assert( mask.empty() || (src1.size == mask.size && mask.type() == CV_8U) );
CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 );
const Mat *arrays[]={&src1, &src2, &mask, 0};
Mat planes[3];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total();
int i, nplanes = it.nplanes, depth = src1.depth();
int cn = planes[0].channels();
double result = 0;
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr1 = planes[0].data;
const uchar* sptr2 = planes[1].data;
const uchar* mptr = planes[2].data;
switch( depth )
{
case CV_8U:
result = norm_((const uchar*)sptr1, (const uchar*)sptr2, total, cn, normType, result, mptr);
break;
case CV_8S:
result = norm_((const schar*)sptr1, (const schar*)sptr2, total, cn, normType, result, mptr);
break;
case CV_16U:
result = norm_((const ushort*)sptr1, (const ushort*)sptr2, total, cn, normType, result, mptr);
break;
case CV_16S:
result = norm_((const short*)sptr1, (const short*)sptr2, total, cn, normType, result, mptr);
break;
case CV_32S:
result = norm_((const int*)sptr1, (const int*)sptr2, total, cn, normType, result, mptr);
break;
case CV_32F:
result = norm_((const float*)sptr1, (const float*)sptr2, total, cn, normType, result, mptr);
break;
case CV_64F:
result = norm_((const double*)sptr1, (const double*)sptr2, total, cn, normType, result, mptr);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
};
}
if( normType == NORM_L2 )
result = sqrt(result);
return result;
}
template<typename _Tp> static double
crossCorr_(const _Tp* src1, const _Tp* src2, size_t total)
{
double result = 0;
for( size_t i = 0; i < total; i++ )
result += (double)src1[i]*src2[i];
return result;
}
double crossCorr(const Mat& src1, const Mat& src2)
{
CV_Assert( src1.size == src2.size && src1.type() == src2.type() );
const Mat *arrays[]={&src1, &src2, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total()*planes[0].channels();
int i, nplanes = it.nplanes, depth = src1.depth();
double result = 0;
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr1 = planes[0].data;
const uchar* sptr2 = planes[1].data;
switch( depth )
{
case CV_8U:
result += crossCorr_((const uchar*)sptr1, (const uchar*)sptr2, total);
break;
case CV_8S:
result += crossCorr_((const schar*)sptr1, (const schar*)sptr2, total);
break;
case CV_16U:
result += crossCorr_((const ushort*)sptr1, (const ushort*)sptr2, total);
break;
case CV_16S:
result += crossCorr_((const short*)sptr1, (const short*)sptr2, total);
break;
case CV_32S:
result += crossCorr_((const int*)sptr1, (const int*)sptr2, total);
break;
case CV_32F:
result += crossCorr_((const float*)sptr1, (const float*)sptr2, total);
break;
case CV_64F:
result += crossCorr_((const double*)sptr1, (const double*)sptr2, total);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
};
}
return result;
}
static void
logicOp_(const uchar* src1, const uchar* src2, uchar* dst, size_t total, char c)
{
size_t i;
if( c == '&' )
for( i = 0; i < total; i++ )
dst[i] = src1[i] & src2[i];
else if( c == '|' )
for( i = 0; i < total; i++ )
dst[i] = src1[i] | src2[i];
else
for( i = 0; i < total; i++ )
dst[i] = src1[i] ^ src2[i];
}
static void
logicOpS_(const uchar* src, const uchar* scalar, uchar* dst, size_t total, char c)
{
const size_t blockSize = 96;
size_t i, j;
if( c == '&' )
for( i = 0; i < total; i += blockSize, dst += blockSize, src += blockSize )
{
size_t sz = std::min(total - i, blockSize);
for( j = 0; j < sz; j++ )
dst[j] = src[j] & scalar[j];
}
else if( c == '|' )
for( i = 0; i < total; i += blockSize, dst += blockSize, src += blockSize )
{
size_t sz = std::min(total - i, blockSize);
for( j = 0; j < sz; j++ )
dst[j] = src[j] | scalar[j];
}
else if( c == '^' )
{
for( i = 0; i < total; i += blockSize, dst += blockSize, src += blockSize )
{
size_t sz = std::min(total - i, blockSize);
for( j = 0; j < sz; j++ )
dst[j] = src[j] ^ scalar[j];
}
}
else
for( i = 0; i < total; i++ )
dst[i] = ~src[i];
}
void logicOp( const Mat& src1, const Mat& src2, Mat& dst, char op )
{
CV_Assert( op == '&' || op == '|' || op == '^' );
CV_Assert( src1.type() == src2.type() && src1.size == src2.size );
dst.create( src1.dims, &src1.size[0], src1.type() );
const Mat *arrays[]={&src1, &src2, &dst, 0};
Mat planes[3];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total()*planes[0].elemSize();
int i, nplanes = it.nplanes;
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr1 = planes[0].data;
const uchar* sptr2 = planes[1].data;
uchar* dptr = planes[2].data;
logicOp_(sptr1, sptr2, dptr, total, op);
}
}
void logicOp(const Mat& src, const Scalar& s, Mat& dst, char op)
{
CV_Assert( op == '&' || op == '|' || op == '^' || op == '~' );
dst.create( src.dims, &src.size[0], src.type() );
const Mat *arrays[]={&src, &dst, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total()*planes[0].elemSize();
int i, nplanes = it.nplanes;
double buf[12];
scalarToRawData(s, buf, src.type(), 96/planes[0].elemSize1());
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr = planes[0].data;
uchar* dptr = planes[1].data;
logicOpS_(sptr, (uchar*)&buf[0], dptr, total, op);
}
}
template<typename _Tp> static void
compare_(const _Tp* src1, const _Tp* src2, uchar* dst, size_t total, int cmpop)
{
size_t i;
switch( cmpop )
{
case CMP_LT:
for( i = 0; i < total; i++ )
dst[i] = src1[i] < src2[i] ? 255 : 0;
break;
case CMP_LE:
for( i = 0; i < total; i++ )
dst[i] = src1[i] <= src2[i] ? 255 : 0;
break;
case CMP_EQ:
for( i = 0; i < total; i++ )
dst[i] = src1[i] == src2[i] ? 255 : 0;
break;
case CMP_NE:
for( i = 0; i < total; i++ )
dst[i] = src1[i] != src2[i] ? 255 : 0;
break;
case CMP_GE:
for( i = 0; i < total; i++ )
dst[i] = src1[i] >= src2[i] ? 255 : 0;
break;
case CMP_GT:
for( i = 0; i < total; i++ )
dst[i] = src1[i] > src2[i] ? 255 : 0;
break;
default:
CV_Error(CV_StsBadArg, "Unknown comparison operation");
}
}
template<typename _Tp, typename _WTp> static void
compareS_(const _Tp* src1, _WTp value, uchar* dst, size_t total, int cmpop)
{
size_t i;
switch( cmpop )
{
case CMP_LT:
for( i = 0; i < total; i++ )
dst[i] = src1[i] < value ? 255 : 0;
break;
case CMP_LE:
for( i = 0; i < total; i++ )
dst[i] = src1[i] <= value ? 255 : 0;
break;
case CMP_EQ:
for( i = 0; i < total; i++ )
dst[i] = src1[i] == value ? 255 : 0;
break;
case CMP_NE:
for( i = 0; i < total; i++ )
dst[i] = src1[i] != value ? 255 : 0;
break;
case CMP_GE:
for( i = 0; i < total; i++ )
dst[i] = src1[i] >= value ? 255 : 0;
break;
case CMP_GT:
for( i = 0; i < total; i++ )
dst[i] = src1[i] > value ? 255 : 0;
break;
default:
CV_Error(CV_StsBadArg, "Unknown comparison operation");
}
}
void compare(const Mat& src1, const Mat& src2, Mat& dst, int cmpop)
{
CV_Assert( src1.type() == src2.type() && src1.channels() == 1 && src1.size == src2.size );
dst.create( src1.dims, &src1.size[0], CV_8U );
const Mat *arrays[]={&src1, &src2, &dst, 0};
Mat planes[3];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total();
int i, nplanes = it.nplanes, depth = src1.depth();
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr1 = planes[0].data;
const uchar* sptr2 = planes[1].data;
uchar* dptr = planes[2].data;
switch( depth )
{
case CV_8U:
compare_((const uchar*)sptr1, (const uchar*)sptr2, dptr, total, cmpop);
break;
case CV_8S:
compare_((const schar*)sptr1, (const schar*)sptr2, dptr, total, cmpop);
break;
case CV_16U:
compare_((const ushort*)sptr1, (const ushort*)sptr2, dptr, total, cmpop);
break;
case CV_16S:
compare_((const short*)sptr1, (const short*)sptr2, dptr, total, cmpop);
break;
case CV_32S:
compare_((const int*)sptr1, (const int*)sptr2, dptr, total, cmpop);
break;
case CV_32F:
compare_((const float*)sptr1, (const float*)sptr2, dptr, total, cmpop);
break;
case CV_64F:
compare_((const double*)sptr1, (const double*)sptr2, dptr, total, cmpop);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
}
}
}
void compare(const Mat& src, double value, Mat& dst, int cmpop)
{
CV_Assert( src.channels() == 1 );
dst.create( src.dims, &src.size[0], CV_8U );
const Mat *arrays[]={&src, &dst, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total();
int i, nplanes = it.nplanes, depth = src.depth();
int ivalue = saturate_cast<int>(value);
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr = planes[0].data;
uchar* dptr = planes[1].data;
switch( depth )
{
case CV_8U:
compareS_((const uchar*)sptr, ivalue, dptr, total, cmpop);
break;
case CV_8S:
compareS_((const schar*)sptr, ivalue, dptr, total, cmpop);
break;
case CV_16U:
compareS_((const ushort*)sptr, ivalue, dptr, total, cmpop);
break;
case CV_16S:
compareS_((const short*)sptr, ivalue, dptr, total, cmpop);
break;
case CV_32S:
compareS_((const int*)sptr, ivalue, dptr, total, cmpop);
break;
case CV_32F:
compareS_((const float*)sptr, value, dptr, total, cmpop);
break;
case CV_64F:
compareS_((const double*)sptr, value, dptr, total, cmpop);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
}
}
}
template<typename _Tp> static double
cmpUlpsInt_(const _Tp* src1, const _Tp* src2, size_t total, int imaxdiff,
size_t startidx, size_t& idx)
{
size_t i;
int realmaxdiff = 0;
for( i = 0; i < total; i++ )
{
int diff = std::abs(src1[i] - src2[i]);
if( realmaxdiff < diff )
{
realmaxdiff = diff;
if( diff > imaxdiff && idx == 0 )
idx = i + startidx;
}
}
return realmaxdiff;
}
template<> static double cmpUlpsInt_<int>(const int* src1, const int* src2,
size_t total, int imaxdiff,
size_t startidx, size_t& idx)
{
size_t i;
double realmaxdiff = 0;
for( i = 0; i < total; i++ )
{
double diff = fabs((double)src1[i] - (double)src2[i]);
if( realmaxdiff < diff )
{
realmaxdiff = diff;
if( diff > imaxdiff && idx == 0 )
idx = i + startidx;
}
}
return realmaxdiff;
}
template<typename _Tp> static double
cmpUlpsFlt_(const _Tp* src1, const _Tp* src2, size_t total, int imaxdiff, size_t startidx, size_t& idx)
{
const _Tp C = ((_Tp)1 << (sizeof(_Tp)*8-1)) - 1;
_Tp realmaxdiff = 0;
size_t i;
for( i = 0; i < total; i++ )
{
_Tp a = src1[i], b = src2[i];
if( a < 0 ) a ^= C; if( b < 0 ) b ^= C;
_Tp diff = std::abs(a - b);
if( realmaxdiff < diff )
{
realmaxdiff = diff;
if( diff > imaxdiff && idx == 0 )
idx = i + startidx;
}
}
return realmaxdiff;
}
bool cmpUlps(const Mat& src1, const Mat& src2, int imaxDiff, double* _realmaxdiff, vector<int>* loc)
{
CV_Assert( src1.type() == src2.type() && src1.size == src2.size );
const Mat *arrays[]={&src1, &src2, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total()*planes[0].channels();
int i, nplanes = it.nplanes, depth = src1.depth();
size_t startidx = 1, idx = 0;
if(_realmaxdiff)
*_realmaxdiff = 0;
for( i = 0; i < nplanes; i++, ++it, startidx += total )
{
const uchar* sptr1 = planes[0].data;
const uchar* sptr2 = planes[1].data;
double realmaxdiff = 0;
switch( depth )
{
case CV_8U:
realmaxdiff = cmpUlpsInt_((const uchar*)sptr1, (const uchar*)sptr2, total, imaxDiff, startidx, idx);
break;
case CV_8S:
realmaxdiff = cmpUlpsInt_((const schar*)sptr1, (const schar*)sptr2, total, imaxDiff, startidx, idx);
break;
case CV_16U:
realmaxdiff = cmpUlpsInt_((const ushort*)sptr1, (const ushort*)sptr2, total, imaxDiff, startidx, idx);
break;
case CV_16S:
realmaxdiff = cmpUlpsInt_((const short*)sptr1, (const short*)sptr2, total, imaxDiff, startidx, idx);
break;
case CV_32S:
realmaxdiff = cmpUlpsInt_((const int*)sptr1, (const int*)sptr2, total, imaxDiff, startidx, idx);
break;
case CV_32F:
realmaxdiff = cmpUlpsFlt_((const int*)sptr1, (const int*)sptr2, total, imaxDiff, startidx, idx);
break;
case CV_64F:
realmaxdiff = cmpUlpsFlt_((const int64*)sptr1, (const int64*)sptr2, total, imaxDiff, startidx, idx);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
}
if(_realmaxdiff)
*_realmaxdiff = std::max(*_realmaxdiff, realmaxdiff);
}
if(idx > 0 && loc)
setpos(src1, *loc, idx);
return idx == 0;
}
template<typename _Tp> static void
checkInt_(const _Tp* a, size_t total, int imin, int imax, size_t startidx, size_t& idx)
{
for( size_t i = 0; i < total; i++ )
{
int val = a[i];
if( val < imin || val > imax )
{
idx = i + startidx;
break;
}
}
}
template<typename _Tp> static void
checkFlt_(const _Tp* a, size_t total, double fmin, double fmax, size_t startidx, size_t& idx)
{
for( size_t i = 0; i < total; i++ )
{
double val = a[i];
if( cvIsNaN(val) || cvIsInf(val) || val < fmin || val > fmax )
{
idx = i + startidx;
break;
}
}
}
// checks that the array does not have NaNs and/or Infs and all the elements are
// within [min_val,max_val). idx is the index of the first "bad" element.
int check( const Mat& a, double fmin, double fmax, vector<int>* _idx )
{
const Mat *arrays[]={&a, 0};
Mat plane;
NAryMatIterator it(arrays, &plane);
size_t total = plane.total()*plane.channels();
int i, nplanes = it.nplanes, depth = a.depth();
size_t startidx = 1, idx = 0;
int imin = 0, imax = 0;
if( depth <= CV_32S )
{
imin = cvCeil(fmin);
imax = cvFloor(fmax);
}
for( i = 0; i < nplanes; i++, ++it, startidx += total )
{
const uchar* aptr = plane.data;
switch( depth )
{
case CV_8U:
checkInt_((const uchar*)aptr, total, imin, imax, startidx, idx);
break;
case CV_8S:
checkInt_((const schar*)aptr, total, imin, imax, startidx, idx);
break;
case CV_16U:
checkInt_((const ushort*)aptr, total, imin, imax, startidx, idx);
break;
case CV_16S:
checkInt_((const short*)aptr, total, imin, imax, startidx, idx);
break;
case CV_32S:
checkInt_((const int*)aptr, total, imin, imax, startidx, idx);
break;
case CV_32F:
checkFlt_((const float*)aptr, total, fmin, fmax, startidx, idx);
break;
case CV_64F:
checkFlt_((const double*)aptr, total, fmin, fmax, startidx, idx);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
}
if( idx != 0 )
break;
}
if(idx != 0 && _idx)
setpos(a, *_idx, idx);
return idx == 0 ? 0 : -1;
}
// compares two arrays. max_diff is the maximum actual difference,
// success_err_level is maximum allowed difference, idx is the index of the first
// element for which difference is >success_err_level
// (or index of element with the maximum difference)
int cmpEps( const Mat& arr, const Mat& refarr, double* _realmaxdiff,
double success_err_level, vector<int>* _idx,
bool element_wise_relative_error )
{
CV_Assert( arr.type() == refarr.type() && arr.size == refarr.size );
int ilevel = refarr.depth() <= CV_32S ? cvFloor(success_err_level) : 0;
int result = 0;
const Mat *arrays[]={&arr, &refarr, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total()*planes[0].channels(), j = total;
int i, nplanes = it.nplanes, depth = arr.depth();
size_t startidx = 1, idx = 0;
double realmaxdiff = 0, maxval = 0;
if(_realmaxdiff)
*_realmaxdiff = 0;
if( refarr.depth() >= CV_32F && !element_wise_relative_error )
{
maxval = cvtest::norm( refarr, NORM_INF );
maxval = MAX(maxval, 1.);
}
for( i = 0; i < nplanes; i++, ++it, startidx += total )
{
const uchar* sptr1 = planes[0].data;
const uchar* sptr2 = planes[1].data;
switch( depth )
{
case CV_8U:
realmaxdiff = cmpUlpsInt_((const uchar*)sptr1, (const uchar*)sptr2, total, ilevel, startidx, idx);
break;
case CV_8S:
realmaxdiff = cmpUlpsInt_((const schar*)sptr1, (const schar*)sptr2, total, ilevel, startidx, idx);
break;
case CV_16U:
realmaxdiff = cmpUlpsInt_((const ushort*)sptr1, (const ushort*)sptr2, total, ilevel, startidx, idx);
break;
case CV_16S:
realmaxdiff = cmpUlpsInt_((const short*)sptr1, (const short*)sptr2, total, ilevel, startidx, idx);
break;
case CV_32S:
realmaxdiff = cmpUlpsInt_((const int*)sptr1, (const int*)sptr2, total, ilevel, startidx, idx);
break;
case CV_32F:
for( j = 0; j < total; j++ )
{
double a_val = ((float*)sptr1)[j];
double b_val = ((float*)sptr2)[j];
double threshold;
if( ((int*)sptr1)[j] == ((int*)sptr2)[j] )
continue;
if( cvIsNaN(a_val) || cvIsInf(a_val) )
{
result = -2;
idx = startidx + j;
break;
}
if( cvIsNaN(b_val) || cvIsInf(b_val) )
{
result = -3;
idx = startidx + j;
break;
}
a_val = fabs(a_val - b_val);
threshold = element_wise_relative_error ? fabs(b_val) + 1 : maxval;
if( a_val > threshold*success_err_level )
{
realmaxdiff = a_val/threshold;
if( idx == 0 )
idx = startidx + j;
break;
}
}
break;
case CV_64F:
for( j = 0; j < total; j++ )
{
double a_val = ((double*)sptr1)[j];
double b_val = ((double*)sptr2)[j];
double threshold;
if( ((int64*)sptr1)[j] == ((int64*)sptr2)[j] )
continue;
if( cvIsNaN(a_val) || cvIsInf(a_val) )
{
result = -2;
idx = startidx + j;
break;
}
if( cvIsNaN(b_val) || cvIsInf(b_val) )
{
result = -3;
idx = startidx + j;
break;
}
a_val = fabs(a_val - b_val);
threshold = element_wise_relative_error ? fabs(b_val) + 1 : maxval;
if( a_val > threshold*success_err_level )
{
realmaxdiff = a_val/threshold;
idx = startidx + j;
break;
}
}
break;
default:
assert(0);
return -1;
}
if(_realmaxdiff)
*_realmaxdiff = MAX(*_realmaxdiff, realmaxdiff);
if( idx != 0 )
break;
}
if( result == 0 && idx != 0 )
result = -1;
if( result < -1 && _realmaxdiff )
*_realmaxdiff = exp(1000.);
if(idx > 0 && _idx)
setpos(arr, *_idx, idx);
return result;
}
int cmpEps2( TS* ts, const Mat& a, const Mat& b, double success_err_level,
bool element_wise_relative_error, const char* desc )
{
char msg[100];
double diff = 0;
vector<int> idx;
int code = cmpEps( a, b, &diff, success_err_level, &idx, element_wise_relative_error );
switch( code )
{
case -1:
sprintf( msg, "%s: Too big difference (=%g)", desc, diff );
code = TS::FAIL_BAD_ACCURACY;
break;
case -2:
sprintf( msg, "%s: Invalid output", desc );
code = TS::FAIL_INVALID_OUTPUT;
break;
case -3:
sprintf( msg, "%s: Invalid reference output", desc );
code = TS::FAIL_INVALID_OUTPUT;
break;
default:
;
}
if( code < 0 )
{
if( a.total() == 1 )
{
ts->printf( TS::LOG, "%s\n", msg );
}
else if( a.dims == 2 && (a.rows == 1 || a.cols == 1) )
{
ts->printf( TS::LOG, "%s at element %d\n", msg, idx[0] + idx[1] );
}
else
{
string idxstr = vec2str(", ", &idx[0], idx.size());
ts->printf( TS::LOG, "%s at (%s)\n", msg, idxstr.c_str() );
}
}
return code;
}
int cmpEps2_64f( TS* ts, const double* val, const double* refval, int len,
double eps, const char* param_name )
{
Mat _val(1, len, CV_64F, (void*)val);
Mat _refval(1, len, CV_64F, (void*)refval);
return cmpEps2( ts, _val, _refval, eps, true, param_name );
}
template<typename _Tp> static void
GEMM_(const _Tp* a_data0, int a_step, int a_delta,
const _Tp* b_data0, int b_step, int b_delta,
const _Tp* c_data0, int c_step, int c_delta,
_Tp* d_data, int d_step,
int d_rows, int d_cols, int a_cols, int cn,
double alpha, double beta)
{
for( int i = 0; i < d_rows; i++, d_data += d_step, c_data0 += c_step, a_data0 += a_step )
{
for( int j = 0; j < d_cols; j++ )
{
const _Tp* a_data = a_data0;
const _Tp* b_data = b_data0 + j*b_delta;
const _Tp* c_data = c_data0 + j*c_delta;
if( cn == 1 )
{
double s = 0;
for( int k = 0; k < a_cols; k++ )
{
s += ((double)a_data[0])*b_data[0];
a_data += a_delta;
b_data += b_step;
}
d_data[j] = (_Tp)(s*alpha + (c_data ? c_data[0]*beta : 0));
}
else
{
double s_re = 0, s_im = 0;
for( int k = 0; k < a_cols; k++ )
{
s_re += ((double)a_data[0])*b_data[0] - ((double)a_data[1])*b_data[1];
s_im += ((double)a_data[0])*b_data[1] + ((double)a_data[1])*b_data[0];
a_data += a_delta;
b_data += b_step;
}
s_re *= alpha;
s_im *= alpha;
if( c_data )
{
s_re += c_data[0]*beta;
s_im += c_data[1]*beta;
}
d_data[j*2] = (_Tp)s_re;
d_data[j*2+1] = (_Tp)s_im;
}
}
}
}
void gemm( const Mat& _a, const Mat& _b, double alpha,
const Mat& _c, double beta, Mat& d, int flags )
{
Mat a = _a, b = _b, c = _c;
if( a.data == d.data )
a = a.clone();
if( b.data == d.data )
b = b.clone();
if( !c.empty() && c.data == d.data && (flags & cv::GEMM_3_T) )
c = c.clone();
int a_rows = a.rows, a_cols = a.cols, b_rows = b.rows, b_cols = b.cols;
int cn = a.channels();
int a_step = (int)a.step1(), a_delta = cn;
int b_step = (int)b.step1(), b_delta = cn;
int c_rows = 0, c_cols = 0, c_step = 0, c_delta = 0;
CV_Assert( a.type() == b.type() && a.dims == 2 && b.dims == 2 && cn <= 2 );
if( flags & cv::GEMM_1_T )
{
std::swap( a_rows, a_cols );
std::swap( a_step, a_delta );
}
if( flags & cv::GEMM_2_T )
{
std::swap( b_rows, b_cols );
std::swap( b_step, b_delta );
}
if( !c.empty() )
{
c_rows = c.rows;
c_cols = c.cols;
c_step = (int)c.step1();
c_delta = cn;
if( flags & cv::GEMM_3_T )
{
std::swap( c_rows, c_cols );
std::swap( c_step, c_delta );
}
CV_Assert( c.dims == 2 && c.type() == a.type() && c_rows == a_rows && c_cols == b_cols );
}
d.create(a_rows, b_cols, a.type());
if( a.depth() == CV_32F )
GEMM_(a.ptr<float>(), a_step, a_delta, b.ptr<float>(), b_step, b_delta,
!c.empty() ? c.ptr<float>() : 0, c_step, c_delta, d.ptr<float>(),
d.step1(), a_rows, b_cols, a_cols, cn, alpha, beta );
else
GEMM_(a.ptr<double>(), a_step, a_delta, b.ptr<double>(), b_step, b_delta,
!c.empty() ? c.ptr<double>() : 0, c_step, c_delta, d.ptr<double>(),
d.step1(), a_rows, b_cols, a_cols, cn, alpha, beta );
}
double cvTsCrossCorr( const CvMat* a, const CvMat* b )
{
int i, j;
int cn, ncols;
double s = 0;
cn = CV_MAT_CN(a->type);
ncols = a->cols*cn;
assert( CV_ARE_SIZES_EQ( a, b ) && CV_ARE_TYPES_EQ( a, b ) );
for( i = 0; i < a->rows; i++ )
{
uchar* a_data = a->data.ptr + a->step*i;
uchar* b_data = b->data.ptr + b->step*i;
switch( CV_MAT_DEPTH(a->type) )
{
case CV_8U:
for( j = 0; j < ncols; j++ )
s += ((uchar*)a_data)[j]*((uchar*)b_data)[j];
break;
case CV_8S:
for( j = 0; j < ncols; j++ )
s += ((schar*)a_data)[j]*((schar*)b_data)[j];
break;
case CV_16U:
for( j = 0; j < ncols; j++ )
s += (double)((ushort*)a_data)[j]*((ushort*)b_data)[j];
break;
case CV_16S:
for( j = 0; j < ncols; j++ )
s += ((short*)a_data)[j]*((short*)b_data)[j];
break;
case CV_32S:
for( j = 0; j < ncols; j++ )
s += ((double)((int*)a_data)[j])*((int*)b_data)[j];
break;
case CV_32F:
for( j = 0; j < ncols; j++ )
s += ((double)((float*)a_data)[j])*((float*)b_data)[j];
break;
case CV_64F:
for( j = 0; j < ncols; j++ )
s += ((double*)a_data)[j]*((double*)b_data)[j];
break;
default:
assert(0);
return log(-1.);
}
}
return s;
}
template<typename _Tp> static void
transform_(const _Tp* sptr, _Tp* dptr, size_t total, int scn, int dcn, const double* mat)
{
for( size_t i = 0; i < total; i++, sptr += scn, dptr += dcn )
{
for( int j = 0; j < dcn; j++ )
{
double s = mat[j*(scn + 1) + scn];
for( int k = 0; k < scn; k++ )
s += mat[j*(scn + 1) + k]*sptr[k];
dptr[j] = saturate_cast<_Tp>(s);
}
}
}
void transform( const Mat& src, Mat& dst, const Mat& transmat, const Mat& _shift )
{
double mat[20];
int scn = src.channels();
int dcn = dst.channels();
int depth = src.depth();
int mattype = transmat.depth();
Mat shift = _shift.reshape(1, 0);
bool haveShift = !shift.empty();
CV_Assert( scn <= 4 && dcn <= 4 &&
(mattype == CV_32F || mattype == CV_64F) &&
(!haveShift || (shift.type() == mattype && (shift.rows == 1 || shift.cols == 1))) );
// prepare cn x (cn + 1) transform matrix
if( mattype == CV_32F )
{
for( int i = 0; i < transmat.rows; i++ )
{
mat[i*(scn+1)+scn] = 0.;
for( int j = 0; j < transmat.cols; j++ )
mat[i*(scn+1)+j] = transmat.at<float>(i,j);
if( haveShift )
mat[i*(scn+1)+scn] = shift.at<float>(i);
}
}
else
{
for( int i = 0; i < transmat.rows; i++ )
{
mat[i*(scn+1)+scn] = 0.;
for( int j = 0; j < transmat.cols; j++ )
mat[i*(scn+1)+j] = transmat.at<double>(i,j);
if( haveShift )
mat[i*(scn+1)+scn] = shift.at<double>(i);
}
}
const Mat *arrays[]={&src, &dst, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total();
int i, nplanes = it.nplanes;
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr = planes[0].data;
uchar* dptr = planes[1].data;
switch( depth )
{
case CV_8U:
transform_((const uchar*)sptr, (uchar*)dptr, total, scn, dcn, mat);
break;
case CV_8S:
transform_((const schar*)sptr, (schar*)dptr, total, scn, dcn, mat);
break;
case CV_16U:
transform_((const ushort*)sptr, (ushort*)dptr, total, scn, dcn, mat);
break;
case CV_16S:
transform_((const short*)sptr, (short*)dptr, total, scn, dcn, mat);
break;
case CV_32S:
transform_((const int*)sptr, (int*)dptr, total, scn, dcn, mat);
break;
case CV_32F:
transform_((const float*)sptr, (float*)dptr, total, scn, dcn, mat);
break;
case CV_64F:
transform_((const double*)sptr, (double*)dptr, total, scn, dcn, mat);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
}
}
}
template<typename _Tp> static void
minmax_(const _Tp* src1, const _Tp* src2, _Tp* dst, size_t total, char op)
{
if( op == 'M' )
for( size_t i = 0; i < total; i++ )
dst[i] = std::max(src1[i], src2[i]);
else
for( size_t i = 0; i < total; i++ )
dst[i] = std::min(src1[i], src2[i]);
}
static void minmax(const Mat& src1, const Mat& src2, Mat& dst, char op)
{
dst.create(src1.dims, src1.size, src1.type());
CV_Assert( src1.type() == src2.type() && src1.size == src2.size );
const Mat *arrays[]={&src1, &src2, &dst, 0};
Mat planes[3];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total()*planes[0].channels();
int i, nplanes = it.nplanes, depth = src1.depth();
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr1 = planes[0].data;
const uchar* sptr2 = planes[1].data;
uchar* dptr = planes[2].data;
switch( depth )
{
case CV_8U:
minmax_((const uchar*)sptr1, (const uchar*)sptr2, (uchar*)dptr, total, op);
break;
case CV_8S:
minmax_((const schar*)sptr1, (const schar*)sptr2, (schar*)dptr, total, op);
break;
case CV_16U:
minmax_((const ushort*)sptr1, (const ushort*)sptr2, (ushort*)dptr, total, op);
break;
case CV_16S:
minmax_((const short*)sptr1, (const short*)sptr2, (short*)dptr, total, op);
break;
case CV_32S:
minmax_((const int*)sptr1, (const int*)sptr2, (int*)dptr, total, op);
break;
case CV_32F:
minmax_((const float*)sptr1, (const float*)sptr2, (float*)dptr, total, op);
break;
case CV_64F:
minmax_((const double*)sptr1, (const double*)sptr2, (double*)dptr, total, op);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
}
}
}
void min(const Mat& src1, const Mat& src2, Mat& dst)
{
minmax( src1, src2, dst, 'm' );
}
void max(const Mat& src1, const Mat& src2, Mat& dst)
{
minmax( src1, src2, dst, 'M' );
}
template<typename _Tp> static void
minmax_(const _Tp* src1, _Tp val, _Tp* dst, size_t total, char op)
{
if( op == 'M' )
for( size_t i = 0; i < total; i++ )
dst[i] = std::max(src1[i], val);
else
for( size_t i = 0; i < total; i++ )
dst[i] = std::min(src1[i], val);
}
static void minmax(const Mat& src1, double val, Mat& dst, char op)
{
dst.create(src1.dims, src1.size, src1.type());
const Mat *arrays[]={&src1, &dst, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total()*planes[0].channels();
int i, nplanes = it.nplanes, depth = src1.depth();
int ival = saturate_cast<int>(val);
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr1 = planes[0].data;
uchar* dptr = planes[1].data;
switch( depth )
{
case CV_8U:
minmax_((const uchar*)sptr1, saturate_cast<uchar>(ival), (uchar*)dptr, total, op);
break;
case CV_8S:
minmax_((const schar*)sptr1, saturate_cast<schar>(ival), (schar*)dptr, total, op);
break;
case CV_16U:
minmax_((const ushort*)sptr1, saturate_cast<ushort>(ival), (ushort*)dptr, total, op);
break;
case CV_16S:
minmax_((const short*)sptr1, saturate_cast<short>(ival), (short*)dptr, total, op);
break;
case CV_32S:
minmax_((const int*)sptr1, saturate_cast<int>(ival), (int*)dptr, total, op);
break;
case CV_32F:
minmax_((const float*)sptr1, saturate_cast<float>(val), (float*)dptr, total, op);
break;
case CV_64F:
minmax_((const double*)sptr1, saturate_cast<double>(val), (double*)dptr, total, op);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
}
}
}
void min(const Mat& src1, double val, Mat& dst)
{
minmax( src1, val, dst, 'm' );
}
void max(const Mat& src1, double val, Mat& dst)
{
minmax( src1, val, dst, 'M' );
}
template<typename _Tp> static void
muldiv_(const _Tp* src1, const _Tp* src2, _Tp* dst, size_t total, double scale, char op)
{
if( op == '*' )
for( size_t i = 0; i < total; i++ )
dst[i] = saturate_cast<_Tp>((scale*src1[i])*src2[i]);
else if( src1 )
for( size_t i = 0; i < total; i++ )
dst[i] = src2[i] ? saturate_cast<_Tp>((scale*src1[i])/src2[i]) : 0;
else
for( size_t i = 0; i < total; i++ )
dst[i] = src2[i] ? saturate_cast<_Tp>(scale/src2[i]) : 0;
}
static void muldiv(const Mat& src1, const Mat& src2, Mat& dst, double scale, char op)
{
dst.create(src2.dims, src2.size, src2.type());
CV_Assert( src1.empty() || (src1.type() == src2.type() && src1.size == src2.size) );
const Mat *arrays[]={&src1, &src2, &dst, 0};
Mat planes[3];
NAryMatIterator it(arrays, planes);
size_t total = planes[1].total()*planes[1].channels();
int i, nplanes = it.nplanes, depth = src2.depth();
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr1 = planes[0].data;
const uchar* sptr2 = planes[1].data;
uchar* dptr = planes[2].data;
switch( depth )
{
case CV_8U:
muldiv_((const uchar*)sptr1, (const uchar*)sptr2, (uchar*)dptr, total, scale, op);
break;
case CV_8S:
muldiv_((const schar*)sptr1, (const schar*)sptr2, (schar*)dptr, total, scale, op);
break;
case CV_16U:
muldiv_((const ushort*)sptr1, (const ushort*)sptr2, (ushort*)dptr, total, scale, op);
break;
case CV_16S:
muldiv_((const short*)sptr1, (const short*)sptr2, (short*)dptr, total, scale, op);
break;
case CV_32S:
muldiv_((const int*)sptr1, (const int*)sptr2, (int*)dptr, total, scale, op);
break;
case CV_32F:
muldiv_((const float*)sptr1, (const float*)sptr2, (float*)dptr, total, scale, op);
break;
case CV_64F:
muldiv_((const double*)sptr1, (const double*)sptr2, (double*)dptr, total, scale, op);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
}
}
}
void multiply(const Mat& src1, const Mat& src2, Mat& dst, double scale)
{
muldiv( src1, src2, dst, scale, '*' );
}
void divide(const Mat& src1, const Mat& src2, Mat& dst, double scale)
{
muldiv( src1, src2, dst, scale, '/' );
}
template<typename _Tp> static void
mean_(const _Tp* src, const uchar* mask, size_t total, int cn, Scalar& sum, int& nz)
{
if( !mask )
{
nz += (int)total;
total *= cn;
for( size_t i = 0; i < total; i += cn )
{
for( int c = 0; c < cn; c++ )
sum[c] += src[i + c];
}
}
else
{
for( size_t i = 0; i < total; i++ )
if( mask[i] )
{
nz++;
for( int c = 0; c < cn; c++ )
sum[c] += src[i*cn + c];
}
}
}
Scalar mean(const Mat& src, const Mat& mask)
{
CV_Assert(mask.empty() || (mask.type() == CV_8U && mask.size == src.size));
Scalar sum;
int nz = 0;
const Mat *arrays[]={&src, &mask, 0};
Mat planes[2];
NAryMatIterator it(arrays, planes);
size_t total = planes[0].total();
int i, nplanes = it.nplanes, depth = src.depth(), cn = src.channels();
for( i = 0; i < nplanes; i++, ++it )
{
const uchar* sptr = planes[0].data;
const uchar* mptr = planes[1].data;
switch( depth )
{
case CV_8U:
mean_((const uchar*)sptr, mptr, total, cn, sum, nz);
break;
case CV_8S:
mean_((const schar*)sptr, mptr, total, cn, sum, nz);
break;
case CV_16U:
mean_((const ushort*)sptr, mptr, total, cn, sum, nz);
break;
case CV_16S:
mean_((const short*)sptr, mptr, total, cn, sum, nz);
break;
case CV_32S:
mean_((const int*)sptr, mptr, total, cn, sum, nz);
break;
case CV_32F:
mean_((const float*)sptr, mptr, total, cn, sum, nz);
break;
case CV_64F:
mean_((const double*)sptr, mptr, total, cn, sum, nz);
break;
default:
CV_Error(CV_StsUnsupportedFormat, "");
}
}
return sum * (1./std::max(nz, 1));
}
void patchZeros( Mat& mat, double level )
{
int j, ncols = mat.cols * mat.channels();
int depth = mat.depth();
CV_Assert( depth == CV_32F || depth == CV_64F );
for( int i = 0; i < mat.rows; i++ )
{
if( depth == CV_32F )
{
float* data = mat.ptr<float>(i);
for( j = 0; j < ncols; j++ )
if( fabs(data[j]) < level )
data[j] += 1;
}
else
{
double* data = mat.ptr<double>(i);
for( j = 0; j < ncols; j++ )
if( fabs(data[j]) < level )
data[j] += 1;
}
}
}
static void calcSobelKernel1D( int order, int _aperture_size, int size, vector<int>& kernel )
{
int i, j, oldval, newval;
kernel.resize(size + 1);
if( _aperture_size < 0 )
{
static const int scharr[] = { 3, 10, 3, -1, 0, 1 };
assert( size == 3 );
for( i = 0; i < size; i++ )
kernel[i] = scharr[order*3 + i];
return;
}
for( i = 1; i <= size; i++ )
kernel[i] = 0;
kernel[0] = 1;
for( i = 0; i < size - order - 1; i++ )
{
oldval = kernel[0];
for( j = 1; j <= size; j++ )
{
newval = kernel[j] + kernel[j-1];
kernel[j-1] = oldval;
oldval = newval;
}
}
for( i = 0; i < order; i++ )
{
oldval = -kernel[0];
for( j = 1; j <= size; j++ )
{
newval = kernel[j-1] - kernel[j];
kernel[j-1] = oldval;
oldval = newval;
}
}
}
Mat calcSobelKernel2D( int dx, int dy, int _aperture_size, int origin )
{
CV_Assert( (_aperture_size == -1 || (_aperture_size >= 1 && _aperture_size % 2 == 1)) &&
dx >= 0 && dy >= 0 && dx + dy <= 3 );
Size ksize = _aperture_size == -1 ? Size(3,3) : _aperture_size > 1 ?
Size(_aperture_size, _aperture_size) : dx > 0 ? Size(3, 1) : Size(1, 3);
Mat kernel(ksize, CV_32F);
vector<int> kx, ky;
calcSobelKernel1D( dx, _aperture_size, ksize.width, kx );
calcSobelKernel1D( dy, _aperture_size, ksize.height, ky );
for( int i = 0; i < kernel.rows; i++ )
{
float ay = (float)ky[i]*(origin && (dy & 1) ? -1 : 1);
for( int j = 0; j < kernel.cols; j++ )
kernel.at<float>(i, j) = kx[j]*ay;
}
return kernel;
}
Mat calcLaplaceKernel2D( int aperture_size )
{
int ksize = aperture_size == 1 ? 3 : aperture_size;
Mat kernel(ksize, ksize, CV_32F);
vector<int> kx, ky;
calcSobelKernel1D( 2, aperture_size, ksize, kx );
if( aperture_size > 1 )
calcSobelKernel1D( 0, aperture_size, ksize, ky );
else
{
ky.resize(3);
ky[0] = ky[2] = 0; ky[1] = 1;
}
for( int i = 0; i < ksize; i++ )
for( int j = 0; j < ksize; j++ )
kernel.at<float>(i, j) = kx[j]*ky[i] + kx[i]*ky[j];
return kernel;
}
void initUndistortMap( const Mat& _a0, const Mat& _k0, Size sz, Mat& _mapx, Mat& _mapy )
{
_mapx.create(sz, CV_32F);
_mapy.create(sz, CV_32F);
double a[9], k[5]={0,0,0,0,0};
Mat _a(3, 3, CV_64F, a);
Mat _k(_k0.rows,_k0.cols, CV_MAKETYPE(CV_64F,_k0.channels()),k);
double fx, fy, cx, cy, ifx, ify, cxn, cyn;
_a0.convertTo(_a, CV_64F);
_k0.convertTo(_k, CV_64F);
fx = a[0]; fy = a[4]; cx = a[2]; cy = a[5];
ifx = 1./fx; ify = 1./fy;
cxn = cx;
cyn = cy;
for( int v = 0; v < sz.height; v++ )
{
for( int u = 0; u < sz.width; u++ )
{
double x = (u - cxn)*ifx;
double y = (v - cyn)*ify;
double x2 = x*x, y2 = y*y;
double r2 = x2 + y2;
double cdist = 1 + (k[0] + (k[1] + k[4]*r2)*r2)*r2;
double x1 = x*cdist + k[2]*2*x*y + k[3]*(r2 + 2*x2);
double y1 = y*cdist + k[3]*2*x*y + k[2]*(r2 + 2*y2);
_mapy.at<float>(v, u) = (float)(y1*fy + cy);
_mapx.at<float>(v, u) = (float)(x1*fx + cx);
}
}
}
std::ostream& operator << (std::ostream& out, const MatInfo& m)
{
if( !m.m || m.m->empty() )
out << "<Empty>";
else
{
static const char* depthstr[] = {"8u", "8s", "16u", "16s", "32s", "32f", "64f", "?"};
out << depthstr[m.m->depth()] << "C" << m.m->channels() << " " << m.m->dims << "-dim (";
for( int i = 0; i < m.m->dims; i++ )
out << m.m->size[i] << (i < m.m->dims-1 ? " x " : ")");
}
return out;
}
static Mat getSubArray(const Mat& m, int border, vector<int>& ofs0, vector<int>& ofs)
{
ofs.resize(ofs0.size());
if( border < 0 )
{
std::copy(ofs0.begin(), ofs0.end(), ofs.begin());
return m;
}
int i, d = m.dims;
CV_Assert(d == (int)ofs.size());
vector<Range> r(d);
for( i = 0; i < d; i++ )
{
r[i].start = std::max(0, ofs0[i] - border);
r[i].end = std::min(ofs0[i] + 1 + border, m.size[i]);
ofs[i] = std::min(ofs0[i], border);
}
return m(&r[0]);
}
template<typename _Tp, typename _WTp> static void
writeElems(std::ostream& out, const void* data, int nelems, int starpos)
{
for(int i = 0; i < nelems; i++)
{
if( i == starpos )
out << "*";
out << (_WTp)((_Tp*)data)[i];
if( i == starpos )
out << "*";
out << (i+1 < nelems ? ", " : "");
}
}
static void writeElems(std::ostream& out, const void* data, int nelems, int depth, int starpos)
{
if(depth == CV_8U)
writeElems<uchar, int>(out, data, nelems, starpos);
else if(depth == CV_8S)
writeElems<schar, int>(out, data, nelems, starpos);
else if(depth == CV_16U)
writeElems<ushort, int>(out, data, nelems, starpos);
else if(depth == CV_16S)
writeElems<short, int>(out, data, nelems, starpos);
else if(depth == CV_32S)
writeElems<int, int>(out, data, nelems, starpos);
else if(depth == CV_32F)
{
std::streamsize pp = out.precision();
out.precision(8);
writeElems<float, float>(out, data, nelems, starpos);
out.precision(pp);
}
else if(depth == CV_64F)
{
std::streamsize pp = out.precision();
out.precision(16);
writeElems<double, double>(out, data, nelems, starpos);
out.precision(pp);
}
else
CV_Error(CV_StsUnsupportedFormat, "");
}
struct MatPart
{
MatPart(const Mat& _m, const vector<int>* _loc)
: m(&_m), loc(_loc) {}
const Mat* m;
const vector<int>* loc;
};
static std::ostream& operator << (std::ostream& out, const MatPart& m)
{
CV_Assert( !m.loc || ((int)m.loc->size() == m.m->dims && m.m->dims <= 2) );
if( !m.loc )
out << *m.m;
else
{
int i, depth = m.m->depth(), cn = m.m->channels(), width = m.m->cols*cn;
for( i = 0; i < m.m->rows; i++ )
{
writeElems(out, m.m->ptr(i), width, depth, i == (*m.loc)[0] ? (*m.loc)[1] : -1);
out << (i < m.m->rows-1 ? ";\n" : "");
}
}
return out;
}
MatComparator::MatComparator(double _maxdiff, int _context)
: maxdiff(_maxdiff), context(_context) {}
::testing::AssertionResult
MatComparator::operator()(const char* expr1, const char* expr2,
const Mat& m1, const Mat& m2)
{
if( m1.type() != m2.type() || m1.size != m2.size )
return ::testing::AssertionFailure()
<< "The reference and the actual output arrays have different type or size:\n"
<< expr1 << " ~ " << MatInfo(m1) << "\n"
<< expr2 << " ~ " << MatInfo(m2) << "\n";
//bool ok = cvtest::cmpUlps(m1, m2, maxdiff, &realmaxdiff, &loc0);
int code = cmpEps( m1, m2, &realmaxdiff, maxdiff, &loc0, true);
if(code >= 0)
return ::testing::AssertionSuccess();
Mat m[] = {m1.reshape(1,0), m2.reshape(1,0)};
int dims = m[0].dims;
vector<int> loc;
int border = dims <= 2 ? context : 0;
Mat m1part, m2part;
if( border == 0 )
{
loc = loc0;
m1part = Mat(1, 1, m[0].depth(), m[0].ptr(&loc[0]));
m2part = Mat(1, 1, m[1].depth(), m[1].ptr(&loc[0]));
}
else
{
m1part = getSubArray(m[0], border, loc0, loc);
m2part = getSubArray(m[1], border, loc0, loc);
}
return ::testing::AssertionFailure()
<< "too big relative difference (" << realmaxdiff << " > "
<< maxdiff << ") between "
<< MatInfo(m1) << " '" << expr1 << "' and '" << expr2 << "' at " << Mat(loc0) << ".\n\n"
<< "'" << expr1 << "': " << MatPart(m1part, border > 0 ? &loc : 0) << ".\n\n"
<< "'" << expr2 << "': " << MatPart(m2part, border > 0 ? &loc : 0) << ".\n";
}
}
void cvTsConvert( const CvMat* src, CvMat* dst )
{
Mat _src = cvarrToMat(src), _dst = cvarrToMat(dst);
cvtest::convert(_src, _dst, _dst.depth());
}
void cvTsZero( CvMat* dst, const CvMat* mask )
{
Mat _dst = cvarrToMat(dst), _mask = mask ? cvarrToMat(mask) : Mat();
cvtest::set(_dst, Scalar::all(0), _mask);
}
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment