Conflicts: modules/video/doc/motion_analysis_and_object_tracking.rstpull/2942/head
commit
fae69df9a9
601 changed files with 37461 additions and 443658 deletions
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,792 @@ |
||||
/*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
|
||||
//
|
||||
// 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 "old_ml_precomp.hpp" |
||||
#include <ctype.h> |
||||
|
||||
#define MISS_VAL FLT_MAX |
||||
#define CV_VAR_MISS 0 |
||||
|
||||
CvTrainTestSplit::CvTrainTestSplit() |
||||
{ |
||||
train_sample_part_mode = CV_COUNT; |
||||
train_sample_part.count = -1; |
||||
mix = false; |
||||
} |
||||
|
||||
CvTrainTestSplit::CvTrainTestSplit( int _train_sample_count, bool _mix ) |
||||
{ |
||||
train_sample_part_mode = CV_COUNT; |
||||
train_sample_part.count = _train_sample_count; |
||||
mix = _mix; |
||||
} |
||||
|
||||
CvTrainTestSplit::CvTrainTestSplit( float _train_sample_portion, bool _mix ) |
||||
{ |
||||
train_sample_part_mode = CV_PORTION; |
||||
train_sample_part.portion = _train_sample_portion; |
||||
mix = _mix; |
||||
} |
||||
|
||||
////////////////
|
||||
|
||||
CvMLData::CvMLData() |
||||
{ |
||||
values = missing = var_types = var_idx_mask = response_out = var_idx_out = var_types_out = 0; |
||||
train_sample_idx = test_sample_idx = 0; |
||||
header_lines_number = 0; |
||||
sample_idx = 0; |
||||
response_idx = -1; |
||||
|
||||
train_sample_count = -1; |
||||
|
||||
delimiter = ','; |
||||
miss_ch = '?'; |
||||
//flt_separator = '.';
|
||||
|
||||
rng = &cv::theRNG(); |
||||
} |
||||
|
||||
CvMLData::~CvMLData() |
||||
{ |
||||
clear(); |
||||
} |
||||
|
||||
void CvMLData::free_train_test_idx() |
||||
{ |
||||
cvReleaseMat( &train_sample_idx ); |
||||
cvReleaseMat( &test_sample_idx ); |
||||
sample_idx = 0; |
||||
} |
||||
|
||||
void CvMLData::clear() |
||||
{ |
||||
class_map.clear(); |
||||
|
||||
cvReleaseMat( &values ); |
||||
cvReleaseMat( &missing ); |
||||
cvReleaseMat( &var_types ); |
||||
cvReleaseMat( &var_idx_mask ); |
||||
|
||||
cvReleaseMat( &response_out ); |
||||
cvReleaseMat( &var_idx_out ); |
||||
cvReleaseMat( &var_types_out ); |
||||
|
||||
free_train_test_idx(); |
||||
|
||||
total_class_count = 0; |
||||
|
||||
response_idx = -1; |
||||
|
||||
train_sample_count = -1; |
||||
} |
||||
|
||||
|
||||
void CvMLData::set_header_lines_number( int idx ) |
||||
{ |
||||
header_lines_number = std::max(0, idx); |
||||
} |
||||
|
||||
int CvMLData::get_header_lines_number() const |
||||
{ |
||||
return header_lines_number; |
||||
} |
||||
|
||||
static char *fgets_chomp(char *str, int n, FILE *stream) |
||||
{ |
||||
char *head = fgets(str, n, stream); |
||||
if( head ) |
||||
{ |
||||
for(char *tail = head + strlen(head) - 1; tail >= head; --tail) |
||||
{ |
||||
if( *tail != '\r' && *tail != '\n' ) |
||||
break; |
||||
*tail = '\0'; |
||||
} |
||||
} |
||||
return head; |
||||
} |
||||
|
||||
|
||||
int CvMLData::read_csv(const char* filename) |
||||
{ |
||||
const int M = 1000000; |
||||
const char str_delimiter[3] = { ' ', delimiter, '\0' }; |
||||
FILE* file = 0; |
||||
CvMemStorage* storage; |
||||
CvSeq* seq; |
||||
char *ptr; |
||||
float* el_ptr; |
||||
CvSeqReader reader; |
||||
int cols_count = 0; |
||||
uchar *var_types_ptr = 0; |
||||
|
||||
clear(); |
||||
|
||||
file = fopen( filename, "rt" ); |
||||
|
||||
if( !file ) |
||||
return -1; |
||||
|
||||
std::vector<char> _buf(M); |
||||
char* buf = &_buf[0]; |
||||
|
||||
// skip header lines
|
||||
for( int i = 0; i < header_lines_number; i++ ) |
||||
{ |
||||
if( fgets( buf, M, file ) == 0 ) |
||||
{ |
||||
fclose(file); |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
// read the first data line and determine the number of variables
|
||||
if( !fgets_chomp( buf, M, file )) |
||||
{ |
||||
fclose(file); |
||||
return -1; |
||||
} |
||||
|
||||
ptr = buf; |
||||
while( *ptr == ' ' ) |
||||
ptr++; |
||||
for( ; *ptr != '\0'; ) |
||||
{ |
||||
if(*ptr == delimiter || *ptr == ' ') |
||||
{ |
||||
cols_count++; |
||||
ptr++; |
||||
while( *ptr == ' ' ) ptr++; |
||||
} |
||||
else |
||||
ptr++; |
||||
} |
||||
|
||||
cols_count++; |
||||
|
||||
if ( cols_count == 0) |
||||
{ |
||||
fclose(file); |
||||
return -1; |
||||
} |
||||
|
||||
// create temporary memory storage to store the whole database
|
||||
el_ptr = new float[cols_count]; |
||||
storage = cvCreateMemStorage(); |
||||
seq = cvCreateSeq( 0, sizeof(*seq), cols_count*sizeof(float), storage ); |
||||
|
||||
var_types = cvCreateMat( 1, cols_count, CV_8U ); |
||||
cvZero( var_types ); |
||||
var_types_ptr = var_types->data.ptr; |
||||
|
||||
for(;;) |
||||
{ |
||||
char *token = NULL; |
||||
int type; |
||||
token = strtok(buf, str_delimiter); |
||||
if (!token) |
||||
break; |
||||
for (int i = 0; i < cols_count-1; i++) |
||||
{ |
||||
str_to_flt_elem( token, el_ptr[i], type); |
||||
var_types_ptr[i] |= type; |
||||
token = strtok(NULL, str_delimiter); |
||||
if (!token) |
||||
{ |
||||
fclose(file); |
||||
delete [] el_ptr; |
||||
return -1; |
||||
} |
||||
} |
||||
str_to_flt_elem( token, el_ptr[cols_count-1], type); |
||||
var_types_ptr[cols_count-1] |= type; |
||||
cvSeqPush( seq, el_ptr ); |
||||
if( !fgets_chomp( buf, M, file ) ) |
||||
break; |
||||
} |
||||
fclose(file); |
||||
|
||||
values = cvCreateMat( seq->total, cols_count, CV_32FC1 ); |
||||
missing = cvCreateMat( seq->total, cols_count, CV_8U ); |
||||
var_idx_mask = cvCreateMat( 1, values->cols, CV_8UC1 ); |
||||
cvSet( var_idx_mask, cvRealScalar(1) ); |
||||
train_sample_count = seq->total; |
||||
|
||||
cvStartReadSeq( seq, &reader ); |
||||
for(int i = 0; i < seq->total; i++ ) |
||||
{ |
||||
const float* sdata = (float*)reader.ptr; |
||||
float* ddata = values->data.fl + cols_count*i; |
||||
uchar* dm = missing->data.ptr + cols_count*i; |
||||
|
||||
for( int j = 0; j < cols_count; j++ ) |
||||
{ |
||||
ddata[j] = sdata[j]; |
||||
dm[j] = ( fabs( MISS_VAL - sdata[j] ) <= FLT_EPSILON ); |
||||
} |
||||
CV_NEXT_SEQ_ELEM( seq->elem_size, reader ); |
||||
} |
||||
|
||||
if ( cvNorm( missing, 0, CV_L1 ) <= FLT_EPSILON ) |
||||
cvReleaseMat( &missing ); |
||||
|
||||
cvReleaseMemStorage( &storage ); |
||||
delete []el_ptr; |
||||
return 0; |
||||
} |
||||
|
||||
const CvMat* CvMLData::get_values() const |
||||
{ |
||||
return values; |
||||
} |
||||
|
||||
const CvMat* CvMLData::get_missing() const |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::get_missing" ); |
||||
__BEGIN__; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
|
||||
__END__; |
||||
|
||||
return missing; |
||||
} |
||||
|
||||
const std::map<cv::String, int>& CvMLData::get_class_labels_map() const |
||||
{ |
||||
return class_map; |
||||
} |
||||
|
||||
void CvMLData::str_to_flt_elem( const char* token, float& flt_elem, int& type) |
||||
{ |
||||
|
||||
char* stopstring = NULL; |
||||
flt_elem = (float)strtod( token, &stopstring ); |
||||
assert( stopstring ); |
||||
type = CV_VAR_ORDERED; |
||||
if ( *stopstring == miss_ch && strlen(stopstring) == 1 ) // missed value
|
||||
{ |
||||
flt_elem = MISS_VAL; |
||||
type = CV_VAR_MISS; |
||||
} |
||||
else |
||||
{ |
||||
if ( (*stopstring != 0) && (*stopstring != '\n') && (strcmp(stopstring, "\r\n") != 0) ) // class label
|
||||
{ |
||||
int idx = class_map[token]; |
||||
if ( idx == 0) |
||||
{ |
||||
total_class_count++; |
||||
idx = total_class_count; |
||||
class_map[token] = idx; |
||||
} |
||||
flt_elem = (float)idx; |
||||
type = CV_VAR_CATEGORICAL; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void CvMLData::set_delimiter(char ch) |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::set_delimited" ); |
||||
__BEGIN__; |
||||
|
||||
if (ch == miss_ch /*|| ch == flt_separator*/) |
||||
CV_ERROR(CV_StsBadArg, "delimited, miss_character and flt_separator must be different"); |
||||
|
||||
delimiter = ch; |
||||
|
||||
__END__; |
||||
} |
||||
|
||||
char CvMLData::get_delimiter() const |
||||
{ |
||||
return delimiter; |
||||
} |
||||
|
||||
void CvMLData::set_miss_ch(char ch) |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::set_miss_ch" ); |
||||
__BEGIN__; |
||||
|
||||
if (ch == delimiter/* || ch == flt_separator*/) |
||||
CV_ERROR(CV_StsBadArg, "delimited, miss_character and flt_separator must be different"); |
||||
|
||||
miss_ch = ch; |
||||
|
||||
__END__; |
||||
} |
||||
|
||||
char CvMLData::get_miss_ch() const |
||||
{ |
||||
return miss_ch; |
||||
} |
||||
|
||||
void CvMLData::set_response_idx( int idx ) |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::set_response_idx" ); |
||||
__BEGIN__; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
|
||||
if ( idx >= values->cols) |
||||
CV_ERROR( CV_StsBadArg, "idx value is not correct" ); |
||||
|
||||
if ( response_idx >= 0 ) |
||||
chahge_var_idx( response_idx, true ); |
||||
if ( idx >= 0 ) |
||||
chahge_var_idx( idx, false ); |
||||
response_idx = idx; |
||||
|
||||
__END__; |
||||
} |
||||
|
||||
int CvMLData::get_response_idx() const |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::get_response_idx" ); |
||||
__BEGIN__; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
__END__; |
||||
return response_idx; |
||||
} |
||||
|
||||
void CvMLData::change_var_type( int var_idx, int type ) |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::change_var_type" ); |
||||
__BEGIN__; |
||||
|
||||
int var_count = 0; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
|
||||
var_count = values->cols; |
||||
|
||||
if ( var_idx < 0 || var_idx >= var_count) |
||||
CV_ERROR( CV_StsBadArg, "var_idx is not correct" ); |
||||
|
||||
if ( type != CV_VAR_ORDERED && type != CV_VAR_CATEGORICAL) |
||||
CV_ERROR( CV_StsBadArg, "type is not correct" ); |
||||
|
||||
assert( var_types ); |
||||
if ( var_types->data.ptr[var_idx] == CV_VAR_CATEGORICAL && type == CV_VAR_ORDERED) |
||||
CV_ERROR( CV_StsBadArg, "it`s impossible to assign CV_VAR_ORDERED type to categorical variable" ); |
||||
var_types->data.ptr[var_idx] = (uchar)type; |
||||
|
||||
__END__; |
||||
|
||||
return; |
||||
} |
||||
|
||||
void CvMLData::set_var_types( const char* str ) |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::set_var_types" ); |
||||
__BEGIN__; |
||||
|
||||
const char* ord = 0, *cat = 0; |
||||
int var_count = 0, set_var_type_count = 0; |
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
|
||||
var_count = values->cols; |
||||
|
||||
assert( var_types ); |
||||
|
||||
ord = strstr( str, "ord" ); |
||||
cat = strstr( str, "cat" ); |
||||
if ( !ord && !cat ) |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
|
||||
if ( !ord && strlen(cat) == 3 ) // str == "cat"
|
||||
{ |
||||
cvSet( var_types, cvScalarAll(CV_VAR_CATEGORICAL) ); |
||||
return; |
||||
} |
||||
|
||||
if ( !cat && strlen(ord) == 3 ) // str == "ord"
|
||||
{ |
||||
cvSet( var_types, cvScalarAll(CV_VAR_ORDERED) ); |
||||
return; |
||||
} |
||||
|
||||
if ( ord ) // parse ord str
|
||||
{ |
||||
char* stopstring = NULL; |
||||
if ( ord[3] != '[') |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
|
||||
ord += 4; // pass "ord["
|
||||
do |
||||
{ |
||||
int b1 = (int)strtod( ord, &stopstring ); |
||||
if ( *stopstring == 0 || (*stopstring != ',' && *stopstring != ']' && *stopstring != '-') ) |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
ord = stopstring + 1; |
||||
if ( (stopstring[0] == ',') || (stopstring[0] == ']')) |
||||
{ |
||||
if ( var_types->data.ptr[b1] == CV_VAR_CATEGORICAL) |
||||
CV_ERROR( CV_StsBadArg, "it`s impossible to assign CV_VAR_ORDERED type to categorical variable" ); |
||||
var_types->data.ptr[b1] = CV_VAR_ORDERED; |
||||
set_var_type_count++; |
||||
} |
||||
else |
||||
{ |
||||
if ( stopstring[0] == '-') |
||||
{ |
||||
int b2 = (int)strtod( ord, &stopstring); |
||||
if ( (*stopstring == 0) || (*stopstring != ',' && *stopstring != ']') ) |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
ord = stopstring + 1; |
||||
for (int i = b1; i <= b2; i++) |
||||
{ |
||||
if ( var_types->data.ptr[i] == CV_VAR_CATEGORICAL) |
||||
CV_ERROR( CV_StsBadArg, "it`s impossible to assign CV_VAR_ORDERED type to categorical variable" ); |
||||
var_types->data.ptr[i] = CV_VAR_ORDERED; |
||||
} |
||||
set_var_type_count += b2 - b1 + 1; |
||||
} |
||||
else |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
|
||||
} |
||||
} |
||||
while (*stopstring != ']'); |
||||
|
||||
if ( stopstring[1] != '\0' && stopstring[1] != ',') |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
} |
||||
|
||||
if ( cat ) // parse cat str
|
||||
{ |
||||
char* stopstring = NULL; |
||||
if ( cat[3] != '[') |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
|
||||
cat += 4; // pass "cat["
|
||||
do |
||||
{ |
||||
int b1 = (int)strtod( cat, &stopstring ); |
||||
if ( *stopstring == 0 || (*stopstring != ',' && *stopstring != ']' && *stopstring != '-') ) |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
cat = stopstring + 1; |
||||
if ( (stopstring[0] == ',') || (stopstring[0] == ']')) |
||||
{ |
||||
var_types->data.ptr[b1] = CV_VAR_CATEGORICAL; |
||||
set_var_type_count++; |
||||
} |
||||
else |
||||
{ |
||||
if ( stopstring[0] == '-') |
||||
{ |
||||
int b2 = (int)strtod( cat, &stopstring); |
||||
if ( (*stopstring == 0) || (*stopstring != ',' && *stopstring != ']') ) |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
cat = stopstring + 1; |
||||
for (int i = b1; i <= b2; i++) |
||||
var_types->data.ptr[i] = CV_VAR_CATEGORICAL; |
||||
set_var_type_count += b2 - b1 + 1; |
||||
} |
||||
else |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
|
||||
} |
||||
} |
||||
while (*stopstring != ']'); |
||||
|
||||
if ( stopstring[1] != '\0' && stopstring[1] != ',') |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
} |
||||
|
||||
if (set_var_type_count != var_count) |
||||
CV_ERROR( CV_StsBadArg, "types string is not correct" ); |
||||
|
||||
__END__; |
||||
} |
||||
|
||||
const CvMat* CvMLData::get_var_types() |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::get_var_types" ); |
||||
__BEGIN__; |
||||
|
||||
uchar *var_types_out_ptr = 0; |
||||
int avcount, vt_size; |
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
|
||||
assert( var_idx_mask ); |
||||
|
||||
avcount = cvFloor( cvNorm( var_idx_mask, 0, CV_L1 ) ); |
||||
vt_size = avcount + (response_idx >= 0); |
||||
|
||||
if ( avcount == values->cols || (avcount == values->cols-1 && response_idx == values->cols-1) ) |
||||
return var_types; |
||||
|
||||
if ( !var_types_out || ( var_types_out && var_types_out->cols != vt_size ) ) |
||||
{ |
||||
cvReleaseMat( &var_types_out ); |
||||
var_types_out = cvCreateMat( 1, vt_size, CV_8UC1 ); |
||||
} |
||||
|
||||
var_types_out_ptr = var_types_out->data.ptr; |
||||
for( int i = 0; i < var_types->cols; i++) |
||||
{ |
||||
if (i == response_idx || !var_idx_mask->data.ptr[i]) continue; |
||||
*var_types_out_ptr = var_types->data.ptr[i]; |
||||
var_types_out_ptr++; |
||||
} |
||||
if ( response_idx >= 0 ) |
||||
*var_types_out_ptr = var_types->data.ptr[response_idx]; |
||||
|
||||
__END__; |
||||
|
||||
return var_types_out; |
||||
} |
||||
|
||||
int CvMLData::get_var_type( int var_idx ) const |
||||
{ |
||||
return var_types->data.ptr[var_idx]; |
||||
} |
||||
|
||||
const CvMat* CvMLData::get_responses() |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::get_responses_ptr" ); |
||||
__BEGIN__; |
||||
|
||||
int var_count = 0; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
var_count = values->cols; |
||||
|
||||
if ( response_idx < 0 || response_idx >= var_count ) |
||||
return 0; |
||||
if ( !response_out ) |
||||
response_out = cvCreateMatHeader( values->rows, 1, CV_32FC1 ); |
||||
else |
||||
cvInitMatHeader( response_out, values->rows, 1, CV_32FC1); |
||||
cvGetCol( values, response_out, response_idx ); |
||||
|
||||
__END__; |
||||
|
||||
return response_out; |
||||
} |
||||
|
||||
void CvMLData::set_train_test_split( const CvTrainTestSplit * spl) |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::set_division" ); |
||||
__BEGIN__; |
||||
|
||||
int sample_count = 0; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
|
||||
sample_count = values->rows; |
||||
|
||||
float train_sample_portion; |
||||
|
||||
if (spl->train_sample_part_mode == CV_COUNT) |
||||
{ |
||||
train_sample_count = spl->train_sample_part.count; |
||||
if (train_sample_count > sample_count) |
||||
CV_ERROR( CV_StsBadArg, "train samples count is not correct" ); |
||||
train_sample_count = train_sample_count<=0 ? sample_count : train_sample_count; |
||||
} |
||||
else // dtype.train_sample_part_mode == CV_PORTION
|
||||
{ |
||||
train_sample_portion = spl->train_sample_part.portion; |
||||
if ( train_sample_portion > 1) |
||||
CV_ERROR( CV_StsBadArg, "train samples count is not correct" ); |
||||
train_sample_portion = train_sample_portion <= FLT_EPSILON || |
||||
1 - train_sample_portion <= FLT_EPSILON ? 1 : train_sample_portion; |
||||
train_sample_count = std::max(1, cvFloor( train_sample_portion * sample_count )); |
||||
} |
||||
|
||||
if ( train_sample_count == sample_count ) |
||||
{ |
||||
free_train_test_idx(); |
||||
return; |
||||
} |
||||
|
||||
if ( train_sample_idx && train_sample_idx->cols != train_sample_count ) |
||||
free_train_test_idx(); |
||||
|
||||
if ( !sample_idx) |
||||
{ |
||||
int test_sample_count = sample_count- train_sample_count; |
||||
sample_idx = (int*)cvAlloc( sample_count * sizeof(sample_idx[0]) ); |
||||
for (int i = 0; i < sample_count; i++ ) |
||||
sample_idx[i] = i; |
||||
train_sample_idx = cvCreateMatHeader( 1, train_sample_count, CV_32SC1 ); |
||||
*train_sample_idx = cvMat( 1, train_sample_count, CV_32SC1, &sample_idx[0] ); |
||||
|
||||
CV_Assert(test_sample_count > 0); |
||||
test_sample_idx = cvCreateMatHeader( 1, test_sample_count, CV_32SC1 ); |
||||
*test_sample_idx = cvMat( 1, test_sample_count, CV_32SC1, &sample_idx[train_sample_count] ); |
||||
} |
||||
|
||||
mix = spl->mix; |
||||
if ( mix ) |
||||
mix_train_and_test_idx(); |
||||
|
||||
__END__; |
||||
} |
||||
|
||||
const CvMat* CvMLData::get_train_sample_idx() const |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::get_train_sample_idx" ); |
||||
__BEGIN__; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
__END__; |
||||
|
||||
return train_sample_idx; |
||||
} |
||||
|
||||
const CvMat* CvMLData::get_test_sample_idx() const |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::get_test_sample_idx" ); |
||||
__BEGIN__; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
__END__; |
||||
|
||||
return test_sample_idx; |
||||
} |
||||
|
||||
void CvMLData::mix_train_and_test_idx() |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::mix_train_and_test_idx" ); |
||||
__BEGIN__; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
__END__; |
||||
|
||||
if ( !sample_idx) |
||||
return; |
||||
|
||||
if ( train_sample_count > 0 && train_sample_count < values->rows ) |
||||
{ |
||||
int n = values->rows; |
||||
for (int i = 0; i < n; i++) |
||||
{ |
||||
int a = (*rng)(n); |
||||
int b = (*rng)(n); |
||||
int t; |
||||
CV_SWAP( sample_idx[a], sample_idx[b], t ); |
||||
} |
||||
} |
||||
} |
||||
|
||||
const CvMat* CvMLData::get_var_idx() |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::get_var_idx" ); |
||||
__BEGIN__; |
||||
|
||||
int avcount = 0; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
|
||||
assert( var_idx_mask ); |
||||
|
||||
avcount = cvFloor( cvNorm( var_idx_mask, 0, CV_L1 ) ); |
||||
int* vidx; |
||||
|
||||
if ( avcount == values->cols ) |
||||
return 0; |
||||
|
||||
if ( !var_idx_out || ( var_idx_out && var_idx_out->cols != avcount ) ) |
||||
{ |
||||
cvReleaseMat( &var_idx_out ); |
||||
var_idx_out = cvCreateMat( 1, avcount, CV_32SC1); |
||||
if ( response_idx >=0 ) |
||||
var_idx_mask->data.ptr[response_idx] = 0; |
||||
} |
||||
|
||||
vidx = var_idx_out->data.i; |
||||
|
||||
for(int i = 0; i < var_idx_mask->cols; i++) |
||||
if ( var_idx_mask->data.ptr[i] ) |
||||
{ |
||||
*vidx = i; |
||||
vidx++; |
||||
} |
||||
|
||||
__END__; |
||||
|
||||
return var_idx_out; |
||||
} |
||||
|
||||
void CvMLData::chahge_var_idx( int vi, bool state ) |
||||
{ |
||||
change_var_idx( vi, state ); |
||||
} |
||||
|
||||
void CvMLData::change_var_idx( int vi, bool state ) |
||||
{ |
||||
CV_FUNCNAME( "CvMLData::change_var_idx" ); |
||||
__BEGIN__; |
||||
|
||||
int var_count = 0; |
||||
|
||||
if ( !values ) |
||||
CV_ERROR( CV_StsInternal, "data is empty" ); |
||||
|
||||
var_count = values->cols; |
||||
|
||||
if ( vi < 0 || vi >= var_count) |
||||
CV_ERROR( CV_StsBadArg, "variable index is not correct" ); |
||||
|
||||
assert( var_idx_mask ); |
||||
var_idx_mask->data.ptr[vi] = state; |
||||
|
||||
__END__; |
||||
} |
||||
|
||||
/* End of file. */ |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,376 @@ |
||||
/*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
|
||||
//
|
||||
// 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*/
|
||||
|
||||
#ifndef __OPENCV_PRECOMP_H__ |
||||
#define __OPENCV_PRECOMP_H__ |
||||
|
||||
#include "opencv2/core.hpp" |
||||
#include "old_ml.hpp" |
||||
#include "opencv2/core/core_c.h" |
||||
#include "opencv2/core/utility.hpp" |
||||
|
||||
#include "opencv2/core/private.hpp" |
||||
|
||||
#include <assert.h> |
||||
#include <float.h> |
||||
#include <limits.h> |
||||
#include <math.h> |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <time.h> |
||||
|
||||
#define ML_IMPL CV_IMPL |
||||
#define __BEGIN__ __CV_BEGIN__ |
||||
#define __END__ __CV_END__ |
||||
#define EXIT __CV_EXIT__ |
||||
|
||||
#define CV_MAT_ELEM_FLAG( mat, type, comp, vect, tflag ) \ |
||||
(( tflag == CV_ROW_SAMPLE ) \
|
||||
? (CV_MAT_ELEM( mat, type, comp, vect )) \
|
||||
: (CV_MAT_ELEM( mat, type, vect, comp ))) |
||||
|
||||
/* Convert matrix to vector */ |
||||
#define ICV_MAT2VEC( mat, vdata, vstep, num ) \ |
||||
if( MIN( (mat).rows, (mat).cols ) != 1 ) \
|
||||
CV_ERROR( CV_StsBadArg, "" ); \
|
||||
(vdata) = ((mat).data.ptr); \
|
||||
if( (mat).rows == 1 ) \
|
||||
{ \
|
||||
(vstep) = CV_ELEM_SIZE( (mat).type ); \
|
||||
(num) = (mat).cols; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
(vstep) = (mat).step; \
|
||||
(num) = (mat).rows; \
|
||||
} |
||||
|
||||
/* get raw data */ |
||||
#define ICV_RAWDATA( mat, flags, rdata, sstep, cstep, m, n ) \ |
||||
(rdata) = (mat).data.ptr; \
|
||||
if( CV_IS_ROW_SAMPLE( flags ) ) \
|
||||
{ \
|
||||
(sstep) = (mat).step; \
|
||||
(cstep) = CV_ELEM_SIZE( (mat).type ); \
|
||||
(m) = (mat).rows; \
|
||||
(n) = (mat).cols; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
(cstep) = (mat).step; \
|
||||
(sstep) = CV_ELEM_SIZE( (mat).type ); \
|
||||
(n) = (mat).rows; \
|
||||
(m) = (mat).cols; \
|
||||
} |
||||
|
||||
#define ICV_IS_MAT_OF_TYPE( mat, mat_type) \ |
||||
(CV_IS_MAT( mat ) && CV_MAT_TYPE( mat->type ) == (mat_type) && \
|
||||
(mat)->cols > 0 && (mat)->rows > 0) |
||||
|
||||
/*
|
||||
uchar* data; int sstep, cstep; - trainData->data |
||||
uchar* classes; int clstep; int ncl;- trainClasses |
||||
uchar* tmask; int tmstep; int ntm; - typeMask |
||||
uchar* missed;int msstep, mcstep; -missedMeasurements... |
||||
int mm, mn; == m,n == size,dim |
||||
uchar* sidx;int sistep; - sampleIdx |
||||
uchar* cidx;int cistep; - compIdx |
||||
int k, l; == n,m == dim,size (length of cidx, sidx) |
||||
int m, n; == size,dim |
||||
*/ |
||||
#define ICV_DECLARE_TRAIN_ARGS() \ |
||||
uchar* data; \
|
||||
int sstep, cstep; \
|
||||
uchar* classes; \
|
||||
int clstep; \
|
||||
int ncl; \
|
||||
uchar* tmask; \
|
||||
int tmstep; \
|
||||
int ntm; \
|
||||
uchar* missed; \
|
||||
int msstep, mcstep; \
|
||||
int mm, mn; \
|
||||
uchar* sidx; \
|
||||
int sistep; \
|
||||
uchar* cidx; \
|
||||
int cistep; \
|
||||
int k, l; \
|
||||
int m, n; \
|
||||
\
|
||||
data = classes = tmask = missed = sidx = cidx = NULL; \
|
||||
sstep = cstep = clstep = ncl = tmstep = ntm = msstep = mcstep = mm = mn = 0; \
|
||||
sistep = cistep = k = l = m = n = 0; |
||||
|
||||
#define ICV_TRAIN_DATA_REQUIRED( param, flags ) \ |
||||
if( !ICV_IS_MAT_OF_TYPE( (param), CV_32FC1 ) ) \
|
||||
{ \
|
||||
CV_ERROR( CV_StsBadArg, "Invalid " #param " parameter" ); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
ICV_RAWDATA( *(param), (flags), data, sstep, cstep, m, n ); \
|
||||
k = n; \
|
||||
l = m; \
|
||||
} |
||||
|
||||
#define ICV_TRAIN_CLASSES_REQUIRED( param ) \ |
||||
if( !ICV_IS_MAT_OF_TYPE( (param), CV_32FC1 ) ) \
|
||||
{ \
|
||||
CV_ERROR( CV_StsBadArg, "Invalid " #param " parameter" ); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
ICV_MAT2VEC( *(param), classes, clstep, ncl ); \
|
||||
if( m != ncl ) \
|
||||
{ \
|
||||
CV_ERROR( CV_StsBadArg, "Unmatched sizes" ); \
|
||||
} \
|
||||
} |
||||
|
||||
#define ICV_ARG_NULL( param ) \ |
||||
if( (param) != NULL ) \
|
||||
{ \
|
||||
CV_ERROR( CV_StsBadArg, #param " parameter must be NULL" ); \
|
||||
} |
||||
|
||||
#define ICV_MISSED_MEASUREMENTS_OPTIONAL( param, flags ) \ |
||||
if( param ) \
|
||||
{ \
|
||||
if( !ICV_IS_MAT_OF_TYPE( param, CV_8UC1 ) ) \
|
||||
{ \
|
||||
CV_ERROR( CV_StsBadArg, "Invalid " #param " parameter" ); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
ICV_RAWDATA( *(param), (flags), missed, msstep, mcstep, mm, mn ); \
|
||||
if( mm != m || mn != n ) \
|
||||
{ \
|
||||
CV_ERROR( CV_StsBadArg, "Unmatched sizes" ); \
|
||||
} \
|
||||
} \
|
||||
} |
||||
|
||||
#define ICV_COMP_IDX_OPTIONAL( param ) \ |
||||
if( param ) \
|
||||
{ \
|
||||
if( !ICV_IS_MAT_OF_TYPE( param, CV_32SC1 ) ) \
|
||||
{ \
|
||||
CV_ERROR( CV_StsBadArg, "Invalid " #param " parameter" ); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
ICV_MAT2VEC( *(param), cidx, cistep, k ); \
|
||||
if( k > n ) \
|
||||
CV_ERROR( CV_StsBadArg, "Invalid " #param " parameter" ); \
|
||||
} \
|
||||
} |
||||
|
||||
#define ICV_SAMPLE_IDX_OPTIONAL( param ) \ |
||||
if( param ) \
|
||||
{ \
|
||||
if( !ICV_IS_MAT_OF_TYPE( param, CV_32SC1 ) ) \
|
||||
{ \
|
||||
CV_ERROR( CV_StsBadArg, "Invalid " #param " parameter" ); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
ICV_MAT2VEC( *sampleIdx, sidx, sistep, l ); \
|
||||
if( l > m ) \
|
||||
CV_ERROR( CV_StsBadArg, "Invalid " #param " parameter" ); \
|
||||
} \
|
||||
} |
||||
|
||||
/****************************************************************************************/ |
||||
#define ICV_CONVERT_FLOAT_ARRAY_TO_MATRICE( array, matrice ) \ |
||||
{ \
|
||||
CvMat a, b; \
|
||||
int dims = (matrice)->cols; \
|
||||
int nsamples = (matrice)->rows; \
|
||||
int type = CV_MAT_TYPE((matrice)->type); \
|
||||
int i, offset = dims; \
|
||||
\
|
||||
CV_ASSERT( type == CV_32FC1 || type == CV_64FC1 ); \
|
||||
offset *= ((type == CV_32FC1) ? sizeof(float) : sizeof(double));\
|
||||
\
|
||||
b = cvMat( 1, dims, CV_32FC1 ); \
|
||||
cvGetRow( matrice, &a, 0 ); \
|
||||
for( i = 0; i < nsamples; i++, a.data.ptr += offset ) \
|
||||
{ \
|
||||
b.data.fl = (float*)array[i]; \
|
||||
CV_CALL( cvConvert( &b, &a ) ); \
|
||||
} \
|
||||
} |
||||
|
||||
/****************************************************************************************\
|
||||
* Auxiliary functions declarations * |
||||
\****************************************************************************************/ |
||||
|
||||
/* Generates a set of classes centers in quantity <num_of_clusters> that are generated as
|
||||
uniform random vectors in parallelepiped, where <data> is concentrated. Vectors in |
||||
<data> should have horizontal orientation. If <centers> != NULL, the function doesn't |
||||
allocate any memory and stores generated centers in <centers>, returns <centers>. |
||||
If <centers> == NULL, the function allocates memory and creates the matrice. Centers |
||||
are supposed to be oriented horizontally. */ |
||||
CvMat* icvGenerateRandomClusterCenters( int seed, |
||||
const CvMat* data, |
||||
int num_of_clusters, |
||||
CvMat* centers CV_DEFAULT(0)); |
||||
|
||||
/* Fills the <labels> using <probs> by choosing the maximal probability. Outliers are
|
||||
fixed by <oulier_tresh> and have cluster label (-1). Function also controls that there |
||||
weren't "empty" clusters by filling empty clusters with the maximal probability vector. |
||||
If probs_sums != NULL, filles it with the sums of probabilities for each sample (it is |
||||
useful for normalizing probabilities' matrice of FCM) */ |
||||
void icvFindClusterLabels( const CvMat* probs, float outlier_thresh, float r, |
||||
const CvMat* labels ); |
||||
|
||||
typedef struct CvSparseVecElem32f |
||||
{ |
||||
int idx; |
||||
float val; |
||||
} |
||||
CvSparseVecElem32f; |
||||
|
||||
/* Prepare training data and related parameters */ |
||||
#define CV_TRAIN_STATMODEL_DEFRAGMENT_TRAIN_DATA 1 |
||||
#define CV_TRAIN_STATMODEL_SAMPLES_AS_ROWS 2 |
||||
#define CV_TRAIN_STATMODEL_SAMPLES_AS_COLUMNS 4 |
||||
#define CV_TRAIN_STATMODEL_CATEGORICAL_RESPONSE 8 |
||||
#define CV_TRAIN_STATMODEL_ORDERED_RESPONSE 16 |
||||
#define CV_TRAIN_STATMODEL_RESPONSES_ON_OUTPUT 32 |
||||
#define CV_TRAIN_STATMODEL_ALWAYS_COPY_TRAIN_DATA 64 |
||||
#define CV_TRAIN_STATMODEL_SPARSE_AS_SPARSE 128 |
||||
|
||||
int |
||||
cvPrepareTrainData( const char* /*funcname*/, |
||||
const CvMat* train_data, int tflag, |
||||
const CvMat* responses, int response_type, |
||||
const CvMat* var_idx, |
||||
const CvMat* sample_idx, |
||||
bool always_copy_data, |
||||
const float*** out_train_samples, |
||||
int* _sample_count, |
||||
int* _var_count, |
||||
int* _var_all, |
||||
CvMat** out_responses, |
||||
CvMat** out_response_map, |
||||
CvMat** out_var_idx, |
||||
CvMat** out_sample_idx=0 ); |
||||
|
||||
void |
||||
cvSortSamplesByClasses( const float** samples, const CvMat* classes, |
||||
int* class_ranges, const uchar** mask CV_DEFAULT(0) ); |
||||
|
||||
void |
||||
cvCombineResponseMaps (CvMat* _responses, |
||||
const CvMat* old_response_map, |
||||
CvMat* new_response_map, |
||||
CvMat** out_response_map); |
||||
|
||||
void |
||||
cvPreparePredictData( const CvArr* sample, int dims_all, const CvMat* comp_idx, |
||||
int class_count, const CvMat* prob, float** row_sample, |
||||
int as_sparse CV_DEFAULT(0) ); |
||||
|
||||
/* copies clustering [or batch "predict"] results
|
||||
(labels and/or centers and/or probs) back to the output arrays */ |
||||
void |
||||
cvWritebackLabels( const CvMat* labels, CvMat* dst_labels, |
||||
const CvMat* centers, CvMat* dst_centers, |
||||
const CvMat* probs, CvMat* dst_probs, |
||||
const CvMat* sample_idx, int samples_all, |
||||
const CvMat* comp_idx, int dims_all ); |
||||
#define cvWritebackResponses cvWritebackLabels |
||||
|
||||
#define XML_FIELD_NAME "_name" |
||||
CvFileNode* icvFileNodeGetChild(CvFileNode* father, const char* name); |
||||
CvFileNode* icvFileNodeGetChildArrayElem(CvFileNode* father, const char* name,int index); |
||||
CvFileNode* icvFileNodeGetNext(CvFileNode* n, const char* name); |
||||
|
||||
|
||||
void cvCheckTrainData( const CvMat* train_data, int tflag, |
||||
const CvMat* missing_mask, |
||||
int* var_all, int* sample_all ); |
||||
|
||||
CvMat* cvPreprocessIndexArray( const CvMat* idx_arr, int data_arr_size, bool check_for_duplicates=false ); |
||||
|
||||
CvMat* cvPreprocessVarType( const CvMat* type_mask, const CvMat* var_idx, |
||||
int var_all, int* response_type ); |
||||
|
||||
CvMat* cvPreprocessOrderedResponses( const CvMat* responses, |
||||
const CvMat* sample_idx, int sample_all ); |
||||
|
||||
CvMat* cvPreprocessCategoricalResponses( const CvMat* responses, |
||||
const CvMat* sample_idx, int sample_all, |
||||
CvMat** out_response_map, CvMat** class_counts=0 ); |
||||
|
||||
const float** cvGetTrainSamples( const CvMat* train_data, int tflag, |
||||
const CvMat* var_idx, const CvMat* sample_idx, |
||||
int* _var_count, int* _sample_count, |
||||
bool always_copy_data=false ); |
||||
|
||||
namespace cv |
||||
{ |
||||
struct DTreeBestSplitFinder |
||||
{ |
||||
DTreeBestSplitFinder(){ splitSize = 0, tree = 0; node = 0; } |
||||
DTreeBestSplitFinder( CvDTree* _tree, CvDTreeNode* _node); |
||||
DTreeBestSplitFinder( const DTreeBestSplitFinder& finder, Split ); |
||||
virtual ~DTreeBestSplitFinder() {} |
||||
virtual void operator()(const BlockedRange& range); |
||||
void join( DTreeBestSplitFinder& rhs ); |
||||
Ptr<CvDTreeSplit> bestSplit; |
||||
Ptr<CvDTreeSplit> split; |
||||
int splitSize; |
||||
CvDTree* tree; |
||||
CvDTreeNode* node; |
||||
}; |
||||
|
||||
struct ForestTreeBestSplitFinder : DTreeBestSplitFinder |
||||
{ |
||||
ForestTreeBestSplitFinder() : DTreeBestSplitFinder() {} |
||||
ForestTreeBestSplitFinder( CvForestTree* _tree, CvDTreeNode* _node ); |
||||
ForestTreeBestSplitFinder( const ForestTreeBestSplitFinder& finder, Split ); |
||||
virtual void operator()(const BlockedRange& range); |
||||
}; |
||||
} |
||||
|
||||
#endif /* __ML_H__ */ |
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@ |
||||
set(MIN_VER_CMAKE 2.8.7) |
||||
set(MIN_VER_CUDA 4.2) |
||||
set(MIN_VER_PYTHON 2.6) |
||||
set(MIN_VER_PYTHON2 2.6) |
||||
set(MIN_VER_PYTHON3 3.2) |
||||
set(MIN_VER_ZLIB 1.2.3) |
||||
set(MIN_VER_GTK 2.18.0) |
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,112 @@ |
||||
.. _Bindings_Basics: |
||||
|
||||
How OpenCV-Python Bindings Works? |
||||
************************************ |
||||
|
||||
Goal |
||||
===== |
||||
|
||||
Learn: |
||||
|
||||
* How OpenCV-Python bindings are generated? |
||||
* How to extend new OpenCV modules to Python? |
||||
|
||||
How OpenCV-Python bindings are generated? |
||||
========================================= |
||||
|
||||
In OpenCV, all algorithms are implemented in C++. But these algorithms can be used from different languages like Python, Java etc. This is made possible by the bindings generators. These generators create a bridge between C++ and Python which enables users to call C++ functions from Python. To get a complete picture of what is happening in background, a good knowledge of Python/C API is required. A simple example on extending C++ functions to Python can be found in official Python documentation[1]. So extending all functions in OpenCV to Python by writing their wrapper functions manually is a time-consuming task. So OpenCV does it in a more intelligent way. OpenCV generates these wrapper functions automatically from the C++ headers using some Python scripts which are located in ``modules/python/src2``. We will look into what they do. |
||||
|
||||
First, ``modules/python/CMakeFiles.txt`` is a CMake script which checks the modules to be extended to Python. It will automatically check all the modules to be extended and grab their header files. These header files contain list of all classes, functions, constants etc. for that particular modules. |
||||
|
||||
Second, these header files are passed to a Python script, ``modules/python/src2/gen2.py``. This is the Python bindings generator script. It calls another Python script ``modules/python/src2/hdr_parser.py``. This is the header parser script. This header parser splits the complete header file into small Python lists. So these lists contain all details about a particular function, class etc. For example, a function will be parsed to get a list containing function name, return type, input arguments, argument types etc. Final list contains details of all the functions, structs, classes etc. in that header file. |
||||
|
||||
But header parser doesn't parse all the functions/classes in the header file. The developer has to specify which functions should be exported to Python. For that, there are certain macros added to the beginning of these declarations which enables the header parser to identify functions to be parsed. These macros are added by the developer who programs the particular function. In short, the developer decides which functions should be extended to Python and which are not. Details of those macros will be given in next session. |
||||
|
||||
So header parser returns a final big list of parsed functions. Our generator script (gen2.py) will create wrapper functions for all the functions/classes/enums/structs parsed by header parser (You can find these header files during compilation in the ``build/modules/python/`` folder as ``pyopencv_generated_*.h`` files). But there may be some basic OpenCV datatypes like Mat, Vec4i, Size. They need to be extended manually. For example, a Mat type should be extended to Numpy array, Size should be extended to a tuple of two integers etc. Similarly, there may be some complex structs/classes/functions etc. which need to be extended manually. All such manual wrapper functions are placed in ``modules/python/src2/pycv2.hpp``. |
||||
|
||||
So now only thing left is the compilation of these wrapper files which gives us **cv2** module. So when you call a function, say ``res = equalizeHist(img1,img2)`` in Python, you pass two numpy arrays and you expect another numpy array as the output. So these numpy arrays are converted to ``cv::Mat`` and then calls the ``equalizeHist()`` function in C++. Final result, ``res`` will be converted back into a Numpy array. So in short, almost all operations are done in C++ which gives us almost same speed as that of C++. |
||||
|
||||
So this is the basic version of how OpenCV-Python bindings are generated. |
||||
|
||||
|
||||
How to extend new modules to Python? |
||||
===================================== |
||||
|
||||
Header parser parse the header files based on some wrapper macros added to function declaration. Enumeration constants don't need any wrapper macros. They are automatically wrapped. But remaining functions, classes etc. need wrapper macros. |
||||
|
||||
Functions are extended using ``CV_EXPORTS_W`` macro. An example is shown below. |
||||
|
||||
.. code-block:: cpp |
||||
|
||||
CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst ); |
||||
|
||||
Header parser can understand the input and output arguments from keywords like ``InputArray, OutputArray`` etc. But sometimes, we may need to hardcode inputs and outputs. For that, macros like ``CV_OUT, CV_IN_OUT`` etc. are used. |
||||
|
||||
.. code-block:: cpp |
||||
|
||||
CV_EXPORTS_W void minEnclosingCircle( InputArray points, |
||||
CV_OUT Point2f& center, CV_OUT float& radius ); |
||||
|
||||
For large classes also, ``CV_EXPORTS_W`` is used. To extend class methods, ``CV_WRAP`` is used. Similarly, ``CV_PROP`` is used for class fields. |
||||
|
||||
.. code-block:: cpp |
||||
|
||||
class CV_EXPORTS_W CLAHE : public Algorithm |
||||
{ |
||||
public: |
||||
CV_WRAP virtual void apply(InputArray src, OutputArray dst) = 0; |
||||
|
||||
CV_WRAP virtual void setClipLimit(double clipLimit) = 0; |
||||
CV_WRAP virtual double getClipLimit() const = 0; |
||||
} |
||||
|
||||
Overloaded functions can be extended using ``CV_EXPORTS_AS``. But we need to pass a new name so that each function will be called by that name in Python. Take the case of integral function below. Three functions are available, so each one is named with a suffix in Python. Similarly ``CV_WRAP_AS`` can be used to wrap overloaded methods. |
||||
|
||||
.. code-block:: cpp |
||||
|
||||
//! computes the integral image |
||||
CV_EXPORTS_W void integral( InputArray src, OutputArray sum, int sdepth = -1 ); |
||||
|
||||
//! computes the integral image and integral for the squared image |
||||
CV_EXPORTS_AS(integral2) void integral( InputArray src, OutputArray sum, |
||||
OutputArray sqsum, int sdepth = -1, int sqdepth = -1 ); |
||||
|
||||
//! computes the integral image, integral for the squared image and the tilted integral image |
||||
CV_EXPORTS_AS(integral3) void integral( InputArray src, OutputArray sum, |
||||
OutputArray sqsum, OutputArray tilted, |
||||
int sdepth = -1, int sqdepth = -1 ); |
||||
|
||||
Small classes/structs are extended using ``CV_EXPORTS_W_SIMPLE``. These structs are passed by value to C++ functions. Examples are KeyPoint, Match etc. Their methods are extended by ``CV_WRAP`` and fields are extended by ``CV_PROP_RW``. |
||||
|
||||
.. code-block:: cpp |
||||
|
||||
class CV_EXPORTS_W_SIMPLE DMatch |
||||
{ |
||||
public: |
||||
CV_WRAP DMatch(); |
||||
CV_WRAP DMatch(int _queryIdx, int _trainIdx, float _distance); |
||||
CV_WRAP DMatch(int _queryIdx, int _trainIdx, int _imgIdx, float _distance); |
||||
|
||||
CV_PROP_RW int queryIdx; // query descriptor index |
||||
CV_PROP_RW int trainIdx; // train descriptor index |
||||
CV_PROP_RW int imgIdx; // train image index |
||||
|
||||
CV_PROP_RW float distance; |
||||
}; |
||||
|
||||
Some other small classes/structs can be exported using ``CV_EXPORTS_W_MAP`` where it is exported to a Python native dictionary. Moments() is an example of it. |
||||
|
||||
.. code-block:: cpp |
||||
|
||||
class CV_EXPORTS_W_MAP Moments |
||||
{ |
||||
public: |
||||
//! spatial moments |
||||
CV_PROP_RW double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; |
||||
//! central moments |
||||
CV_PROP_RW double mu20, mu11, mu02, mu30, mu21, mu12, mu03; |
||||
//! central normalized moments |
||||
CV_PROP_RW double nu20, nu11, nu02, nu30, nu21, nu12, nu03; |
||||
}; |
||||
|
||||
So these are the major extension macros available in OpenCV. Typically, a developer has to put proper macros in their appropriate positions. Rest is done by generator scripts. Sometimes, there may be an exceptional cases where generator scripts cannot create the wrappers. Such functions need to be handled manually. But most of the time, a code written according to OpenCV coding guidelines will be automatically wrapped by generator scripts. |
After Width: | Height: | Size: 3.6 KiB |
@ -0,0 +1,36 @@ |
||||
.. _PY_Table-Of-Content-Bindings: |
||||
|
||||
|
||||
OpenCV-Python Bindings |
||||
-------------------------------- |
||||
|
||||
Here, you will learn how OpenCV-Python bindings are generated. |
||||
|
||||
|
||||
* :ref:`Bindings_Basics` |
||||
|
||||
.. tabularcolumns:: m{100pt} m{300pt} |
||||
.. cssclass:: toctableopencv |
||||
|
||||
=========== ====================================================== |
||||
|bind1| Learn how OpenCV-Python bindings are generated. |
||||
|
||||
=========== ====================================================== |
||||
|
||||
.. |bind1| image:: images/nlm_icon.jpg |
||||
:height: 90pt |
||||
:width: 90pt |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.. raw:: latex |
||||
|
||||
\pagebreak |
||||
|
||||
.. We use a custom table of content format and as the table of content only informs Sphinx about the hierarchy of the files, no need to show it. |
||||
.. toctree:: |
||||
:hidden: |
||||
|
||||
../py_bindings_basics/py_bindings_basics |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 120 KiB |
@ -0,0 +1,113 @@ |
||||
.. _Raster_IO_GDAL: |
||||
|
||||
|
||||
Reading Geospatial Raster files with GDAL |
||||
***************************************** |
||||
|
||||
Geospatial raster data is a heavily used product in Geographic Information |
||||
Systems and Photogrammetry. Raster data typically can represent imagery |
||||
and Digital Elevation Models (DEM). The standard library for loading |
||||
GIS imagery is the Geographic Data Abstraction Library (GDAL). In this example, we |
||||
will show techniques for loading GIS raster formats using native OpenCV functions. |
||||
In addition, we will show some an example of how OpenCV can use this data for |
||||
novel and interesting purposes. |
||||
|
||||
Goals |
||||
===== |
||||
|
||||
The primary objectives for this tutorial: |
||||
|
||||
.. container:: enumeratevisibleitemswithsquare |
||||
|
||||
+ How to use OpenCV imread to load satellite imagery. |
||||
+ How to use OpenCV imread to load SRTM Digital Elevation Models |
||||
+ Given the corner coordinates of both the image and DEM, correllate the elevation data to the image to find elevations for each pixel. |
||||
+ Show a basic, easy-to-implement example of a terrain heat map. |
||||
+ Show a basic use of DEM data coupled with ortho-rectified imagery. |
||||
|
||||
To implement these goals, the following code takes a Digital Elevation Model as well as a GeoTiff image of San Francisco as input. |
||||
The image and DEM data is processed and generates a terrain heat map of the image as well as labels areas of the city which would |
||||
be affected should the water level of the bay rise 10, 50, and 100 meters. |
||||
|
||||
Code |
||||
==== |
||||
|
||||
.. literalinclude:: ../../../../samples/cpp/tutorial_code/HighGUI/GDAL_IO/gdal-image.cpp |
||||
:language: cpp |
||||
:linenos: |
||||
:tab-width: 4 |
||||
|
||||
|
||||
How to Read Raster Data using GDAL |
||||
====================================== |
||||
|
||||
This demonstration uses the default OpenCV :ocv:func:`imread` function. The primary difference is that in order to force GDAL to load the |
||||
image, you must use the appropriate flag. |
||||
|
||||
.. code-block:: cpp |
||||
|
||||
cv::Mat image = cv::imread( argv[1], cv::IMREAD_LOAD_GDAL ); |
||||
|
||||
When loading digital elevation models, the actual numeric value of each pixel is essential |
||||
and cannot be scaled or truncated. For example, with image data a pixel represented as a double with a value of 1 has |
||||
an equal appearance to a pixel which is represented as an unsigned character with a value of 255. |
||||
With terrain data, the pixel value represents the elevation in meters. In order to ensure that OpenCV preserves the native value, |
||||
use the GDAL flag in imread with the ANYDEPTH flag. |
||||
|
||||
.. code-block:: cpp |
||||
|
||||
cv::Mat dem = cv::imread( argv[2], cv::IMREAD_LOAD_GDAL | cv::IMREAD_ANYDEPTH ); |
||||
|
||||
|
||||
If you know beforehand the type of DEM model you are loading, then it may be a safe bet to test the ``Mat::type()`` or ``Mat::depth()`` |
||||
using an assert or other mechanism. NASA or DOD specification documents can provide the input types for various |
||||
elevation models. The major types, SRTM and DTED, are both signed shorts. |
||||
|
||||
Notes |
||||
===== |
||||
|
||||
Lat/Lon (Geodetic) Coordinates should normally be avoided |
||||
--------------------------------------------------------- |
||||
|
||||
The Geodetic Coordinate System is a spherical coordinate system, meaning that using them with Cartesian mathematics is technically incorrect. This |
||||
demo uses them to increase the readability and is accurate enough to make the point. A better coordinate system would be Universal Transverse Mercator. |
||||
|
||||
Finding the corner coordinates |
||||
------------------------------ |
||||
|
||||
One easy method to find the corner coordinates of an image is to use the command-line tool ``gdalinfo``. For imagery which is ortho-rectified and contains |
||||
the projection information, you can use the `USGS EarthExplorer <http://http://earthexplorer.usgs.gov>`_. |
||||
|
||||
.. code-block:: bash |
||||
|
||||
$> gdalinfo N37W123.hgt |
||||
|
||||
Driver: SRTMHGT/SRTMHGT File Format |
||||
Files: N37W123.hgt |
||||
Size is 3601, 3601 |
||||
Coordinate System is: |
||||
GEOGCS["WGS 84", |
||||
DATUM["WGS_1984", |
||||
|
||||
... more output ... |
||||
|
||||
Corner Coordinates: |
||||
Upper Left (-123.0001389, 38.0001389) (123d 0' 0.50"W, 38d 0' 0.50"N) |
||||
Lower Left (-123.0001389, 36.9998611) (123d 0' 0.50"W, 36d59'59.50"N) |
||||
Upper Right (-121.9998611, 38.0001389) (121d59'59.50"W, 38d 0' 0.50"N) |
||||
Lower Right (-121.9998611, 36.9998611) (121d59'59.50"W, 36d59'59.50"N) |
||||
Center (-122.5000000, 37.5000000) (122d30' 0.00"W, 37d30' 0.00"N) |
||||
|
||||
... more output ... |
||||
|
||||
|
||||
Results |
||||
======= |
||||
|
||||
Below is the output of the program. Use the first image as the input. For the DEM model, download the SRTM file located at the USGS here. `http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip <http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip>`_ |
||||
|
||||
.. image:: images/output.jpg |
||||
|
||||
.. image:: images/heat-map.jpg |
||||
|
||||
.. image:: images/flood-zone.jpg |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 84 KiB |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,61 @@ |
||||
#ifndef FISHEYE_INTERNAL_H |
||||
#define FISHEYE_INTERNAL_H |
||||
#include "precomp.hpp" |
||||
|
||||
namespace cv { namespace internal { |
||||
|
||||
struct CV_EXPORTS IntrinsicParams |
||||
{ |
||||
Vec2d f; |
||||
Vec2d c; |
||||
Vec4d k; |
||||
double alpha; |
||||
std::vector<int> isEstimate; |
||||
|
||||
IntrinsicParams(); |
||||
IntrinsicParams(Vec2d f, Vec2d c, Vec4d k, double alpha = 0); |
||||
IntrinsicParams operator+(const Mat& a); |
||||
IntrinsicParams& operator =(const Mat& a); |
||||
void Init(const cv::Vec2d& f, const cv::Vec2d& c, const cv::Vec4d& k = Vec4d(0,0,0,0), const double& alpha = 0); |
||||
}; |
||||
|
||||
void projectPoints(cv::InputArray objectPoints, cv::OutputArray imagePoints, |
||||
cv::InputArray _rvec,cv::InputArray _tvec, |
||||
const IntrinsicParams& param, cv::OutputArray jacobian); |
||||
|
||||
void ComputeExtrinsicRefine(const Mat& imagePoints, const Mat& objectPoints, Mat& rvec, |
||||
Mat& tvec, Mat& J, const int MaxIter, |
||||
const IntrinsicParams& param, const double thresh_cond); |
||||
CV_EXPORTS Mat ComputeHomography(Mat m, Mat M); |
||||
|
||||
CV_EXPORTS Mat NormalizePixels(const Mat& imagePoints, const IntrinsicParams& param); |
||||
|
||||
void InitExtrinsics(const Mat& _imagePoints, const Mat& _objectPoints, const IntrinsicParams& param, Mat& omckk, Mat& Tckk); |
||||
|
||||
void CalibrateExtrinsics(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, |
||||
const IntrinsicParams& param, const int check_cond, |
||||
const double thresh_cond, InputOutputArray omc, InputOutputArray Tc); |
||||
|
||||
void ComputeJacobians(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, |
||||
const IntrinsicParams& param, InputArray omc, InputArray Tc, |
||||
const int& check_cond, const double& thresh_cond, Mat& JJ2_inv, Mat& ex3); |
||||
|
||||
CV_EXPORTS void EstimateUncertainties(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, |
||||
const IntrinsicParams& params, InputArray omc, InputArray Tc, |
||||
IntrinsicParams& errors, Vec2d& std_err, double thresh_cond, int check_cond, double& rms); |
||||
|
||||
void dAB(cv::InputArray A, InputArray B, OutputArray dABdA, OutputArray dABdB); |
||||
|
||||
void JRodriguesMatlab(const Mat& src, Mat& dst); |
||||
|
||||
void compose_motion(InputArray _om1, InputArray _T1, InputArray _om2, InputArray _T2, |
||||
Mat& om3, Mat& T3, Mat& dom3dom1, Mat& dom3dT1, Mat& dom3dom2, |
||||
Mat& dom3dT2, Mat& dT3dom1, Mat& dT3dT1, Mat& dT3dom2, Mat& dT3dT2); |
||||
|
||||
double median(const Mat& row); |
||||
|
||||
Vec3d median3d(InputArray m); |
||||
|
||||
}} |
||||
|
||||
#endif |
@ -0,0 +1,482 @@ |
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This is a homography decomposition implementation contributed to OpenCV
|
||||
// by Samson Yilma. It implements the homography decomposition algorithm
|
||||
// descriped in the research report:
|
||||
// Malis, E and Vargas, M, "Deeper understanding of the homography decomposition
|
||||
// for vision-based control", Research Report 6303, INRIA (2007)
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2014, Samson Yilma¸ (samson_yilma@yahoo.com), all rights reserved.
|
||||
//
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp" |
||||
#include <memory> |
||||
|
||||
namespace cv |
||||
{ |
||||
|
||||
namespace HomographyDecomposition |
||||
{ |
||||
|
||||
//struct to hold solutions of homography decomposition
|
||||
typedef struct _CameraMotion { |
||||
cv::Matx33d R; //!< rotation matrix
|
||||
cv::Vec3d n; //!< normal of the plane the camera is looking at
|
||||
cv::Vec3d t; //!< translation vector
|
||||
} CameraMotion; |
||||
|
||||
inline int signd(const double x) |
||||
{ |
||||
return ( x >= 0 ? 1 : -1 ); |
||||
} |
||||
|
||||
class HomographyDecomp { |
||||
|
||||
public: |
||||
HomographyDecomp() {} |
||||
virtual ~HomographyDecomp() {} |
||||
virtual void decomposeHomography(const cv::Matx33d& H, const cv::Matx33d& K, |
||||
std::vector<CameraMotion>& camMotions); |
||||
bool isRotationValid(const cv::Matx33d& R, const double epsilon=0.01); |
||||
|
||||
protected: |
||||
bool passesSameSideOfPlaneConstraint(CameraMotion& motion); |
||||
virtual void decompose(std::vector<CameraMotion>& camMotions) = 0; |
||||
const cv::Matx33d& getHnorm() const { |
||||
return _Hnorm; |
||||
} |
||||
|
||||
private: |
||||
cv::Matx33d normalize(const cv::Matx33d& H, const cv::Matx33d& K); |
||||
void removeScale(); |
||||
cv::Matx33d _Hnorm; |
||||
}; |
||||
|
||||
class HomographyDecompZhang : public HomographyDecomp { |
||||
|
||||
public: |
||||
HomographyDecompZhang():HomographyDecomp() {} |
||||
virtual ~HomographyDecompZhang() {} |
||||
|
||||
private: |
||||
virtual void decompose(std::vector<CameraMotion>& camMotions); |
||||
bool findMotionFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, CameraMotion& motion); |
||||
}; |
||||
|
||||
class HomographyDecompInria : public HomographyDecomp { |
||||
|
||||
public: |
||||
HomographyDecompInria():HomographyDecomp() {} |
||||
virtual ~HomographyDecompInria() {} |
||||
|
||||
private: |
||||
virtual void decompose(std::vector<CameraMotion>& camMotions); |
||||
double oppositeOfMinor(const cv::Matx33d& M, const int row, const int col); |
||||
void findRmatFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, const double v, cv::Matx33d& R); |
||||
}; |
||||
|
||||
// normalizes homography with intrinsic camera parameters
|
||||
Matx33d HomographyDecomp::normalize(const Matx33d& H, const Matx33d& K) |
||||
{ |
||||
return K.inv() * H * K; |
||||
} |
||||
|
||||
void HomographyDecomp::removeScale() |
||||
{ |
||||
Mat W; |
||||
SVD::compute(_Hnorm, W); |
||||
_Hnorm = _Hnorm * (1.0/W.at<double>(1)); |
||||
} |
||||
|
||||
/*! This checks that the input is a pure rotation matrix 'm'.
|
||||
* The conditions for this are: R' * R = I and det(R) = 1 (proper rotation matrix) |
||||
*/ |
||||
bool HomographyDecomp::isRotationValid(const Matx33d& R, const double epsilon) |
||||
{ |
||||
Matx33d RtR = R.t() * R; |
||||
Matx33d I(1,0,0, 0,1,0, 0,0,1); |
||||
if (norm(RtR, I, NORM_INF) > epsilon) |
||||
return false; |
||||
return (fabs(determinant(R) - 1.0) < epsilon); |
||||
} |
||||
|
||||
bool HomographyDecomp::passesSameSideOfPlaneConstraint(CameraMotion& motion) |
||||
{ |
||||
typedef Matx<double, 1, 1> Matx11d; |
||||
Matx31d t = Matx31d(motion.t); |
||||
Matx31d n = Matx31d(motion.n); |
||||
Matx11d proj = n.t() * motion.R.t() * t; |
||||
if ( (1 + proj(0, 0) ) <= 0 ) |
||||
return false; |
||||
return true; |
||||
} |
||||
|
||||
//!main routine to decompose homography
|
||||
void HomographyDecomp::decomposeHomography(const Matx33d& H, const cv::Matx33d& K, |
||||
std::vector<CameraMotion>& camMotions) |
||||
{ |
||||
//normalize homography matrix with intrinsic camera matrix
|
||||
_Hnorm = normalize(H, K); |
||||
//remove scale of the normalized homography
|
||||
removeScale(); |
||||
//apply decomposition
|
||||
decompose(camMotions); |
||||
} |
||||
|
||||
/* function computes R&t from tstar, and plane normal(n) using
|
||||
R = H * inv(I + tstar*transpose(n) ); |
||||
t = R * tstar; |
||||
returns true if computed R&t is a valid solution |
||||
*/ |
||||
bool HomographyDecompZhang::findMotionFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, CameraMotion& motion) |
||||
{ |
||||
Matx31d tstar_m = Mat(tstar); |
||||
Matx31d n_m = Mat(n); |
||||
Matx33d temp = tstar_m * n_m.t(); |
||||
temp(0, 0) += 1.0; |
||||
temp(1, 1) += 1.0; |
||||
temp(2, 2) += 1.0; |
||||
motion.R = getHnorm() * temp.inv(); |
||||
motion.t = motion.R * tstar; |
||||
motion.n = n; |
||||
return passesSameSideOfPlaneConstraint(motion); |
||||
} |
||||
|
||||
void HomographyDecompZhang::decompose(std::vector<CameraMotion>& camMotions) |
||||
{ |
||||
Mat W, U, Vt; |
||||
SVD::compute(getHnorm(), W, U, Vt); |
||||
double lambda1=W.at<double>(0); |
||||
double lambda3=W.at<double>(2); |
||||
double lambda1m3 = (lambda1-lambda3); |
||||
double lambda1m3_2 = lambda1m3*lambda1m3; |
||||
double lambda1t3 = lambda1*lambda3; |
||||
|
||||
double t1 = 1.0/(2.0*lambda1t3); |
||||
double t2 = sqrt(1.0+4.0*lambda1t3/lambda1m3_2); |
||||
double t12 = t1*t2; |
||||
|
||||
double e1 = -t1 + t12; //t1*(-1.0f + t2 );
|
||||
double e3 = -t1 - t12; //t1*(-1.0f - t2);
|
||||
double e1_2 = e1*e1; |
||||
double e3_2 = e3*e3; |
||||
|
||||
double nv1p = sqrt(e1_2*lambda1m3_2 + 2*e1*(lambda1t3-1) + 1.0); |
||||
double nv3p = sqrt(e3_2*lambda1m3_2 + 2*e3*(lambda1t3-1) + 1.0); |
||||
double v1p[3], v3p[3]; |
||||
|
||||
v1p[0]=Vt.at<double>(0)*nv1p, v1p[1]=Vt.at<double>(1)*nv1p, v1p[2]=Vt.at<double>(2)*nv1p; |
||||
v3p[0]=Vt.at<double>(6)*nv3p, v3p[1]=Vt.at<double>(7)*nv3p, v3p[2]=Vt.at<double>(8)*nv3p; |
||||
|
||||
/*The eight solutions are
|
||||
(A): tstar = +- (v1p - v3p)/(e1 -e3), n = +- (e1*v3p - e3*v1p)/(e1-e3) |
||||
(B): tstar = +- (v1p + v3p)/(e1 -e3), n = +- (e1*v3p + e3*v1p)/(e1-e3) |
||||
*/ |
||||
double v1pmv3p[3], v1ppv3p[3]; |
||||
double e1v3me3v1[3], e1v3pe3v1[3]; |
||||
double inv_e1me3 = 1.0/(e1-e3); |
||||
|
||||
for(int kk=0;kk<3;++kk){ |
||||
v1pmv3p[kk] = v1p[kk]-v3p[kk]; |
||||
v1ppv3p[kk] = v1p[kk]+v3p[kk]; |
||||
} |
||||
|
||||
for(int kk=0; kk<3; ++kk){ |
||||
double e1v3 = e1*v3p[kk]; |
||||
double e3v1=e3*v1p[kk]; |
||||
e1v3me3v1[kk] = e1v3-e3v1; |
||||
e1v3pe3v1[kk] = e1v3+e3v1; |
||||
} |
||||
|
||||
Vec3d tstar_p, tstar_n; |
||||
Vec3d n_p, n_n; |
||||
|
||||
///Solution group A
|
||||
for(int kk=0; kk<3; ++kk) { |
||||
tstar_p[kk] = v1pmv3p[kk]*inv_e1me3; |
||||
tstar_n[kk] = -tstar_p[kk]; |
||||
n_p[kk] = e1v3me3v1[kk]*inv_e1me3; |
||||
n_n[kk] = -n_p[kk]; |
||||
} |
||||
|
||||
CameraMotion cmotion; |
||||
//(A) Four different combinations for solution A
|
||||
// (i) (+, +)
|
||||
if (findMotionFrom_tstar_n(tstar_p, n_p, cmotion)) |
||||
camMotions.push_back(cmotion); |
||||
|
||||
// (ii) (+, -)
|
||||
if (findMotionFrom_tstar_n(tstar_p, n_n, cmotion)) |
||||
camMotions.push_back(cmotion); |
||||
|
||||
// (iii) (-, +)
|
||||
if (findMotionFrom_tstar_n(tstar_n, n_p, cmotion)) |
||||
camMotions.push_back(cmotion); |
||||
|
||||
// (iv) (-, -)
|
||||
if (findMotionFrom_tstar_n(tstar_n, n_n, cmotion)) |
||||
camMotions.push_back(cmotion); |
||||
//////////////////////////////////////////////////////////////////
|
||||
///Solution group B
|
||||
for(int kk=0;kk<3;++kk){ |
||||
tstar_p[kk] = v1ppv3p[kk]*inv_e1me3; |
||||
tstar_n[kk] = -tstar_p[kk]; |
||||
n_p[kk] = e1v3pe3v1[kk]*inv_e1me3; |
||||
n_n[kk] = -n_p[kk]; |
||||
} |
||||
|
||||
//(B) Four different combinations for solution B
|
||||
// (i) (+, +)
|
||||
if (findMotionFrom_tstar_n(tstar_p, n_p, cmotion)) |
||||
camMotions.push_back(cmotion); |
||||
|
||||
// (ii) (+, -)
|
||||
if (findMotionFrom_tstar_n(tstar_p, n_n, cmotion)) |
||||
camMotions.push_back(cmotion); |
||||
|
||||
// (iii) (-, +)
|
||||
if (findMotionFrom_tstar_n(tstar_n, n_p, cmotion)) |
||||
camMotions.push_back(cmotion); |
||||
|
||||
// (iv) (-, -)
|
||||
if (findMotionFrom_tstar_n(tstar_n, n_n, cmotion)) |
||||
camMotions.push_back(cmotion); |
||||
} |
||||
|
||||
double HomographyDecompInria::oppositeOfMinor(const Matx33d& M, const int row, const int col) |
||||
{ |
||||
int x1 = col == 0 ? 1 : 0; |
||||
int x2 = col == 2 ? 1 : 2; |
||||
int y1 = row == 0 ? 1 : 0; |
||||
int y2 = row == 2 ? 1 : 2; |
||||
|
||||
return (M(y1, x2) * M(y2, x1) - M(y1, x1) * M(y2, x2)); |
||||
} |
||||
|
||||
//computes R = H( I - (2/v)*te_star*ne_t )
|
||||
void HomographyDecompInria::findRmatFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, const double v, cv::Matx33d& R) |
||||
{ |
||||
Matx31d tstar_m = Matx31d(tstar); |
||||
Matx31d n_m = Matx31d(n); |
||||
Matx33d I(1.0, 0.0, 0.0, |
||||
0.0, 1.0, 0.0, |
||||
0.0, 0.0, 1.0); |
||||
|
||||
R = getHnorm() * (I - (2/v) * tstar_m * n_m.t() ); |
||||
} |
||||
|
||||
void HomographyDecompInria::decompose(std::vector<CameraMotion>& camMotions) |
||||
{ |
||||
const double epsilon = 0.001; |
||||
Matx33d S; |
||||
|
||||
//S = H'H - I
|
||||
S = getHnorm().t() * getHnorm(); |
||||
S(0, 0) -= 1.0; |
||||
S(1, 1) -= 1.0; |
||||
S(2, 2) -= 1.0; |
||||
|
||||
//check if H is rotation matrix
|
||||
if( norm(S, NORM_INF) < epsilon) { |
||||
CameraMotion motion; |
||||
motion.R = Matx33d(getHnorm()); |
||||
motion.t = Vec3d(0, 0, 0); |
||||
motion.n = Vec3d(0, 0, 0); |
||||
camMotions.push_back(motion); |
||||
return; |
||||
} |
||||
|
||||
//! Compute nvectors
|
||||
Vec3d npa, npb; |
||||
|
||||
double M00 = oppositeOfMinor(S, 0, 0); |
||||
double M11 = oppositeOfMinor(S, 1, 1); |
||||
double M22 = oppositeOfMinor(S, 2, 2); |
||||
|
||||
double rtM00 = sqrt(M00); |
||||
double rtM11 = sqrt(M11); |
||||
double rtM22 = sqrt(M22); |
||||
|
||||
double M01 = oppositeOfMinor(S, 0, 1); |
||||
double M12 = oppositeOfMinor(S, 1, 2); |
||||
double M02 = oppositeOfMinor(S, 0, 2); |
||||
|
||||
int e12 = signd(M12); |
||||
int e02 = signd(M02); |
||||
int e01 = signd(M01); |
||||
|
||||
double nS00 = abs(S(0, 0)); |
||||
double nS11 = abs(S(1, 1)); |
||||
double nS22 = abs(S(2, 2)); |
||||
|
||||
//find max( |Sii| ), i=0, 1, 2
|
||||
int indx = 0; |
||||
if(nS00 < nS11){ |
||||
indx = 1; |
||||
if( nS11 < nS22 ) |
||||
indx = 2; |
||||
} |
||||
else { |
||||
if(nS00 < nS22 ) |
||||
indx = 2; |
||||
} |
||||
|
||||
switch (indx) { |
||||
case 0: |
||||
npa[0] = S(0, 0), npb[0] = S(0, 0); |
||||
npa[1] = S(0, 1) + rtM22, npb[1] = S(0, 1) - rtM22; |
||||
npa[2] = S(0, 2) + e12 * rtM11, npb[2] = S(0, 2) - e12 * rtM11; |
||||
break; |
||||
case 1: |
||||
npa[0] = S(0, 1) + rtM22, npb[0] = S(0, 1) - rtM22; |
||||
npa[1] = S(1, 1), npb[1] = S(1, 1); |
||||
npa[2] = S(1, 2) - e02 * rtM00, npb[2] = S(1, 2) + e02 * rtM00; |
||||
break; |
||||
case 2: |
||||
npa[0] = S(0, 2) + e01 * rtM11, npb[0] = S(0, 2) - e01 * rtM11; |
||||
npa[1] = S(1, 2) + rtM00, npb[1] = S(1, 2) - rtM00; |
||||
npa[2] = S(2, 2), npb[2] = S(2, 2); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
double traceS = S(0, 0) + S(1, 1) + S(2, 2); |
||||
double v = 2.0 * sqrt(1 + traceS - M00 - M11 - M22); |
||||
|
||||
double ESii = signd(S(indx, indx)) ; |
||||
double r_2 = 2 + traceS + v; |
||||
double nt_2 = 2 + traceS - v; |
||||
|
||||
double r = sqrt(r_2); |
||||
double n_t = sqrt(nt_2); |
||||
|
||||
Vec3d na = npa / norm(npa); |
||||
Vec3d nb = npb / norm(npb); |
||||
|
||||
double half_nt = 0.5 * n_t; |
||||
double esii_t_r = ESii * r; |
||||
|
||||
Vec3d ta_star = half_nt * (esii_t_r * nb - n_t * na); |
||||
Vec3d tb_star = half_nt * (esii_t_r * na - n_t * nb); |
||||
|
||||
camMotions.resize(4); |
||||
|
||||
Matx33d Ra, Rb; |
||||
Vec3d ta, tb; |
||||
|
||||
//Ra, ta, na
|
||||
findRmatFrom_tstar_n(ta_star, na, v, Ra); |
||||
ta = Ra * ta_star; |
||||
|
||||
camMotions[0].R = Ra; |
||||
camMotions[0].t = ta; |
||||
camMotions[0].n = na; |
||||
|
||||
//Ra, -ta, -na
|
||||
camMotions[1].R = Ra; |
||||
camMotions[1].t = -ta; |
||||
camMotions[1].n = -na; |
||||
|
||||
//Rb, tb, nb
|
||||
findRmatFrom_tstar_n(tb_star, nb, v, Rb); |
||||
tb = Rb * tb_star; |
||||
|
||||
camMotions[2].R = Rb; |
||||
camMotions[2].t = tb; |
||||
camMotions[2].n = nb; |
||||
|
||||
//Rb, -tb, -nb
|
||||
camMotions[3].R = Rb; |
||||
camMotions[3].t = -tb; |
||||
camMotions[3].n = -nb; |
||||
} |
||||
|
||||
} //namespace HomographyDecomposition
|
||||
|
||||
// function decomposes image-to-image homography to rotation and translation matrices
|
||||
int decomposeHomographyMat(InputArray _H, |
||||
InputArray _K, |
||||
OutputArrayOfArrays _rotations, |
||||
OutputArrayOfArrays _translations, |
||||
OutputArrayOfArrays _normals) |
||||
{ |
||||
using namespace std; |
||||
using namespace HomographyDecomposition; |
||||
|
||||
Mat H = _H.getMat().reshape(1, 3); |
||||
CV_Assert(H.cols == 3 && H.rows == 3); |
||||
|
||||
Mat K = _K.getMat().reshape(1, 3); |
||||
CV_Assert(K.cols == 3 && K.rows == 3); |
||||
|
||||
auto_ptr<HomographyDecomp> hdecomp(new HomographyDecompInria); |
||||
|
||||
vector<CameraMotion> motions; |
||||
hdecomp->decomposeHomography(H, K, motions); |
||||
|
||||
int nsols = static_cast<int>(motions.size()); |
||||
int depth = CV_64F; //double precision matrices used in CameraMotion struct
|
||||
|
||||
if (_rotations.needed()) { |
||||
_rotations.create(nsols, 1, depth); |
||||
for (int k = 0; k < nsols; ++k ) { |
||||
_rotations.getMatRef(k) = Mat(motions[k].R); |
||||
} |
||||
} |
||||
|
||||
if (_translations.needed()) { |
||||
_translations.create(nsols, 1, depth); |
||||
for (int k = 0; k < nsols; ++k ) { |
||||
_translations.getMatRef(k) = Mat(motions[k].t); |
||||
} |
||||
} |
||||
|
||||
if (_normals.needed()) { |
||||
_normals.create(nsols, 1, depth); |
||||
for (int k = 0; k < nsols; ++k ) { |
||||
_normals.getMatRef(k) = Mat(motions[k].n); |
||||
} |
||||
} |
||||
|
||||
return nsols; |
||||
} |
||||
|
||||
} //namespace cv
|
@ -0,0 +1,613 @@ |
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp" |
||||
#include <opencv2/ts/cuda_test.hpp> |
||||
#include "../src/fisheye.hpp" |
||||
|
||||
class fisheyeTest : public ::testing::Test { |
||||
|
||||
protected: |
||||
const static cv::Size imageSize; |
||||
const static cv::Matx33d K; |
||||
const static cv::Vec4d D; |
||||
const static cv::Matx33d R; |
||||
const static cv::Vec3d T; |
||||
std::string datasets_repository_path; |
||||
|
||||
virtual void SetUp() { |
||||
datasets_repository_path = combine(cvtest::TS::ptr()->get_data_path(), "cv/cameracalibration/fisheye"); |
||||
} |
||||
|
||||
protected: |
||||
std::string combine(const std::string& _item1, const std::string& _item2); |
||||
cv::Mat mergeRectification(const cv::Mat& l, const cv::Mat& r); |
||||
}; |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// TESTS::
|
||||
|
||||
TEST_F(fisheyeTest, projectPoints) |
||||
{ |
||||
double cols = this->imageSize.width, |
||||
rows = this->imageSize.height; |
||||
|
||||
const int N = 20; |
||||
cv::Mat distorted0(1, N*N, CV_64FC2), undist1, undist2, distorted1, distorted2; |
||||
undist2.create(distorted0.size(), CV_MAKETYPE(distorted0.depth(), 3)); |
||||
cv::Vec2d* pts = distorted0.ptr<cv::Vec2d>(); |
||||
|
||||
cv::Vec2d c(this->K(0, 2), this->K(1, 2)); |
||||
for(int y = 0, k = 0; y < N; ++y) |
||||
for(int x = 0; x < N; ++x) |
||||
{ |
||||
cv::Vec2d point(x*cols/(N-1.f), y*rows/(N-1.f)); |
||||
pts[k++] = (point - c) * 0.85 + c; |
||||
} |
||||
|
||||
cv::fisheye::undistortPoints(distorted0, undist1, this->K, this->D); |
||||
|
||||
cv::Vec2d* u1 = undist1.ptr<cv::Vec2d>(); |
||||
cv::Vec3d* u2 = undist2.ptr<cv::Vec3d>(); |
||||
for(int i = 0; i < (int)distorted0.total(); ++i) |
||||
u2[i] = cv::Vec3d(u1[i][0], u1[i][1], 1.0); |
||||
|
||||
cv::fisheye::distortPoints(undist1, distorted1, this->K, this->D); |
||||
cv::fisheye::projectPoints(undist2, distorted2, cv::Vec3d::all(0), cv::Vec3d::all(0), this->K, this->D); |
||||
|
||||
EXPECT_MAT_NEAR(distorted0, distorted1, 1e-10); |
||||
EXPECT_MAT_NEAR(distorted0, distorted2, 1e-10); |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, DISABLED_undistortImage) |
||||
{ |
||||
cv::Matx33d K = this->K; |
||||
cv::Mat D = cv::Mat(this->D); |
||||
std::string file = combine(datasets_repository_path, "/calib-3_stereo_from_JY/left/stereo_pair_014.jpg"); |
||||
cv::Matx33d newK = K; |
||||
cv::Mat distorted = cv::imread(file), undistorted; |
||||
{ |
||||
newK(0, 0) = 100; |
||||
newK(1, 1) = 100; |
||||
cv::fisheye::undistortImage(distorted, undistorted, K, D, newK); |
||||
cv::Mat correct = cv::imread(combine(datasets_repository_path, "new_f_100.png")); |
||||
if (correct.empty()) |
||||
CV_Assert(cv::imwrite(combine(datasets_repository_path, "new_f_100.png"), undistorted)); |
||||
else |
||||
EXPECT_MAT_NEAR(correct, undistorted, 1e-10); |
||||
} |
||||
{ |
||||
double balance = 1.0; |
||||
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(K, D, distorted.size(), cv::noArray(), newK, balance); |
||||
cv::fisheye::undistortImage(distorted, undistorted, K, D, newK); |
||||
cv::Mat correct = cv::imread(combine(datasets_repository_path, "balance_1.0.png")); |
||||
if (correct.empty()) |
||||
CV_Assert(cv::imwrite(combine(datasets_repository_path, "balance_1.0.png"), undistorted)); |
||||
else |
||||
EXPECT_MAT_NEAR(correct, undistorted, 1e-10); |
||||
} |
||||
|
||||
{ |
||||
double balance = 0.0; |
||||
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(K, D, distorted.size(), cv::noArray(), newK, balance); |
||||
cv::fisheye::undistortImage(distorted, undistorted, K, D, newK); |
||||
cv::Mat correct = cv::imread(combine(datasets_repository_path, "balance_0.0.png")); |
||||
if (correct.empty()) |
||||
CV_Assert(cv::imwrite(combine(datasets_repository_path, "balance_0.0.png"), undistorted)); |
||||
else |
||||
EXPECT_MAT_NEAR(correct, undistorted, 1e-10); |
||||
} |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, jacobians) |
||||
{ |
||||
int n = 10; |
||||
cv::Mat X(1, n, CV_64FC3); |
||||
cv::Mat om(3, 1, CV_64F), T(3, 1, CV_64F); |
||||
cv::Mat f(2, 1, CV_64F), c(2, 1, CV_64F); |
||||
cv::Mat k(4, 1, CV_64F); |
||||
double alpha; |
||||
|
||||
cv::RNG r; |
||||
|
||||
r.fill(X, cv::RNG::NORMAL, 2, 1); |
||||
X = cv::abs(X) * 10; |
||||
|
||||
r.fill(om, cv::RNG::NORMAL, 0, 1); |
||||
om = cv::abs(om); |
||||
|
||||
r.fill(T, cv::RNG::NORMAL, 0, 1); |
||||
T = cv::abs(T); T.at<double>(2) = 4; T *= 10; |
||||
|
||||
r.fill(f, cv::RNG::NORMAL, 0, 1); |
||||
f = cv::abs(f) * 1000; |
||||
|
||||
r.fill(c, cv::RNG::NORMAL, 0, 1); |
||||
c = cv::abs(c) * 1000; |
||||
|
||||
r.fill(k, cv::RNG::NORMAL, 0, 1); |
||||
k*= 0.5; |
||||
|
||||
alpha = 0.01*r.gaussian(1); |
||||
|
||||
cv::Mat x1, x2, xpred; |
||||
cv::Matx33d K(f.at<double>(0), alpha * f.at<double>(0), c.at<double>(0), |
||||
0, f.at<double>(1), c.at<double>(1), |
||||
0, 0, 1); |
||||
|
||||
cv::Mat jacobians; |
||||
cv::fisheye::projectPoints(X, x1, om, T, K, k, alpha, jacobians); |
||||
|
||||
//test on T:
|
||||
cv::Mat dT(3, 1, CV_64FC1); |
||||
r.fill(dT, cv::RNG::NORMAL, 0, 1); |
||||
dT *= 1e-9*cv::norm(T); |
||||
cv::Mat T2 = T + dT; |
||||
cv::fisheye::projectPoints(X, x2, om, T2, K, k, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(11,14) * dT).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on om:
|
||||
cv::Mat dom(3, 1, CV_64FC1); |
||||
r.fill(dom, cv::RNG::NORMAL, 0, 1); |
||||
dom *= 1e-9*cv::norm(om); |
||||
cv::Mat om2 = om + dom; |
||||
cv::fisheye::projectPoints(X, x2, om2, T, K, k, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(8,11) * dom).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on f:
|
||||
cv::Mat df(2, 1, CV_64FC1); |
||||
r.fill(df, cv::RNG::NORMAL, 0, 1); |
||||
df *= 1e-9*cv::norm(f); |
||||
cv::Matx33d K2 = K + cv::Matx33d(df.at<double>(0), df.at<double>(0) * alpha, 0, 0, df.at<double>(1), 0, 0, 0, 0); |
||||
cv::fisheye::projectPoints(X, x2, om, T, K2, k, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(0,2) * df).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on c:
|
||||
cv::Mat dc(2, 1, CV_64FC1); |
||||
r.fill(dc, cv::RNG::NORMAL, 0, 1); |
||||
dc *= 1e-9*cv::norm(c); |
||||
K2 = K + cv::Matx33d(0, 0, dc.at<double>(0), 0, 0, dc.at<double>(1), 0, 0, 0); |
||||
cv::fisheye::projectPoints(X, x2, om, T, K2, k, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(2,4) * dc).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on k:
|
||||
cv::Mat dk(4, 1, CV_64FC1); |
||||
r.fill(dk, cv::RNG::NORMAL, 0, 1); |
||||
dk *= 1e-9*cv::norm(k); |
||||
cv::Mat k2 = k + dk; |
||||
cv::fisheye::projectPoints(X, x2, om, T, K, k2, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(4,8) * dk).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on alpha:
|
||||
cv::Mat dalpha(1, 1, CV_64FC1); |
||||
r.fill(dalpha, cv::RNG::NORMAL, 0, 1); |
||||
dalpha *= 1e-9*cv::norm(f); |
||||
double alpha2 = alpha + dalpha.at<double>(0); |
||||
K2 = K + cv::Matx33d(0, f.at<double>(0) * dalpha.at<double>(0), 0, 0, 0, 0, 0, 0, 0); |
||||
cv::fisheye::projectPoints(X, x2, om, T, K, k, alpha2, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.col(14) * dalpha).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, Calibration) |
||||
{ |
||||
const int n_images = 34; |
||||
|
||||
std::vector<std::vector<cv::Point2d> > imagePoints(n_images); |
||||
std::vector<std::vector<cv::Point3d> > objectPoints(n_images); |
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY"); |
||||
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_left.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_left[cv::format("image_%d", i )] >> imagePoints[i]; |
||||
fs_left.release(); |
||||
|
||||
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_object.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_object[cv::format("image_%d", i )] >> objectPoints[i]; |
||||
fs_object.release(); |
||||
|
||||
int flag = 0; |
||||
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC; |
||||
flag |= cv::fisheye::CALIB_CHECK_COND; |
||||
flag |= cv::fisheye::CALIB_FIX_SKEW; |
||||
|
||||
cv::Matx33d K; |
||||
cv::Vec4d D; |
||||
|
||||
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, K, D, |
||||
cv::noArray(), cv::noArray(), flag, cv::TermCriteria(3, 20, 1e-6)); |
||||
|
||||
EXPECT_MAT_NEAR(K, this->K, 1e-10); |
||||
EXPECT_MAT_NEAR(D, this->D, 1e-10); |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, Homography) |
||||
{ |
||||
const int n_images = 1; |
||||
|
||||
std::vector<std::vector<cv::Point2d> > imagePoints(n_images); |
||||
std::vector<std::vector<cv::Point3d> > objectPoints(n_images); |
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY"); |
||||
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_left.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_left[cv::format("image_%d", i )] >> imagePoints[i]; |
||||
fs_left.release(); |
||||
|
||||
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_object.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_object[cv::format("image_%d", i )] >> objectPoints[i]; |
||||
fs_object.release(); |
||||
|
||||
cv::internal::IntrinsicParams param; |
||||
param.Init(cv::Vec2d(cv::max(imageSize.width, imageSize.height) / CV_PI, cv::max(imageSize.width, imageSize.height) / CV_PI), |
||||
cv::Vec2d(imageSize.width / 2.0 - 0.5, imageSize.height / 2.0 - 0.5)); |
||||
|
||||
cv::Mat _imagePoints (imagePoints[0]); |
||||
cv::Mat _objectPoints(objectPoints[0]); |
||||
|
||||
cv::Mat imagePointsNormalized = NormalizePixels(_imagePoints, param).reshape(1).t(); |
||||
_objectPoints = _objectPoints.reshape(1).t(); |
||||
cv::Mat objectPointsMean, covObjectPoints; |
||||
|
||||
int Np = imagePointsNormalized.cols; |
||||
cv::calcCovarMatrix(_objectPoints, covObjectPoints, objectPointsMean, cv::COVAR_NORMAL | cv::COVAR_COLS); |
||||
cv::SVD svd(covObjectPoints); |
||||
cv::Mat R(svd.vt); |
||||
|
||||
if (cv::norm(R(cv::Rect(2, 0, 1, 2))) < 1e-6) |
||||
R = cv::Mat::eye(3,3, CV_64FC1); |
||||
if (cv::determinant(R) < 0) |
||||
R = -R; |
||||
|
||||
cv::Mat T = -R * objectPointsMean; |
||||
cv::Mat X_new = R * _objectPoints + T * cv::Mat::ones(1, Np, CV_64FC1); |
||||
cv::Mat H = cv::internal::ComputeHomography(imagePointsNormalized, X_new.rowRange(0, 2)); |
||||
|
||||
cv::Mat M = cv::Mat::ones(3, X_new.cols, CV_64FC1); |
||||
X_new.rowRange(0, 2).copyTo(M.rowRange(0, 2)); |
||||
cv::Mat mrep = H * M; |
||||
|
||||
cv::divide(mrep, cv::Mat::ones(3,1, CV_64FC1) * mrep.row(2).clone(), mrep); |
||||
|
||||
cv::Mat merr = (mrep.rowRange(0, 2) - imagePointsNormalized).t(); |
||||
|
||||
cv::Vec2d std_err; |
||||
cv::meanStdDev(merr.reshape(2), cv::noArray(), std_err); |
||||
std_err *= sqrt((double)merr.reshape(2).total() / (merr.reshape(2).total() - 1)); |
||||
|
||||
cv::Vec2d correct_std_err(0.00516740156010384, 0.00644205331553901); |
||||
EXPECT_MAT_NEAR(std_err, correct_std_err, 1e-12); |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, EtimateUncertainties) |
||||
{ |
||||
const int n_images = 34; |
||||
|
||||
std::vector<std::vector<cv::Point2d> > imagePoints(n_images); |
||||
std::vector<std::vector<cv::Point3d> > objectPoints(n_images); |
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY"); |
||||
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_left.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_left[cv::format("image_%d", i )] >> imagePoints[i]; |
||||
fs_left.release(); |
||||
|
||||
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_object.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_object[cv::format("image_%d", i )] >> objectPoints[i]; |
||||
fs_object.release(); |
||||
|
||||
int flag = 0; |
||||
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC; |
||||
flag |= cv::fisheye::CALIB_CHECK_COND; |
||||
flag |= cv::fisheye::CALIB_FIX_SKEW; |
||||
|
||||
cv::Matx33d K; |
||||
cv::Vec4d D; |
||||
std::vector<cv::Vec3d> rvec; |
||||
std::vector<cv::Vec3d> tvec; |
||||
|
||||
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, K, D, |
||||
rvec, tvec, flag, cv::TermCriteria(3, 20, 1e-6)); |
||||
|
||||
cv::internal::IntrinsicParams param, errors; |
||||
cv::Vec2d err_std; |
||||
double thresh_cond = 1e6; |
||||
int check_cond = 1; |
||||
param.Init(cv::Vec2d(K(0,0), K(1,1)), cv::Vec2d(K(0,2), K(1, 2)), D); |
||||
param.isEstimate = std::vector<int>(9, 1); |
||||
param.isEstimate[4] = 0; |
||||
|
||||
errors.isEstimate = param.isEstimate; |
||||
|
||||
double rms; |
||||
|
||||
cv::internal::EstimateUncertainties(objectPoints, imagePoints, param, rvec, tvec, |
||||
errors, err_std, thresh_cond, check_cond, rms); |
||||
|
||||
EXPECT_MAT_NEAR(errors.f, cv::Vec2d(1.29837104202046, 1.31565641071524), 1e-10); |
||||
EXPECT_MAT_NEAR(errors.c, cv::Vec2d(0.890439368129246, 0.816096854937896), 1e-10); |
||||
EXPECT_MAT_NEAR(errors.k, cv::Vec4d(0.00516248605191506, 0.0168181467500934, 0.0213118690274604, 0.00916010877545648), 1e-10); |
||||
EXPECT_MAT_NEAR(err_std, cv::Vec2d(0.187475975266883, 0.185678953263995), 1e-10); |
||||
CV_Assert(abs(rms - 0.263782587133546) < 1e-10); |
||||
CV_Assert(errors.alpha == 0); |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, rectify) |
||||
{ |
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY"); |
||||
|
||||
cv::Size calibration_size = this->imageSize, requested_size = calibration_size; |
||||
cv::Matx33d K1 = this->K, K2 = K1; |
||||
cv::Mat D1 = cv::Mat(this->D), D2 = D1; |
||||
|
||||
cv::Vec3d T = this->T; |
||||
cv::Matx33d R = this->R; |
||||
|
||||
double balance = 0.0, fov_scale = 1.1; |
||||
cv::Mat R1, R2, P1, P2, Q; |
||||
cv::fisheye::stereoRectify(K1, D1, K2, D2, calibration_size, R, T, R1, R2, P1, P2, Q, |
||||
cv::CALIB_ZERO_DISPARITY, requested_size, balance, fov_scale); |
||||
|
||||
cv::Mat lmapx, lmapy, rmapx, rmapy; |
||||
//rewrite for fisheye
|
||||
cv::fisheye::initUndistortRectifyMap(K1, D1, R1, P1, requested_size, CV_32F, lmapx, lmapy); |
||||
cv::fisheye::initUndistortRectifyMap(K2, D2, R2, P2, requested_size, CV_32F, rmapx, rmapy); |
||||
|
||||
cv::Mat l, r, lundist, rundist; |
||||
cv::VideoCapture lcap(combine(folder, "left/stereo_pair_%03d.jpg")), |
||||
rcap(combine(folder, "right/stereo_pair_%03d.jpg")); |
||||
|
||||
for(int i = 0;; ++i) |
||||
{ |
||||
lcap >> l; rcap >> r; |
||||
if (l.empty() || r.empty()) |
||||
break; |
||||
|
||||
int ndisp = 128; |
||||
cv::rectangle(l, cv::Rect(255, 0, 829, l.rows-1), cv::Scalar(0, 0, 255)); |
||||
cv::rectangle(r, cv::Rect(255, 0, 829, l.rows-1), cv::Scalar(0, 0, 255)); |
||||
cv::rectangle(r, cv::Rect(255-ndisp, 0, 829+ndisp ,l.rows-1), cv::Scalar(0, 0, 255)); |
||||
cv::remap(l, lundist, lmapx, lmapy, cv::INTER_LINEAR); |
||||
cv::remap(r, rundist, rmapx, rmapy, cv::INTER_LINEAR); |
||||
|
||||
cv::Mat rectification = mergeRectification(lundist, rundist); |
||||
|
||||
cv::Mat correct = cv::imread(combine(datasets_repository_path, cv::format("rectification_AB_%03d.png", i))); |
||||
|
||||
if (correct.empty()) |
||||
cv::imwrite(combine(datasets_repository_path, cv::format("rectification_AB_%03d.png", i)), rectification); |
||||
else |
||||
EXPECT_MAT_NEAR(correct, rectification, 1e-10); |
||||
} |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, stereoCalibrate) |
||||
{ |
||||
const int n_images = 34; |
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY"); |
||||
|
||||
std::vector<std::vector<cv::Point2d> > leftPoints(n_images); |
||||
std::vector<std::vector<cv::Point2d> > rightPoints(n_images); |
||||
std::vector<std::vector<cv::Point3d> > objectPoints(n_images); |
||||
|
||||
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_left.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_left[cv::format("image_%d", i )] >> leftPoints[i]; |
||||
fs_left.release(); |
||||
|
||||
cv::FileStorage fs_right(combine(folder, "right.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_right.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_right[cv::format("image_%d", i )] >> rightPoints[i]; |
||||
fs_right.release(); |
||||
|
||||
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_object.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_object[cv::format("image_%d", i )] >> objectPoints[i]; |
||||
fs_object.release(); |
||||
|
||||
cv::Matx33d K1, K2, R; |
||||
cv::Vec3d T; |
||||
cv::Vec4d D1, D2; |
||||
|
||||
int flag = 0; |
||||
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC; |
||||
flag |= cv::fisheye::CALIB_CHECK_COND; |
||||
flag |= cv::fisheye::CALIB_FIX_SKEW; |
||||
// flag |= cv::fisheye::CALIB_FIX_INTRINSIC;
|
||||
|
||||
cv::fisheye::stereoCalibrate(objectPoints, leftPoints, rightPoints, |
||||
K1, D1, K2, D2, imageSize, R, T, flag, |
||||
cv::TermCriteria(3, 12, 0)); |
||||
|
||||
cv::Matx33d R_correct( 0.9975587205950972, 0.06953016383322372, 0.006492709911733523, |
||||
-0.06956823121068059, 0.9975601387249519, 0.005833595226966235, |
||||
-0.006071257768382089, -0.006271040135405457, 0.9999619062167968); |
||||
cv::Vec3d T_correct(-0.099402724724121, 0.00270812139265413, 0.00129330292472699); |
||||
cv::Matx33d K1_correct (561.195925927249, 0, 621.282400272412, |
||||
0, 562.849402029712, 380.555455380889, |
||||
0, 0, 1); |
||||
|
||||
cv::Matx33d K2_correct (560.395452535348, 0, 678.971652040359, |
||||
0, 561.90171021422, 380.401340535339, |
||||
0, 0, 1); |
||||
|
||||
cv::Vec4d D1_correct (-7.44253716539556e-05, -0.00702662033932424, 0.00737569823650885, -0.00342230256441771); |
||||
cv::Vec4d D2_correct (-0.0130785435677431, 0.0284434505383497, -0.0360333869900506, 0.0144724062347222); |
||||
|
||||
EXPECT_MAT_NEAR(R, R_correct, 1e-10); |
||||
EXPECT_MAT_NEAR(T, T_correct, 1e-10); |
||||
|
||||
EXPECT_MAT_NEAR(K1, K1_correct, 1e-10); |
||||
EXPECT_MAT_NEAR(K2, K2_correct, 1e-10); |
||||
|
||||
EXPECT_MAT_NEAR(D1, D1_correct, 1e-10); |
||||
EXPECT_MAT_NEAR(D2, D2_correct, 1e-10); |
||||
|
||||
} |
||||
|
||||
TEST_F(fisheyeTest, stereoCalibrateFixIntrinsic) |
||||
{ |
||||
const int n_images = 34; |
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY"); |
||||
|
||||
std::vector<std::vector<cv::Point2d> > leftPoints(n_images); |
||||
std::vector<std::vector<cv::Point2d> > rightPoints(n_images); |
||||
std::vector<std::vector<cv::Point3d> > objectPoints(n_images); |
||||
|
||||
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_left.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_left[cv::format("image_%d", i )] >> leftPoints[i]; |
||||
fs_left.release(); |
||||
|
||||
cv::FileStorage fs_right(combine(folder, "right.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_right.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_right[cv::format("image_%d", i )] >> rightPoints[i]; |
||||
fs_right.release(); |
||||
|
||||
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ); |
||||
CV_Assert(fs_object.isOpened()); |
||||
for(int i = 0; i < n_images; ++i) |
||||
fs_object[cv::format("image_%d", i )] >> objectPoints[i]; |
||||
fs_object.release(); |
||||
|
||||
cv::Matx33d R; |
||||
cv::Vec3d T; |
||||
|
||||
int flag = 0; |
||||
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC; |
||||
flag |= cv::fisheye::CALIB_CHECK_COND; |
||||
flag |= cv::fisheye::CALIB_FIX_SKEW; |
||||
flag |= cv::fisheye::CALIB_FIX_INTRINSIC; |
||||
|
||||
cv::Matx33d K1 (561.195925927249, 0, 621.282400272412, |
||||
0, 562.849402029712, 380.555455380889, |
||||
0, 0, 1); |
||||
|
||||
cv::Matx33d K2 (560.395452535348, 0, 678.971652040359, |
||||
0, 561.90171021422, 380.401340535339, |
||||
0, 0, 1); |
||||
|
||||
cv::Vec4d D1 (-7.44253716539556e-05, -0.00702662033932424, 0.00737569823650885, -0.00342230256441771); |
||||
cv::Vec4d D2 (-0.0130785435677431, 0.0284434505383497, -0.0360333869900506, 0.0144724062347222); |
||||
|
||||
cv::fisheye::stereoCalibrate(objectPoints, leftPoints, rightPoints, |
||||
K1, D1, K2, D2, imageSize, R, T, flag, |
||||
cv::TermCriteria(3, 12, 0)); |
||||
|
||||
cv::Matx33d R_correct( 0.9975587205950972, 0.06953016383322372, 0.006492709911733523, |
||||
-0.06956823121068059, 0.9975601387249519, 0.005833595226966235, |
||||
-0.006071257768382089, -0.006271040135405457, 0.9999619062167968); |
||||
cv::Vec3d T_correct(-0.099402724724121, 0.00270812139265413, 0.00129330292472699); |
||||
|
||||
|
||||
EXPECT_MAT_NEAR(R, R_correct, 1e-10); |
||||
EXPECT_MAT_NEAR(T, T_correct, 1e-10); |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// fisheyeTest::
|
||||
|
||||
const cv::Size fisheyeTest::imageSize(1280, 800); |
||||
|
||||
const cv::Matx33d fisheyeTest::K(558.478087865323, 0, 620.458515360843, |
||||
0, 560.506767351568, 381.939424848348, |
||||
0, 0, 1); |
||||
|
||||
const cv::Vec4d fisheyeTest::D(-0.0014613319981768, -0.00329861110580401, 0.00605760088590183, -0.00374209380722371); |
||||
|
||||
const cv::Matx33d fisheyeTest::R ( 9.9756700084424932e-01, 6.9698277640183867e-02, 1.4929569991321144e-03, |
||||
-6.9711825162322980e-02, 9.9748249845531767e-01, 1.2997180766418455e-02, |
||||
-5.8331736398316541e-04,-1.3069635393884985e-02, 9.9991441852366736e-01); |
||||
|
||||
const cv::Vec3d fisheyeTest::T(-9.9217369356044638e-02, 3.1741831972356663e-03, 1.8551007952921010e-04); |
||||
|
||||
std::string fisheyeTest::combine(const std::string& _item1, const std::string& _item2) |
||||
{ |
||||
std::string item1 = _item1, item2 = _item2; |
||||
std::replace(item1.begin(), item1.end(), '\\', '/'); |
||||
std::replace(item2.begin(), item2.end(), '\\', '/'); |
||||
|
||||
if (item1.empty()) |
||||
return item2; |
||||
|
||||
if (item2.empty()) |
||||
return item1; |
||||
|
||||
char last = item1[item1.size()-1]; |
||||
return item1 + (last != '/' ? "/" : "") + item2; |
||||
} |
||||
|
||||
cv::Mat fisheyeTest::mergeRectification(const cv::Mat& l, const cv::Mat& r) |
||||
{ |
||||
CV_Assert(l.type() == r.type() && l.size() == r.size()); |
||||
cv::Mat merged(l.rows, l.cols * 2, l.type()); |
||||
cv::Mat lpart = merged.colRange(0, l.cols); |
||||
cv::Mat rpart = merged.colRange(l.cols, merged.cols); |
||||
l.copyTo(lpart); |
||||
r.copyTo(rpart); |
||||
|
||||
for(int i = 0; i < l.rows; i+=20) |
||||
cv::line(merged, cv::Point(0, i), cv::Point(merged.cols, i), cv::Scalar(0, 255, 0)); |
||||
|
||||
return merged; |
||||
} |
@ -0,0 +1,138 @@ |
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This is a test file for the function decomposeHomography contributed to OpenCV
|
||||
// by Samson Yilma.
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2014, Samson Yilma¸ (samson_yilma@yahoo.com), all rights reserved.
|
||||
//
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp" |
||||
#include "opencv2/calib3d.hpp" |
||||
#include <vector> |
||||
|
||||
using namespace cv; |
||||
using namespace std; |
||||
|
||||
class CV_HomographyDecompTest: public cvtest::BaseTest { |
||||
|
||||
public: |
||||
CV_HomographyDecompTest() |
||||
{ |
||||
buildTestDataSet(); |
||||
} |
||||
|
||||
protected: |
||||
void run(int) |
||||
{ |
||||
vector<Mat> rotations; |
||||
vector<Mat> translations; |
||||
vector<Mat> normals; |
||||
|
||||
decomposeHomographyMat(_H, _K, rotations, translations, normals); |
||||
|
||||
//there should be at least 1 solution
|
||||
ASSERT_GT(static_cast<int>(rotations.size()), 0); |
||||
ASSERT_GT(static_cast<int>(translations.size()), 0); |
||||
ASSERT_GT(static_cast<int>(normals.size()), 0); |
||||
|
||||
ASSERT_EQ(rotations.size(), normals.size()); |
||||
ASSERT_EQ(translations.size(), normals.size()); |
||||
|
||||
ASSERT_TRUE(containsValidMotion(rotations, translations, normals)); |
||||
|
||||
decomposeHomographyMat(_H, _K, rotations, noArray(), noArray()); |
||||
ASSERT_GT(static_cast<int>(rotations.size()), 0); |
||||
} |
||||
|
||||
private: |
||||
|
||||
void buildTestDataSet() |
||||
{ |
||||
_K = Matx33d(640, 0.0, 320, |
||||
0, 640, 240, |
||||
0, 0, 1); |
||||
|
||||
_H = Matx33d(2.649157564634028, 4.583875997496426, 70.694447785121326, |
||||
-1.072756858861583, 3.533262150437228, 1513.656999614321649, |
||||
0.001303887589576, 0.003042206876298, 1.000000000000000 |
||||
); |
||||
|
||||
//expected solution for the given homography and intrinsic matrices
|
||||
_R = Matx33d(0.43307983549125, 0.545749113549648, -0.717356090899523, |
||||
-0.85630229674426, 0.497582023798831, -0.138414255706431, |
||||
0.281404038139784, 0.67421809131173, 0.682818960388909); |
||||
|
||||
_t = Vec3d(1.826751712278038, 1.264718492450820, 0.195080809998819); |
||||
_n = Vec3d(0.244875830334816, 0.480857890778889, 0.841909446789566); |
||||
} |
||||
|
||||
bool containsValidMotion(std::vector<Mat>& rotations, |
||||
std::vector<Mat>& translations, |
||||
std::vector<Mat>& normals |
||||
) |
||||
{ |
||||
double max_error = 1.0e-3; |
||||
|
||||
vector<Mat>::iterator riter = rotations.begin(); |
||||
vector<Mat>::iterator titer = translations.begin(); |
||||
vector<Mat>::iterator niter = normals.begin(); |
||||
|
||||
for (; |
||||
riter != rotations.end() && titer != translations.end() && niter != normals.end(); |
||||
++riter, ++titer, ++niter) { |
||||
|
||||
double rdist = norm(*riter, _R, NORM_INF); |
||||
double tdist = norm(*titer, _t, NORM_INF); |
||||
double ndist = norm(*niter, _n, NORM_INF); |
||||
|
||||
if ( rdist < max_error |
||||
&& tdist < max_error |
||||
&& ndist < max_error ) |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
Matx33d _R, _K, _H; |
||||
Vec3d _t, _n; |
||||
}; |
||||
|
||||
TEST(Calib3d_DecomposeHomography, regression) { CV_HomographyDecompTest test; test.safe_run(); } |
@ -0,0 +1,341 @@ |
||||
Optimization Algorithms |
||||
======================= |
||||
|
||||
.. highlight:: cpp |
||||
|
||||
The algorithms in this section minimize or maximize function value within specified constraints or without any constraints. |
||||
|
||||
solveLP |
||||
-------------------- |
||||
Solve given (non-integer) linear programming problem using the Simplex Algorithm (Simplex Method). |
||||
What we mean here by "linear programming problem" (or LP problem, for short) can be |
||||
formulated as: |
||||
|
||||
.. math:: |
||||
\mbox{Maximize } c\cdot x\\ |
||||
\mbox{Subject to:}\\ |
||||
Ax\leq b\\ |
||||
x\geq 0 |
||||
|
||||
Where :math:`c` is fixed *1*-by-*n* row-vector, :math:`A` is fixed *m*-by-*n* matrix, :math:`b` is fixed *m*-by-*1* column vector and |
||||
:math:`x` is an arbitrary *n*-by-*1* column vector, which satisfies the constraints. |
||||
|
||||
Simplex algorithm is one of many algorithms that are designed to handle this sort of problems efficiently. Although it is not optimal in theoretical |
||||
sense (there exist algorithms that can solve any problem written as above in polynomial type, while simplex method degenerates to exponential time |
||||
for some special cases), it is well-studied, easy to implement and is shown to work well for real-life purposes. |
||||
|
||||
The particular implementation is taken almost verbatim from **Introduction to Algorithms, third edition** |
||||
by T. H. Cormen, C. E. Leiserson, R. L. Rivest and Clifford Stein. In particular, the Bland's rule |
||||
(`http://en.wikipedia.org/wiki/Bland%27s\_rule <http://en.wikipedia.org/wiki/Bland%27s_rule>`_) is used to prevent cycling. |
||||
|
||||
.. ocv:function:: int solveLP(const Mat& Func, const Mat& Constr, Mat& z) |
||||
|
||||
:param Func: This row-vector corresponds to :math:`c` in the LP problem formulation (see above). It should contain 32- or 64-bit floating point numbers. As a convenience, column-vector may be also submitted, in the latter case it is understood to correspond to :math:`c^T`. |
||||
|
||||
:param Constr: *m*-by-*n\+1* matrix, whose rightmost column corresponds to :math:`b` in formulation above and the remaining to :math:`A`. It should containt 32- or 64-bit floating point numbers. |
||||
|
||||
:param z: The solution will be returned here as a column-vector - it corresponds to :math:`c` in the formulation above. It will contain 64-bit floating point numbers. |
||||
|
||||
:return: One of the return codes: |
||||
|
||||
:: |
||||
|
||||
//!the return codes for solveLP() function |
||||
enum |
||||
{ |
||||
SOLVELP_UNBOUNDED = -2, //problem is unbounded (target function can achieve arbitrary high values) |
||||
SOLVELP_UNFEASIBLE = -1, //problem is unfeasible (there are no points that satisfy all the constraints imposed) |
||||
SOLVELP_SINGLE = 0, //there is only one maximum for target function |
||||
SOLVELP_MULTI = 1 //there are multiple maxima for target function - the arbitrary one is returned |
||||
}; |
||||
|
||||
DownhillSolver |
||||
--------------------------------- |
||||
|
||||
.. ocv:class:: DownhillSolver |
||||
|
||||
This class is used to perform the non-linear non-constrained *minimization* of a function, defined on an *n*-dimensional Euclidean space, |
||||
using the **Nelder-Mead method**, also known as **downhill simplex method**. The basic idea about the method can be obtained from |
||||
(`http://en.wikipedia.org/wiki/Nelder-Mead\_method <http://en.wikipedia.org/wiki/Nelder-Mead_method>`_). It should be noted, that |
||||
this method, although deterministic, is rather a heuristic and therefore may converge to a local minima, not necessary a global one. |
||||
It is iterative optimization technique, which at each step uses an information about the values of a function evaluated only at |
||||
*n+1* points, arranged as a *simplex* in *n*-dimensional space (hence the second name of the method). At each step new point is |
||||
chosen to evaluate function at, obtained value is compared with previous ones and based on this information simplex changes it's shape |
||||
, slowly moving to the local minimum. Thus this method is using *only* function values to make decision, on contrary to, say, Nonlinear |
||||
Conjugate Gradient method (which is also implemented in ``optim``). |
||||
|
||||
Algorithm stops when the number of function evaluations done exceeds ``termcrit.maxCount``, when the function values at the |
||||
vertices of simplex are within ``termcrit.epsilon`` range or simplex becomes so small that it |
||||
can enclosed in a box with ``termcrit.epsilon`` sides, whatever comes first, for some defined by user |
||||
positive integer ``termcrit.maxCount`` and positive non-integer ``termcrit.epsilon``. |
||||
|
||||
:: |
||||
|
||||
class CV_EXPORTS Solver : public Algorithm |
||||
{ |
||||
public: |
||||
class CV_EXPORTS Function |
||||
{ |
||||
public: |
||||
virtual ~Function() {} |
||||
virtual double calc(const double* x) const = 0; |
||||
virtual void getGradient(const double* /*x*/,double* /*grad*/) {} |
||||
}; |
||||
|
||||
virtual Ptr<Function> getFunction() const = 0; |
||||
virtual void setFunction(const Ptr<Function>& f) = 0; |
||||
|
||||
virtual TermCriteria getTermCriteria() const = 0; |
||||
virtual void setTermCriteria(const TermCriteria& termcrit) = 0; |
||||
|
||||
// x contain the initial point before the call and the minima position (if algorithm converged) after. x is assumed to be (something that |
||||
// after getMat() will return) row-vector or column-vector. *It's size and should |
||||
// be consisted with previous dimensionality data given, if any (otherwise, it determines dimensionality)* |
||||
virtual double minimize(InputOutputArray x) = 0; |
||||
}; |
||||
|
||||
class CV_EXPORTS DownhillSolver : public Solver |
||||
{ |
||||
public: |
||||
//! returns row-vector, even if the column-vector was given |
||||
virtual void getInitStep(OutputArray step) const=0; |
||||
//!This should be called at least once before the first call to minimize() and step is assumed to be (something that |
||||
//! after getMat() will return) row-vector or column-vector. *It's dimensionality determines the dimensionality of a problem.* |
||||
virtual void setInitStep(InputArray step)=0; |
||||
}; |
||||
|
||||
It should be noted, that ``DownhillSolver`` is a derivative of the abstract interface ``Solver``, which in |
||||
turn is derived from the ``Algorithm`` interface and is used to encapsulate the functionality, common to all non-linear optimization |
||||
algorithms in the ``optim`` module. |
||||
|
||||
DownhillSolver::getFunction |
||||
-------------------------------------------- |
||||
|
||||
Getter for the optimized function. The optimized function is represented by ``Solver::Function`` interface, which requires |
||||
derivatives to implement the sole method ``calc(double*)`` to evaluate the function. |
||||
|
||||
.. ocv:function:: Ptr<Solver::Function> DownhillSolver::getFunction() |
||||
|
||||
:return: Smart-pointer to an object that implements ``Solver::Function`` interface - it represents the function that is being optimized. It can be empty, if no function was given so far. |
||||
|
||||
DownhillSolver::setFunction |
||||
----------------------------------------------- |
||||
|
||||
Setter for the optimized function. *It should be called at least once before the call to* ``DownhillSolver::minimize()``, as |
||||
default value is not usable. |
||||
|
||||
.. ocv:function:: void DownhillSolver::setFunction(const Ptr<Solver::Function>& f) |
||||
|
||||
:param f: The new function to optimize. |
||||
|
||||
DownhillSolver::getTermCriteria |
||||
---------------------------------------------------- |
||||
|
||||
Getter for the previously set terminal criteria for this algorithm. |
||||
|
||||
.. ocv:function:: TermCriteria DownhillSolver::getTermCriteria() |
||||
|
||||
:return: Deep copy of the terminal criteria used at the moment. |
||||
|
||||
DownhillSolver::setTermCriteria |
||||
------------------------------------------ |
||||
|
||||
Set terminal criteria for downhill simplex method. Two things should be noted. First, this method *is not necessary* to be called |
||||
before the first call to ``DownhillSolver::minimize()``, as the default value is sensible. Second, the method will raise an error |
||||
if ``termcrit.type!=(TermCriteria::MAX_ITER+TermCriteria::EPS)``, ``termcrit.epsilon<=0`` or ``termcrit.maxCount<=0``. That is, |
||||
both ``epsilon`` and ``maxCount`` should be set to positive values (non-integer and integer respectively) and they represent |
||||
tolerance and maximal number of function evaluations that is allowed. |
||||
|
||||
Algorithm stops when the number of function evaluations done exceeds ``termcrit.maxCount``, when the function values at the |
||||
vertices of simplex are within ``termcrit.epsilon`` range or simplex becomes so small that it |
||||
can enclosed in a box with ``termcrit.epsilon`` sides, whatever comes first. |
||||
|
||||
.. ocv:function:: void DownhillSolver::setTermCriteria(const TermCriteria& termcrit) |
||||
|
||||
:param termcrit: Terminal criteria to be used, represented as ``TermCriteria`` structure (defined elsewhere in openCV). Mind you, that it should meet ``(termcrit.type==(TermCriteria::MAX_ITER+TermCriteria::EPS) && termcrit.epsilon>0 && termcrit.maxCount>0)``, otherwise the error will be raised. |
||||
|
||||
DownhillSolver::getInitStep |
||||
----------------------------------- |
||||
|
||||
Returns the initial step that will be used in downhill simplex algorithm. See the description |
||||
of corresponding setter (follows next) for the meaning of this parameter. |
||||
|
||||
.. ocv:function:: void getInitStep(OutputArray step) |
||||
|
||||
:param step: Initial step that will be used in algorithm. Note, that although corresponding setter accepts column-vectors as well as row-vectors, this method will return a row-vector. |
||||
|
||||
DownhillSolver::setInitStep |
||||
---------------------------------- |
||||
|
||||
Sets the initial step that will be used in downhill simplex algorithm. Step, together with initial point (givin in ``DownhillSolver::minimize``) |
||||
are two *n*-dimensional vectors that are used to determine the shape of initial simplex. Roughly said, initial point determines the position |
||||
of a simplex (it will become simplex's centroid), while step determines the spread (size in each dimension) of a simplex. To be more precise, |
||||
if :math:`s,x_0\in\mathbb{R}^n` are the initial step and initial point respectively, the vertices of a simplex will be: :math:`v_0:=x_0-\frac{1}{2} |
||||
s` and :math:`v_i:=x_0+s_i` for :math:`i=1,2,\dots,n` where :math:`s_i` denotes projections of the initial step of *n*-th coordinate (the result |
||||
of projection is treated to be vector given by :math:`s_i:=e_i\cdot\left<e_i\cdot s\right>`, where :math:`e_i` form canonical basis) |
||||
|
||||
.. ocv:function:: void setInitStep(InputArray step) |
||||
|
||||
:param step: Initial step that will be used in algorithm. Roughly said, it determines the spread (size in each dimension) of an initial simplex. |
||||
|
||||
DownhillSolver::minimize |
||||
----------------------------------- |
||||
|
||||
The main method of the ``DownhillSolver``. It actually runs the algorithm and performs the minimization. The sole input parameter determines the |
||||
centroid of the starting simplex (roughly, it tells where to start), all the others (terminal criteria, initial step, function to be minimized) |
||||
are supposed to be set via the setters before the call to this method or the default values (not always sensible) will be used. |
||||
|
||||
.. ocv:function:: double DownhillSolver::minimize(InputOutputArray x) |
||||
|
||||
:param x: The initial point, that will become a centroid of an initial simplex. After the algorithm will terminate, it will be setted to the point where the algorithm stops, the point of possible minimum. |
||||
|
||||
:return: The value of a function at the point found. |
||||
|
||||
createDownhillSolver |
||||
------------------------------------ |
||||
|
||||
This function returns the reference to the ready-to-use ``DownhillSolver`` object. All the parameters are optional, so this procedure can be called |
||||
even without parameters at all. In this case, the default values will be used. As default value for terminal criteria are the only sensible ones, |
||||
``DownhillSolver::setFunction()`` and ``DownhillSolver::setInitStep()`` should be called upon the obtained object, if the respective parameters |
||||
were not given to ``createDownhillSolver()``. Otherwise, the two ways (give parameters to ``createDownhillSolver()`` or miss them out and call the |
||||
``DownhillSolver::setFunction()`` and ``DownhillSolver::setInitStep()``) are absolutely equivalent (and will drop the same errors in the same way, |
||||
should invalid input be detected). |
||||
|
||||
.. ocv:function:: Ptr<DownhillSolver> createDownhillSolver(const Ptr<Solver::Function>& f,InputArray initStep, TermCriteria termcrit) |
||||
|
||||
:param f: Pointer to the function that will be minimized, similarly to the one you submit via ``DownhillSolver::setFunction``. |
||||
:param step: Initial step, that will be used to construct the initial simplex, similarly to the one you submit via ``DownhillSolver::setInitStep``. |
||||
:param termcrit: Terminal criteria to the algorithm, similarly to the one you submit via ``DownhillSolver::setTermCriteria``. |
||||
|
||||
|
||||
ConjGradSolver |
||||
--------------------------------- |
||||
|
||||
.. ocv:class:: ConjGradSolver |
||||
|
||||
This class is used to perform the non-linear non-constrained *minimization* of a function with *known gradient* |
||||
, defined on an *n*-dimensional Euclidean space, |
||||
using the **Nonlinear Conjugate Gradient method**. The implementation was done based on the beautifully clear explanatory article `An Introduction to the Conjugate Gradient Method Without the Agonizing Pain <http://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf>`_ |
||||
by Jonathan Richard Shewchuk. The method can be seen as an adaptation of a standard Conjugate Gradient method (see, for example |
||||
`http://en.wikipedia.org/wiki/Conjugate_gradient_method <http://en.wikipedia.org/wiki/Conjugate_gradient_method>`_) for numerically solving the |
||||
systems of linear equations. |
||||
|
||||
It should be noted, that |
||||
this method, although deterministic, is rather a heuristic method and therefore may converge to a local minima, not necessary a global one. What |
||||
is even more disastrous, most of its behaviour is ruled by gradient, therefore it essentially cannot distinguish between local minima and maxima. |
||||
Therefore, if it starts sufficiently near to the local maximum, it may converge to it. Another obvious restriction is that it should be possible |
||||
to compute the gradient of a function at any point, thus it is preferable to have analytic expression for gradient and computational burden |
||||
should be born by the user. |
||||
|
||||
The latter responsibility is accompilished via the ``getGradient(const double* x,double* grad)`` method of a |
||||
``Solver::Function`` interface (which represents function that is being optimized). This method takes point a point in *n*-dimensional space |
||||
(first argument represents the array of coordinates of that point) and comput its gradient (it should be stored in the second argument as an array). |
||||
|
||||
:: |
||||
|
||||
class CV_EXPORTS Solver : public Algorithm |
||||
{ |
||||
public: |
||||
class CV_EXPORTS Function |
||||
{ |
||||
public: |
||||
virtual ~Function() {} |
||||
virtual double calc(const double* x) const = 0; |
||||
virtual void getGradient(const double* /*x*/,double* /*grad*/) {} |
||||
}; |
||||
|
||||
virtual Ptr<Function> getFunction() const = 0; |
||||
virtual void setFunction(const Ptr<Function>& f) = 0; |
||||
|
||||
virtual TermCriteria getTermCriteria() const = 0; |
||||
virtual void setTermCriteria(const TermCriteria& termcrit) = 0; |
||||
|
||||
// x contain the initial point before the call and the minima position (if algorithm converged) after. x is assumed to be (something that |
||||
// after getMat() will return) row-vector or column-vector. *It's size and should |
||||
// be consisted with previous dimensionality data given, if any (otherwise, it determines dimensionality)* |
||||
virtual double minimize(InputOutputArray x) = 0; |
||||
}; |
||||
|
||||
class CV_EXPORTS ConjGradSolver : public Solver{ |
||||
}; |
||||
|
||||
Note, that class ``ConjGradSolver`` thus does not add any new methods to the basic ``Solver`` interface. |
||||
|
||||
ConjGradSolver::getFunction |
||||
-------------------------------------------- |
||||
|
||||
Getter for the optimized function. The optimized function is represented by ``Solver::Function`` interface, which requires |
||||
derivatives to implement the method ``calc(double*)`` to evaluate the function. It should be emphasized once more, that since Nonlinear |
||||
Conjugate Gradient method requires gradient to be computable in addition to the function values, |
||||
``getGradient(const double* x,double* grad)`` method of a ``Solver::Function`` interface should be also implemented meaningfully. |
||||
|
||||
.. ocv:function:: Ptr<Solver::Function> ConjGradSolver::getFunction() |
||||
|
||||
:return: Smart-pointer to an object that implements ``Solver::Function`` interface - it represents the function that is being optimized. It can be empty, if no function was given so far. |
||||
|
||||
ConjGradSolver::setFunction |
||||
----------------------------------------------- |
||||
|
||||
Setter for the optimized function. *It should be called at least once before the call to* ``ConjGradSolver::minimize()``, as |
||||
default value is not usable. |
||||
|
||||
.. ocv:function:: void ConjGradSolver::setFunction(const Ptr<Solver::Function>& f) |
||||
|
||||
:param f: The new function to optimize. |
||||
|
||||
ConjGradSolver::getTermCriteria |
||||
---------------------------------------------------- |
||||
|
||||
Getter for the previously set terminal criteria for this algorithm. |
||||
|
||||
.. ocv:function:: TermCriteria ConjGradSolver::getTermCriteria() |
||||
|
||||
:return: Deep copy of the terminal criteria used at the moment. |
||||
|
||||
ConjGradSolver::setTermCriteria |
||||
------------------------------------------ |
||||
|
||||
Set terminal criteria for downhill simplex method. Two things should be noted. First, this method *is not necessary* to be called |
||||
before the first call to ``ConjGradSolver::minimize()``, as the default value is sensible. Second, the method will raise an error |
||||
if ``termcrit.type!=(TermCriteria::MAX_ITER+TermCriteria::EPS)`` and ``termcrit.type!=TermCriteria::MAX_ITER``. This means that termination criteria |
||||
has to restrict maximum number of iterations to be done and may optionally allow algorithm to stop earlier if certain tolerance |
||||
is achieved (what we mean by "tolerance is achieved" will be clarified below). If ``termcrit`` restricts both tolerance and maximum iteration |
||||
number, both ``termcrit.epsilon`` and ``termcrit.maxCount`` should be positive. In case, if ``termcrit.type==TermCriteria::MAX_ITER``, |
||||
only member ``termcrit.maxCount`` is required to be positive and in this case algorithm will just work for required number of iterations. |
||||
|
||||
In current implementation, "tolerance is achieved" means that we have arrived at the point where the :math:`L_2`-norm of the gradient is less |
||||
than the tolerance value. |
||||
|
||||
.. ocv:function:: void ConjGradSolver::setTermCriteria(const TermCriteria& termcrit) |
||||
|
||||
:param termcrit: Terminal criteria to be used, represented as ``TermCriteria`` structure (defined elsewhere in openCV). Mind you, that it should meet ``termcrit.type==(TermCriteria::MAX_ITER+TermCriteria::EPS) && termcrit.epsilon>0 && termcrit.maxCount>0`` or ``termcrit.type==TermCriteria::MAX_ITER) && termcrit.maxCount>0``, otherwise the error will be raised. |
||||
|
||||
ConjGradSolver::minimize |
||||
----------------------------------- |
||||
|
||||
The main method of the ``ConjGradSolver``. It actually runs the algorithm and performs the minimization. The sole input parameter determines the |
||||
centroid of the starting simplex (roughly, it tells where to start), all the others (terminal criteria and function to be minimized) |
||||
are supposed to be set via the setters before the call to this method or the default values (not always sensible) will be used. Sometimes it may |
||||
throw an error, if these default values cannot be used (say, you forgot to set the function to minimize and default value, that is, empty function, |
||||
cannot be used). |
||||
|
||||
.. ocv:function:: double ConjGradSolver::minimize(InputOutputArray x) |
||||
|
||||
:param x: The initial point. It is hard to overemphasize how important the choise of initial point is when you are using the heuristic algorithm like this one. Badly chosen initial point can make algorithm converge to (local) maximum instead of minimum, do not converge at all, converge to local minimum instead of global one. |
||||
|
||||
:return: The value of a function at the point found. |
||||
|
||||
createConjGradSolver |
||||
------------------------------------ |
||||
|
||||
This function returns the reference to the ready-to-use ``ConjGradSolver`` object. All the parameters are optional, so this procedure can be called |
||||
even without parameters at all. In this case, the default values will be used. As default value for terminal criteria are the only sensible ones, |
||||
``ConjGradSolver::setFunction()`` should be called upon the obtained object, if the function |
||||
was not given to ``createConjGradSolver()``. Otherwise, the two ways (submit it to ``createConjGradSolver()`` or miss it out and call the |
||||
``ConjGradSolver::setFunction()``) are absolutely equivalent (and will drop the same errors in the same way, |
||||
should invalid input be detected). |
||||
|
||||
.. ocv:function:: Ptr<ConjGradSolver> createConjGradSolver(const Ptr<Solver::Function>& f, TermCriteria termcrit) |
||||
|
||||
:param f: Pointer to the function that will be minimized, similarly to the one you submit via ``ConjGradSolver::setFunction``. |
||||
:param termcrit: Terminal criteria to the algorithm, similarly to the one you submit via ``ConjGradSolver::setTermCriteria``. |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue