attempt screw recognition
This commit is contained in:
57
opencv-apps/CMakeLists.txt
Normal file
57
opencv-apps/CMakeLists.txt
Normal file
@ -0,0 +1,57 @@
|
||||
add_definitions(-D__OPENCV_BUILD=1)
|
||||
add_definitions(-D__OPENCV_APPS=1)
|
||||
|
||||
# Unified function for creating OpenCV applications:
|
||||
# ocv_add_application(tgt [MODULES <m1> [<m2> ...]] SRCS <src1> [<src2> ...])
|
||||
function(ocv_add_application the_target)
|
||||
cmake_parse_arguments(APP "" "" "MODULES;SRCS" ${ARGN})
|
||||
ocv_check_dependencies(${APP_MODULES})
|
||||
if(NOT OCV_DEPENDENCIES_FOUND)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(${the_target})
|
||||
ocv_target_include_modules_recurse(${the_target} ${APP_MODULES})
|
||||
ocv_target_include_directories(${the_target} PRIVATE "${OpenCV_SOURCE_DIR}/include/opencv")
|
||||
ocv_add_executable(${the_target} ${APP_SRCS})
|
||||
ocv_target_link_libraries(${the_target} ${APP_MODULES})
|
||||
set_target_properties(${the_target} PROPERTIES
|
||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
|
||||
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
|
||||
OUTPUT_NAME "${the_target}")
|
||||
|
||||
if(ENABLE_SOLUTION_FOLDERS)
|
||||
set_target_properties(${the_target} PROPERTIES FOLDER "applications")
|
||||
endif()
|
||||
|
||||
if(INSTALL_CREATE_DISTRIB)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev)
|
||||
endif()
|
||||
else()
|
||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
link_libraries(${OPENCV_LINKER_LIBS})
|
||||
|
||||
macro(ocv_add_app directory)
|
||||
if(DEFINED BUILD_APPS_LIST)
|
||||
list(FIND BUILD_APPS_LIST ${directory} _index)
|
||||
if (${_index} GREATER -1)
|
||||
add_subdirectory(${directory})
|
||||
else()
|
||||
message(STATUS "Skip OpenCV app: ${directory}")
|
||||
endif()
|
||||
else()
|
||||
add_subdirectory(${directory})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
#ocv_add_app(traincascade)
|
||||
#ocv_add_app(createsamples)
|
||||
ocv_add_app(annotation)
|
||||
ocv_add_app(visualisation)
|
||||
ocv_add_app(interactive-calibration)
|
||||
ocv_add_app(version)
|
3
opencv-apps/annotation/CMakeLists.txt
Normal file
3
opencv-apps/annotation/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
ocv_add_application(opencv_annotation
|
||||
MODULES opencv_core opencv_highgui opencv_imgproc opencv_imgcodecs opencv_videoio
|
||||
SRCS opencv_annotation.cpp)
|
317
opencv-apps/annotation/opencv_annotation.cpp
Normal file
317
opencv-apps/annotation/opencv_annotation.cpp
Normal file
@ -0,0 +1,317 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*****************************************************************************************************
|
||||
USAGE:
|
||||
./opencv_annotation -images <folder location> -annotations <output file>
|
||||
|
||||
Created by: Puttemans Steven - February 2015
|
||||
Adapted by: Puttemans Steven - April 2016 - Vectorize the process to enable better processing
|
||||
+ early leave and store by pressing an ESC key
|
||||
+ enable delete `d` button, to remove last annotation
|
||||
*****************************************************************************************************/
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
// Function prototypes
|
||||
void on_mouse(int, int, int, int, void*);
|
||||
vector<Rect> get_annotations(Mat);
|
||||
|
||||
// Public parameters
|
||||
Mat image;
|
||||
int roi_x0 = 0, roi_y0 = 0, roi_x1 = 0, roi_y1 = 0, num_of_rec = 0;
|
||||
bool start_draw = false, stop = false;
|
||||
|
||||
// Window name for visualisation purposes
|
||||
const string window_name = "OpenCV Based Annotation Tool";
|
||||
|
||||
// FUNCTION : Mouse response for selecting objects in images
|
||||
// If left button is clicked, start drawing a rectangle as long as mouse moves
|
||||
// Stop drawing once a new left click is detected by the on_mouse function
|
||||
void on_mouse(int event, int x, int y, int , void * )
|
||||
{
|
||||
// Action when left button is clicked
|
||||
if(event == EVENT_LBUTTONDOWN)
|
||||
{
|
||||
if(!start_draw)
|
||||
{
|
||||
roi_x0 = x;
|
||||
roi_y0 = y;
|
||||
start_draw = true;
|
||||
} else {
|
||||
roi_x1 = x;
|
||||
roi_y1 = y;
|
||||
start_draw = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Action when mouse is moving and drawing is enabled
|
||||
if((event == EVENT_MOUSEMOVE) && start_draw)
|
||||
{
|
||||
// Redraw bounding box for annotation
|
||||
Mat current_view;
|
||||
image.copyTo(current_view);
|
||||
rectangle(current_view, Point(roi_x0,roi_y0), Point(x,y), Scalar(0,0,255));
|
||||
imshow(window_name, current_view);
|
||||
}
|
||||
}
|
||||
|
||||
// FUNCTION : returns a vector of Rect objects given an image containing positive object instances
|
||||
vector<Rect> get_annotations(Mat input_image)
|
||||
{
|
||||
vector<Rect> current_annotations;
|
||||
|
||||
// Make it possible to exit the annotation process
|
||||
stop = false;
|
||||
|
||||
// Init window interface and couple mouse actions
|
||||
namedWindow(window_name, WINDOW_AUTOSIZE);
|
||||
setMouseCallback(window_name, on_mouse);
|
||||
|
||||
image = input_image;
|
||||
imshow(window_name, image);
|
||||
int key_pressed = 0;
|
||||
|
||||
do
|
||||
{
|
||||
// Get a temporary image clone
|
||||
Mat temp_image = input_image.clone();
|
||||
Rect currentRect(0, 0, 0, 0);
|
||||
|
||||
// Keys for processing
|
||||
// You need to select one for confirming a selection and one to continue to the next image
|
||||
// Based on the universal ASCII code of the keystroke: http://www.asciitable.com/
|
||||
// c = 99 add rectangle to current image
|
||||
// n = 110 save added rectangles and show next image
|
||||
// d = 100 delete the last annotation made
|
||||
// <ESC> = 27 exit program
|
||||
key_pressed = 0xFF & waitKey(0);
|
||||
switch( key_pressed )
|
||||
{
|
||||
case 27:
|
||||
stop = true;
|
||||
break;
|
||||
case 99:
|
||||
// Draw initiated from top left corner
|
||||
if(roi_x0<roi_x1 && roi_y0<roi_y1)
|
||||
{
|
||||
currentRect.x = roi_x0;
|
||||
currentRect.y = roi_y0;
|
||||
currentRect.width = roi_x1-roi_x0;
|
||||
currentRect.height = roi_y1-roi_y0;
|
||||
}
|
||||
// Draw initiated from bottom right corner
|
||||
if(roi_x0>roi_x1 && roi_y0>roi_y1)
|
||||
{
|
||||
currentRect.x = roi_x1;
|
||||
currentRect.y = roi_y1;
|
||||
currentRect.width = roi_x0-roi_x1;
|
||||
currentRect.height = roi_y0-roi_y1;
|
||||
}
|
||||
// Draw initiated from top right corner
|
||||
if(roi_x0>roi_x1 && roi_y0<roi_y1)
|
||||
{
|
||||
currentRect.x = roi_x1;
|
||||
currentRect.y = roi_y0;
|
||||
currentRect.width = roi_x0-roi_x1;
|
||||
currentRect.height = roi_y1-roi_y0;
|
||||
}
|
||||
// Draw initiated from bottom left corner
|
||||
if(roi_x0<roi_x1 && roi_y0>roi_y1)
|
||||
{
|
||||
currentRect.x = roi_x0;
|
||||
currentRect.y = roi_y1;
|
||||
currentRect.width = roi_x1-roi_x0;
|
||||
currentRect.height = roi_y0-roi_y1;
|
||||
}
|
||||
// Draw the rectangle on the canvas
|
||||
// Add the rectangle to the vector of annotations
|
||||
current_annotations.push_back(currentRect);
|
||||
break;
|
||||
case 100:
|
||||
// Remove the last annotation
|
||||
if(current_annotations.size() > 0){
|
||||
current_annotations.pop_back();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Default case --> do nothing at all
|
||||
// Other keystrokes can simply be ignored
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if escape has been pressed
|
||||
if(stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Draw all the current rectangles onto the top image and make sure that the global image is linked
|
||||
for(int i=0; i < (int)current_annotations.size(); i++){
|
||||
rectangle(temp_image, current_annotations[i], Scalar(0,255,0), 1);
|
||||
}
|
||||
image = temp_image;
|
||||
|
||||
// Force an explicit redraw of the canvas --> necessary to visualize delete correctly
|
||||
imshow(window_name, image);
|
||||
}
|
||||
// Continue as long as the next image key has not been pressed
|
||||
while(key_pressed != 110);
|
||||
|
||||
// Close down the window
|
||||
destroyWindow(window_name);
|
||||
|
||||
// Return the data
|
||||
return current_annotations;
|
||||
}
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
// Use the cmdlineparser to process input arguments
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | show this message }"
|
||||
"{ images i | | (required) path to image folder [example - /data/testimages/] }"
|
||||
"{ annotations a | | (required) path to annotations txt file [example - /data/annotations.txt] }"
|
||||
"{ maxWindowHeight m | -1 | (optional) images larger in height than this value will be scaled down }"
|
||||
"{ resizeFactor r | 2 | (optional) factor for scaling down [default = half the size] }"
|
||||
);
|
||||
// Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return 0;
|
||||
}
|
||||
string image_folder(parser.get<string>("images"));
|
||||
string annotations_file(parser.get<string>("annotations"));
|
||||
if (image_folder.empty() || annotations_file.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int resizeFactor = parser.get<int>("resizeFactor");
|
||||
int const maxWindowHeight = parser.get<int>("maxWindowHeight") > 0 ? parser.get<int>("maxWindowHeight") : -1;
|
||||
|
||||
// Start by processing the data
|
||||
// Return the image filenames inside the image folder
|
||||
map< String, vector<Rect> > annotations;
|
||||
vector<String> filenames;
|
||||
String folder(image_folder);
|
||||
glob(folder, filenames);
|
||||
|
||||
// Add key tips on how to use the software when running it
|
||||
cout << "* mark rectangles with the left mouse button," << endl;
|
||||
cout << "* press 'c' to accept a selection," << endl;
|
||||
cout << "* press 'd' to delete the latest selection," << endl;
|
||||
cout << "* press 'n' to proceed with next image," << endl;
|
||||
cout << "* press 'esc' to stop." << endl;
|
||||
|
||||
// Loop through each image stored in the images folder
|
||||
// Create and temporarily store the annotations
|
||||
// At the end write everything to the annotations file
|
||||
for (size_t i = 0; i < filenames.size(); i++){
|
||||
// Read in an image
|
||||
Mat current_image = imread(filenames[i]);
|
||||
bool const resize_bool = (maxWindowHeight > 0) && (current_image.rows > maxWindowHeight);
|
||||
|
||||
// Check if the image is actually read - avoid other files in the folder, because glob() takes them all
|
||||
// If not then simply skip this iteration
|
||||
if(current_image.empty()){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(resize_bool){
|
||||
resize(current_image, current_image, Size(current_image.cols/resizeFactor, current_image.rows/resizeFactor), 0, 0, INTER_LINEAR_EXACT);
|
||||
}
|
||||
|
||||
// Perform annotations & store the result inside the vectorized structure
|
||||
// If the image was resized before, then resize the found annotations back to original dimensions
|
||||
vector<Rect> current_annotations = get_annotations(current_image);
|
||||
if(resize_bool){
|
||||
for(int j =0; j < (int)current_annotations.size(); j++){
|
||||
current_annotations[j].x = current_annotations[j].x * resizeFactor;
|
||||
current_annotations[j].y = current_annotations[j].y * resizeFactor;
|
||||
current_annotations[j].width = current_annotations[j].width * resizeFactor;
|
||||
current_annotations[j].height = current_annotations[j].height * resizeFactor;
|
||||
}
|
||||
}
|
||||
annotations[filenames[i]] = current_annotations;
|
||||
|
||||
// Check if the ESC key was hit, then exit earlier then expected
|
||||
if(stop){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// When all data is processed, store the data gathered inside the proper file
|
||||
// This now even gets called when the ESC button was hit to store preliminary results
|
||||
ofstream output(annotations_file.c_str());
|
||||
if ( !output.is_open() ){
|
||||
cerr << "The path for the output file contains an error and could not be opened. Please check again!" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Store the annotations, write to the output file
|
||||
for(map<String, vector<Rect> >::iterator it = annotations.begin(); it != annotations.end(); it++){
|
||||
vector<Rect> &anno = it->second;
|
||||
output << it->first << " " << anno.size();
|
||||
for(size_t j=0; j < anno.size(); j++){
|
||||
Rect temp = anno[j];
|
||||
output << " " << temp.x << " " << temp.y << " " << temp.width << " " << temp.height;
|
||||
}
|
||||
output << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
65
opencv-apps/build/CMakeCache.txt
Normal file
65
opencv-apps/build/CMakeCache.txt
Normal file
@ -0,0 +1,65 @@
|
||||
# This is the CMakeCache file.
|
||||
# For build in directory: /home/cole/item-sort/opencv-apps/build
|
||||
# It was generated by CMake: /usr/bin/cmake
|
||||
# You can edit this file to change values found and used by cmake.
|
||||
# If you do not want to change any of the values, simply exit the editor.
|
||||
# If you do want to change a value, simply edit, save, and exit the editor.
|
||||
# The syntax for the file is as follows:
|
||||
# KEY:TYPE=VALUE
|
||||
# KEY is the name of a variable in the cache.
|
||||
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
|
||||
# VALUE is the current value for the KEY.
|
||||
|
||||
########################
|
||||
# EXTERNAL cache entries
|
||||
########################
|
||||
|
||||
//For backwards compatibility, what version of CMake commands and
|
||||
// syntax should this version of CMake try to support.
|
||||
CMAKE_BACKWARDS_COMPATIBILITY:STRING=2.4
|
||||
|
||||
//Single output directory for building all executables.
|
||||
EXECUTABLE_OUTPUT_PATH:PATH=
|
||||
|
||||
//Single output directory for building all libraries.
|
||||
LIBRARY_OUTPUT_PATH:PATH=
|
||||
|
||||
|
||||
########################
|
||||
# INTERNAL cache entries
|
||||
########################
|
||||
|
||||
//This is the directory where this CMakeCache.txt was created
|
||||
CMAKE_CACHEFILE_DIR:INTERNAL=/home/cole/item-sort/opencv-apps/build
|
||||
//Major version of cmake used to create the current loaded cache
|
||||
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
|
||||
//Minor version of cmake used to create the current loaded cache
|
||||
CMAKE_CACHE_MINOR_VERSION:INTERNAL=16
|
||||
//Patch version of cmake used to create the current loaded cache
|
||||
CMAKE_CACHE_PATCH_VERSION:INTERNAL=0
|
||||
//Path to CMake executable.
|
||||
CMAKE_COMMAND:INTERNAL=/usr/bin/cmake
|
||||
//Path to cpack program executable.
|
||||
CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack
|
||||
//Path to ctest program executable.
|
||||
CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest
|
||||
//Path to cache edit program executable.
|
||||
CMAKE_EDIT_COMMAND:INTERNAL=/usr/bin/ccmake
|
||||
//Name of external makefile project generator.
|
||||
CMAKE_EXTRA_GENERATOR:INTERNAL=
|
||||
//Name of generator.
|
||||
CMAKE_GENERATOR:INTERNAL=Unix Makefiles
|
||||
//Generator instance identifier.
|
||||
CMAKE_GENERATOR_INSTANCE:INTERNAL=
|
||||
//Name of generator platform.
|
||||
CMAKE_GENERATOR_PLATFORM:INTERNAL=
|
||||
//Name of generator toolset.
|
||||
CMAKE_GENERATOR_TOOLSET:INTERNAL=
|
||||
//Source directory with the top level CMakeLists.txt file for this
|
||||
// project
|
||||
CMAKE_HOME_DIRECTORY:INTERNAL=/home/cole/item-sort/opencv-apps
|
||||
//number of local generators
|
||||
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=2
|
||||
//Path to CMake installation.
|
||||
CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.16
|
||||
|
1
opencv-apps/build/CMakeFiles/cmake.check_cache
Normal file
1
opencv-apps/build/CMakeFiles/cmake.check_cache
Normal file
@ -0,0 +1 @@
|
||||
# This file is generated by cmake for dependency checking of the CMakeCache.txt file
|
4
opencv-apps/createsamples/CMakeLists.txt
Normal file
4
opencv-apps/createsamples/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
file(GLOB SRCS *.cpp)
|
||||
ocv_add_application(opencv_createsamples
|
||||
MODULES opencv_core opencv_imgproc opencv_objdetect opencv_imgcodecs opencv_highgui opencv_calib3d opencv_features2d opencv_videoio
|
||||
SRCS ${SRCS})
|
258
opencv-apps/createsamples/createsamples.cpp
Normal file
258
opencv-apps/createsamples/createsamples.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
/*
|
||||
* createsamples.cpp
|
||||
*
|
||||
* Create test/training samples
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "utility.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
int i = 0;
|
||||
char* nullname = (char*)"(NULL)";
|
||||
char* vecname = NULL; /* .vec file name */
|
||||
char* infoname = NULL; /* file name with marked up image descriptions */
|
||||
char* imagename = NULL; /* single sample image */
|
||||
char* bgfilename = NULL; /* background */
|
||||
int num = 1000;
|
||||
int bgcolor = 0;
|
||||
int bgthreshold = 80;
|
||||
int invert = 0;
|
||||
int maxintensitydev = 40;
|
||||
double maxxangle = 1.1;
|
||||
double maxyangle = 1.1;
|
||||
double maxzangle = 0.5;
|
||||
int showsamples = 0;
|
||||
/* the samples are adjusted to this scale in the sample preview window */
|
||||
double scale = 4.0;
|
||||
int width = 24;
|
||||
int height = 24;
|
||||
double maxscale = -1.0;
|
||||
int rngseed = 12345;
|
||||
|
||||
if( argc == 1 )
|
||||
{
|
||||
printf( "Usage: %s\n [-info <collection_file_name>]\n"
|
||||
" [-img <image_file_name>]\n"
|
||||
" [-vec <vec_file_name>]\n"
|
||||
" [-bg <background_file_name>]\n [-num <number_of_samples = %d>]\n"
|
||||
" [-bgcolor <background_color = %d>]\n"
|
||||
" [-inv] [-randinv] [-bgthresh <background_color_threshold = %d>]\n"
|
||||
" [-maxidev <max_intensity_deviation = %d>]\n"
|
||||
" [-maxxangle <max_x_rotation_angle = %f>]\n"
|
||||
" [-maxyangle <max_y_rotation_angle = %f>]\n"
|
||||
" [-maxzangle <max_z_rotation_angle = %f>]\n"
|
||||
" [-show [<scale = %f>]]\n"
|
||||
" [-w <sample_width = %d>]\n [-h <sample_height = %d>]\n"
|
||||
" [-maxscale <max sample scale = %f>]\n"
|
||||
" [-rngseed <rng seed = %d>]\n",
|
||||
argv[0], num, bgcolor, bgthreshold, maxintensitydev,
|
||||
maxxangle, maxyangle, maxzangle, scale, width, height, maxscale, rngseed );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for( i = 1; i < argc; ++i )
|
||||
{
|
||||
if( !strcmp( argv[i], "-info" ) )
|
||||
{
|
||||
infoname = argv[++i];
|
||||
}
|
||||
else if( !strcmp( argv[i], "-img" ) )
|
||||
{
|
||||
imagename = argv[++i];
|
||||
}
|
||||
else if( !strcmp( argv[i], "-vec" ) )
|
||||
{
|
||||
vecname = argv[++i];
|
||||
}
|
||||
else if( !strcmp( argv[i], "-bg" ) )
|
||||
{
|
||||
bgfilename = argv[++i];
|
||||
}
|
||||
else if( !strcmp( argv[i], "-num" ) )
|
||||
{
|
||||
num = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-bgcolor" ) )
|
||||
{
|
||||
bgcolor = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-bgthresh" ) )
|
||||
{
|
||||
bgthreshold = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-inv" ) )
|
||||
{
|
||||
invert = 1;
|
||||
}
|
||||
else if( !strcmp( argv[i], "-randinv" ) )
|
||||
{
|
||||
invert = CV_RANDOM_INVERT;
|
||||
}
|
||||
else if( !strcmp( argv[i], "-maxidev" ) )
|
||||
{
|
||||
maxintensitydev = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-maxxangle" ) )
|
||||
{
|
||||
maxxangle = atof( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-maxyangle" ) )
|
||||
{
|
||||
maxyangle = atof( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-maxzangle" ) )
|
||||
{
|
||||
maxzangle = atof( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-show" ) )
|
||||
{
|
||||
showsamples = 1;
|
||||
if( i+1 < argc && strlen( argv[i+1] ) > 0 && argv[i+1][0] != '-' )
|
||||
{
|
||||
double d;
|
||||
d = strtod( argv[i+1], 0 );
|
||||
if( d != -HUGE_VAL && d != HUGE_VAL && d > 0 ) scale = d;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
else if( !strcmp( argv[i], "-w" ) )
|
||||
{
|
||||
width = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-h" ) )
|
||||
{
|
||||
height = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-maxscale" ) )
|
||||
{
|
||||
maxscale = atof( argv[++i] );
|
||||
}
|
||||
else if (!strcmp(argv[i], "-rngseed"))
|
||||
{
|
||||
rngseed = atoi(argv[++i]);
|
||||
}
|
||||
}
|
||||
|
||||
cv::setRNGSeed( rngseed );
|
||||
|
||||
printf( "Info file name: %s\n", ((infoname == NULL) ? nullname : infoname ) );
|
||||
printf( "Img file name: %s\n", ((imagename == NULL) ? nullname : imagename ) );
|
||||
printf( "Vec file name: %s\n", ((vecname == NULL) ? nullname : vecname ) );
|
||||
printf( "BG file name: %s\n", ((bgfilename == NULL) ? nullname : bgfilename ) );
|
||||
printf( "Num: %d\n", num );
|
||||
printf( "BG color: %d\n", bgcolor );
|
||||
printf( "BG threshold: %d\n", bgthreshold );
|
||||
printf( "Invert: %s\n", (invert == CV_RANDOM_INVERT) ? "RANDOM"
|
||||
: ( (invert) ? "TRUE" : "FALSE" ) );
|
||||
printf( "Max intensity deviation: %d\n", maxintensitydev );
|
||||
printf( "Max x angle: %g\n", maxxangle );
|
||||
printf( "Max y angle: %g\n", maxyangle );
|
||||
printf( "Max z angle: %g\n", maxzangle );
|
||||
printf( "Show samples: %s\n", (showsamples) ? "TRUE" : "FALSE" );
|
||||
if( showsamples )
|
||||
{
|
||||
printf( "Scale: %g\n", scale );
|
||||
}
|
||||
printf( "Width: %d\n", width );
|
||||
printf( "Height: %d\n", height );
|
||||
printf( "Max Scale: %g\n", maxscale );
|
||||
printf( "RNG Seed: %d\n", rngseed );
|
||||
|
||||
/* determine action */
|
||||
if( imagename && vecname )
|
||||
{
|
||||
printf( "Create training samples from single image applying distortions...\n" );
|
||||
|
||||
cvCreateTrainingSamples( vecname, imagename, bgcolor, bgthreshold, bgfilename,
|
||||
num, invert, maxintensitydev,
|
||||
maxxangle, maxyangle, maxzangle,
|
||||
showsamples, width, height );
|
||||
|
||||
printf( "Done\n" );
|
||||
}
|
||||
else if( imagename && bgfilename && infoname )
|
||||
{
|
||||
printf( "Create test samples from single image applying distortions...\n" );
|
||||
|
||||
cvCreateTestSamples( infoname, imagename, bgcolor, bgthreshold, bgfilename, num,
|
||||
invert, maxintensitydev,
|
||||
maxxangle, maxyangle, maxzangle, showsamples, width, height, maxscale);
|
||||
|
||||
printf( "Done\n" );
|
||||
}
|
||||
else if( infoname && vecname )
|
||||
{
|
||||
int total;
|
||||
|
||||
printf( "Create training samples from images collection...\n" );
|
||||
|
||||
total = cvCreateTrainingSamplesFromInfo( infoname, vecname, num, showsamples,
|
||||
width, height );
|
||||
|
||||
printf( "Done. Created %d samples\n", total );
|
||||
}
|
||||
else if( vecname )
|
||||
{
|
||||
printf( "View samples from vec file (press ESC to exit)...\n" );
|
||||
|
||||
cvShowVecSamples( vecname, width, height, scale );
|
||||
|
||||
printf( "Done\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "Nothing to do\n" );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
1463
opencv-apps/createsamples/utility.cpp
Normal file
1463
opencv-apps/createsamples/utility.cpp
Normal file
File diff suppressed because it is too large
Load Diff
124
opencv-apps/createsamples/utility.hpp
Normal file
124
opencv-apps/createsamples/utility.hpp
Normal file
@ -0,0 +1,124 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __CREATESAMPLES_UTILITY_HPP__
|
||||
#define __CREATESAMPLES_UTILITY_HPP__
|
||||
|
||||
#define CV_VERBOSE 1
|
||||
|
||||
/*
|
||||
* cvCreateTrainingSamples
|
||||
*
|
||||
* Create training samples applying random distortions to sample image and
|
||||
* store them in .vec file
|
||||
*
|
||||
* filename - .vec file name
|
||||
* imgfilename - sample image file name
|
||||
* bgcolor - background color for sample image
|
||||
* bgthreshold - background color threshold. Pixels those colors are in range
|
||||
* [bgcolor-bgthreshold, bgcolor+bgthreshold] are considered as transparent
|
||||
* bgfilename - background description file name. If not NULL samples
|
||||
* will be put on arbitrary background
|
||||
* count - desired number of samples
|
||||
* invert - if not 0 sample foreground pixels will be inverted
|
||||
* if invert == CV_RANDOM_INVERT then samples will be inverted randomly
|
||||
* maxintensitydev - desired max intensity deviation of foreground samples pixels
|
||||
* maxxangle - max rotation angles
|
||||
* maxyangle
|
||||
* maxzangle
|
||||
* showsamples - if not 0 samples will be shown
|
||||
* winwidth - desired samples width
|
||||
* winheight - desired samples height
|
||||
*/
|
||||
#define CV_RANDOM_INVERT 0x7FFFFFFF
|
||||
|
||||
void cvCreateTrainingSamples( const char* filename,
|
||||
const char* imgfilename, int bgcolor, int bgthreshold,
|
||||
const char* bgfilename, int count,
|
||||
int invert = 0, int maxintensitydev = 40,
|
||||
double maxxangle = 1.1,
|
||||
double maxyangle = 1.1,
|
||||
double maxzangle = 0.5,
|
||||
int showsamples = 0,
|
||||
int winwidth = 24, int winheight = 24 );
|
||||
|
||||
void cvCreateTestSamples( const char* infoname,
|
||||
const char* imgfilename, int bgcolor, int bgthreshold,
|
||||
const char* bgfilename, int count,
|
||||
int invert, int maxintensitydev,
|
||||
double maxxangle, double maxyangle, double maxzangle,
|
||||
int showsamples,
|
||||
int winwidth, int winheight, double maxscale );
|
||||
|
||||
/*
|
||||
* cvCreateTrainingSamplesFromInfo
|
||||
*
|
||||
* Create training samples from a set of marked up images and store them into .vec file
|
||||
* infoname - file in which marked up image descriptions are stored
|
||||
* num - desired number of samples
|
||||
* showsamples - if not 0 samples will be shown
|
||||
* winwidth - sample width
|
||||
* winheight - sample height
|
||||
*
|
||||
* Return number of successfully created samples
|
||||
*/
|
||||
int cvCreateTrainingSamplesFromInfo( const char* infoname, const char* vecfilename,
|
||||
int num,
|
||||
int showsamples,
|
||||
int winwidth, int winheight );
|
||||
|
||||
/*
|
||||
* cvShowVecSamples
|
||||
*
|
||||
* Shows samples stored in .vec file
|
||||
*
|
||||
* filename
|
||||
* .vec file name
|
||||
* winwidth
|
||||
* sample width
|
||||
* winheight
|
||||
* sample height
|
||||
* scale
|
||||
* the scale each sample is adjusted to
|
||||
*/
|
||||
void cvShowVecSamples( const char* filename, int winwidth, int winheight, double scale );
|
||||
|
||||
#endif //__CREATESAMPLES_UTILITY_HPP__
|
BIN
opencv-apps/createsamples/utility.hpp.gch
Normal file
BIN
opencv-apps/createsamples/utility.hpp.gch
Normal file
Binary file not shown.
6
opencv-apps/interactive-calibration/CMakeLists.txt
Normal file
6
opencv-apps/interactive-calibration/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
set(DEPS opencv_core opencv_imgproc opencv_features2d opencv_highgui opencv_calib3d opencv_videoio)
|
||||
if(${BUILD_opencv_aruco})
|
||||
list(APPEND DEPS opencv_aruco)
|
||||
endif()
|
||||
file(GLOB SRCS *.cpp)
|
||||
ocv_add_application(opencv_interactive-calibration MODULES ${DEPS} SRCS ${SRCS})
|
123
opencv-apps/interactive-calibration/calibCommon.hpp
Normal file
123
opencv-apps/interactive-calibration/calibCommon.hpp
Normal file
@ -0,0 +1,123 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef CALIB_COMMON_HPP
|
||||
#define CALIB_COMMON_HPP
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace calib
|
||||
{
|
||||
#define OVERLAY_DELAY 1000
|
||||
#define IMAGE_MAX_WIDTH 1280
|
||||
#define IMAGE_MAX_HEIGHT 960
|
||||
|
||||
bool showOverlayMessage(const std::string& message);
|
||||
|
||||
enum InputType { Video, Pictures };
|
||||
enum InputVideoSource { Camera, File };
|
||||
enum TemplateType { AcirclesGrid, Chessboard, chAruco, DoubleAcirclesGrid };
|
||||
|
||||
static const std::string mainWindowName = "Calibration";
|
||||
static const std::string gridWindowName = "Board locations";
|
||||
static const std::string consoleHelp = "Hot keys:\nesc - exit application\n"
|
||||
"s - save current data to .xml file\n"
|
||||
"r - delete last frame\n"
|
||||
"u - enable/disable applying undistortion\n"
|
||||
"d - delete all frames\n"
|
||||
"v - switch visualization";
|
||||
|
||||
static const double sigmaMult = 1.96;
|
||||
|
||||
struct calibrationData
|
||||
{
|
||||
cv::Mat cameraMatrix;
|
||||
cv::Mat distCoeffs;
|
||||
cv::Mat stdDeviations;
|
||||
cv::Mat perViewErrors;
|
||||
std::vector<cv::Mat> rvecs;
|
||||
std::vector<cv::Mat> tvecs;
|
||||
double totalAvgErr;
|
||||
cv::Size imageSize;
|
||||
|
||||
std::vector<std::vector<cv::Point2f> > imagePoints;
|
||||
std::vector< std::vector<cv::Point3f> > objectPoints;
|
||||
|
||||
std::vector<cv::Mat> allCharucoCorners;
|
||||
std::vector<cv::Mat> allCharucoIds;
|
||||
|
||||
cv::Mat undistMap1, undistMap2;
|
||||
|
||||
calibrationData()
|
||||
{
|
||||
imageSize = cv::Size(IMAGE_MAX_WIDTH, IMAGE_MAX_HEIGHT);
|
||||
}
|
||||
};
|
||||
|
||||
struct cameraParameters
|
||||
{
|
||||
cv::Mat cameraMatrix;
|
||||
cv::Mat distCoeffs;
|
||||
cv::Mat stdDeviations;
|
||||
double avgError;
|
||||
|
||||
cameraParameters(){}
|
||||
cameraParameters(cv::Mat& _cameraMatrix, cv::Mat& _distCoeffs, cv::Mat& _stdDeviations, double _avgError = 0) :
|
||||
cameraMatrix(_cameraMatrix), distCoeffs(_distCoeffs), stdDeviations(_stdDeviations), avgError(_avgError)
|
||||
{}
|
||||
};
|
||||
|
||||
struct captureParameters
|
||||
{
|
||||
InputType captureMethod;
|
||||
InputVideoSource source;
|
||||
TemplateType board;
|
||||
cv::Size boardSize;
|
||||
int charucoDictName;
|
||||
int calibrationStep;
|
||||
float charucoSquareLenght, charucoMarkerSize;
|
||||
float captureDelay;
|
||||
float squareSize;
|
||||
float templDst;
|
||||
std::string videoFileName;
|
||||
bool flipVertical;
|
||||
int camID;
|
||||
int fps;
|
||||
cv::Size cameraResolution;
|
||||
int maxFramesNum;
|
||||
int minFramesNum;
|
||||
|
||||
captureParameters()
|
||||
{
|
||||
calibrationStep = 1;
|
||||
captureDelay = 500.f;
|
||||
maxFramesNum = 30;
|
||||
minFramesNum = 10;
|
||||
fps = 30;
|
||||
cameraResolution = cv::Size(IMAGE_MAX_WIDTH, IMAGE_MAX_HEIGHT);
|
||||
}
|
||||
};
|
||||
|
||||
struct internalParameters
|
||||
{
|
||||
double solverEps;
|
||||
int solverMaxIters;
|
||||
bool fastSolving;
|
||||
double filterAlpha;
|
||||
|
||||
internalParameters()
|
||||
{
|
||||
solverEps = 1e-7;
|
||||
solverMaxIters = 30;
|
||||
fastSolving = false;
|
||||
filterAlpha = 0.1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
334
opencv-apps/interactive-calibration/calibController.cpp
Normal file
334
opencv-apps/interactive-calibration/calibController.cpp
Normal file
@ -0,0 +1,334 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "calibController.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
|
||||
#include <opencv2/calib3d.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
double calib::calibController::estimateCoverageQuality()
|
||||
{
|
||||
int gridSize = 10;
|
||||
int xGridStep = mCalibData->imageSize.width / gridSize;
|
||||
int yGridStep = mCalibData->imageSize.height / gridSize;
|
||||
std::vector<int> pointsInCell(gridSize*gridSize);
|
||||
|
||||
std::fill(pointsInCell.begin(), pointsInCell.end(), 0);
|
||||
|
||||
for(std::vector<std::vector<cv::Point2f> >::iterator it = mCalibData->imagePoints.begin(); it != mCalibData->imagePoints.end(); ++it)
|
||||
for(std::vector<cv::Point2f>::iterator pointIt = (*it).begin(); pointIt != (*it).end(); ++pointIt) {
|
||||
int i = (int)((*pointIt).x / xGridStep);
|
||||
int j = (int)((*pointIt).y / yGridStep);
|
||||
pointsInCell[i*gridSize + j]++;
|
||||
}
|
||||
|
||||
for(std::vector<cv::Mat>::iterator it = mCalibData->allCharucoCorners.begin(); it != mCalibData->allCharucoCorners.end(); ++it)
|
||||
for(int l = 0; l < (*it).size[0]; l++) {
|
||||
int i = (int)((*it).at<float>(l, 0) / xGridStep);
|
||||
int j = (int)((*it).at<float>(l, 1) / yGridStep);
|
||||
pointsInCell[i*gridSize + j]++;
|
||||
}
|
||||
|
||||
cv::Mat mean, stdDev;
|
||||
cv::meanStdDev(pointsInCell, mean, stdDev);
|
||||
|
||||
return mean.at<double>(0) / (stdDev.at<double>(0) + 1e-7);
|
||||
}
|
||||
|
||||
calib::calibController::calibController()
|
||||
{
|
||||
mCalibFlags = 0;
|
||||
}
|
||||
|
||||
calib::calibController::calibController(cv::Ptr<calib::calibrationData> data, int initialFlags, bool autoTuning, int minFramesNum) :
|
||||
mCalibData(data)
|
||||
{
|
||||
mCalibFlags = initialFlags;
|
||||
mNeedTuning = autoTuning;
|
||||
mMinFramesNum = minFramesNum;
|
||||
mConfIntervalsState = false;
|
||||
mCoverageQualityState = false;
|
||||
}
|
||||
|
||||
void calib::calibController::updateState()
|
||||
{
|
||||
if(mCalibData->cameraMatrix.total()) {
|
||||
const double relErrEps = 0.05;
|
||||
bool fConfState = false, cConfState = false, dConfState = true;
|
||||
if(sigmaMult*mCalibData->stdDeviations.at<double>(0) / mCalibData->cameraMatrix.at<double>(0,0) < relErrEps &&
|
||||
sigmaMult*mCalibData->stdDeviations.at<double>(1) / mCalibData->cameraMatrix.at<double>(1,1) < relErrEps)
|
||||
fConfState = true;
|
||||
if(sigmaMult*mCalibData->stdDeviations.at<double>(2) / mCalibData->cameraMatrix.at<double>(0,2) < relErrEps &&
|
||||
sigmaMult*mCalibData->stdDeviations.at<double>(3) / mCalibData->cameraMatrix.at<double>(1,2) < relErrEps)
|
||||
cConfState = true;
|
||||
|
||||
for(int i = 0; i < 5; i++)
|
||||
if(mCalibData->stdDeviations.at<double>(4+i) / fabs(mCalibData->distCoeffs.at<double>(i)) > 1)
|
||||
dConfState = false;
|
||||
|
||||
mConfIntervalsState = fConfState && cConfState && dConfState;
|
||||
}
|
||||
|
||||
if(getFramesNumberState())
|
||||
mCoverageQualityState = estimateCoverageQuality() > 1.8 ? true : false;
|
||||
|
||||
if (getFramesNumberState() && mNeedTuning) {
|
||||
if( !(mCalibFlags & cv::CALIB_FIX_ASPECT_RATIO) &&
|
||||
mCalibData->cameraMatrix.total()) {
|
||||
double fDiff = fabs(mCalibData->cameraMatrix.at<double>(0,0) -
|
||||
mCalibData->cameraMatrix.at<double>(1,1));
|
||||
|
||||
if (fDiff < 3*mCalibData->stdDeviations.at<double>(0) &&
|
||||
fDiff < 3*mCalibData->stdDeviations.at<double>(1)) {
|
||||
mCalibFlags |= cv::CALIB_FIX_ASPECT_RATIO;
|
||||
mCalibData->cameraMatrix.at<double>(0,0) =
|
||||
mCalibData->cameraMatrix.at<double>(1,1);
|
||||
}
|
||||
}
|
||||
|
||||
if(!(mCalibFlags & cv::CALIB_ZERO_TANGENT_DIST)) {
|
||||
const double eps = 0.005;
|
||||
if(fabs(mCalibData->distCoeffs.at<double>(2)) < eps &&
|
||||
fabs(mCalibData->distCoeffs.at<double>(3)) < eps)
|
||||
mCalibFlags |= cv::CALIB_ZERO_TANGENT_DIST;
|
||||
}
|
||||
|
||||
if(!(mCalibFlags & cv::CALIB_FIX_K1)) {
|
||||
const double eps = 0.005;
|
||||
if(fabs(mCalibData->distCoeffs.at<double>(0)) < eps)
|
||||
mCalibFlags |= cv::CALIB_FIX_K1;
|
||||
}
|
||||
|
||||
if(!(mCalibFlags & cv::CALIB_FIX_K2)) {
|
||||
const double eps = 0.005;
|
||||
if(fabs(mCalibData->distCoeffs.at<double>(1)) < eps)
|
||||
mCalibFlags |= cv::CALIB_FIX_K2;
|
||||
}
|
||||
|
||||
if(!(mCalibFlags & cv::CALIB_FIX_K3)) {
|
||||
const double eps = 0.005;
|
||||
if(fabs(mCalibData->distCoeffs.at<double>(4)) < eps)
|
||||
mCalibFlags |= cv::CALIB_FIX_K3;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool calib::calibController::getCommonCalibrationState() const
|
||||
{
|
||||
int rating = (int)getFramesNumberState() + (int)getConfidenceIntrervalsState() +
|
||||
(int)getRMSState() + (int)mCoverageQualityState;
|
||||
return rating == 4;
|
||||
}
|
||||
|
||||
bool calib::calibController::getFramesNumberState() const
|
||||
{
|
||||
return std::max(mCalibData->imagePoints.size(), mCalibData->allCharucoCorners.size()) > mMinFramesNum;
|
||||
}
|
||||
|
||||
bool calib::calibController::getConfidenceIntrervalsState() const
|
||||
{
|
||||
return mConfIntervalsState;
|
||||
}
|
||||
|
||||
bool calib::calibController::getRMSState() const
|
||||
{
|
||||
return mCalibData->totalAvgErr < 0.5;
|
||||
}
|
||||
|
||||
int calib::calibController::getNewFlags() const
|
||||
{
|
||||
return mCalibFlags;
|
||||
}
|
||||
|
||||
|
||||
//////////////////// calibDataController
|
||||
|
||||
double calib::calibDataController::estimateGridSubsetQuality(size_t excludedIndex)
|
||||
{
|
||||
{
|
||||
int gridSize = 10;
|
||||
int xGridStep = mCalibData->imageSize.width / gridSize;
|
||||
int yGridStep = mCalibData->imageSize.height / gridSize;
|
||||
std::vector<int> pointsInCell(gridSize*gridSize);
|
||||
|
||||
std::fill(pointsInCell.begin(), pointsInCell.end(), 0);
|
||||
|
||||
for(size_t k = 0; k < mCalibData->imagePoints.size(); k++)
|
||||
if(k != excludedIndex)
|
||||
for(std::vector<cv::Point2f>::iterator pointIt = mCalibData->imagePoints[k].begin(); pointIt != mCalibData->imagePoints[k].end(); ++pointIt) {
|
||||
int i = (int)((*pointIt).x / xGridStep);
|
||||
int j = (int)((*pointIt).y / yGridStep);
|
||||
pointsInCell[i*gridSize + j]++;
|
||||
}
|
||||
|
||||
for(size_t k = 0; k < mCalibData->allCharucoCorners.size(); k++)
|
||||
if(k != excludedIndex)
|
||||
for(int l = 0; l < mCalibData->allCharucoCorners[k].size[0]; l++) {
|
||||
int i = (int)(mCalibData->allCharucoCorners[k].at<float>(l, 0) / xGridStep);
|
||||
int j = (int)(mCalibData->allCharucoCorners[k].at<float>(l, 1) / yGridStep);
|
||||
pointsInCell[i*gridSize + j]++;
|
||||
}
|
||||
|
||||
cv::Mat mean, stdDev;
|
||||
cv::meanStdDev(pointsInCell, mean, stdDev);
|
||||
|
||||
return mean.at<double>(0) / (stdDev.at<double>(0) + 1e-7);
|
||||
}
|
||||
}
|
||||
|
||||
calib::calibDataController::calibDataController(cv::Ptr<calib::calibrationData> data, int maxFrames, double convParameter) :
|
||||
mCalibData(data), mParamsFileName("CamParams.xml")
|
||||
{
|
||||
mMaxFramesNum = maxFrames;
|
||||
mAlpha = convParameter;
|
||||
}
|
||||
|
||||
calib::calibDataController::calibDataController()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void calib::calibDataController::filterFrames()
|
||||
{
|
||||
size_t numberOfFrames = std::max(mCalibData->allCharucoIds.size(), mCalibData->imagePoints.size());
|
||||
CV_Assert(numberOfFrames == mCalibData->perViewErrors.total());
|
||||
if(numberOfFrames >= mMaxFramesNum) {
|
||||
|
||||
double worstValue = -HUGE_VAL, maxQuality = estimateGridSubsetQuality(numberOfFrames);
|
||||
size_t worstElemIndex = 0;
|
||||
for(size_t i = 0; i < numberOfFrames; i++) {
|
||||
double gridQDelta = estimateGridSubsetQuality(i) - maxQuality;
|
||||
double currentValue = mCalibData->perViewErrors.at<double>((int)i)*mAlpha + gridQDelta*(1. - mAlpha);
|
||||
if(currentValue > worstValue) {
|
||||
worstValue = currentValue;
|
||||
worstElemIndex = i;
|
||||
}
|
||||
}
|
||||
showOverlayMessage(cv::format("Frame %zu is worst", worstElemIndex + 1));
|
||||
|
||||
if(mCalibData->imagePoints.size()) {
|
||||
mCalibData->imagePoints.erase(mCalibData->imagePoints.begin() + worstElemIndex);
|
||||
mCalibData->objectPoints.erase(mCalibData->objectPoints.begin() + worstElemIndex);
|
||||
}
|
||||
else {
|
||||
mCalibData->allCharucoCorners.erase(mCalibData->allCharucoCorners.begin() + worstElemIndex);
|
||||
mCalibData->allCharucoIds.erase(mCalibData->allCharucoIds.begin() + worstElemIndex);
|
||||
}
|
||||
|
||||
cv::Mat newErrorsVec = cv::Mat((int)numberOfFrames - 1, 1, CV_64F);
|
||||
std::copy(mCalibData->perViewErrors.ptr<double>(0),
|
||||
mCalibData->perViewErrors.ptr<double>((int)worstElemIndex), newErrorsVec.ptr<double>(0));
|
||||
if((int)worstElemIndex < (int)numberOfFrames-1) {
|
||||
std::copy(mCalibData->perViewErrors.ptr<double>((int)worstElemIndex + 1), mCalibData->perViewErrors.ptr<double>((int)numberOfFrames),
|
||||
newErrorsVec.ptr<double>((int)worstElemIndex));
|
||||
}
|
||||
mCalibData->perViewErrors = newErrorsVec;
|
||||
}
|
||||
}
|
||||
|
||||
void calib::calibDataController::setParametersFileName(const std::string &name)
|
||||
{
|
||||
mParamsFileName = name;
|
||||
}
|
||||
|
||||
void calib::calibDataController::deleteLastFrame()
|
||||
{
|
||||
if( !mCalibData->imagePoints.empty()) {
|
||||
mCalibData->imagePoints.pop_back();
|
||||
mCalibData->objectPoints.pop_back();
|
||||
}
|
||||
|
||||
if (!mCalibData->allCharucoCorners.empty()) {
|
||||
mCalibData->allCharucoCorners.pop_back();
|
||||
mCalibData->allCharucoIds.pop_back();
|
||||
}
|
||||
|
||||
if(!mParamsStack.empty()) {
|
||||
mCalibData->cameraMatrix = (mParamsStack.top()).cameraMatrix;
|
||||
mCalibData->distCoeffs = (mParamsStack.top()).distCoeffs;
|
||||
mCalibData->stdDeviations = (mParamsStack.top()).stdDeviations;
|
||||
mCalibData->totalAvgErr = (mParamsStack.top()).avgError;
|
||||
mParamsStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void calib::calibDataController::rememberCurrentParameters()
|
||||
{
|
||||
cv::Mat oldCameraMat, oldDistcoeefs, oldStdDevs;
|
||||
mCalibData->cameraMatrix.copyTo(oldCameraMat);
|
||||
mCalibData->distCoeffs.copyTo(oldDistcoeefs);
|
||||
mCalibData->stdDeviations.copyTo(oldStdDevs);
|
||||
mParamsStack.push(cameraParameters(oldCameraMat, oldDistcoeefs, oldStdDevs, mCalibData->totalAvgErr));
|
||||
}
|
||||
|
||||
void calib::calibDataController::deleteAllData()
|
||||
{
|
||||
mCalibData->imagePoints.clear();
|
||||
mCalibData->objectPoints.clear();
|
||||
mCalibData->allCharucoCorners.clear();
|
||||
mCalibData->allCharucoIds.clear();
|
||||
mCalibData->cameraMatrix = mCalibData->distCoeffs = cv::Mat();
|
||||
mParamsStack = std::stack<cameraParameters>();
|
||||
rememberCurrentParameters();
|
||||
}
|
||||
|
||||
bool calib::calibDataController::saveCurrentCameraParameters() const
|
||||
{
|
||||
bool success = false;
|
||||
if(mCalibData->cameraMatrix.total()) {
|
||||
cv::FileStorage parametersWriter(mParamsFileName, cv::FileStorage::WRITE);
|
||||
if(parametersWriter.isOpened()) {
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
char buf[256];
|
||||
strftime(buf, sizeof(buf)-1, "%c", localtime(&rawtime));
|
||||
|
||||
parametersWriter << "calibrationDate" << buf;
|
||||
parametersWriter << "framesCount" << std::max((int)mCalibData->objectPoints.size(), (int)mCalibData->allCharucoCorners.size());
|
||||
parametersWriter << "cameraResolution" << mCalibData->imageSize;
|
||||
parametersWriter << "cameraMatrix" << mCalibData->cameraMatrix;
|
||||
parametersWriter << "cameraMatrix_std_dev" << mCalibData->stdDeviations.rowRange(cv::Range(0, 4));
|
||||
parametersWriter << "dist_coeffs" << mCalibData->distCoeffs;
|
||||
parametersWriter << "dist_coeffs_std_dev" << mCalibData->stdDeviations.rowRange(cv::Range(4, 9));
|
||||
parametersWriter << "avg_reprojection_error" << mCalibData->totalAvgErr;
|
||||
|
||||
parametersWriter.release();
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void calib::calibDataController::printParametersToConsole(std::ostream &output) const
|
||||
{
|
||||
const char* border = "---------------------------------------------------";
|
||||
output << border << std::endl;
|
||||
output << "Frames used for calibration: " << std::max(mCalibData->objectPoints.size(), mCalibData->allCharucoCorners.size())
|
||||
<< " \t RMS = " << mCalibData->totalAvgErr << std::endl;
|
||||
if(mCalibData->cameraMatrix.at<double>(0,0) == mCalibData->cameraMatrix.at<double>(1,1))
|
||||
output << "F = " << mCalibData->cameraMatrix.at<double>(1,1) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(1) << std::endl;
|
||||
else
|
||||
output << "Fx = " << mCalibData->cameraMatrix.at<double>(0,0) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(0) << " \t "
|
||||
<< "Fy = " << mCalibData->cameraMatrix.at<double>(1,1) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(1) << std::endl;
|
||||
output << "Cx = " << mCalibData->cameraMatrix.at<double>(0,2) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(2) << " \t"
|
||||
<< "Cy = " << mCalibData->cameraMatrix.at<double>(1,2) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(3) << std::endl;
|
||||
output << "K1 = " << mCalibData->distCoeffs.at<double>(0) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(4) << std::endl;
|
||||
output << "K2 = " << mCalibData->distCoeffs.at<double>(1) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(5) << std::endl;
|
||||
output << "K3 = " << mCalibData->distCoeffs.at<double>(4) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(8) << std::endl;
|
||||
output << "TD1 = " << mCalibData->distCoeffs.at<double>(2) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(6) << std::endl;
|
||||
output << "TD2 = " << mCalibData->distCoeffs.at<double>(3) << " +- " << sigmaMult*mCalibData->stdDeviations.at<double>(7) << std::endl;
|
||||
}
|
||||
|
||||
void calib::calibDataController::updateUndistortMap()
|
||||
{
|
||||
cv::initUndistortRectifyMap(mCalibData->cameraMatrix, mCalibData->distCoeffs, cv::noArray(),
|
||||
cv::getOptimalNewCameraMatrix(mCalibData->cameraMatrix, mCalibData->distCoeffs, mCalibData->imageSize, 0.0, mCalibData->imageSize),
|
||||
mCalibData->imageSize, CV_16SC2, mCalibData->undistMap1, mCalibData->undistMap2);
|
||||
|
||||
}
|
69
opencv-apps/interactive-calibration/calibController.hpp
Normal file
69
opencv-apps/interactive-calibration/calibController.hpp
Normal file
@ -0,0 +1,69 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef CALIB_CONTROLLER_HPP
|
||||
#define CALIB_CONTROLLER_HPP
|
||||
|
||||
#include "calibCommon.hpp"
|
||||
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
|
||||
namespace calib {
|
||||
|
||||
class calibController
|
||||
{
|
||||
protected:
|
||||
cv::Ptr<calibrationData> mCalibData;
|
||||
int mCalibFlags;
|
||||
unsigned mMinFramesNum;
|
||||
bool mNeedTuning;
|
||||
bool mConfIntervalsState;
|
||||
bool mCoverageQualityState;
|
||||
|
||||
double estimateCoverageQuality();
|
||||
public:
|
||||
calibController();
|
||||
calibController(cv::Ptr<calibrationData> data, int initialFlags, bool autoTuning,
|
||||
int minFramesNum);
|
||||
|
||||
void updateState();
|
||||
|
||||
bool getCommonCalibrationState() const;
|
||||
|
||||
bool getFramesNumberState() const;
|
||||
bool getConfidenceIntrervalsState() const;
|
||||
bool getRMSState() const;
|
||||
bool getPointsCoverageState() const;
|
||||
int getNewFlags() const;
|
||||
};
|
||||
|
||||
class calibDataController
|
||||
{
|
||||
protected:
|
||||
cv::Ptr<calibrationData> mCalibData;
|
||||
std::stack<cameraParameters> mParamsStack;
|
||||
std::string mParamsFileName;
|
||||
unsigned mMaxFramesNum;
|
||||
double mAlpha;
|
||||
|
||||
double estimateGridSubsetQuality(size_t excludedIndex);
|
||||
public:
|
||||
calibDataController(cv::Ptr<calibrationData> data, int maxFrames, double convParameter);
|
||||
calibDataController();
|
||||
|
||||
void filterFrames();
|
||||
void setParametersFileName(const std::string& name);
|
||||
void deleteLastFrame();
|
||||
void rememberCurrentParameters();
|
||||
void deleteAllData();
|
||||
bool saveCurrentCameraParameters() const;
|
||||
void printParametersToConsole(std::ostream &output) const;
|
||||
void updateUndistortMap();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
97
opencv-apps/interactive-calibration/calibPipeline.cpp
Normal file
97
opencv-apps/interactive-calibration/calibPipeline.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "calibPipeline.hpp"
|
||||
|
||||
#include <opencv2/highgui.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace calib;
|
||||
|
||||
#define CAP_DELAY 10
|
||||
|
||||
cv::Size CalibPipeline::getCameraResolution()
|
||||
{
|
||||
mCapture.set(cv::CAP_PROP_FRAME_WIDTH, 10000);
|
||||
mCapture.set(cv::CAP_PROP_FRAME_HEIGHT, 10000);
|
||||
int w = (int)mCapture.get(cv::CAP_PROP_FRAME_WIDTH);
|
||||
int h = (int)mCapture.get(cv::CAP_PROP_FRAME_HEIGHT);
|
||||
return cv::Size(w,h);
|
||||
}
|
||||
|
||||
CalibPipeline::CalibPipeline(captureParameters params) :
|
||||
mCaptureParams(params)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PipelineExitStatus CalibPipeline::start(std::vector<cv::Ptr<FrameProcessor> > processors)
|
||||
{
|
||||
if(mCaptureParams.source == Camera && !mCapture.isOpened())
|
||||
{
|
||||
mCapture.open(mCaptureParams.camID);
|
||||
cv::Size maxRes = getCameraResolution();
|
||||
cv::Size neededRes = mCaptureParams.cameraResolution;
|
||||
|
||||
if(maxRes.width < neededRes.width) {
|
||||
double aR = (double)maxRes.width / maxRes.height;
|
||||
mCapture.set(cv::CAP_PROP_FRAME_WIDTH, neededRes.width);
|
||||
mCapture.set(cv::CAP_PROP_FRAME_HEIGHT, neededRes.width/aR);
|
||||
}
|
||||
else if(maxRes.height < neededRes.height) {
|
||||
double aR = (double)maxRes.width / maxRes.height;
|
||||
mCapture.set(cv::CAP_PROP_FRAME_HEIGHT, neededRes.height);
|
||||
mCapture.set(cv::CAP_PROP_FRAME_WIDTH, neededRes.height*aR);
|
||||
}
|
||||
else {
|
||||
mCapture.set(cv::CAP_PROP_FRAME_HEIGHT, neededRes.height);
|
||||
mCapture.set(cv::CAP_PROP_FRAME_WIDTH, neededRes.width);
|
||||
}
|
||||
mCapture.set(cv::CAP_PROP_AUTOFOCUS, 0);
|
||||
}
|
||||
else if (mCaptureParams.source == File && !mCapture.isOpened())
|
||||
mCapture.open(mCaptureParams.videoFileName);
|
||||
mImageSize = cv::Size((int)mCapture.get(cv::CAP_PROP_FRAME_WIDTH), (int)mCapture.get(cv::CAP_PROP_FRAME_HEIGHT));
|
||||
|
||||
if(!mCapture.isOpened())
|
||||
throw std::runtime_error("Unable to open video source");
|
||||
|
||||
cv::Mat frame, processedFrame;
|
||||
while(mCapture.grab()) {
|
||||
mCapture.retrieve(frame);
|
||||
if(mCaptureParams.flipVertical)
|
||||
cv::flip(frame, frame, -1);
|
||||
|
||||
frame.copyTo(processedFrame);
|
||||
for (std::vector<cv::Ptr<FrameProcessor> >::iterator it = processors.begin(); it != processors.end(); ++it)
|
||||
processedFrame = (*it)->processFrame(processedFrame);
|
||||
cv::imshow(mainWindowName, processedFrame);
|
||||
char key = (char)cv::waitKey(CAP_DELAY);
|
||||
|
||||
if(key == 27) // esc
|
||||
return Finished;
|
||||
else if (key == 114) // r
|
||||
return DeleteLastFrame;
|
||||
else if (key == 100) // d
|
||||
return DeleteAllFrames;
|
||||
else if (key == 115) // s
|
||||
return SaveCurrentData;
|
||||
else if (key == 117) // u
|
||||
return SwitchUndistort;
|
||||
else if (key == 118) // v
|
||||
return SwitchVisualisation;
|
||||
|
||||
for (std::vector<cv::Ptr<FrameProcessor> >::iterator it = processors.begin(); it != processors.end(); ++it)
|
||||
if((*it)->isProcessed())
|
||||
return Calibrate;
|
||||
}
|
||||
|
||||
return Finished;
|
||||
}
|
||||
|
||||
cv::Size CalibPipeline::getImageSize() const
|
||||
{
|
||||
return mImageSize;
|
||||
}
|
45
opencv-apps/interactive-calibration/calibPipeline.hpp
Normal file
45
opencv-apps/interactive-calibration/calibPipeline.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
|
||||
#ifndef CALIB_PIPELINE_HPP
|
||||
#define CALIB_PIPELINE_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <opencv2/highgui.hpp>
|
||||
|
||||
#include "calibCommon.hpp"
|
||||
#include "frameProcessor.hpp"
|
||||
|
||||
namespace calib
|
||||
{
|
||||
|
||||
enum PipelineExitStatus { Finished,
|
||||
DeleteLastFrame,
|
||||
Calibrate,
|
||||
DeleteAllFrames,
|
||||
SaveCurrentData,
|
||||
SwitchUndistort,
|
||||
SwitchVisualisation
|
||||
};
|
||||
|
||||
class CalibPipeline
|
||||
{
|
||||
protected:
|
||||
captureParameters mCaptureParams;
|
||||
cv::Size mImageSize;
|
||||
cv::VideoCapture mCapture;
|
||||
|
||||
cv::Size getCameraResolution();
|
||||
|
||||
public:
|
||||
CalibPipeline(captureParameters params);
|
||||
PipelineExitStatus start(std::vector<cv::Ptr<FrameProcessor> > processors);
|
||||
cv::Size getImageSize() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
14
opencv-apps/interactive-calibration/defaultConfig.xml
Normal file
14
opencv-apps/interactive-calibration/defaultConfig.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<opencv_storage>
|
||||
<charuco_dict>0</charuco_dict>
|
||||
<charuco_square_lenght>200</charuco_square_lenght>
|
||||
<charuco_marker_size>100</charuco_marker_size>
|
||||
<calibration_step>1</calibration_step>
|
||||
<max_frames_num>30</max_frames_num>
|
||||
<min_frames_num>10</min_frames_num>
|
||||
<solver_eps>1e-7</solver_eps>
|
||||
<solver_max_iters>30</solver_max_iters>
|
||||
<fast_solver>0</fast_solver>
|
||||
<frame_filter_conv_param>0.1</frame_filter_conv_param>
|
||||
<camera_resolution>800 600</camera_resolution>
|
||||
</opencv_storage>
|
530
opencv-apps/interactive-calibration/frameProcessor.cpp
Normal file
530
opencv-apps/interactive-calibration/frameProcessor.cpp
Normal file
@ -0,0 +1,530 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "frameProcessor.hpp"
|
||||
#include "rotationConverters.hpp"
|
||||
|
||||
#include <opencv2/calib3d.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
using namespace calib;
|
||||
|
||||
#define VIDEO_TEXT_SIZE 4
|
||||
#define POINT_SIZE 5
|
||||
|
||||
static cv::SimpleBlobDetector::Params getDetectorParams()
|
||||
{
|
||||
cv::SimpleBlobDetector::Params detectorParams;
|
||||
|
||||
detectorParams.thresholdStep = 40;
|
||||
detectorParams.minThreshold = 20;
|
||||
detectorParams.maxThreshold = 500;
|
||||
detectorParams.minRepeatability = 2;
|
||||
detectorParams.minDistBetweenBlobs = 5;
|
||||
|
||||
detectorParams.filterByColor = true;
|
||||
detectorParams.blobColor = 0;
|
||||
|
||||
detectorParams.filterByArea = true;
|
||||
detectorParams.minArea = 5;
|
||||
detectorParams.maxArea = 5000;
|
||||
|
||||
detectorParams.filterByCircularity = false;
|
||||
detectorParams.minCircularity = 0.8f;
|
||||
detectorParams.maxCircularity = std::numeric_limits<float>::max();
|
||||
|
||||
detectorParams.filterByInertia = true;
|
||||
detectorParams.minInertiaRatio = 0.1f;
|
||||
detectorParams.maxInertiaRatio = std::numeric_limits<float>::max();
|
||||
|
||||
detectorParams.filterByConvexity = true;
|
||||
detectorParams.minConvexity = 0.8f;
|
||||
detectorParams.maxConvexity = std::numeric_limits<float>::max();
|
||||
|
||||
return detectorParams;
|
||||
}
|
||||
|
||||
FrameProcessor::~FrameProcessor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CalibProcessor::detectAndParseChessboard(const cv::Mat &frame)
|
||||
{
|
||||
int chessBoardFlags = cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE | cv::CALIB_CB_FAST_CHECK;
|
||||
bool isTemplateFound = cv::findChessboardCorners(frame, mBoardSize, mCurrentImagePoints, chessBoardFlags);
|
||||
|
||||
if (isTemplateFound) {
|
||||
cv::Mat viewGray;
|
||||
cv::cvtColor(frame, viewGray, cv::COLOR_BGR2GRAY);
|
||||
cv::cornerSubPix(viewGray, mCurrentImagePoints, cv::Size(11,11),
|
||||
cv::Size(-1,-1), cv::TermCriteria( cv::TermCriteria::EPS+cv::TermCriteria::COUNT, 30, 0.1 ));
|
||||
cv::drawChessboardCorners(frame, mBoardSize, cv::Mat(mCurrentImagePoints), isTemplateFound);
|
||||
mTemplateLocations.insert(mTemplateLocations.begin(), mCurrentImagePoints[0]);
|
||||
}
|
||||
return isTemplateFound;
|
||||
}
|
||||
|
||||
bool CalibProcessor::detectAndParseChAruco(const cv::Mat &frame)
|
||||
{
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
cv::Ptr<cv::aruco::Board> board = mCharucoBoard.staticCast<cv::aruco::Board>();
|
||||
|
||||
std::vector<std::vector<cv::Point2f> > corners, rejected;
|
||||
std::vector<int> ids;
|
||||
cv::aruco::detectMarkers(frame, mArucoDictionary, corners, ids, cv::aruco::DetectorParameters::create(), rejected);
|
||||
cv::aruco::refineDetectedMarkers(frame, board, corners, ids, rejected);
|
||||
cv::Mat currentCharucoCorners, currentCharucoIds;
|
||||
if(ids.size() > 0)
|
||||
cv::aruco::interpolateCornersCharuco(corners, ids, frame, mCharucoBoard, currentCharucoCorners,
|
||||
currentCharucoIds);
|
||||
if(ids.size() > 0) cv::aruco::drawDetectedMarkers(frame, corners);
|
||||
|
||||
if(currentCharucoCorners.total() > 3) {
|
||||
float centerX = 0, centerY = 0;
|
||||
for (int i = 0; i < currentCharucoCorners.size[0]; i++) {
|
||||
centerX += currentCharucoCorners.at<float>(i, 0);
|
||||
centerY += currentCharucoCorners.at<float>(i, 1);
|
||||
}
|
||||
centerX /= currentCharucoCorners.size[0];
|
||||
centerY /= currentCharucoCorners.size[0];
|
||||
|
||||
mTemplateLocations.insert(mTemplateLocations.begin(), cv::Point2f(centerX, centerY));
|
||||
cv::aruco::drawDetectedCornersCharuco(frame, currentCharucoCorners, currentCharucoIds);
|
||||
mCurrentCharucoCorners = currentCharucoCorners;
|
||||
mCurrentCharucoIds = currentCharucoIds;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
CV_UNUSED(frame);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CalibProcessor::detectAndParseACircles(const cv::Mat &frame)
|
||||
{
|
||||
bool isTemplateFound = findCirclesGrid(frame, mBoardSize, mCurrentImagePoints, cv::CALIB_CB_ASYMMETRIC_GRID, mBlobDetectorPtr);
|
||||
if(isTemplateFound) {
|
||||
mTemplateLocations.insert(mTemplateLocations.begin(), mCurrentImagePoints[0]);
|
||||
cv::drawChessboardCorners(frame, mBoardSize, cv::Mat(mCurrentImagePoints), isTemplateFound);
|
||||
}
|
||||
return isTemplateFound;
|
||||
}
|
||||
|
||||
bool CalibProcessor::detectAndParseDualACircles(const cv::Mat &frame)
|
||||
{
|
||||
std::vector<cv::Point2f> blackPointbuf;
|
||||
|
||||
cv::Mat invertedView;
|
||||
cv::bitwise_not(frame, invertedView);
|
||||
bool isWhiteGridFound = cv::findCirclesGrid(frame, mBoardSize, mCurrentImagePoints, cv::CALIB_CB_ASYMMETRIC_GRID, mBlobDetectorPtr);
|
||||
if(!isWhiteGridFound)
|
||||
return false;
|
||||
bool isBlackGridFound = cv::findCirclesGrid(invertedView, mBoardSize, blackPointbuf, cv::CALIB_CB_ASYMMETRIC_GRID, mBlobDetectorPtr);
|
||||
|
||||
if(!isBlackGridFound)
|
||||
{
|
||||
mCurrentImagePoints.clear();
|
||||
return false;
|
||||
}
|
||||
cv::drawChessboardCorners(frame, mBoardSize, cv::Mat(mCurrentImagePoints), isWhiteGridFound);
|
||||
cv::drawChessboardCorners(frame, mBoardSize, cv::Mat(blackPointbuf), isBlackGridFound);
|
||||
mCurrentImagePoints.insert(mCurrentImagePoints.end(), blackPointbuf.begin(), blackPointbuf.end());
|
||||
mTemplateLocations.insert(mTemplateLocations.begin(), mCurrentImagePoints[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CalibProcessor::saveFrameData()
|
||||
{
|
||||
std::vector<cv::Point3f> objectPoints;
|
||||
|
||||
switch(mBoardType)
|
||||
{
|
||||
case Chessboard:
|
||||
objectPoints.reserve(mBoardSize.height*mBoardSize.width);
|
||||
for( int i = 0; i < mBoardSize.height; ++i )
|
||||
for( int j = 0; j < mBoardSize.width; ++j )
|
||||
objectPoints.push_back(cv::Point3f(j*mSquareSize, i*mSquareSize, 0));
|
||||
mCalibData->imagePoints.push_back(mCurrentImagePoints);
|
||||
mCalibData->objectPoints.push_back(objectPoints);
|
||||
break;
|
||||
case chAruco:
|
||||
mCalibData->allCharucoCorners.push_back(mCurrentCharucoCorners);
|
||||
mCalibData->allCharucoIds.push_back(mCurrentCharucoIds);
|
||||
break;
|
||||
case AcirclesGrid:
|
||||
objectPoints.reserve(mBoardSize.height*mBoardSize.width);
|
||||
for( int i = 0; i < mBoardSize.height; i++ )
|
||||
for( int j = 0; j < mBoardSize.width; j++ )
|
||||
objectPoints.push_back(cv::Point3f((2*j + i % 2)*mSquareSize, i*mSquareSize, 0));
|
||||
mCalibData->imagePoints.push_back(mCurrentImagePoints);
|
||||
mCalibData->objectPoints.push_back(objectPoints);
|
||||
break;
|
||||
case DoubleAcirclesGrid:
|
||||
{
|
||||
float gridCenterX = (2*((float)mBoardSize.width - 1) + 1)*mSquareSize + mTemplDist / 2;
|
||||
float gridCenterY = (mBoardSize.height - 1)*mSquareSize / 2;
|
||||
objectPoints.reserve(2*mBoardSize.height*mBoardSize.width);
|
||||
|
||||
//white part
|
||||
for( int i = 0; i < mBoardSize.height; i++ )
|
||||
for( int j = 0; j < mBoardSize.width; j++ )
|
||||
objectPoints.push_back(
|
||||
cv::Point3f(-float((2*j + i % 2)*mSquareSize + mTemplDist +
|
||||
(2*(mBoardSize.width - 1) + 1)*mSquareSize - gridCenterX),
|
||||
-float(i*mSquareSize) - gridCenterY,
|
||||
0));
|
||||
//black part
|
||||
for( int i = 0; i < mBoardSize.height; i++ )
|
||||
for( int j = 0; j < mBoardSize.width; j++ )
|
||||
objectPoints.push_back(cv::Point3f(-float((2*j + i % 2)*mSquareSize - gridCenterX),
|
||||
-float(i*mSquareSize) - gridCenterY, 0));
|
||||
|
||||
mCalibData->imagePoints.push_back(mCurrentImagePoints);
|
||||
mCalibData->objectPoints.push_back(objectPoints);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CalibProcessor::showCaptureMessage(const cv::Mat& frame, const std::string &message)
|
||||
{
|
||||
cv::Point textOrigin(100, 100);
|
||||
double textSize = VIDEO_TEXT_SIZE * frame.cols / (double) IMAGE_MAX_WIDTH;
|
||||
cv::bitwise_not(frame, frame);
|
||||
cv::putText(frame, message, textOrigin, 1, textSize, cv::Scalar(0,0,255), 2, cv::LINE_AA);
|
||||
cv::imshow(mainWindowName, frame);
|
||||
cv::waitKey(300);
|
||||
}
|
||||
|
||||
bool CalibProcessor::checkLastFrame()
|
||||
{
|
||||
bool isFrameBad = false;
|
||||
cv::Mat tmpCamMatrix;
|
||||
const double badAngleThresh = 40;
|
||||
|
||||
if(!mCalibData->cameraMatrix.total()) {
|
||||
tmpCamMatrix = cv::Mat::eye(3, 3, CV_64F);
|
||||
tmpCamMatrix.at<double>(0,0) = 20000;
|
||||
tmpCamMatrix.at<double>(1,1) = 20000;
|
||||
tmpCamMatrix.at<double>(0,2) = mCalibData->imageSize.height/2;
|
||||
tmpCamMatrix.at<double>(1,2) = mCalibData->imageSize.width/2;
|
||||
}
|
||||
else
|
||||
mCalibData->cameraMatrix.copyTo(tmpCamMatrix);
|
||||
|
||||
if(mBoardType != chAruco) {
|
||||
cv::Mat r, t, angles;
|
||||
cv::solvePnP(mCalibData->objectPoints.back(), mCurrentImagePoints, tmpCamMatrix, mCalibData->distCoeffs, r, t);
|
||||
RodriguesToEuler(r, angles, CALIB_DEGREES);
|
||||
|
||||
if(fabs(angles.at<double>(0)) > badAngleThresh || fabs(angles.at<double>(1)) > badAngleThresh) {
|
||||
mCalibData->objectPoints.pop_back();
|
||||
mCalibData->imagePoints.pop_back();
|
||||
isFrameBad = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
cv::Mat r, t, angles;
|
||||
std::vector<cv::Point3f> allObjPoints;
|
||||
allObjPoints.reserve(mCurrentCharucoIds.total());
|
||||
for(size_t i = 0; i < mCurrentCharucoIds.total(); i++) {
|
||||
int pointID = mCurrentCharucoIds.at<int>((int)i);
|
||||
CV_Assert(pointID >= 0 && pointID < (int)mCharucoBoard->chessboardCorners.size());
|
||||
allObjPoints.push_back(mCharucoBoard->chessboardCorners[pointID]);
|
||||
}
|
||||
|
||||
cv::solvePnP(allObjPoints, mCurrentCharucoCorners, tmpCamMatrix, mCalibData->distCoeffs, r, t);
|
||||
RodriguesToEuler(r, angles, CALIB_DEGREES);
|
||||
|
||||
if(180.0 - fabs(angles.at<double>(0)) > badAngleThresh || fabs(angles.at<double>(1)) > badAngleThresh) {
|
||||
isFrameBad = true;
|
||||
mCalibData->allCharucoCorners.pop_back();
|
||||
mCalibData->allCharucoIds.pop_back();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return isFrameBad;
|
||||
}
|
||||
|
||||
CalibProcessor::CalibProcessor(cv::Ptr<calibrationData> data, captureParameters &capParams) :
|
||||
mCalibData(data), mBoardType(capParams.board), mBoardSize(capParams.boardSize)
|
||||
{
|
||||
mCapuredFrames = 0;
|
||||
mNeededFramesNum = capParams.calibrationStep;
|
||||
mDelayBetweenCaptures = static_cast<int>(capParams.captureDelay * capParams.fps);
|
||||
mMaxTemplateOffset = std::sqrt(static_cast<float>(mCalibData->imageSize.height * mCalibData->imageSize.height) +
|
||||
static_cast<float>(mCalibData->imageSize.width * mCalibData->imageSize.width)) / 20.0;
|
||||
mSquareSize = capParams.squareSize;
|
||||
mTemplDist = capParams.templDst;
|
||||
|
||||
switch(mBoardType)
|
||||
{
|
||||
case chAruco:
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
mArucoDictionary = cv::aruco::getPredefinedDictionary(
|
||||
cv::aruco::PREDEFINED_DICTIONARY_NAME(capParams.charucoDictName));
|
||||
mCharucoBoard = cv::aruco::CharucoBoard::create(mBoardSize.width, mBoardSize.height, capParams.charucoSquareLenght,
|
||||
capParams.charucoMarkerSize, mArucoDictionary);
|
||||
#endif
|
||||
break;
|
||||
case AcirclesGrid:
|
||||
mBlobDetectorPtr = cv::SimpleBlobDetector::create();
|
||||
break;
|
||||
case DoubleAcirclesGrid:
|
||||
mBlobDetectorPtr = cv::SimpleBlobDetector::create(getDetectorParams());
|
||||
break;
|
||||
case Chessboard:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat CalibProcessor::processFrame(const cv::Mat &frame)
|
||||
{
|
||||
cv::Mat frameCopy;
|
||||
frame.copyTo(frameCopy);
|
||||
bool isTemplateFound = false;
|
||||
mCurrentImagePoints.clear();
|
||||
|
||||
switch(mBoardType)
|
||||
{
|
||||
case Chessboard:
|
||||
isTemplateFound = detectAndParseChessboard(frameCopy);
|
||||
break;
|
||||
case chAruco:
|
||||
isTemplateFound = detectAndParseChAruco(frameCopy);
|
||||
break;
|
||||
case AcirclesGrid:
|
||||
isTemplateFound = detectAndParseACircles(frameCopy);
|
||||
break;
|
||||
case DoubleAcirclesGrid:
|
||||
isTemplateFound = detectAndParseDualACircles(frameCopy);
|
||||
break;
|
||||
}
|
||||
|
||||
if(mTemplateLocations.size() > mDelayBetweenCaptures)
|
||||
mTemplateLocations.pop_back();
|
||||
if(mTemplateLocations.size() == mDelayBetweenCaptures && isTemplateFound) {
|
||||
if(cv::norm(mTemplateLocations.front() - mTemplateLocations.back()) < mMaxTemplateOffset) {
|
||||
saveFrameData();
|
||||
bool isFrameBad = checkLastFrame();
|
||||
if (!isFrameBad) {
|
||||
std::string displayMessage = cv::format("Frame # %zu captured", std::max(mCalibData->imagePoints.size(),
|
||||
mCalibData->allCharucoCorners.size()));
|
||||
if(!showOverlayMessage(displayMessage))
|
||||
showCaptureMessage(frame, displayMessage);
|
||||
mCapuredFrames++;
|
||||
}
|
||||
else {
|
||||
std::string displayMessage = "Frame rejected";
|
||||
if(!showOverlayMessage(displayMessage))
|
||||
showCaptureMessage(frame, displayMessage);
|
||||
}
|
||||
mTemplateLocations.clear();
|
||||
mTemplateLocations.reserve(mDelayBetweenCaptures);
|
||||
}
|
||||
}
|
||||
|
||||
return frameCopy;
|
||||
}
|
||||
|
||||
bool CalibProcessor::isProcessed() const
|
||||
{
|
||||
if(mCapuredFrames < mNeededFramesNum)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void CalibProcessor::resetState()
|
||||
{
|
||||
mCapuredFrames = 0;
|
||||
mTemplateLocations.clear();
|
||||
}
|
||||
|
||||
CalibProcessor::~CalibProcessor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
|
||||
void ShowProcessor::drawBoard(cv::Mat &img, cv::InputArray points)
|
||||
{
|
||||
cv::Mat tmpView = cv::Mat::zeros(img.rows, img.cols, CV_8UC3);
|
||||
std::vector<cv::Point2f> templateHull;
|
||||
std::vector<cv::Point> poly;
|
||||
cv::convexHull(points, templateHull);
|
||||
poly.resize(templateHull.size());
|
||||
for(size_t i=0; i<templateHull.size();i++)
|
||||
poly[i] = cv::Point((int)(templateHull[i].x*mGridViewScale), (int)(templateHull[i].y*mGridViewScale));
|
||||
cv::fillConvexPoly(tmpView, poly, cv::Scalar(0, 255, 0), cv::LINE_AA);
|
||||
cv::addWeighted(tmpView, .2, img, 1, 0, img);
|
||||
}
|
||||
|
||||
void ShowProcessor::drawGridPoints(const cv::Mat &frame)
|
||||
{
|
||||
if(mBoardType != chAruco)
|
||||
for(std::vector<std::vector<cv::Point2f> >::iterator it = mCalibdata->imagePoints.begin(); it != mCalibdata->imagePoints.end(); ++it)
|
||||
for(std::vector<cv::Point2f>::iterator pointIt = (*it).begin(); pointIt != (*it).end(); ++pointIt)
|
||||
cv::circle(frame, *pointIt, POINT_SIZE, cv::Scalar(0, 255, 0), 1, cv::LINE_AA);
|
||||
else
|
||||
for(std::vector<cv::Mat>::iterator it = mCalibdata->allCharucoCorners.begin(); it != mCalibdata->allCharucoCorners.end(); ++it)
|
||||
for(int i = 0; i < (*it).size[0]; i++)
|
||||
cv::circle(frame, cv::Point((int)(*it).at<float>(i, 0), (int)(*it).at<float>(i, 1)),
|
||||
POINT_SIZE, cv::Scalar(0, 255, 0), 1, cv::LINE_AA);
|
||||
}
|
||||
|
||||
ShowProcessor::ShowProcessor(cv::Ptr<calibrationData> data, cv::Ptr<calibController> controller, TemplateType board) :
|
||||
mCalibdata(data), mController(controller), mBoardType(board)
|
||||
{
|
||||
mNeedUndistort = true;
|
||||
mVisMode = Grid;
|
||||
mGridViewScale = 0.5;
|
||||
mTextSize = VIDEO_TEXT_SIZE;
|
||||
}
|
||||
|
||||
cv::Mat ShowProcessor::processFrame(const cv::Mat &frame)
|
||||
{
|
||||
if (!mCalibdata->cameraMatrix.empty() && !mCalibdata->distCoeffs.empty())
|
||||
{
|
||||
mTextSize = VIDEO_TEXT_SIZE * (double) frame.cols / IMAGE_MAX_WIDTH;
|
||||
cv::Scalar textColor = cv::Scalar(0,0,255);
|
||||
cv::Mat frameCopy;
|
||||
|
||||
if (mNeedUndistort && mController->getFramesNumberState()) {
|
||||
if(mVisMode == Grid)
|
||||
drawGridPoints(frame);
|
||||
cv::remap(frame, frameCopy, mCalibdata->undistMap1, mCalibdata->undistMap2, cv::INTER_LINEAR);
|
||||
int baseLine = 100;
|
||||
cv::Size textSize = cv::getTextSize("Undistorted view", 1, mTextSize, 2, &baseLine);
|
||||
cv::Point textOrigin(baseLine, frame.rows - (int)(2.5*textSize.height));
|
||||
cv::putText(frameCopy, "Undistorted view", textOrigin, 1, mTextSize, textColor, 2, cv::LINE_AA);
|
||||
}
|
||||
else {
|
||||
frame.copyTo(frameCopy);
|
||||
if(mVisMode == Grid)
|
||||
drawGridPoints(frameCopy);
|
||||
}
|
||||
std::string displayMessage;
|
||||
if(mCalibdata->stdDeviations.at<double>(0) == 0)
|
||||
displayMessage = cv::format("F = %d RMS = %.3f", (int)mCalibdata->cameraMatrix.at<double>(0,0), mCalibdata->totalAvgErr);
|
||||
else
|
||||
displayMessage = cv::format("Fx = %d Fy = %d RMS = %.3f", (int)mCalibdata->cameraMatrix.at<double>(0,0),
|
||||
(int)mCalibdata->cameraMatrix.at<double>(1,1), mCalibdata->totalAvgErr);
|
||||
if(mController->getRMSState() && mController->getFramesNumberState())
|
||||
displayMessage.append(" OK");
|
||||
|
||||
int baseLine = 100;
|
||||
cv::Size textSize = cv::getTextSize(displayMessage, 1, mTextSize - 1, 2, &baseLine);
|
||||
cv::Point textOrigin = cv::Point(baseLine, 2*textSize.height);
|
||||
cv::putText(frameCopy, displayMessage, textOrigin, 1, mTextSize - 1, textColor, 2, cv::LINE_AA);
|
||||
|
||||
if(mCalibdata->stdDeviations.at<double>(0) == 0)
|
||||
displayMessage = cv::format("DF = %.2f", mCalibdata->stdDeviations.at<double>(1)*sigmaMult);
|
||||
else
|
||||
displayMessage = cv::format("DFx = %.2f DFy = %.2f", mCalibdata->stdDeviations.at<double>(0)*sigmaMult,
|
||||
mCalibdata->stdDeviations.at<double>(1)*sigmaMult);
|
||||
if(mController->getConfidenceIntrervalsState() && mController->getFramesNumberState())
|
||||
displayMessage.append(" OK");
|
||||
cv::putText(frameCopy, displayMessage, cv::Point(baseLine, 4*textSize.height), 1, mTextSize - 1, textColor, 2, cv::LINE_AA);
|
||||
|
||||
if(mController->getCommonCalibrationState()) {
|
||||
displayMessage = cv::format("Calibration is done");
|
||||
cv::putText(frameCopy, displayMessage, cv::Point(baseLine, 6*textSize.height), 1, mTextSize - 1, textColor, 2, cv::LINE_AA);
|
||||
}
|
||||
int calibFlags = mController->getNewFlags();
|
||||
displayMessage = "";
|
||||
if(!(calibFlags & cv::CALIB_FIX_ASPECT_RATIO))
|
||||
displayMessage.append(cv::format("AR=%.3f ", mCalibdata->cameraMatrix.at<double>(0,0)/mCalibdata->cameraMatrix.at<double>(1,1)));
|
||||
if(calibFlags & cv::CALIB_ZERO_TANGENT_DIST)
|
||||
displayMessage.append("TD=0 ");
|
||||
displayMessage.append(cv::format("K1=%.2f K2=%.2f K3=%.2f", mCalibdata->distCoeffs.at<double>(0), mCalibdata->distCoeffs.at<double>(1),
|
||||
mCalibdata->distCoeffs.at<double>(4)));
|
||||
cv::putText(frameCopy, displayMessage, cv::Point(baseLine, frameCopy.rows - (int)(1.5*textSize.height)),
|
||||
1, mTextSize - 1, textColor, 2, cv::LINE_AA);
|
||||
return frameCopy;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool ShowProcessor::isProcessed() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowProcessor::resetState()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ShowProcessor::setVisualizationMode(visualisationMode mode)
|
||||
{
|
||||
mVisMode = mode;
|
||||
}
|
||||
|
||||
void ShowProcessor::switchVisualizationMode()
|
||||
{
|
||||
if(mVisMode == Grid) {
|
||||
mVisMode = Window;
|
||||
updateBoardsView();
|
||||
}
|
||||
else {
|
||||
mVisMode = Grid;
|
||||
cv::destroyWindow(gridWindowName);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowProcessor::clearBoardsView()
|
||||
{
|
||||
cv::imshow(gridWindowName, cv::Mat());
|
||||
}
|
||||
|
||||
void ShowProcessor::updateBoardsView()
|
||||
{
|
||||
if(mVisMode == Window) {
|
||||
cv::Size originSize = mCalibdata->imageSize;
|
||||
cv::Mat altGridView = cv::Mat::zeros((int)(originSize.height*mGridViewScale), (int)(originSize.width*mGridViewScale), CV_8UC3);
|
||||
if(mBoardType != chAruco)
|
||||
for(std::vector<std::vector<cv::Point2f> >::iterator it = mCalibdata->imagePoints.begin(); it != mCalibdata->imagePoints.end(); ++it)
|
||||
if(mBoardType != DoubleAcirclesGrid)
|
||||
drawBoard(altGridView, *it);
|
||||
else {
|
||||
size_t pointsNum = (*it).size()/2;
|
||||
std::vector<cv::Point2f> points(pointsNum);
|
||||
std::copy((*it).begin(), (*it).begin() + pointsNum, points.begin());
|
||||
drawBoard(altGridView, points);
|
||||
std::copy((*it).begin() + pointsNum, (*it).begin() + 2*pointsNum, points.begin());
|
||||
drawBoard(altGridView, points);
|
||||
}
|
||||
else
|
||||
for(std::vector<cv::Mat>::iterator it = mCalibdata->allCharucoCorners.begin(); it != mCalibdata->allCharucoCorners.end(); ++it)
|
||||
drawBoard(altGridView, *it);
|
||||
cv::imshow(gridWindowName, altGridView);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowProcessor::switchUndistort()
|
||||
{
|
||||
mNeedUndistort = !mNeedUndistort;
|
||||
}
|
||||
|
||||
void ShowProcessor::setUndistort(bool isEnabled)
|
||||
{
|
||||
mNeedUndistort = isEnabled;
|
||||
}
|
||||
|
||||
ShowProcessor::~ShowProcessor()
|
||||
{
|
||||
|
||||
}
|
104
opencv-apps/interactive-calibration/frameProcessor.hpp
Normal file
104
opencv-apps/interactive-calibration/frameProcessor.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef FRAME_PROCESSOR_HPP
|
||||
#define FRAME_PROCESSOR_HPP
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/calib3d.hpp>
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
#include <opencv2/aruco/charuco.hpp>
|
||||
#endif
|
||||
|
||||
#include "calibCommon.hpp"
|
||||
#include "calibController.hpp"
|
||||
|
||||
namespace calib
|
||||
{
|
||||
class FrameProcessor
|
||||
{
|
||||
protected:
|
||||
|
||||
public:
|
||||
virtual ~FrameProcessor();
|
||||
virtual cv::Mat processFrame(const cv::Mat& frame) = 0;
|
||||
virtual bool isProcessed() const = 0;
|
||||
virtual void resetState() = 0;
|
||||
};
|
||||
|
||||
class CalibProcessor : public FrameProcessor
|
||||
{
|
||||
protected:
|
||||
cv::Ptr<calibrationData> mCalibData;
|
||||
TemplateType mBoardType;
|
||||
cv::Size mBoardSize;
|
||||
std::vector<cv::Point2f> mTemplateLocations;
|
||||
std::vector<cv::Point2f> mCurrentImagePoints;
|
||||
cv::Mat mCurrentCharucoCorners;
|
||||
cv::Mat mCurrentCharucoIds;
|
||||
|
||||
cv::Ptr<cv::SimpleBlobDetector> mBlobDetectorPtr;
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
cv::Ptr<cv::aruco::Dictionary> mArucoDictionary;
|
||||
cv::Ptr<cv::aruco::CharucoBoard> mCharucoBoard;
|
||||
#endif
|
||||
|
||||
int mNeededFramesNum;
|
||||
unsigned mDelayBetweenCaptures;
|
||||
int mCapuredFrames;
|
||||
double mMaxTemplateOffset;
|
||||
float mSquareSize;
|
||||
float mTemplDist;
|
||||
|
||||
bool detectAndParseChessboard(const cv::Mat& frame);
|
||||
bool detectAndParseChAruco(const cv::Mat& frame);
|
||||
bool detectAndParseACircles(const cv::Mat& frame);
|
||||
bool detectAndParseDualACircles(const cv::Mat& frame);
|
||||
void saveFrameData();
|
||||
void showCaptureMessage(const cv::Mat &frame, const std::string& message);
|
||||
bool checkLastFrame();
|
||||
|
||||
public:
|
||||
CalibProcessor(cv::Ptr<calibrationData> data, captureParameters& capParams);
|
||||
virtual cv::Mat processFrame(const cv::Mat& frame) CV_OVERRIDE;
|
||||
virtual bool isProcessed() const CV_OVERRIDE;
|
||||
virtual void resetState() CV_OVERRIDE;
|
||||
~CalibProcessor() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
enum visualisationMode {Grid, Window};
|
||||
|
||||
class ShowProcessor : public FrameProcessor
|
||||
{
|
||||
protected:
|
||||
cv::Ptr<calibrationData> mCalibdata;
|
||||
cv::Ptr<calibController> mController;
|
||||
TemplateType mBoardType;
|
||||
visualisationMode mVisMode;
|
||||
bool mNeedUndistort;
|
||||
double mGridViewScale;
|
||||
double mTextSize;
|
||||
|
||||
void drawBoard(cv::Mat& img, cv::InputArray points);
|
||||
void drawGridPoints(const cv::Mat& frame);
|
||||
public:
|
||||
ShowProcessor(cv::Ptr<calibrationData> data, cv::Ptr<calibController> controller, TemplateType board);
|
||||
virtual cv::Mat processFrame(const cv::Mat& frame) CV_OVERRIDE;
|
||||
virtual bool isProcessed() const CV_OVERRIDE;
|
||||
virtual void resetState() CV_OVERRIDE;
|
||||
|
||||
void setVisualizationMode(visualisationMode mode);
|
||||
void switchVisualizationMode();
|
||||
void clearBoardsView();
|
||||
void updateBoardsView();
|
||||
|
||||
void switchUndistort();
|
||||
void setUndistort(bool isEnabled);
|
||||
~ShowProcessor() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
225
opencv-apps/interactive-calibration/main.cpp
Normal file
225
opencv-apps/interactive-calibration/main.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/calib3d.hpp>
|
||||
#include <opencv2/cvconfig.h>
|
||||
#include <opencv2/highgui.hpp>
|
||||
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
#include <opencv2/aruco/charuco.hpp>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include "calibCommon.hpp"
|
||||
#include "calibPipeline.hpp"
|
||||
#include "frameProcessor.hpp"
|
||||
#include "calibController.hpp"
|
||||
#include "parametersController.hpp"
|
||||
#include "rotationConverters.hpp"
|
||||
|
||||
using namespace calib;
|
||||
|
||||
const std::string keys =
|
||||
"{v | | Input from video file }"
|
||||
"{ci | 0 | Default camera id }"
|
||||
"{flip | false | Vertical flip of input frames }"
|
||||
"{t | circles | Template for calibration (circles, chessboard, dualCircles, charuco) }"
|
||||
"{sz | 16.3 | Distance between two nearest centers of circles or squares on calibration board}"
|
||||
"{dst | 295 | Distance between white and black parts of daulCircles template}"
|
||||
"{w | | Width of template (in corners or circles)}"
|
||||
"{h | | Height of template (in corners or circles)}"
|
||||
"{of | cameraParameters.xml | Output file name}"
|
||||
"{ft | true | Auto tuning of calibration flags}"
|
||||
"{vis | grid | Captured boards visualisation (grid, window)}"
|
||||
"{d | 0.8 | Min delay between captures}"
|
||||
"{pf | defaultConfig.xml| Advanced application parameters}"
|
||||
"{help | | Print help}";
|
||||
|
||||
bool calib::showOverlayMessage(const std::string& message)
|
||||
{
|
||||
#ifdef HAVE_QT
|
||||
cv::displayOverlay(mainWindowName, message, OVERLAY_DELAY);
|
||||
return true;
|
||||
#else
|
||||
std::cout << message << std::endl;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void deleteButton(int, void* data)
|
||||
{
|
||||
(static_cast<cv::Ptr<calibDataController>*>(data))->get()->deleteLastFrame();
|
||||
calib::showOverlayMessage("Last frame deleted");
|
||||
}
|
||||
|
||||
static void deleteAllButton(int, void* data)
|
||||
{
|
||||
(static_cast<cv::Ptr<calibDataController>*>(data))->get()->deleteAllData();
|
||||
calib::showOverlayMessage("All frames deleted");
|
||||
}
|
||||
|
||||
static void saveCurrentParamsButton(int, void* data)
|
||||
{
|
||||
if((static_cast<cv::Ptr<calibDataController>*>(data))->get()->saveCurrentCameraParameters())
|
||||
calib::showOverlayMessage("Calibration parameters saved");
|
||||
}
|
||||
|
||||
#ifdef HAVE_QT
|
||||
static void switchVisualizationModeButton(int, void* data)
|
||||
{
|
||||
ShowProcessor* processor = static_cast<ShowProcessor*>(((cv::Ptr<FrameProcessor>*)data)->get());
|
||||
processor->switchVisualizationMode();
|
||||
}
|
||||
|
||||
static void undistortButton(int state, void* data)
|
||||
{
|
||||
ShowProcessor* processor = static_cast<ShowProcessor*>(((cv::Ptr<FrameProcessor>*)data)->get());
|
||||
processor->setUndistort(static_cast<bool>(state));
|
||||
calib::showOverlayMessage(std::string("Undistort is ") +
|
||||
(static_cast<bool>(state) ? std::string("on") : std::string("off")));
|
||||
}
|
||||
#endif //HAVE_QT
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, keys);
|
||||
if(parser.has("help")) {
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
std::cout << consoleHelp << std::endl;
|
||||
parametersController paramsController;
|
||||
|
||||
if(!paramsController.loadFromParser(parser))
|
||||
return 0;
|
||||
|
||||
captureParameters capParams = paramsController.getCaptureParameters();
|
||||
internalParameters intParams = paramsController.getInternalParameters();
|
||||
#ifndef HAVE_OPENCV_ARUCO
|
||||
if(capParams.board == chAruco)
|
||||
CV_Error(cv::Error::StsNotImplemented, "Aruco module is disabled in current build configuration."
|
||||
" Consider usage of another calibration pattern\n");
|
||||
#endif
|
||||
|
||||
cv::TermCriteria solverTermCrit = cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS,
|
||||
intParams.solverMaxIters, intParams.solverEps);
|
||||
cv::Ptr<calibrationData> globalData(new calibrationData);
|
||||
if(!parser.has("v")) globalData->imageSize = capParams.cameraResolution;
|
||||
|
||||
int calibrationFlags = 0;
|
||||
if(intParams.fastSolving) calibrationFlags |= cv::CALIB_USE_QR;
|
||||
cv::Ptr<calibController> controller(new calibController(globalData, calibrationFlags,
|
||||
parser.get<bool>("ft"), capParams.minFramesNum));
|
||||
cv::Ptr<calibDataController> dataController(new calibDataController(globalData, capParams.maxFramesNum,
|
||||
intParams.filterAlpha));
|
||||
dataController->setParametersFileName(parser.get<std::string>("of"));
|
||||
|
||||
cv::Ptr<FrameProcessor> capProcessor, showProcessor;
|
||||
capProcessor = cv::Ptr<FrameProcessor>(new CalibProcessor(globalData, capParams));
|
||||
showProcessor = cv::Ptr<FrameProcessor>(new ShowProcessor(globalData, controller, capParams.board));
|
||||
|
||||
if(parser.get<std::string>("vis").find("window") == 0) {
|
||||
static_cast<ShowProcessor*>(showProcessor.get())->setVisualizationMode(Window);
|
||||
cv::namedWindow(gridWindowName);
|
||||
cv::moveWindow(gridWindowName, 1280, 500);
|
||||
}
|
||||
|
||||
cv::Ptr<CalibPipeline> pipeline(new CalibPipeline(capParams));
|
||||
std::vector<cv::Ptr<FrameProcessor> > processors;
|
||||
processors.push_back(capProcessor);
|
||||
processors.push_back(showProcessor);
|
||||
|
||||
cv::namedWindow(mainWindowName);
|
||||
cv::moveWindow(mainWindowName, 10, 10);
|
||||
#ifdef HAVE_QT
|
||||
cv::createButton("Delete last frame", deleteButton, &dataController,
|
||||
cv::QT_PUSH_BUTTON | cv::QT_NEW_BUTTONBAR);
|
||||
cv::createButton("Delete all frames", deleteAllButton, &dataController,
|
||||
cv::QT_PUSH_BUTTON | cv::QT_NEW_BUTTONBAR);
|
||||
cv::createButton("Undistort", undistortButton, &showProcessor,
|
||||
cv::QT_CHECKBOX | cv::QT_NEW_BUTTONBAR, false);
|
||||
cv::createButton("Save current parameters", saveCurrentParamsButton, &dataController,
|
||||
cv::QT_PUSH_BUTTON | cv::QT_NEW_BUTTONBAR);
|
||||
cv::createButton("Switch visualisation mode", switchVisualizationModeButton, &showProcessor,
|
||||
cv::QT_PUSH_BUTTON | cv::QT_NEW_BUTTONBAR);
|
||||
#endif //HAVE_QT
|
||||
try {
|
||||
bool pipelineFinished = false;
|
||||
while(!pipelineFinished)
|
||||
{
|
||||
PipelineExitStatus exitStatus = pipeline->start(processors);
|
||||
if (exitStatus == Finished) {
|
||||
if(controller->getCommonCalibrationState())
|
||||
saveCurrentParamsButton(0, &dataController);
|
||||
pipelineFinished = true;
|
||||
continue;
|
||||
}
|
||||
else if (exitStatus == Calibrate) {
|
||||
|
||||
dataController->rememberCurrentParameters();
|
||||
globalData->imageSize = pipeline->getImageSize();
|
||||
calibrationFlags = controller->getNewFlags();
|
||||
|
||||
if(capParams.board != chAruco) {
|
||||
globalData->totalAvgErr =
|
||||
cv::calibrateCamera(globalData->objectPoints, globalData->imagePoints,
|
||||
globalData->imageSize, globalData->cameraMatrix,
|
||||
globalData->distCoeffs, cv::noArray(), cv::noArray(),
|
||||
globalData->stdDeviations, cv::noArray(), globalData->perViewErrors,
|
||||
calibrationFlags, solverTermCrit);
|
||||
}
|
||||
else {
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
cv::Ptr<cv::aruco::Dictionary> dictionary =
|
||||
cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME(capParams.charucoDictName));
|
||||
cv::Ptr<cv::aruco::CharucoBoard> charucoboard =
|
||||
cv::aruco::CharucoBoard::create(capParams.boardSize.width, capParams.boardSize.height,
|
||||
capParams.charucoSquareLenght, capParams.charucoMarkerSize, dictionary);
|
||||
globalData->totalAvgErr =
|
||||
cv::aruco::calibrateCameraCharuco(globalData->allCharucoCorners, globalData->allCharucoIds,
|
||||
charucoboard, globalData->imageSize,
|
||||
globalData->cameraMatrix, globalData->distCoeffs,
|
||||
cv::noArray(), cv::noArray(), globalData->stdDeviations, cv::noArray(),
|
||||
globalData->perViewErrors, calibrationFlags, solverTermCrit);
|
||||
#endif
|
||||
}
|
||||
dataController->updateUndistortMap();
|
||||
dataController->printParametersToConsole(std::cout);
|
||||
controller->updateState();
|
||||
for(int j = 0; j < capParams.calibrationStep; j++)
|
||||
dataController->filterFrames();
|
||||
static_cast<ShowProcessor*>(showProcessor.get())->updateBoardsView();
|
||||
}
|
||||
else if (exitStatus == DeleteLastFrame) {
|
||||
deleteButton(0, &dataController);
|
||||
static_cast<ShowProcessor*>(showProcessor.get())->updateBoardsView();
|
||||
}
|
||||
else if (exitStatus == DeleteAllFrames) {
|
||||
deleteAllButton(0, &dataController);
|
||||
static_cast<ShowProcessor*>(showProcessor.get())->updateBoardsView();
|
||||
}
|
||||
else if (exitStatus == SaveCurrentData) {
|
||||
saveCurrentParamsButton(0, &dataController);
|
||||
}
|
||||
else if (exitStatus == SwitchUndistort)
|
||||
static_cast<ShowProcessor*>(showProcessor.get())->switchUndistort();
|
||||
else if (exitStatus == SwitchVisualisation)
|
||||
static_cast<ShowProcessor*>(showProcessor.get())->switchVisualizationMode();
|
||||
|
||||
for (std::vector<cv::Ptr<FrameProcessor> >::iterator it = processors.begin(); it != processors.end(); ++it)
|
||||
(*it)->resetState();
|
||||
}
|
||||
}
|
||||
catch (const std::runtime_error& exp) {
|
||||
std::cout << exp.what() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
143
opencv-apps/interactive-calibration/parametersController.cpp
Normal file
143
opencv-apps/interactive-calibration/parametersController.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "parametersController.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
template <typename T>
|
||||
static bool readFromNode(cv::FileNode node, T& value)
|
||||
{
|
||||
if(!node.isNone()) {
|
||||
node >> value;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool checkAssertion(bool value, const std::string& msg)
|
||||
{
|
||||
if(!value)
|
||||
std::cerr << "Error: " << msg << std::endl;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool calib::parametersController::loadFromFile(const std::string &inputFileName)
|
||||
{
|
||||
cv::FileStorage reader;
|
||||
reader.open(inputFileName, cv::FileStorage::READ);
|
||||
|
||||
if(!reader.isOpened()) {
|
||||
std::cerr << "Warning: Unable to open " << inputFileName <<
|
||||
" Applicatioin stated with default advanced parameters" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
readFromNode(reader["charuco_dict"], mCapParams.charucoDictName);
|
||||
readFromNode(reader["charuco_square_lenght"], mCapParams.charucoSquareLenght);
|
||||
readFromNode(reader["charuco_marker_size"], mCapParams.charucoMarkerSize);
|
||||
readFromNode(reader["camera_resolution"], mCapParams.cameraResolution);
|
||||
readFromNode(reader["calibration_step"], mCapParams.calibrationStep);
|
||||
readFromNode(reader["max_frames_num"], mCapParams.maxFramesNum);
|
||||
readFromNode(reader["min_frames_num"], mCapParams.minFramesNum);
|
||||
readFromNode(reader["solver_eps"], mInternalParameters.solverEps);
|
||||
readFromNode(reader["solver_max_iters"], mInternalParameters.solverMaxIters);
|
||||
readFromNode(reader["fast_solver"], mInternalParameters.fastSolving);
|
||||
readFromNode(reader["frame_filter_conv_param"], mInternalParameters.filterAlpha);
|
||||
|
||||
bool retValue =
|
||||
checkAssertion(mCapParams.charucoDictName >= 0, "Dict name must be >= 0") &&
|
||||
checkAssertion(mCapParams.charucoMarkerSize > 0, "Marker size must be positive") &&
|
||||
checkAssertion(mCapParams.charucoSquareLenght > 0, "Square size must be positive") &&
|
||||
checkAssertion(mCapParams.minFramesNum > 1, "Minimal number of frames for calibration < 1") &&
|
||||
checkAssertion(mCapParams.calibrationStep > 0, "Calibration step must be positive") &&
|
||||
checkAssertion(mCapParams.maxFramesNum > mCapParams.minFramesNum, "maxFramesNum < minFramesNum") &&
|
||||
checkAssertion(mInternalParameters.solverEps > 0, "Solver precision must be positive") &&
|
||||
checkAssertion(mInternalParameters.solverMaxIters > 0, "Max solver iterations number must be positive") &&
|
||||
checkAssertion(mInternalParameters.filterAlpha >=0 && mInternalParameters.filterAlpha <=1 ,
|
||||
"Frame filter convolution parameter must be in [0,1] interval") &&
|
||||
checkAssertion(mCapParams.cameraResolution.width > 0 && mCapParams.cameraResolution.height > 0,
|
||||
"Wrong camera resolution values");
|
||||
|
||||
reader.release();
|
||||
return retValue;
|
||||
}
|
||||
|
||||
calib::parametersController::parametersController()
|
||||
{
|
||||
}
|
||||
|
||||
calib::captureParameters calib::parametersController::getCaptureParameters() const
|
||||
{
|
||||
return mCapParams;
|
||||
}
|
||||
|
||||
calib::internalParameters calib::parametersController::getInternalParameters() const
|
||||
{
|
||||
return mInternalParameters;
|
||||
}
|
||||
|
||||
bool calib::parametersController::loadFromParser(cv::CommandLineParser &parser)
|
||||
{
|
||||
mCapParams.flipVertical = parser.get<bool>("flip");
|
||||
mCapParams.captureDelay = parser.get<float>("d");
|
||||
mCapParams.squareSize = parser.get<float>("sz");
|
||||
mCapParams.templDst = parser.get<float>("dst");
|
||||
|
||||
if(!checkAssertion(mCapParams.squareSize > 0, "Distance between corners or circles must be positive"))
|
||||
return false;
|
||||
if(!checkAssertion(mCapParams.templDst > 0, "Distance between parts of dual template must be positive"))
|
||||
return false;
|
||||
|
||||
if (parser.has("v")) {
|
||||
mCapParams.source = File;
|
||||
mCapParams.videoFileName = parser.get<std::string>("v");
|
||||
}
|
||||
else {
|
||||
mCapParams.source = Camera;
|
||||
mCapParams.camID = parser.get<int>("ci");
|
||||
}
|
||||
|
||||
std::string templateType = parser.get<std::string>("t");
|
||||
|
||||
if(templateType.find("circles", 0) == 0) {
|
||||
mCapParams.board = AcirclesGrid;
|
||||
mCapParams.boardSize = cv::Size(4, 11);
|
||||
}
|
||||
else if(templateType.find("chessboard", 0) == 0) {
|
||||
mCapParams.board = Chessboard;
|
||||
mCapParams.boardSize = cv::Size(7, 7);
|
||||
}
|
||||
else if(templateType.find("dualcircles", 0) == 0) {
|
||||
mCapParams.board = DoubleAcirclesGrid;
|
||||
mCapParams.boardSize = cv::Size(4, 11);
|
||||
}
|
||||
else if(templateType.find("charuco", 0) == 0) {
|
||||
mCapParams.board = chAruco;
|
||||
mCapParams.boardSize = cv::Size(6, 8);
|
||||
mCapParams.charucoDictName = 0;
|
||||
mCapParams.charucoSquareLenght = 200;
|
||||
mCapParams.charucoMarkerSize = 100;
|
||||
}
|
||||
else {
|
||||
std::cerr << "Wrong template name\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(parser.has("w") && parser.has("h")) {
|
||||
mCapParams.boardSize = cv::Size(parser.get<int>("w"), parser.get<int>("h"));
|
||||
if(!checkAssertion(mCapParams.boardSize.width > 0 || mCapParams.boardSize.height > 0,
|
||||
"Board size must be positive"))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!checkAssertion(parser.get<std::string>("of").find(".xml") > 0,
|
||||
"Wrong output file name: correct format is [name].xml"))
|
||||
return false;
|
||||
|
||||
loadFromFile(parser.get<std::string>("pf"));
|
||||
return true;
|
||||
}
|
35
opencv-apps/interactive-calibration/parametersController.hpp
Normal file
35
opencv-apps/interactive-calibration/parametersController.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef PARAMETERS_CONTROLLER_HPP
|
||||
#define PARAMETERS_CONTROLLER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#include "calibCommon.hpp"
|
||||
|
||||
namespace calib {
|
||||
|
||||
class parametersController
|
||||
{
|
||||
protected:
|
||||
captureParameters mCapParams;
|
||||
internalParameters mInternalParameters;
|
||||
|
||||
bool loadFromFile(const std::string& inputFileName);
|
||||
public:
|
||||
parametersController();
|
||||
parametersController(cv::Ptr<captureParameters> params);
|
||||
|
||||
captureParameters getCaptureParameters() const;
|
||||
internalParameters getInternalParameters() const;
|
||||
|
||||
bool loadFromParser(cv::CommandLineParser& parser);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
126
opencv-apps/interactive-calibration/rotationConverters.cpp
Normal file
126
opencv-apps/interactive-calibration/rotationConverters.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "rotationConverters.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <opencv2/calib3d.hpp>
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#define CALIB_PI 3.14159265358979323846
|
||||
#define CALIB_PI_2 1.57079632679489661923
|
||||
|
||||
void calib::Euler(const cv::Mat& src, cv::Mat& dst, int argType)
|
||||
{
|
||||
if((src.rows == 3) && (src.cols == 3))
|
||||
{
|
||||
//convert rotation matrix to 3 angles (pitch, yaw, roll)
|
||||
dst = cv::Mat(3, 1, CV_64F);
|
||||
double pitch, yaw, roll;
|
||||
|
||||
if(src.at<double>(0,2) < -0.998)
|
||||
{
|
||||
pitch = -atan2(src.at<double>(1,0), src.at<double>(1,1));
|
||||
yaw = -CALIB_PI_2;
|
||||
roll = 0.;
|
||||
}
|
||||
else if(src.at<double>(0,2) > 0.998)
|
||||
{
|
||||
pitch = atan2(src.at<double>(1,0), src.at<double>(1,1));
|
||||
yaw = CALIB_PI_2;
|
||||
roll = 0.;
|
||||
}
|
||||
else
|
||||
{
|
||||
pitch = atan2(-src.at<double>(1,2), src.at<double>(2,2));
|
||||
yaw = asin(src.at<double>(0,2));
|
||||
roll = atan2(-src.at<double>(0,1), src.at<double>(0,0));
|
||||
}
|
||||
|
||||
if(argType == CALIB_DEGREES)
|
||||
{
|
||||
pitch *= 180./CALIB_PI;
|
||||
yaw *= 180./CALIB_PI;
|
||||
roll *= 180./CALIB_PI;
|
||||
}
|
||||
else if(argType != CALIB_RADIANS)
|
||||
CV_Error(cv::Error::StsBadFlag, "Invalid argument type");
|
||||
|
||||
dst.at<double>(0,0) = pitch;
|
||||
dst.at<double>(1,0) = yaw;
|
||||
dst.at<double>(2,0) = roll;
|
||||
}
|
||||
else if( (src.cols == 1 && src.rows == 3) ||
|
||||
(src.cols == 3 && src.rows == 1 ) )
|
||||
{
|
||||
//convert vector which contains 3 angles (pitch, yaw, roll) to rotation matrix
|
||||
double pitch, yaw, roll;
|
||||
if(src.cols == 1 && src.rows == 3)
|
||||
{
|
||||
pitch = src.at<double>(0,0);
|
||||
yaw = src.at<double>(1,0);
|
||||
roll = src.at<double>(2,0);
|
||||
}
|
||||
else{
|
||||
pitch = src.at<double>(0,0);
|
||||
yaw = src.at<double>(0,1);
|
||||
roll = src.at<double>(0,2);
|
||||
}
|
||||
|
||||
if(argType == CALIB_DEGREES)
|
||||
{
|
||||
pitch *= CALIB_PI / 180.;
|
||||
yaw *= CALIB_PI / 180.;
|
||||
roll *= CALIB_PI / 180.;
|
||||
}
|
||||
else if(argType != CALIB_RADIANS)
|
||||
CV_Error(cv::Error::StsBadFlag, "Invalid argument type");
|
||||
|
||||
dst = cv::Mat(3, 3, CV_64F);
|
||||
cv::Mat M(3, 3, CV_64F);
|
||||
cv::Mat i = cv::Mat::eye(3, 3, CV_64F);
|
||||
i.copyTo(dst);
|
||||
i.copyTo(M);
|
||||
|
||||
double* pR = dst.ptr<double>();
|
||||
pR[4] = cos(pitch);
|
||||
pR[7] = sin(pitch);
|
||||
pR[8] = pR[4];
|
||||
pR[5] = -pR[7];
|
||||
|
||||
double* pM = M.ptr<double>();
|
||||
pM[0] = cos(yaw);
|
||||
pM[2] = sin(yaw);
|
||||
pM[8] = pM[0];
|
||||
pM[6] = -pM[2];
|
||||
|
||||
dst *= M;
|
||||
i.copyTo(M);
|
||||
pM[0] = cos(roll);
|
||||
pM[3] = sin(roll);
|
||||
pM[4] = pM[0];
|
||||
pM[1] = -pM[3];
|
||||
|
||||
dst *= M;
|
||||
}
|
||||
else
|
||||
CV_Error(cv::Error::StsBadFlag, "Input matrix must be 1x3, 3x1 or 3x3" );
|
||||
}
|
||||
|
||||
void calib::RodriguesToEuler(const cv::Mat& src, cv::Mat& dst, int argType)
|
||||
{
|
||||
CV_Assert((src.cols == 1 && src.rows == 3) || (src.cols == 3 && src.rows == 1));
|
||||
cv::Mat R;
|
||||
cv::Rodrigues(src, R);
|
||||
Euler(R, dst, argType);
|
||||
}
|
||||
|
||||
void calib::EulerToRodrigues(const cv::Mat& src, cv::Mat& dst, int argType)
|
||||
{
|
||||
CV_Assert((src.cols == 1 && src.rows == 3) || (src.cols == 3 && src.rows == 1));
|
||||
cv::Mat R;
|
||||
Euler(src, R, argType);
|
||||
cv::Rodrigues(R, dst);
|
||||
}
|
20
opencv-apps/interactive-calibration/rotationConverters.hpp
Normal file
20
opencv-apps/interactive-calibration/rotationConverters.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef ROTATION_CONVERTERS_HPP
|
||||
#define ROTATION_CONVERTERS_HPP
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
namespace calib
|
||||
{
|
||||
#define CALIB_RADIANS 0
|
||||
#define CALIB_DEGREES 1
|
||||
|
||||
void Euler(const cv::Mat& src, cv::Mat& dst, int argType = CALIB_RADIANS);
|
||||
void RodriguesToEuler(const cv::Mat& src, cv::Mat& dst, int argType = CALIB_RADIANS);
|
||||
void EulerToRodrigues(const cv::Mat& src, cv::Mat& dst, int argType = CALIB_RADIANS);
|
||||
|
||||
}
|
||||
#endif
|
5
opencv-apps/traincascade/CMakeLists.txt
Normal file
5
opencv-apps/traincascade/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
ocv_warnings_disable(CMAKE_CXX_FLAGS -Woverloaded-virtual -Winconsistent-missing-override -Wsuggest-override)
|
||||
file(GLOB SRCS *.cpp)
|
||||
ocv_add_application(opencv_traincascade
|
||||
MODULES opencv_core opencv_imgproc opencv_objdetect opencv_imgcodecs opencv_highgui opencv_calib3d opencv_features2d
|
||||
SRCS ${SRCS})
|
250
opencv-apps/traincascade/HOGfeatures.cpp
Normal file
250
opencv-apps/traincascade/HOGfeatures.cpp
Normal file
@ -0,0 +1,250 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include "HOGfeatures.h"
|
||||
#include "cascadeclassifier.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
CvHOGFeatureParams::CvHOGFeatureParams()
|
||||
{
|
||||
maxCatCount = 0;
|
||||
name = HOGF_NAME;
|
||||
featSize = N_BINS * N_CELLS;
|
||||
}
|
||||
|
||||
void CvHOGEvaluator::init(const CvFeatureParams *_featureParams, int _maxSampleCount, Size _winSize)
|
||||
{
|
||||
CV_Assert( _maxSampleCount > 0);
|
||||
int cols = (_winSize.width + 1) * (_winSize.height + 1);
|
||||
for (int bin = 0; bin < N_BINS; bin++)
|
||||
{
|
||||
hist.push_back(Mat(_maxSampleCount, cols, CV_32FC1));
|
||||
}
|
||||
normSum.create( (int)_maxSampleCount, cols, CV_32FC1 );
|
||||
CvFeatureEvaluator::init( _featureParams, _maxSampleCount, _winSize );
|
||||
}
|
||||
|
||||
void CvHOGEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)
|
||||
{
|
||||
CV_DbgAssert( !hist.empty());
|
||||
CvFeatureEvaluator::setImage( img, clsLabel, idx );
|
||||
vector<Mat> integralHist;
|
||||
for (int bin = 0; bin < N_BINS; bin++)
|
||||
{
|
||||
integralHist.push_back( Mat(winSize.height + 1, winSize.width + 1, hist[bin].type(), hist[bin].ptr<float>((int)idx)) );
|
||||
}
|
||||
Mat integralNorm(winSize.height + 1, winSize.width + 1, normSum.type(), normSum.ptr<float>((int)idx));
|
||||
integralHistogram(img, integralHist, integralNorm, (int)N_BINS);
|
||||
}
|
||||
|
||||
//void CvHOGEvaluator::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
|
||||
//{
|
||||
// _writeFeatures( features, fs, featureMap );
|
||||
//}
|
||||
|
||||
void CvHOGEvaluator::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
|
||||
{
|
||||
int featIdx;
|
||||
int componentIdx;
|
||||
const Mat_<int>& featureMap_ = (const Mat_<int>&)featureMap;
|
||||
fs << FEATURES << "[";
|
||||
for ( int fi = 0; fi < featureMap.cols; fi++ )
|
||||
if ( featureMap_(0, fi) >= 0 )
|
||||
{
|
||||
fs << "{";
|
||||
featIdx = fi / getFeatureSize();
|
||||
componentIdx = fi % getFeatureSize();
|
||||
features[featIdx].write( fs, componentIdx );
|
||||
fs << "}";
|
||||
}
|
||||
fs << "]";
|
||||
}
|
||||
|
||||
void CvHOGEvaluator::generateFeatures()
|
||||
{
|
||||
int offset = winSize.width + 1;
|
||||
Size blockStep;
|
||||
int x, y, t, w, h;
|
||||
|
||||
for (t = 8; t <= winSize.width/2; t+=8) //t = size of a cell. blocksize = 4*cellSize
|
||||
{
|
||||
blockStep = Size(4,4);
|
||||
w = 2*t; //width of a block
|
||||
h = 2*t; //height of a block
|
||||
for (x = 0; x <= winSize.width - w; x += blockStep.width)
|
||||
{
|
||||
for (y = 0; y <= winSize.height - h; y += blockStep.height)
|
||||
{
|
||||
features.push_back(Feature(offset, x, y, t, t));
|
||||
}
|
||||
}
|
||||
w = 2*t;
|
||||
h = 4*t;
|
||||
for (x = 0; x <= winSize.width - w; x += blockStep.width)
|
||||
{
|
||||
for (y = 0; y <= winSize.height - h; y += blockStep.height)
|
||||
{
|
||||
features.push_back(Feature(offset, x, y, t, 2*t));
|
||||
}
|
||||
}
|
||||
w = 4*t;
|
||||
h = 2*t;
|
||||
for (x = 0; x <= winSize.width - w; x += blockStep.width)
|
||||
{
|
||||
for (y = 0; y <= winSize.height - h; y += blockStep.height)
|
||||
{
|
||||
features.push_back(Feature(offset, x, y, 2*t, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
numFeatures = (int)features.size();
|
||||
}
|
||||
|
||||
CvHOGEvaluator::Feature::Feature()
|
||||
{
|
||||
for (int i = 0; i < N_CELLS; i++)
|
||||
{
|
||||
rect[i] = Rect(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
CvHOGEvaluator::Feature::Feature( int offset, int x, int y, int cellW, int cellH )
|
||||
{
|
||||
rect[0] = Rect(x, y, cellW, cellH); //cell0
|
||||
rect[1] = Rect(x+cellW, y, cellW, cellH); //cell1
|
||||
rect[2] = Rect(x, y+cellH, cellW, cellH); //cell2
|
||||
rect[3] = Rect(x+cellW, y+cellH, cellW, cellH); //cell3
|
||||
|
||||
for (int i = 0; i < N_CELLS; i++)
|
||||
{
|
||||
CV_SUM_OFFSETS(fastRect[i].p0, fastRect[i].p1, fastRect[i].p2, fastRect[i].p3, rect[i], offset);
|
||||
}
|
||||
}
|
||||
|
||||
void CvHOGEvaluator::Feature::write(FileStorage &fs) const
|
||||
{
|
||||
fs << CC_RECTS << "[";
|
||||
for( int i = 0; i < N_CELLS; i++ )
|
||||
{
|
||||
fs << "[:" << rect[i].x << rect[i].y << rect[i].width << rect[i].height << "]";
|
||||
}
|
||||
fs << "]";
|
||||
}
|
||||
|
||||
//cell and bin idx writing
|
||||
//void CvHOGEvaluator::Feature::write(FileStorage &fs, int varIdx) const
|
||||
//{
|
||||
// int featComponent = varIdx % (N_CELLS * N_BINS);
|
||||
// int cellIdx = featComponent / N_BINS;
|
||||
// int binIdx = featComponent % N_BINS;
|
||||
//
|
||||
// fs << CC_RECTS << "[:" << rect[cellIdx].x << rect[cellIdx].y <<
|
||||
// rect[cellIdx].width << rect[cellIdx].height << binIdx << "]";
|
||||
//}
|
||||
|
||||
//cell[0] and featComponent idx writing. By cell[0] it's possible to recover all block
|
||||
//All block is necessary for block normalization
|
||||
void CvHOGEvaluator::Feature::write(FileStorage &fs, int featComponentIdx) const
|
||||
{
|
||||
fs << CC_RECT << "[:" << rect[0].x << rect[0].y <<
|
||||
rect[0].width << rect[0].height << featComponentIdx << "]";
|
||||
}
|
||||
|
||||
|
||||
void CvHOGEvaluator::integralHistogram(const Mat &img, vector<Mat> &histogram, Mat &norm, int nbins) const
|
||||
{
|
||||
CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );
|
||||
int x, y, binIdx;
|
||||
|
||||
Size gradSize(img.size());
|
||||
Size histSize(histogram[0].size());
|
||||
Mat grad(gradSize, CV_32F);
|
||||
Mat qangle(gradSize, CV_8U);
|
||||
|
||||
AutoBuffer<int> mapbuf(gradSize.width + gradSize.height + 4);
|
||||
int* xmap = mapbuf.data() + 1;
|
||||
int* ymap = xmap + gradSize.width + 2;
|
||||
|
||||
const int borderType = (int)BORDER_REPLICATE;
|
||||
|
||||
for( x = -1; x < gradSize.width + 1; x++ )
|
||||
xmap[x] = borderInterpolate(x, gradSize.width, borderType);
|
||||
for( y = -1; y < gradSize.height + 1; y++ )
|
||||
ymap[y] = borderInterpolate(y, gradSize.height, borderType);
|
||||
|
||||
int width = gradSize.width;
|
||||
AutoBuffer<float> _dbuf(width*4);
|
||||
float* dbuf = _dbuf.data();
|
||||
Mat Dx(1, width, CV_32F, dbuf);
|
||||
Mat Dy(1, width, CV_32F, dbuf + width);
|
||||
Mat Mag(1, width, CV_32F, dbuf + width*2);
|
||||
Mat Angle(1, width, CV_32F, dbuf + width*3);
|
||||
|
||||
float angleScale = (float)(nbins/CV_PI);
|
||||
|
||||
for( y = 0; y < gradSize.height; y++ )
|
||||
{
|
||||
const uchar* currPtr = img.ptr(ymap[y]);
|
||||
const uchar* prevPtr = img.ptr(ymap[y-1]);
|
||||
const uchar* nextPtr = img.ptr(ymap[y+1]);
|
||||
float* gradPtr = grad.ptr<float>(y);
|
||||
uchar* qanglePtr = qangle.ptr(y);
|
||||
|
||||
for( x = 0; x < width; x++ )
|
||||
{
|
||||
dbuf[x] = (float)(currPtr[xmap[x+1]] - currPtr[xmap[x-1]]);
|
||||
dbuf[width + x] = (float)(nextPtr[xmap[x]] - prevPtr[xmap[x]]);
|
||||
}
|
||||
cartToPolar( Dx, Dy, Mag, Angle, false );
|
||||
for( x = 0; x < width; x++ )
|
||||
{
|
||||
float mag = dbuf[x+width*2];
|
||||
float angle = dbuf[x+width*3];
|
||||
angle = angle*angleScale - 0.5f;
|
||||
int bidx = cvFloor(angle);
|
||||
angle -= bidx;
|
||||
if( bidx < 0 )
|
||||
bidx += nbins;
|
||||
else if( bidx >= nbins )
|
||||
bidx -= nbins;
|
||||
|
||||
qanglePtr[x] = (uchar)bidx;
|
||||
gradPtr[x] = mag;
|
||||
}
|
||||
}
|
||||
integral(grad, norm, grad.depth());
|
||||
|
||||
float* histBuf;
|
||||
const float* magBuf;
|
||||
const uchar* binsBuf;
|
||||
|
||||
int binsStep = (int)( qangle.step / sizeof(uchar) );
|
||||
int histStep = (int)( histogram[0].step / sizeof(float) );
|
||||
int magStep = (int)( grad.step / sizeof(float) );
|
||||
for( binIdx = 0; binIdx < nbins; binIdx++ )
|
||||
{
|
||||
histBuf = histogram[binIdx].ptr<float>();
|
||||
magBuf = grad.ptr<float>();
|
||||
binsBuf = qangle.ptr();
|
||||
|
||||
memset( histBuf, 0, histSize.width * sizeof(histBuf[0]) );
|
||||
histBuf += histStep + 1;
|
||||
for( y = 0; y < qangle.rows; y++ )
|
||||
{
|
||||
histBuf[-1] = 0.f;
|
||||
float strSum = 0.f;
|
||||
for( x = 0; x < qangle.cols; x++ )
|
||||
{
|
||||
if( binsBuf[x] == binIdx )
|
||||
strSum += magBuf[x];
|
||||
histBuf[x] = histBuf[-histStep + x] + strSum;
|
||||
}
|
||||
histBuf += histStep;
|
||||
binsBuf += binsStep;
|
||||
magBuf += magStep;
|
||||
}
|
||||
}
|
||||
}
|
78
opencv-apps/traincascade/HOGfeatures.h
Normal file
78
opencv-apps/traincascade/HOGfeatures.h
Normal file
@ -0,0 +1,78 @@
|
||||
#ifndef _OPENCV_HOGFEATURES_H_
|
||||
#define _OPENCV_HOGFEATURES_H_
|
||||
|
||||
#include "traincascade_features.h"
|
||||
|
||||
//#define TEST_INTHIST_BUILD
|
||||
//#define TEST_FEAT_CALC
|
||||
|
||||
#define N_BINS 9
|
||||
#define N_CELLS 4
|
||||
|
||||
#define HOGF_NAME "HOGFeatureParams"
|
||||
struct CvHOGFeatureParams : public CvFeatureParams
|
||||
{
|
||||
CvHOGFeatureParams();
|
||||
};
|
||||
|
||||
class CvHOGEvaluator : public CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
virtual ~CvHOGEvaluator() {}
|
||||
virtual void init(const CvFeatureParams *_featureParams,
|
||||
int _maxSampleCount, cv::Size _winSize );
|
||||
virtual void setImage(const cv::Mat& img, uchar clsLabel, int idx);
|
||||
virtual float operator()(int varIdx, int sampleIdx) const;
|
||||
virtual void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const;
|
||||
protected:
|
||||
virtual void generateFeatures();
|
||||
virtual void integralHistogram(const cv::Mat &img, std::vector<cv::Mat> &histogram, cv::Mat &norm, int nbins) const;
|
||||
class Feature
|
||||
{
|
||||
public:
|
||||
Feature();
|
||||
Feature( int offset, int x, int y, int cellW, int cellH );
|
||||
float calc( const std::vector<cv::Mat> &_hists, const cv::Mat &_normSum, size_t y, int featComponent ) const;
|
||||
void write( cv::FileStorage &fs ) const;
|
||||
void write( cv::FileStorage &fs, int varIdx ) const;
|
||||
|
||||
cv::Rect rect[N_CELLS]; //cells
|
||||
|
||||
struct
|
||||
{
|
||||
int p0, p1, p2, p3;
|
||||
} fastRect[N_CELLS];
|
||||
};
|
||||
std::vector<Feature> features;
|
||||
|
||||
cv::Mat normSum; //for nomalization calculation (L1 or L2)
|
||||
std::vector<cv::Mat> hist;
|
||||
};
|
||||
|
||||
inline float CvHOGEvaluator::operator()(int varIdx, int sampleIdx) const
|
||||
{
|
||||
int featureIdx = varIdx / (N_BINS * N_CELLS);
|
||||
int componentIdx = varIdx % (N_BINS * N_CELLS);
|
||||
//return features[featureIdx].calc( hist, sampleIdx, componentIdx);
|
||||
return features[featureIdx].calc( hist, normSum, sampleIdx, componentIdx);
|
||||
}
|
||||
|
||||
inline float CvHOGEvaluator::Feature::calc( const std::vector<cv::Mat>& _hists, const cv::Mat& _normSum, size_t y, int featComponent ) const
|
||||
{
|
||||
float normFactor;
|
||||
float res;
|
||||
|
||||
int binIdx = featComponent % N_BINS;
|
||||
int cellIdx = featComponent / N_BINS;
|
||||
|
||||
const float *phist = _hists[binIdx].ptr<float>((int)y);
|
||||
res = phist[fastRect[cellIdx].p0] - phist[fastRect[cellIdx].p1] - phist[fastRect[cellIdx].p2] + phist[fastRect[cellIdx].p3];
|
||||
|
||||
const float *pnormSum = _normSum.ptr<float>((int)y);
|
||||
normFactor = (float)(pnormSum[fastRect[0].p0] - pnormSum[fastRect[1].p1] - pnormSum[fastRect[2].p2] + pnormSum[fastRect[3].p3]);
|
||||
res = (res > 0.001f) ? ( res / (normFactor + 0.001f) ) : 0.f; //for cutting negative values, which apper due to floating precision
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // _OPENCV_HOGFEATURES_H_
|
1718
opencv-apps/traincascade/boost.cpp
Normal file
1718
opencv-apps/traincascade/boost.cpp
Normal file
File diff suppressed because it is too large
Load Diff
86
opencv-apps/traincascade/boost.h
Normal file
86
opencv-apps/traincascade/boost.h
Normal file
@ -0,0 +1,86 @@
|
||||
#ifndef _OPENCV_BOOST_H_
|
||||
#define _OPENCV_BOOST_H_
|
||||
|
||||
#include "traincascade_features.h"
|
||||
#include "old_ml.hpp"
|
||||
|
||||
struct CvCascadeBoostParams : CvBoostParams
|
||||
{
|
||||
float minHitRate;
|
||||
float maxFalseAlarm;
|
||||
|
||||
CvCascadeBoostParams();
|
||||
CvCascadeBoostParams( int _boostType, float _minHitRate, float _maxFalseAlarm,
|
||||
double _weightTrimRate, int _maxDepth, int _maxWeakCount );
|
||||
virtual ~CvCascadeBoostParams() {}
|
||||
void write( cv::FileStorage &fs ) const;
|
||||
bool read( const cv::FileNode &node );
|
||||
virtual void printDefaults() const;
|
||||
virtual void printAttrs() const;
|
||||
virtual bool scanAttr( const std::string prmName, const std::string val);
|
||||
};
|
||||
|
||||
struct CvCascadeBoostTrainData : CvDTreeTrainData
|
||||
{
|
||||
CvCascadeBoostTrainData( const CvFeatureEvaluator* _featureEvaluator,
|
||||
const CvDTreeParams& _params );
|
||||
CvCascadeBoostTrainData( const CvFeatureEvaluator* _featureEvaluator,
|
||||
int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize,
|
||||
const CvDTreeParams& _params = CvDTreeParams() );
|
||||
virtual void setData( const CvFeatureEvaluator* _featureEvaluator,
|
||||
int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize,
|
||||
const CvDTreeParams& _params=CvDTreeParams() );
|
||||
void precalculate();
|
||||
|
||||
virtual CvDTreeNode* subsample_data( const CvMat* _subsample_idx );
|
||||
|
||||
virtual const int* get_class_labels( CvDTreeNode* n, int* labelsBuf );
|
||||
virtual const int* get_cv_labels( CvDTreeNode* n, int* labelsBuf);
|
||||
virtual const int* get_sample_indices( CvDTreeNode* n, int* indicesBuf );
|
||||
|
||||
virtual void get_ord_var_data( CvDTreeNode* n, int vi, float* ordValuesBuf, int* sortedIndicesBuf,
|
||||
const float** ordValues, const int** sortedIndices, int* sampleIndicesBuf );
|
||||
virtual const int* get_cat_var_data( CvDTreeNode* n, int vi, int* catValuesBuf );
|
||||
virtual float getVarValue( int vi, int si );
|
||||
virtual void free_train_data();
|
||||
|
||||
const CvFeatureEvaluator* featureEvaluator;
|
||||
cv::Mat valCache; // precalculated feature values (CV_32FC1)
|
||||
CvMat _resp; // for casting
|
||||
int numPrecalcVal, numPrecalcIdx;
|
||||
};
|
||||
|
||||
class CvCascadeBoostTree : public CvBoostTree
|
||||
{
|
||||
public:
|
||||
virtual CvDTreeNode* predict( int sampleIdx ) const;
|
||||
void write( cv::FileStorage &fs, const cv::Mat& featureMap );
|
||||
void read( const cv::FileNode &node, CvBoost* _ensemble, CvDTreeTrainData* _data );
|
||||
void markFeaturesInMap( cv::Mat& featureMap );
|
||||
protected:
|
||||
virtual void split_node_data( CvDTreeNode* n );
|
||||
};
|
||||
|
||||
class CvCascadeBoost : public CvBoost
|
||||
{
|
||||
public:
|
||||
virtual bool train( const CvFeatureEvaluator* _featureEvaluator,
|
||||
int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize,
|
||||
const CvCascadeBoostParams& _params=CvCascadeBoostParams() );
|
||||
virtual float predict( int sampleIdx, bool returnSum = false ) const;
|
||||
|
||||
float getThreshold() const { return threshold; }
|
||||
void write( cv::FileStorage &fs, const cv::Mat& featureMap ) const;
|
||||
bool read( const cv::FileNode &node, const CvFeatureEvaluator* _featureEvaluator,
|
||||
const CvCascadeBoostParams& _params );
|
||||
void markUsedFeaturesInMap( cv::Mat& featureMap );
|
||||
protected:
|
||||
virtual bool set_params( const CvBoostParams& _params );
|
||||
virtual void update_weights( CvBoostTree* tree );
|
||||
virtual bool isErrDesired();
|
||||
|
||||
float threshold;
|
||||
float minHitRate, maxFalseAlarm;
|
||||
};
|
||||
|
||||
#endif
|
571
opencv-apps/traincascade/cascadeclassifier.cpp
Normal file
571
opencv-apps/traincascade/cascadeclassifier.cpp
Normal file
@ -0,0 +1,571 @@
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
#include "cascadeclassifier.h"
|
||||
#include <queue>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static const char* stageTypes[] = { CC_BOOST };
|
||||
static const char* featureTypes[] = { CC_HAAR, CC_LBP, CC_HOG };
|
||||
|
||||
CvCascadeParams::CvCascadeParams() : stageType( defaultStageType ),
|
||||
featureType( defaultFeatureType ), winSize( cvSize(24, 24) )
|
||||
{
|
||||
name = CC_CASCADE_PARAMS;
|
||||
}
|
||||
CvCascadeParams::CvCascadeParams( int _stageType, int _featureType ) : stageType( _stageType ),
|
||||
featureType( _featureType ), winSize( cvSize(24, 24) )
|
||||
{
|
||||
name = CC_CASCADE_PARAMS;
|
||||
}
|
||||
|
||||
//---------------------------- CascadeParams --------------------------------------
|
||||
|
||||
void CvCascadeParams::write( FileStorage &fs ) const
|
||||
{
|
||||
string stageTypeStr = stageType == BOOST ? CC_BOOST : string();
|
||||
CV_Assert( !stageTypeStr.empty() );
|
||||
fs << CC_STAGE_TYPE << stageTypeStr;
|
||||
string featureTypeStr = featureType == CvFeatureParams::HAAR ? CC_HAAR :
|
||||
featureType == CvFeatureParams::LBP ? CC_LBP :
|
||||
featureType == CvFeatureParams::HOG ? CC_HOG :
|
||||
0;
|
||||
CV_Assert( !stageTypeStr.empty() );
|
||||
fs << CC_FEATURE_TYPE << featureTypeStr;
|
||||
fs << CC_HEIGHT << winSize.height;
|
||||
fs << CC_WIDTH << winSize.width;
|
||||
}
|
||||
|
||||
bool CvCascadeParams::read( const FileNode &node )
|
||||
{
|
||||
if ( node.empty() )
|
||||
return false;
|
||||
string stageTypeStr, featureTypeStr;
|
||||
FileNode rnode = node[CC_STAGE_TYPE];
|
||||
if ( !rnode.isString() )
|
||||
return false;
|
||||
rnode >> stageTypeStr;
|
||||
stageType = !stageTypeStr.compare( CC_BOOST ) ? BOOST : -1;
|
||||
if (stageType == -1)
|
||||
return false;
|
||||
rnode = node[CC_FEATURE_TYPE];
|
||||
if ( !rnode.isString() )
|
||||
return false;
|
||||
rnode >> featureTypeStr;
|
||||
featureType = !featureTypeStr.compare( CC_HAAR ) ? CvFeatureParams::HAAR :
|
||||
!featureTypeStr.compare( CC_LBP ) ? CvFeatureParams::LBP :
|
||||
!featureTypeStr.compare( CC_HOG ) ? CvFeatureParams::HOG :
|
||||
-1;
|
||||
if (featureType == -1)
|
||||
return false;
|
||||
node[CC_HEIGHT] >> winSize.height;
|
||||
node[CC_WIDTH] >> winSize.width;
|
||||
return winSize.height > 0 && winSize.width > 0;
|
||||
}
|
||||
|
||||
void CvCascadeParams::printDefaults() const
|
||||
{
|
||||
CvParams::printDefaults();
|
||||
cout << " [-stageType <";
|
||||
for( int i = 0; i < (int)(sizeof(stageTypes)/sizeof(stageTypes[0])); i++ )
|
||||
{
|
||||
cout << (i ? " | " : "") << stageTypes[i];
|
||||
if ( i == defaultStageType )
|
||||
cout << "(default)";
|
||||
}
|
||||
cout << ">]" << endl;
|
||||
|
||||
cout << " [-featureType <{";
|
||||
for( int i = 0; i < (int)(sizeof(featureTypes)/sizeof(featureTypes[0])); i++ )
|
||||
{
|
||||
cout << (i ? ", " : "") << featureTypes[i];
|
||||
if ( i == defaultStageType )
|
||||
cout << "(default)";
|
||||
}
|
||||
cout << "}>]" << endl;
|
||||
cout << " [-w <sampleWidth = " << winSize.width << ">]" << endl;
|
||||
cout << " [-h <sampleHeight = " << winSize.height << ">]" << endl;
|
||||
}
|
||||
|
||||
void CvCascadeParams::printAttrs() const
|
||||
{
|
||||
cout << "stageType: " << stageTypes[stageType] << endl;
|
||||
cout << "featureType: " << featureTypes[featureType] << endl;
|
||||
cout << "sampleWidth: " << winSize.width << endl;
|
||||
cout << "sampleHeight: " << winSize.height << endl;
|
||||
}
|
||||
|
||||
bool CvCascadeParams::scanAttr( const string prmName, const string val )
|
||||
{
|
||||
bool res = true;
|
||||
if( !prmName.compare( "-stageType" ) )
|
||||
{
|
||||
for( int i = 0; i < (int)(sizeof(stageTypes)/sizeof(stageTypes[0])); i++ )
|
||||
if( !val.compare( stageTypes[i] ) )
|
||||
stageType = i;
|
||||
}
|
||||
else if( !prmName.compare( "-featureType" ) )
|
||||
{
|
||||
for( int i = 0; i < (int)(sizeof(featureTypes)/sizeof(featureTypes[0])); i++ )
|
||||
if( !val.compare( featureTypes[i] ) )
|
||||
featureType = i;
|
||||
}
|
||||
else if( !prmName.compare( "-w" ) )
|
||||
{
|
||||
winSize.width = atoi( val.c_str() );
|
||||
}
|
||||
else if( !prmName.compare( "-h" ) )
|
||||
{
|
||||
winSize.height = atoi( val.c_str() );
|
||||
}
|
||||
else
|
||||
res = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------- CascadeClassifier --------------------------------------
|
||||
|
||||
bool CvCascadeClassifier::train( const string _cascadeDirName,
|
||||
const string _posFilename,
|
||||
const string _negFilename,
|
||||
int _numPos, int _numNeg,
|
||||
int _precalcValBufSize, int _precalcIdxBufSize,
|
||||
int _numStages,
|
||||
const CvCascadeParams& _cascadeParams,
|
||||
const CvFeatureParams& _featureParams,
|
||||
const CvCascadeBoostParams& _stageParams,
|
||||
bool baseFormatSave,
|
||||
double acceptanceRatioBreakValue )
|
||||
{
|
||||
// Start recording clock ticks for training time output
|
||||
double time = (double)getTickCount();
|
||||
|
||||
if( _cascadeDirName.empty() || _posFilename.empty() || _negFilename.empty() )
|
||||
CV_Error( CV_StsBadArg, "_cascadeDirName or _bgfileName or _vecFileName is NULL" );
|
||||
|
||||
string dirName;
|
||||
if (_cascadeDirName.find_last_of("/\\") == (_cascadeDirName.length() - 1) )
|
||||
dirName = _cascadeDirName;
|
||||
else
|
||||
dirName = _cascadeDirName + '/';
|
||||
|
||||
numPos = _numPos;
|
||||
numNeg = _numNeg;
|
||||
numStages = _numStages;
|
||||
if ( !imgReader.create( _posFilename, _negFilename, _cascadeParams.winSize ) )
|
||||
{
|
||||
cout << "Image reader can not be created from -vec " << _posFilename
|
||||
<< " and -bg " << _negFilename << "." << endl;
|
||||
return false;
|
||||
}
|
||||
if ( !load( dirName ) )
|
||||
{
|
||||
cascadeParams = _cascadeParams;
|
||||
featureParams = CvFeatureParams::create(cascadeParams.featureType);
|
||||
featureParams->init(_featureParams);
|
||||
stageParams = makePtr<CvCascadeBoostParams>();
|
||||
*stageParams = _stageParams;
|
||||
featureEvaluator = CvFeatureEvaluator::create(cascadeParams.featureType);
|
||||
featureEvaluator->init( featureParams, numPos + numNeg, cascadeParams.winSize );
|
||||
stageClassifiers.reserve( numStages );
|
||||
}else{
|
||||
// Make sure that if model parameters are preloaded, that people are aware of this,
|
||||
// even when passing other parameters to the training command
|
||||
cout << "---------------------------------------------------------------------------------" << endl;
|
||||
cout << "Training parameters are pre-loaded from the parameter file in data folder!" << endl;
|
||||
cout << "Please empty this folder if you want to use a NEW set of training parameters." << endl;
|
||||
cout << "---------------------------------------------------------------------------------" << endl;
|
||||
}
|
||||
cout << "PARAMETERS:" << endl;
|
||||
cout << "cascadeDirName: " << _cascadeDirName << endl;
|
||||
cout << "vecFileName: " << _posFilename << endl;
|
||||
cout << "bgFileName: " << _negFilename << endl;
|
||||
cout << "numPos: " << _numPos << endl;
|
||||
cout << "numNeg: " << _numNeg << endl;
|
||||
cout << "numStages: " << numStages << endl;
|
||||
cout << "precalcValBufSize[Mb] : " << _precalcValBufSize << endl;
|
||||
cout << "precalcIdxBufSize[Mb] : " << _precalcIdxBufSize << endl;
|
||||
cout << "acceptanceRatioBreakValue : " << acceptanceRatioBreakValue << endl;
|
||||
cascadeParams.printAttrs();
|
||||
stageParams->printAttrs();
|
||||
featureParams->printAttrs();
|
||||
cout << "Number of unique features given windowSize [" << _cascadeParams.winSize.width << "," << _cascadeParams.winSize.height << "] : " << featureEvaluator->getNumFeatures() << "" << endl;
|
||||
|
||||
int startNumStages = (int)stageClassifiers.size();
|
||||
if ( startNumStages > 1 )
|
||||
cout << endl << "Stages 0-" << startNumStages-1 << " are loaded" << endl;
|
||||
else if ( startNumStages == 1)
|
||||
cout << endl << "Stage 0 is loaded" << endl;
|
||||
|
||||
double requiredLeafFARate = pow( (double) stageParams->maxFalseAlarm, (double) numStages ) /
|
||||
(double)stageParams->max_depth;
|
||||
double tempLeafFARate;
|
||||
|
||||
for( int i = startNumStages; i < numStages; i++ )
|
||||
{
|
||||
cout << endl << "===== TRAINING " << i << "-stage =====" << endl;
|
||||
cout << "<BEGIN" << endl;
|
||||
|
||||
if ( !updateTrainingSet( requiredLeafFARate, tempLeafFARate ) )
|
||||
{
|
||||
cout << "Train dataset for temp stage can not be filled. "
|
||||
"Branch training terminated." << endl;
|
||||
break;
|
||||
}
|
||||
if( tempLeafFARate <= requiredLeafFARate )
|
||||
{
|
||||
cout << "Required leaf false alarm rate achieved. "
|
||||
"Branch training terminated." << endl;
|
||||
break;
|
||||
}
|
||||
if( (tempLeafFARate <= acceptanceRatioBreakValue) && (acceptanceRatioBreakValue >= 0) ){
|
||||
cout << "The required acceptanceRatio for the model has been reached to avoid overfitting of trainingdata. "
|
||||
"Branch training terminated." << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
Ptr<CvCascadeBoost> tempStage = makePtr<CvCascadeBoost>();
|
||||
bool isStageTrained = tempStage->train( featureEvaluator,
|
||||
curNumSamples, _precalcValBufSize, _precalcIdxBufSize,
|
||||
*stageParams );
|
||||
cout << "END>" << endl;
|
||||
|
||||
if(!isStageTrained)
|
||||
break;
|
||||
|
||||
stageClassifiers.push_back( tempStage );
|
||||
|
||||
// save params
|
||||
if( i == 0)
|
||||
{
|
||||
std::string paramsFilename = dirName + CC_PARAMS_FILENAME;
|
||||
FileStorage fs( paramsFilename, FileStorage::WRITE);
|
||||
if ( !fs.isOpened() )
|
||||
{
|
||||
cout << "Parameters can not be written, because file " << paramsFilename
|
||||
<< " can not be opened." << endl;
|
||||
return false;
|
||||
}
|
||||
fs << FileStorage::getDefaultObjectName(paramsFilename) << "{";
|
||||
writeParams( fs );
|
||||
fs << "}";
|
||||
}
|
||||
// save current stage
|
||||
char buf[10];
|
||||
sprintf(buf, "%s%d", "stage", i );
|
||||
string stageFilename = dirName + buf + ".xml";
|
||||
FileStorage fs( stageFilename, FileStorage::WRITE );
|
||||
if ( !fs.isOpened() )
|
||||
{
|
||||
cout << "Current stage can not be written, because file " << stageFilename
|
||||
<< " can not be opened." << endl;
|
||||
return false;
|
||||
}
|
||||
fs << FileStorage::getDefaultObjectName(stageFilename) << "{";
|
||||
tempStage->write( fs, Mat() );
|
||||
fs << "}";
|
||||
|
||||
// Output training time up till now
|
||||
double seconds = ( (double)getTickCount() - time)/ getTickFrequency();
|
||||
int days = int(seconds) / 60 / 60 / 24;
|
||||
int hours = (int(seconds) / 60 / 60) % 24;
|
||||
int minutes = (int(seconds) / 60) % 60;
|
||||
int seconds_left = int(seconds) % 60;
|
||||
cout << "Training until now has taken " << days << " days " << hours << " hours " << minutes << " minutes " << seconds_left <<" seconds." << endl;
|
||||
}
|
||||
|
||||
if(stageClassifiers.size() == 0)
|
||||
{
|
||||
cout << "Cascade classifier can't be trained. Check the used training parameters." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
save( dirName + CC_CASCADE_FILENAME, baseFormatSave );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CvCascadeClassifier::predict( int sampleIdx )
|
||||
{
|
||||
CV_DbgAssert( sampleIdx < numPos + numNeg );
|
||||
for (vector< Ptr<CvCascadeBoost> >::iterator it = stageClassifiers.begin();
|
||||
it != stageClassifiers.end();++it )
|
||||
{
|
||||
if ( (*it)->predict( sampleIdx ) == 0.f )
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool CvCascadeClassifier::updateTrainingSet( double minimumAcceptanceRatio, double& acceptanceRatio)
|
||||
{
|
||||
int64 posConsumed = 0, negConsumed = 0;
|
||||
imgReader.restart();
|
||||
int posCount = fillPassedSamples( 0, numPos, true, 0, posConsumed );
|
||||
if( !posCount )
|
||||
return false;
|
||||
cout << "POS count : consumed " << posCount << " : " << (int)posConsumed << endl;
|
||||
|
||||
int proNumNeg = cvRound( ( ((double)numNeg) * ((double)posCount) ) / numPos ); // apply only a fraction of negative samples. double is required since overflow is possible
|
||||
int negCount = fillPassedSamples( posCount, proNumNeg, false, minimumAcceptanceRatio, negConsumed );
|
||||
if ( !negCount )
|
||||
if ( !(negConsumed > 0 && ((double)negCount+1)/(double)negConsumed <= minimumAcceptanceRatio) )
|
||||
return false;
|
||||
|
||||
curNumSamples = posCount + negCount;
|
||||
acceptanceRatio = negConsumed == 0 ? 0 : ( (double)negCount/(double)(int64)negConsumed );
|
||||
cout << "NEG count : acceptanceRatio " << negCount << " : " << acceptanceRatio << endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
int CvCascadeClassifier::fillPassedSamples( int first, int count, bool isPositive, double minimumAcceptanceRatio, int64& consumed )
|
||||
{
|
||||
int getcount = 0;
|
||||
Mat img(cascadeParams.winSize, CV_8UC1);
|
||||
for( int i = first; i < first + count; i++ )
|
||||
{
|
||||
for( ; ; )
|
||||
{
|
||||
if( consumed != 0 && ((double)getcount+1)/(double)(int64)consumed <= minimumAcceptanceRatio )
|
||||
return getcount;
|
||||
|
||||
bool isGetImg = isPositive ? imgReader.getPos( img ) :
|
||||
imgReader.getNeg( img );
|
||||
if( !isGetImg )
|
||||
return getcount;
|
||||
consumed++;
|
||||
|
||||
featureEvaluator->setImage( img, isPositive ? 1 : 0, i );
|
||||
if( predict( i ) == 1 )
|
||||
{
|
||||
getcount++;
|
||||
printf("%s current samples: %d\r", isPositive ? "POS":"NEG", getcount);
|
||||
fflush(stdout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return getcount;
|
||||
}
|
||||
|
||||
void CvCascadeClassifier::writeParams( FileStorage &fs ) const
|
||||
{
|
||||
cascadeParams.write( fs );
|
||||
fs << CC_STAGE_PARAMS << "{"; stageParams->write( fs ); fs << "}";
|
||||
fs << CC_FEATURE_PARAMS << "{"; featureParams->write( fs ); fs << "}";
|
||||
}
|
||||
|
||||
void CvCascadeClassifier::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
|
||||
{
|
||||
featureEvaluator->writeFeatures( fs, featureMap );
|
||||
}
|
||||
|
||||
void CvCascadeClassifier::writeStages( FileStorage &fs, const Mat& featureMap ) const
|
||||
{
|
||||
char cmnt[30];
|
||||
int i = 0;
|
||||
fs << CC_STAGES << "[";
|
||||
for( vector< Ptr<CvCascadeBoost> >::const_iterator it = stageClassifiers.begin();
|
||||
it != stageClassifiers.end();++it, ++i )
|
||||
{
|
||||
sprintf( cmnt, "stage %d", i );
|
||||
cvWriteComment( fs.fs, cmnt, 0 );
|
||||
fs << "{";
|
||||
(*it)->write( fs, featureMap );
|
||||
fs << "}";
|
||||
}
|
||||
fs << "]";
|
||||
}
|
||||
|
||||
bool CvCascadeClassifier::readParams( const FileNode &node )
|
||||
{
|
||||
if ( !node.isMap() || !cascadeParams.read( node ) )
|
||||
return false;
|
||||
|
||||
stageParams = makePtr<CvCascadeBoostParams>();
|
||||
FileNode rnode = node[CC_STAGE_PARAMS];
|
||||
if ( !stageParams->read( rnode ) )
|
||||
return false;
|
||||
|
||||
featureParams = CvFeatureParams::create(cascadeParams.featureType);
|
||||
rnode = node[CC_FEATURE_PARAMS];
|
||||
if ( !featureParams->read( rnode ) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CvCascadeClassifier::readStages( const FileNode &node)
|
||||
{
|
||||
FileNode rnode = node[CC_STAGES];
|
||||
if (!rnode.empty() || !rnode.isSeq())
|
||||
return false;
|
||||
stageClassifiers.reserve(numStages);
|
||||
FileNodeIterator it = rnode.begin();
|
||||
for( int i = 0; i < min( (int)rnode.size(), numStages ); i++, it++ )
|
||||
{
|
||||
Ptr<CvCascadeBoost> tempStage = makePtr<CvCascadeBoost>();
|
||||
if ( !tempStage->read( *it, featureEvaluator, *stageParams) )
|
||||
return false;
|
||||
stageClassifiers.push_back(tempStage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// For old Haar Classifier file saving
|
||||
#define ICV_HAAR_TYPE_ID "opencv-haar-classifier"
|
||||
#define ICV_HAAR_SIZE_NAME "size"
|
||||
#define ICV_HAAR_STAGES_NAME "stages"
|
||||
#define ICV_HAAR_TREES_NAME "trees"
|
||||
#define ICV_HAAR_FEATURE_NAME "feature"
|
||||
#define ICV_HAAR_RECTS_NAME "rects"
|
||||
#define ICV_HAAR_TILTED_NAME "tilted"
|
||||
#define ICV_HAAR_THRESHOLD_NAME "threshold"
|
||||
#define ICV_HAAR_LEFT_NODE_NAME "left_node"
|
||||
#define ICV_HAAR_LEFT_VAL_NAME "left_val"
|
||||
#define ICV_HAAR_RIGHT_NODE_NAME "right_node"
|
||||
#define ICV_HAAR_RIGHT_VAL_NAME "right_val"
|
||||
#define ICV_HAAR_STAGE_THRESHOLD_NAME "stage_threshold"
|
||||
#define ICV_HAAR_PARENT_NAME "parent"
|
||||
#define ICV_HAAR_NEXT_NAME "next"
|
||||
|
||||
void CvCascadeClassifier::save( const string filename, bool baseFormat )
|
||||
{
|
||||
FileStorage fs( filename, FileStorage::WRITE );
|
||||
|
||||
if ( !fs.isOpened() )
|
||||
return;
|
||||
|
||||
fs << FileStorage::getDefaultObjectName(filename);
|
||||
if ( !baseFormat )
|
||||
{
|
||||
Mat featureMap;
|
||||
getUsedFeaturesIdxMap( featureMap );
|
||||
fs << "{";
|
||||
writeParams( fs );
|
||||
fs << CC_STAGE_NUM << (int)stageClassifiers.size();
|
||||
writeStages( fs, featureMap );
|
||||
writeFeatures( fs, featureMap );
|
||||
}
|
||||
else
|
||||
{
|
||||
//char buf[256];
|
||||
CvSeq* weak;
|
||||
if ( cascadeParams.featureType != CvFeatureParams::HAAR )
|
||||
CV_Error( CV_StsBadFunc, "old file format is used for Haar-like features only");
|
||||
fs << "{:" ICV_HAAR_TYPE_ID;
|
||||
fs << ICV_HAAR_SIZE_NAME << "[:" << cascadeParams.winSize.width <<
|
||||
cascadeParams.winSize.height << "]";
|
||||
fs << ICV_HAAR_STAGES_NAME << "[";
|
||||
for( size_t si = 0; si < stageClassifiers.size(); si++ )
|
||||
{
|
||||
fs << "{"; //stage
|
||||
/*sprintf( buf, "stage %d", si );
|
||||
CV_CALL( cvWriteComment( fs, buf, 1 ) );*/
|
||||
weak = stageClassifiers[si]->get_weak_predictors();
|
||||
fs << ICV_HAAR_TREES_NAME << "[";
|
||||
for( int wi = 0; wi < weak->total; wi++ )
|
||||
{
|
||||
int inner_node_idx = -1, total_inner_node_idx = -1;
|
||||
queue<const CvDTreeNode*> inner_nodes_queue;
|
||||
CvCascadeBoostTree* tree = *((CvCascadeBoostTree**) cvGetSeqElem( weak, wi ));
|
||||
|
||||
fs << "[";
|
||||
/*sprintf( buf, "tree %d", wi );
|
||||
CV_CALL( cvWriteComment( fs, buf, 1 ) );*/
|
||||
|
||||
const CvDTreeNode* tempNode;
|
||||
|
||||
inner_nodes_queue.push( tree->get_root() );
|
||||
total_inner_node_idx++;
|
||||
|
||||
while (!inner_nodes_queue.empty())
|
||||
{
|
||||
tempNode = inner_nodes_queue.front();
|
||||
inner_node_idx++;
|
||||
|
||||
fs << "{";
|
||||
fs << ICV_HAAR_FEATURE_NAME << "{";
|
||||
((CvHaarEvaluator*)featureEvaluator.get())->writeFeature( fs, tempNode->split->var_idx );
|
||||
fs << "}";
|
||||
|
||||
fs << ICV_HAAR_THRESHOLD_NAME << tempNode->split->ord.c;
|
||||
|
||||
if( tempNode->left->left || tempNode->left->right )
|
||||
{
|
||||
inner_nodes_queue.push( tempNode->left );
|
||||
total_inner_node_idx++;
|
||||
fs << ICV_HAAR_LEFT_NODE_NAME << total_inner_node_idx;
|
||||
}
|
||||
else
|
||||
fs << ICV_HAAR_LEFT_VAL_NAME << tempNode->left->value;
|
||||
|
||||
if( tempNode->right->left || tempNode->right->right )
|
||||
{
|
||||
inner_nodes_queue.push( tempNode->right );
|
||||
total_inner_node_idx++;
|
||||
fs << ICV_HAAR_RIGHT_NODE_NAME << total_inner_node_idx;
|
||||
}
|
||||
else
|
||||
fs << ICV_HAAR_RIGHT_VAL_NAME << tempNode->right->value;
|
||||
fs << "}"; // ICV_HAAR_FEATURE_NAME
|
||||
inner_nodes_queue.pop();
|
||||
}
|
||||
fs << "]";
|
||||
}
|
||||
fs << "]"; //ICV_HAAR_TREES_NAME
|
||||
fs << ICV_HAAR_STAGE_THRESHOLD_NAME << stageClassifiers[si]->getThreshold();
|
||||
fs << ICV_HAAR_PARENT_NAME << (int)si-1 << ICV_HAAR_NEXT_NAME << -1;
|
||||
fs << "}"; //stage
|
||||
} /* for each stage */
|
||||
fs << "]"; //ICV_HAAR_STAGES_NAME
|
||||
}
|
||||
fs << "}";
|
||||
}
|
||||
|
||||
bool CvCascadeClassifier::load( const string cascadeDirName )
|
||||
{
|
||||
FileStorage fs( cascadeDirName + CC_PARAMS_FILENAME, FileStorage::READ );
|
||||
if ( !fs.isOpened() )
|
||||
return false;
|
||||
FileNode node = fs.getFirstTopLevelNode();
|
||||
if ( !readParams( node ) )
|
||||
return false;
|
||||
featureEvaluator = CvFeatureEvaluator::create(cascadeParams.featureType);
|
||||
featureEvaluator->init( featureParams, numPos + numNeg, cascadeParams.winSize );
|
||||
fs.release();
|
||||
|
||||
char buf[16] = {0};
|
||||
for ( int si = 0; si < numStages; si++ )
|
||||
{
|
||||
sprintf( buf, "%s%d", "stage", si);
|
||||
fs.open( cascadeDirName + buf + ".xml", FileStorage::READ );
|
||||
node = fs.getFirstTopLevelNode();
|
||||
if ( !fs.isOpened() )
|
||||
break;
|
||||
Ptr<CvCascadeBoost> tempStage = makePtr<CvCascadeBoost>();
|
||||
|
||||
if ( !tempStage->read( node, featureEvaluator, *stageParams ))
|
||||
{
|
||||
fs.release();
|
||||
break;
|
||||
}
|
||||
stageClassifiers.push_back(tempStage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CvCascadeClassifier::getUsedFeaturesIdxMap( Mat& featureMap )
|
||||
{
|
||||
int varCount = featureEvaluator->getNumFeatures() * featureEvaluator->getFeatureSize();
|
||||
featureMap.create( 1, varCount, CV_32SC1 );
|
||||
featureMap.setTo(Scalar(-1));
|
||||
|
||||
for( vector< Ptr<CvCascadeBoost> >::const_iterator it = stageClassifiers.begin();
|
||||
it != stageClassifiers.end();++it )
|
||||
(*it)->markUsedFeaturesInMap( featureMap );
|
||||
|
||||
for( int fi = 0, idx = 0; fi < varCount; fi++ )
|
||||
if ( featureMap.at<int>(0, fi) >= 0 )
|
||||
featureMap.ptr<int>(0)[fi] = idx++;
|
||||
}
|
125
opencv-apps/traincascade/cascadeclassifier.h
Normal file
125
opencv-apps/traincascade/cascadeclassifier.h
Normal file
@ -0,0 +1,125 @@
|
||||
#ifndef _OPENCV_CASCADECLASSIFIER_H_
|
||||
#define _OPENCV_CASCADECLASSIFIER_H_
|
||||
|
||||
#include <ctime>
|
||||
#include "traincascade_features.h"
|
||||
#include "haarfeatures.h"
|
||||
#include "lbpfeatures.h"
|
||||
#include "HOGfeatures.h" //new
|
||||
#include "boost.h"
|
||||
|
||||
#define CC_CASCADE_FILENAME "cascade.xml"
|
||||
#define CC_PARAMS_FILENAME "params.xml"
|
||||
|
||||
#define CC_CASCADE_PARAMS "cascadeParams"
|
||||
#define CC_STAGE_TYPE "stageType"
|
||||
#define CC_FEATURE_TYPE "featureType"
|
||||
#define CC_HEIGHT "height"
|
||||
#define CC_WIDTH "width"
|
||||
|
||||
#define CC_STAGE_NUM "stageNum"
|
||||
#define CC_STAGES "stages"
|
||||
#define CC_STAGE_PARAMS "stageParams"
|
||||
|
||||
#define CC_BOOST "BOOST"
|
||||
#define CC_BOOST_TYPE "boostType"
|
||||
#define CC_DISCRETE_BOOST "DAB"
|
||||
#define CC_REAL_BOOST "RAB"
|
||||
#define CC_LOGIT_BOOST "LB"
|
||||
#define CC_GENTLE_BOOST "GAB"
|
||||
#define CC_MINHITRATE "minHitRate"
|
||||
#define CC_MAXFALSEALARM "maxFalseAlarm"
|
||||
#define CC_TRIM_RATE "weightTrimRate"
|
||||
#define CC_MAX_DEPTH "maxDepth"
|
||||
#define CC_WEAK_COUNT "maxWeakCount"
|
||||
#define CC_STAGE_THRESHOLD "stageThreshold"
|
||||
#define CC_WEAK_CLASSIFIERS "weakClassifiers"
|
||||
#define CC_INTERNAL_NODES "internalNodes"
|
||||
#define CC_LEAF_VALUES "leafValues"
|
||||
|
||||
#define CC_FEATURES FEATURES
|
||||
#define CC_FEATURE_PARAMS "featureParams"
|
||||
#define CC_MAX_CAT_COUNT "maxCatCount"
|
||||
#define CC_FEATURE_SIZE "featSize"
|
||||
|
||||
#define CC_HAAR "HAAR"
|
||||
#define CC_MODE "mode"
|
||||
#define CC_MODE_BASIC "BASIC"
|
||||
#define CC_MODE_CORE "CORE"
|
||||
#define CC_MODE_ALL "ALL"
|
||||
#define CC_RECTS "rects"
|
||||
#define CC_TILTED "tilted"
|
||||
|
||||
#define CC_LBP "LBP"
|
||||
#define CC_RECT "rect"
|
||||
|
||||
#define CC_HOG "HOG"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define TIME( arg ) (((double) clock()) / CLOCKS_PER_SEC)
|
||||
#else
|
||||
#define TIME( arg ) (time( arg ))
|
||||
#endif
|
||||
|
||||
class CvCascadeParams : public CvParams
|
||||
{
|
||||
public:
|
||||
enum { BOOST = 0 };
|
||||
static const int defaultStageType = BOOST;
|
||||
static const int defaultFeatureType = CvFeatureParams::HAAR;
|
||||
|
||||
CvCascadeParams();
|
||||
CvCascadeParams( int _stageType, int _featureType );
|
||||
void write( cv::FileStorage &fs ) const;
|
||||
bool read( const cv::FileNode &node );
|
||||
|
||||
void printDefaults() const;
|
||||
void printAttrs() const;
|
||||
bool scanAttr( const std::string prmName, const std::string val );
|
||||
|
||||
int stageType;
|
||||
int featureType;
|
||||
cv::Size winSize;
|
||||
};
|
||||
|
||||
class CvCascadeClassifier
|
||||
{
|
||||
public:
|
||||
bool train( const std::string _cascadeDirName,
|
||||
const std::string _posFilename,
|
||||
const std::string _negFilename,
|
||||
int _numPos, int _numNeg,
|
||||
int _precalcValBufSize, int _precalcIdxBufSize,
|
||||
int _numStages,
|
||||
const CvCascadeParams& _cascadeParams,
|
||||
const CvFeatureParams& _featureParams,
|
||||
const CvCascadeBoostParams& _stageParams,
|
||||
bool baseFormatSave = false,
|
||||
double acceptanceRatioBreakValue = -1.0 );
|
||||
private:
|
||||
int predict( int sampleIdx );
|
||||
void save( const std::string cascadeDirName, bool baseFormat = false );
|
||||
bool load( const std::string cascadeDirName );
|
||||
bool updateTrainingSet( double minimumAcceptanceRatio, double& acceptanceRatio );
|
||||
int fillPassedSamples( int first, int count, bool isPositive, double requiredAcceptanceRatio, int64& consumed );
|
||||
|
||||
void writeParams( cv::FileStorage &fs ) const;
|
||||
void writeStages( cv::FileStorage &fs, const cv::Mat& featureMap ) const;
|
||||
void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const;
|
||||
bool readParams( const cv::FileNode &node );
|
||||
bool readStages( const cv::FileNode &node );
|
||||
|
||||
void getUsedFeaturesIdxMap( cv::Mat& featureMap );
|
||||
|
||||
CvCascadeParams cascadeParams;
|
||||
cv::Ptr<CvFeatureParams> featureParams;
|
||||
cv::Ptr<CvCascadeBoostParams> stageParams;
|
||||
|
||||
cv::Ptr<CvFeatureEvaluator> featureEvaluator;
|
||||
std::vector< cv::Ptr<CvCascadeBoost> > stageClassifiers;
|
||||
CvCascadeImageReader imgReader;
|
||||
int numStages, curNumSamples;
|
||||
int numPos, numNeg;
|
||||
};
|
||||
|
||||
#endif
|
93
opencv-apps/traincascade/features.cpp
Normal file
93
opencv-apps/traincascade/features.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
#include "traincascade_features.h"
|
||||
#include "cascadeclassifier.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
float calcNormFactor( const Mat& sum, const Mat& sqSum )
|
||||
{
|
||||
CV_DbgAssert( sum.cols > 3 && sqSum.rows > 3 );
|
||||
Rect normrect( 1, 1, sum.cols - 3, sum.rows - 3 );
|
||||
size_t p0, p1, p2, p3;
|
||||
CV_SUM_OFFSETS( p0, p1, p2, p3, normrect, sum.step1() )
|
||||
double area = normrect.width * normrect.height;
|
||||
const int *sp = sum.ptr<int>();
|
||||
int valSum = sp[p0] - sp[p1] - sp[p2] + sp[p3];
|
||||
const double *sqp = sqSum.ptr<double>();
|
||||
double valSqSum = sqp[p0] - sqp[p1] - sqp[p2] + sqp[p3];
|
||||
return (float) sqrt( (double) (area * valSqSum - (double)valSum * valSum) );
|
||||
}
|
||||
|
||||
CvParams::CvParams() : name( "params" ) {}
|
||||
void CvParams::printDefaults() const
|
||||
{ cout << "--" << name << "--" << endl; }
|
||||
void CvParams::printAttrs() const {}
|
||||
bool CvParams::scanAttr( const string, const string ) { return false; }
|
||||
|
||||
|
||||
//---------------------------- FeatureParams --------------------------------------
|
||||
|
||||
CvFeatureParams::CvFeatureParams() : maxCatCount( 0 ), featSize( 1 )
|
||||
{
|
||||
name = CC_FEATURE_PARAMS;
|
||||
}
|
||||
|
||||
void CvFeatureParams::init( const CvFeatureParams& fp )
|
||||
{
|
||||
maxCatCount = fp.maxCatCount;
|
||||
featSize = fp.featSize;
|
||||
}
|
||||
|
||||
void CvFeatureParams::write( FileStorage &fs ) const
|
||||
{
|
||||
fs << CC_MAX_CAT_COUNT << maxCatCount;
|
||||
fs << CC_FEATURE_SIZE << featSize;
|
||||
}
|
||||
|
||||
bool CvFeatureParams::read( const FileNode &node )
|
||||
{
|
||||
if ( node.empty() )
|
||||
return false;
|
||||
maxCatCount = node[CC_MAX_CAT_COUNT];
|
||||
featSize = node[CC_FEATURE_SIZE];
|
||||
return ( maxCatCount >= 0 && featSize >= 1 );
|
||||
}
|
||||
|
||||
Ptr<CvFeatureParams> CvFeatureParams::create( int featureType )
|
||||
{
|
||||
return featureType == HAAR ? Ptr<CvFeatureParams>(new CvHaarFeatureParams) :
|
||||
featureType == LBP ? Ptr<CvFeatureParams>(new CvLBPFeatureParams) :
|
||||
featureType == HOG ? Ptr<CvFeatureParams>(new CvHOGFeatureParams) :
|
||||
Ptr<CvFeatureParams>();
|
||||
}
|
||||
|
||||
//------------------------------------- FeatureEvaluator ---------------------------------------
|
||||
|
||||
void CvFeatureEvaluator::init(const CvFeatureParams *_featureParams,
|
||||
int _maxSampleCount, Size _winSize )
|
||||
{
|
||||
CV_Assert(_maxSampleCount > 0);
|
||||
featureParams = (CvFeatureParams *)_featureParams;
|
||||
winSize = _winSize;
|
||||
numFeatures = 0;
|
||||
cls.create( (int)_maxSampleCount, 1, CV_32FC1 );
|
||||
generateFeatures();
|
||||
}
|
||||
|
||||
void CvFeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)
|
||||
{
|
||||
CV_Assert(img.cols == winSize.width);
|
||||
CV_Assert(img.rows == winSize.height);
|
||||
CV_Assert(idx < cls.rows);
|
||||
cls.ptr<float>(idx)[0] = clsLabel;
|
||||
}
|
||||
|
||||
Ptr<CvFeatureEvaluator> CvFeatureEvaluator::create(int type)
|
||||
{
|
||||
return type == CvFeatureParams::HAAR ? Ptr<CvFeatureEvaluator>(new CvHaarEvaluator) :
|
||||
type == CvFeatureParams::LBP ? Ptr<CvFeatureEvaluator>(new CvLBPEvaluator) :
|
||||
type == CvFeatureParams::HOG ? Ptr<CvFeatureEvaluator>(new CvHOGEvaluator) :
|
||||
Ptr<CvFeatureEvaluator>();
|
||||
}
|
312
opencv-apps/traincascade/haarfeatures.cpp
Normal file
312
opencv-apps/traincascade/haarfeatures.cpp
Normal file
@ -0,0 +1,312 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include "haarfeatures.h"
|
||||
#include "cascadeclassifier.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
CvHaarFeatureParams::CvHaarFeatureParams() : mode(BASIC)
|
||||
{
|
||||
name = HFP_NAME;
|
||||
}
|
||||
|
||||
CvHaarFeatureParams::CvHaarFeatureParams( int _mode ) : mode( _mode )
|
||||
{
|
||||
name = HFP_NAME;
|
||||
}
|
||||
|
||||
void CvHaarFeatureParams::init( const CvFeatureParams& fp )
|
||||
{
|
||||
CvFeatureParams::init( fp );
|
||||
mode = ((const CvHaarFeatureParams&)fp).mode;
|
||||
}
|
||||
|
||||
void CvHaarFeatureParams::write( FileStorage &fs ) const
|
||||
{
|
||||
CvFeatureParams::write( fs );
|
||||
string modeStr = mode == BASIC ? CC_MODE_BASIC :
|
||||
mode == CORE ? CC_MODE_CORE :
|
||||
mode == ALL ? CC_MODE_ALL : string();
|
||||
CV_Assert( !modeStr.empty() );
|
||||
fs << CC_MODE << modeStr;
|
||||
}
|
||||
|
||||
bool CvHaarFeatureParams::read( const FileNode &node )
|
||||
{
|
||||
if( !CvFeatureParams::read( node ) )
|
||||
return false;
|
||||
|
||||
FileNode rnode = node[CC_MODE];
|
||||
if( !rnode.isString() )
|
||||
return false;
|
||||
string modeStr;
|
||||
rnode >> modeStr;
|
||||
mode = !modeStr.compare( CC_MODE_BASIC ) ? BASIC :
|
||||
!modeStr.compare( CC_MODE_CORE ) ? CORE :
|
||||
!modeStr.compare( CC_MODE_ALL ) ? ALL : -1;
|
||||
return (mode >= 0);
|
||||
}
|
||||
|
||||
void CvHaarFeatureParams::printDefaults() const
|
||||
{
|
||||
CvFeatureParams::printDefaults();
|
||||
cout << " [-mode <" CC_MODE_BASIC << "(default) | "
|
||||
<< CC_MODE_CORE <<" | " << CC_MODE_ALL << endl;
|
||||
}
|
||||
|
||||
void CvHaarFeatureParams::printAttrs() const
|
||||
{
|
||||
CvFeatureParams::printAttrs();
|
||||
string mode_str = mode == BASIC ? CC_MODE_BASIC :
|
||||
mode == CORE ? CC_MODE_CORE :
|
||||
mode == ALL ? CC_MODE_ALL : 0;
|
||||
cout << "mode: " << mode_str << endl;
|
||||
}
|
||||
|
||||
bool CvHaarFeatureParams::scanAttr( const string prmName, const string val)
|
||||
{
|
||||
if ( !CvFeatureParams::scanAttr( prmName, val ) )
|
||||
{
|
||||
if( !prmName.compare("-mode") )
|
||||
{
|
||||
mode = !val.compare( CC_MODE_CORE ) ? CORE :
|
||||
!val.compare( CC_MODE_ALL ) ? ALL :
|
||||
!val.compare( CC_MODE_BASIC ) ? BASIC : -1;
|
||||
if (mode == -1)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------- HaarFeatureEvaluator ----------------
|
||||
|
||||
void CvHaarEvaluator::init(const CvFeatureParams *_featureParams,
|
||||
int _maxSampleCount, Size _winSize )
|
||||
{
|
||||
CV_Assert(_maxSampleCount > 0);
|
||||
int cols = (_winSize.width + 1) * (_winSize.height + 1);
|
||||
sum.create((int)_maxSampleCount, cols, CV_32SC1);
|
||||
tilted.create((int)_maxSampleCount, cols, CV_32SC1);
|
||||
normfactor.create(1, (int)_maxSampleCount, CV_32FC1);
|
||||
CvFeatureEvaluator::init( _featureParams, _maxSampleCount, _winSize );
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::setImage(const Mat& img, uchar clsLabel, int idx)
|
||||
{
|
||||
CV_DbgAssert( !sum.empty() && !tilted.empty() && !normfactor.empty() );
|
||||
CvFeatureEvaluator::setImage( img, clsLabel, idx);
|
||||
Mat innSum(winSize.height + 1, winSize.width + 1, sum.type(), sum.ptr<int>((int)idx));
|
||||
Mat innSqSum;
|
||||
if (((const CvHaarFeatureParams*)featureParams)->mode == CvHaarFeatureParams::ALL)
|
||||
{
|
||||
Mat innTilted(winSize.height + 1, winSize.width + 1, tilted.type(), tilted.ptr<int>((int)idx));
|
||||
integral(img, innSum, innSqSum, innTilted);
|
||||
}
|
||||
else
|
||||
integral(img, innSum, innSqSum);
|
||||
normfactor.ptr<float>(0)[idx] = calcNormFactor( innSum, innSqSum );
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
|
||||
{
|
||||
_writeFeatures( features, fs, featureMap );
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::writeFeature(FileStorage &fs, int fi) const
|
||||
{
|
||||
CV_DbgAssert( fi < (int)features.size() );
|
||||
features[fi].write(fs);
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::generateFeatures()
|
||||
{
|
||||
int mode = ((const CvHaarFeatureParams*)((CvFeatureParams*)featureParams))->mode;
|
||||
int offset = winSize.width + 1;
|
||||
for( int x = 0; x < winSize.width; x++ )
|
||||
{
|
||||
for( int y = 0; y < winSize.height; y++ )
|
||||
{
|
||||
for( int dx = 1; dx <= winSize.width; dx++ )
|
||||
{
|
||||
for( int dy = 1; dy <= winSize.height; dy++ )
|
||||
{
|
||||
// haar_x2
|
||||
if ( (x+dx*2 <= winSize.width) && (y+dy <= winSize.height) )
|
||||
{
|
||||
features.push_back( Feature( offset, false,
|
||||
x, y, dx*2, dy, -1,
|
||||
x+dx, y, dx , dy, +2 ) );
|
||||
}
|
||||
// haar_y2
|
||||
if ( (x+dx <= winSize.width) && (y+dy*2 <= winSize.height) )
|
||||
{
|
||||
features.push_back( Feature( offset, false,
|
||||
x, y, dx, dy*2, -1,
|
||||
x, y+dy, dx, dy, +2 ) );
|
||||
}
|
||||
// haar_x3
|
||||
if ( (x+dx*3 <= winSize.width) && (y+dy <= winSize.height) )
|
||||
{
|
||||
features.push_back( Feature( offset, false,
|
||||
x, y, dx*3, dy, -1,
|
||||
x+dx, y, dx , dy, +2 ) );
|
||||
}
|
||||
// haar_y3
|
||||
if ( (x+dx <= winSize.width) && (y+dy*3 <= winSize.height) )
|
||||
{
|
||||
features.push_back( Feature( offset, false,
|
||||
x, y, dx, dy*3, -1,
|
||||
x, y+dy, dx, dy, +2 ) );
|
||||
}
|
||||
if( mode != CvHaarFeatureParams::BASIC )
|
||||
{
|
||||
// haar_x4
|
||||
if ( (x+dx*4 <= winSize.width) && (y+dy <= winSize.height) )
|
||||
{
|
||||
features.push_back( Feature( offset, false,
|
||||
x, y, dx*4, dy, -1,
|
||||
x+dx, y, dx*2, dy, +2 ) );
|
||||
}
|
||||
// haar_y4
|
||||
if ( (x+dx <= winSize.width ) && (y+dy*4 <= winSize.height) )
|
||||
{
|
||||
features.push_back( Feature( offset, false,
|
||||
x, y, dx, dy*4, -1,
|
||||
x, y+dy, dx, dy*2, +2 ) );
|
||||
}
|
||||
}
|
||||
// x2_y2
|
||||
if ( (x+dx*2 <= winSize.width) && (y+dy*2 <= winSize.height) )
|
||||
{
|
||||
features.push_back( Feature( offset, false,
|
||||
x, y, dx*2, dy*2, -1,
|
||||
x, y, dx, dy, +2,
|
||||
x+dx, y+dy, dx, dy, +2 ) );
|
||||
}
|
||||
if (mode != CvHaarFeatureParams::BASIC)
|
||||
{
|
||||
if ( (x+dx*3 <= winSize.width) && (y+dy*3 <= winSize.height) )
|
||||
{
|
||||
features.push_back( Feature( offset, false,
|
||||
x , y , dx*3, dy*3, -1,
|
||||
x+dx, y+dy, dx , dy , +9) );
|
||||
}
|
||||
}
|
||||
if (mode == CvHaarFeatureParams::ALL)
|
||||
{
|
||||
// tilted haar_x2
|
||||
if ( (x+2*dx <= winSize.width) && (y+2*dx+dy <= winSize.height) && (x-dy>= 0) )
|
||||
{
|
||||
features.push_back( Feature( offset, true,
|
||||
x, y, dx*2, dy, -1,
|
||||
x, y, dx, dy, +2 ) );
|
||||
}
|
||||
// tilted haar_y2
|
||||
if ( (x+dx <= winSize.width) && (y+dx+2*dy <= winSize.height) && (x-2*dy>= 0) )
|
||||
{
|
||||
features.push_back( Feature( offset, true,
|
||||
x, y, dx, 2*dy, -1,
|
||||
x, y, dx, dy, +2 ) );
|
||||
}
|
||||
// tilted haar_x3
|
||||
if ( (x+3*dx <= winSize.width) && (y+3*dx+dy <= winSize.height) && (x-dy>= 0) )
|
||||
{
|
||||
features.push_back( Feature( offset, true,
|
||||
x, y, dx*3, dy, -1,
|
||||
x+dx, y+dx, dx, dy, +3 ) );
|
||||
}
|
||||
// tilted haar_y3
|
||||
if ( (x+dx <= winSize.width) && (y+dx+3*dy <= winSize.height) && (x-3*dy>= 0) )
|
||||
{
|
||||
features.push_back( Feature( offset, true,
|
||||
x, y, dx, 3*dy, -1,
|
||||
x-dy, y+dy, dx, dy, +3 ) );
|
||||
}
|
||||
// tilted haar_x4
|
||||
if ( (x+4*dx <= winSize.width) && (y+4*dx+dy <= winSize.height) && (x-dy>= 0) )
|
||||
{
|
||||
features.push_back( Feature( offset, true,
|
||||
x, y, dx*4, dy, -1,
|
||||
x+dx, y+dx, dx*2, dy, +2 ) );
|
||||
}
|
||||
// tilted haar_y4
|
||||
if ( (x+dx <= winSize.width) && (y+dx+4*dy <= winSize.height) && (x-4*dy>= 0) )
|
||||
{
|
||||
features.push_back( Feature( offset, true,
|
||||
x, y, dx, 4*dy, -1,
|
||||
x-dy, y+dy, dx, 2*dy, +2 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
numFeatures = (int)features.size();
|
||||
}
|
||||
|
||||
CvHaarEvaluator::Feature::Feature()
|
||||
{
|
||||
tilted = false;
|
||||
rect[0].r = rect[1].r = rect[2].r = Rect(0,0,0,0);
|
||||
rect[0].weight = rect[1].weight = rect[2].weight = 0;
|
||||
}
|
||||
|
||||
CvHaarEvaluator::Feature::Feature( int offset, bool _tilted,
|
||||
int x0, int y0, int w0, int h0, float wt0,
|
||||
int x1, int y1, int w1, int h1, float wt1,
|
||||
int x2, int y2, int w2, int h2, float wt2 )
|
||||
{
|
||||
tilted = _tilted;
|
||||
|
||||
rect[0].r.x = x0;
|
||||
rect[0].r.y = y0;
|
||||
rect[0].r.width = w0;
|
||||
rect[0].r.height = h0;
|
||||
rect[0].weight = wt0;
|
||||
|
||||
rect[1].r.x = x1;
|
||||
rect[1].r.y = y1;
|
||||
rect[1].r.width = w1;
|
||||
rect[1].r.height = h1;
|
||||
rect[1].weight = wt1;
|
||||
|
||||
rect[2].r.x = x2;
|
||||
rect[2].r.y = y2;
|
||||
rect[2].r.width = w2;
|
||||
rect[2].r.height = h2;
|
||||
rect[2].weight = wt2;
|
||||
|
||||
if( !tilted )
|
||||
{
|
||||
for( int j = 0; j < CV_HAAR_FEATURE_MAX; j++ )
|
||||
{
|
||||
if( rect[j].weight == 0.0F )
|
||||
break;
|
||||
CV_SUM_OFFSETS( fastRect[j].p0, fastRect[j].p1, fastRect[j].p2, fastRect[j].p3, rect[j].r, offset )
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int j = 0; j < CV_HAAR_FEATURE_MAX; j++ )
|
||||
{
|
||||
if( rect[j].weight == 0.0F )
|
||||
break;
|
||||
CV_TILTED_OFFSETS( fastRect[j].p0, fastRect[j].p1, fastRect[j].p2, fastRect[j].p3, rect[j].r, offset )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::Feature::write( FileStorage &fs ) const
|
||||
{
|
||||
fs << CC_RECTS << "[";
|
||||
for( int ri = 0; ri < CV_HAAR_FEATURE_MAX && rect[ri].r.width != 0; ++ri )
|
||||
{
|
||||
fs << "[:" << rect[ri].r.x << rect[ri].r.y <<
|
||||
rect[ri].r.width << rect[ri].r.height << rect[ri].weight << "]";
|
||||
}
|
||||
fs << "]" << CC_TILTED << tilted;
|
||||
}
|
89
opencv-apps/traincascade/haarfeatures.h
Normal file
89
opencv-apps/traincascade/haarfeatures.h
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef _OPENCV_HAARFEATURES_H_
|
||||
#define _OPENCV_HAARFEATURES_H_
|
||||
|
||||
#include "traincascade_features.h"
|
||||
|
||||
#define CV_HAAR_FEATURE_MAX 3
|
||||
|
||||
#define HFP_NAME "haarFeatureParams"
|
||||
class CvHaarFeatureParams : public CvFeatureParams
|
||||
{
|
||||
public:
|
||||
enum { BASIC = 0, CORE = 1, ALL = 2 };
|
||||
/* 0 - BASIC = Viola
|
||||
* 1 - CORE = All upright
|
||||
* 2 - ALL = All features */
|
||||
|
||||
CvHaarFeatureParams();
|
||||
CvHaarFeatureParams( int _mode );
|
||||
|
||||
virtual void init( const CvFeatureParams& fp );
|
||||
virtual void write( cv::FileStorage &fs ) const;
|
||||
virtual bool read( const cv::FileNode &node );
|
||||
|
||||
virtual void printDefaults() const;
|
||||
virtual void printAttrs() const;
|
||||
virtual bool scanAttr( const std::string prm, const std::string val);
|
||||
|
||||
int mode;
|
||||
};
|
||||
|
||||
class CvHaarEvaluator : public CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
virtual void init(const CvFeatureParams *_featureParams,
|
||||
int _maxSampleCount, cv::Size _winSize );
|
||||
virtual void setImage(const cv::Mat& img, uchar clsLabel, int idx);
|
||||
virtual float operator()(int featureIdx, int sampleIdx) const;
|
||||
virtual void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const;
|
||||
void writeFeature( cv::FileStorage &fs, int fi ) const; // for old file fornat
|
||||
protected:
|
||||
virtual void generateFeatures();
|
||||
|
||||
class Feature
|
||||
{
|
||||
public:
|
||||
Feature();
|
||||
Feature( int offset, bool _tilted,
|
||||
int x0, int y0, int w0, int h0, float wt0,
|
||||
int x1, int y1, int w1, int h1, float wt1,
|
||||
int x2 = 0, int y2 = 0, int w2 = 0, int h2 = 0, float wt2 = 0.0F );
|
||||
float calc( const cv::Mat &sum, const cv::Mat &tilted, size_t y) const;
|
||||
void write( cv::FileStorage &fs ) const;
|
||||
|
||||
bool tilted;
|
||||
struct
|
||||
{
|
||||
cv::Rect r;
|
||||
float weight;
|
||||
} rect[CV_HAAR_FEATURE_MAX];
|
||||
|
||||
struct
|
||||
{
|
||||
int p0, p1, p2, p3;
|
||||
} fastRect[CV_HAAR_FEATURE_MAX];
|
||||
};
|
||||
|
||||
std::vector<Feature> features;
|
||||
cv::Mat sum; /* sum images (each row represents image) */
|
||||
cv::Mat tilted; /* tilted sum images (each row represents image) */
|
||||
cv::Mat normfactor; /* normalization factor */
|
||||
};
|
||||
|
||||
inline float CvHaarEvaluator::operator()(int featureIdx, int sampleIdx) const
|
||||
{
|
||||
float nf = normfactor.at<float>(0, sampleIdx);
|
||||
return !nf ? 0.0f : (features[featureIdx].calc( sum, tilted, sampleIdx)/nf);
|
||||
}
|
||||
|
||||
inline float CvHaarEvaluator::Feature::calc( const cv::Mat &_sum, const cv::Mat &_tilted, size_t y) const
|
||||
{
|
||||
const int* img = tilted ? _tilted.ptr<int>((int)y) : _sum.ptr<int>((int)y);
|
||||
float ret = rect[0].weight * (img[fastRect[0].p0] - img[fastRect[0].p1] - img[fastRect[0].p2] + img[fastRect[0].p3] ) +
|
||||
rect[1].weight * (img[fastRect[1].p0] - img[fastRect[1].p1] - img[fastRect[1].p2] + img[fastRect[1].p3] );
|
||||
if( rect[2].weight != 0.0f )
|
||||
ret += rect[2].weight * (img[fastRect[2].p0] - img[fastRect[2].p1] - img[fastRect[2].p2] + img[fastRect[2].p3] );
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
186
opencv-apps/traincascade/imagestorage.cpp
Normal file
186
opencv-apps/traincascade/imagestorage.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/core/core_c.h"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
|
||||
#include "imagestorage.h"
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
bool CvCascadeImageReader::create( const string _posFilename, const string _negFilename, Size _winSize )
|
||||
{
|
||||
return posReader.create(_posFilename) && negReader.create(_negFilename, _winSize);
|
||||
}
|
||||
|
||||
CvCascadeImageReader::NegReader::NegReader()
|
||||
{
|
||||
src.create( 0, 0 , CV_8UC1 );
|
||||
img.create( 0, 0, CV_8UC1 );
|
||||
point = offset = Point( 0, 0 );
|
||||
scale = 1.0F;
|
||||
scaleFactor = 1.4142135623730950488016887242097F;
|
||||
stepFactor = 0.5F;
|
||||
}
|
||||
|
||||
bool CvCascadeImageReader::NegReader::create( const string _filename, Size _winSize )
|
||||
{
|
||||
string str;
|
||||
std::ifstream file(_filename.c_str());
|
||||
if ( !file.is_open() )
|
||||
return false;
|
||||
|
||||
while( !file.eof() )
|
||||
{
|
||||
std::getline(file, str);
|
||||
str.erase(str.find_last_not_of(" \n\r\t")+1);
|
||||
if (str.empty()) break;
|
||||
if (str.at(0) == '#' ) continue; /* comment */
|
||||
imgFilenames.push_back(str);
|
||||
}
|
||||
file.close();
|
||||
|
||||
winSize = _winSize;
|
||||
last = round = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CvCascadeImageReader::NegReader::nextImg()
|
||||
{
|
||||
Point _offset = Point(0,0);
|
||||
size_t count = imgFilenames.size();
|
||||
for( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
src = imread( imgFilenames[last++], 0 );
|
||||
if( src.empty() ){
|
||||
last %= count;
|
||||
continue;
|
||||
}
|
||||
round += last / count;
|
||||
round = round % (winSize.width * winSize.height);
|
||||
last %= count;
|
||||
|
||||
_offset.x = std::min( (int)round % winSize.width, src.cols - winSize.width );
|
||||
_offset.y = std::min( (int)round / winSize.width, src.rows - winSize.height );
|
||||
if( !src.empty() && src.type() == CV_8UC1
|
||||
&& _offset.x >= 0 && _offset.y >= 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
if( src.empty() )
|
||||
return false; // no appropriate image
|
||||
point = offset = _offset;
|
||||
scale = max( ((float)winSize.width + point.x) / ((float)src.cols),
|
||||
((float)winSize.height + point.y) / ((float)src.rows) );
|
||||
|
||||
Size sz( (int)(scale*src.cols + 0.5F), (int)(scale*src.rows + 0.5F) );
|
||||
resize( src, img, sz, 0, 0, INTER_LINEAR_EXACT );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CvCascadeImageReader::NegReader::get( Mat& _img )
|
||||
{
|
||||
CV_Assert( !_img.empty() );
|
||||
CV_Assert( _img.type() == CV_8UC1 );
|
||||
CV_Assert( _img.cols == winSize.width );
|
||||
CV_Assert( _img.rows == winSize.height );
|
||||
|
||||
if( img.empty() )
|
||||
if ( !nextImg() )
|
||||
return false;
|
||||
|
||||
Mat mat( winSize.height, winSize.width, CV_8UC1,
|
||||
(void*)(img.ptr(point.y) + point.x * img.elemSize()), img.step );
|
||||
mat.copyTo(_img);
|
||||
|
||||
if( (int)( point.x + (1.0F + stepFactor ) * winSize.width ) < img.cols )
|
||||
point.x += (int)(stepFactor * winSize.width);
|
||||
else
|
||||
{
|
||||
point.x = offset.x;
|
||||
if( (int)( point.y + (1.0F + stepFactor ) * winSize.height ) < img.rows )
|
||||
point.y += (int)(stepFactor * winSize.height);
|
||||
else
|
||||
{
|
||||
point.y = offset.y;
|
||||
scale *= scaleFactor;
|
||||
if( scale <= 1.0F )
|
||||
resize( src, img, Size( (int)(scale*src.cols), (int)(scale*src.rows) ), 0, 0, INTER_LINEAR_EXACT );
|
||||
else
|
||||
{
|
||||
if ( !nextImg() )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CvCascadeImageReader::PosReader::PosReader()
|
||||
{
|
||||
file = 0;
|
||||
vec = 0;
|
||||
}
|
||||
|
||||
bool CvCascadeImageReader::PosReader::create( const string _filename )
|
||||
{
|
||||
if ( file )
|
||||
fclose( file );
|
||||
file = fopen( _filename.c_str(), "rb" );
|
||||
|
||||
if( !file )
|
||||
return false;
|
||||
short tmp = 0;
|
||||
if( fread( &count, sizeof( count ), 1, file ) != 1 ||
|
||||
fread( &vecSize, sizeof( vecSize ), 1, file ) != 1 ||
|
||||
fread( &tmp, sizeof( tmp ), 1, file ) != 1 ||
|
||||
fread( &tmp, sizeof( tmp ), 1, file ) != 1 )
|
||||
CV_Error_( CV_StsParseError, ("wrong file format for %s\n", _filename.c_str()) );
|
||||
base = sizeof( count ) + sizeof( vecSize ) + 2*sizeof( tmp );
|
||||
if( feof( file ) )
|
||||
return false;
|
||||
last = 0;
|
||||
vec = (short*) cvAlloc( sizeof( *vec ) * vecSize );
|
||||
CV_Assert( vec );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CvCascadeImageReader::PosReader::get( Mat &_img )
|
||||
{
|
||||
CV_Assert( _img.rows * _img.cols == vecSize );
|
||||
uchar tmp = 0;
|
||||
size_t elements_read = fread( &tmp, sizeof( tmp ), 1, file );
|
||||
if( elements_read != 1 )
|
||||
CV_Error( CV_StsBadArg, "Can not get new positive sample. The most possible reason is "
|
||||
"insufficient count of samples in given vec-file.\n");
|
||||
elements_read = fread( vec, sizeof( vec[0] ), vecSize, file );
|
||||
if( elements_read != (size_t)(vecSize) )
|
||||
CV_Error( CV_StsBadArg, "Can not get new positive sample. Seems that vec-file has incorrect structure.\n");
|
||||
|
||||
if( feof( file ) || last++ >= count )
|
||||
CV_Error( CV_StsBadArg, "Can not get new positive sample. vec-file is over.\n");
|
||||
|
||||
for( int r = 0; r < _img.rows; r++ )
|
||||
{
|
||||
for( int c = 0; c < _img.cols; c++ )
|
||||
_img.ptr(r)[c] = (uchar)vec[r * _img.cols + c];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CvCascadeImageReader::PosReader::restart()
|
||||
{
|
||||
CV_Assert( file );
|
||||
last = 0;
|
||||
fseek( file, base, SEEK_SET );
|
||||
}
|
||||
|
||||
CvCascadeImageReader::PosReader::~PosReader()
|
||||
{
|
||||
if (file)
|
||||
fclose( file );
|
||||
cvFree( &vec );
|
||||
}
|
50
opencv-apps/traincascade/imagestorage.h
Normal file
50
opencv-apps/traincascade/imagestorage.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef _OPENCV_IMAGESTORAGE_H_
|
||||
#define _OPENCV_IMAGESTORAGE_H_
|
||||
|
||||
|
||||
class CvCascadeImageReader
|
||||
{
|
||||
public:
|
||||
bool create( const std::string _posFilename, const std::string _negFilename, cv::Size _winSize );
|
||||
void restart() { posReader.restart(); }
|
||||
bool getNeg(cv::Mat &_img) { return negReader.get( _img ); }
|
||||
bool getPos(cv::Mat &_img) { return posReader.get( _img ); }
|
||||
|
||||
private:
|
||||
class PosReader
|
||||
{
|
||||
public:
|
||||
PosReader();
|
||||
virtual ~PosReader();
|
||||
bool create( const std::string _filename );
|
||||
bool get( cv::Mat &_img );
|
||||
void restart();
|
||||
|
||||
short* vec;
|
||||
FILE* file;
|
||||
int count;
|
||||
int vecSize;
|
||||
int last;
|
||||
int base;
|
||||
} posReader;
|
||||
|
||||
class NegReader
|
||||
{
|
||||
public:
|
||||
NegReader();
|
||||
bool create( const std::string _filename, cv::Size _winSize );
|
||||
bool get( cv::Mat& _img );
|
||||
bool nextImg();
|
||||
|
||||
cv::Mat src, img;
|
||||
std::vector<std::string> imgFilenames;
|
||||
cv::Point offset, point;
|
||||
float scale;
|
||||
float scaleFactor;
|
||||
float stepFactor;
|
||||
size_t last, round;
|
||||
cv::Size winSize;
|
||||
} negReader;
|
||||
};
|
||||
|
||||
#endif
|
67
opencv-apps/traincascade/lbpfeatures.cpp
Normal file
67
opencv-apps/traincascade/lbpfeatures.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include "lbpfeatures.h"
|
||||
#include "cascadeclassifier.h"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
CvLBPFeatureParams::CvLBPFeatureParams()
|
||||
{
|
||||
maxCatCount = 256;
|
||||
name = LBPF_NAME;
|
||||
}
|
||||
|
||||
void CvLBPEvaluator::init(const CvFeatureParams *_featureParams, int _maxSampleCount, Size _winSize)
|
||||
{
|
||||
CV_Assert( _maxSampleCount > 0);
|
||||
sum.create((int)_maxSampleCount, (_winSize.width + 1) * (_winSize.height + 1), CV_32SC1);
|
||||
CvFeatureEvaluator::init( _featureParams, _maxSampleCount, _winSize );
|
||||
}
|
||||
|
||||
void CvLBPEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)
|
||||
{
|
||||
CV_DbgAssert( !sum.empty() );
|
||||
CvFeatureEvaluator::setImage( img, clsLabel, idx );
|
||||
Mat innSum(winSize.height + 1, winSize.width + 1, sum.type(), sum.ptr<int>((int)idx));
|
||||
integral( img, innSum );
|
||||
}
|
||||
|
||||
void CvLBPEvaluator::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
|
||||
{
|
||||
_writeFeatures( features, fs, featureMap );
|
||||
}
|
||||
|
||||
void CvLBPEvaluator::generateFeatures()
|
||||
{
|
||||
int offset = winSize.width + 1;
|
||||
for( int x = 0; x < winSize.width; x++ )
|
||||
for( int y = 0; y < winSize.height; y++ )
|
||||
for( int w = 1; w <= winSize.width / 3; w++ )
|
||||
for( int h = 1; h <= winSize.height / 3; h++ )
|
||||
if ( (x+3*w <= winSize.width) && (y+3*h <= winSize.height) )
|
||||
features.push_back( Feature(offset, x, y, w, h ) );
|
||||
numFeatures = (int)features.size();
|
||||
}
|
||||
|
||||
CvLBPEvaluator::Feature::Feature()
|
||||
{
|
||||
rect = cvRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
CvLBPEvaluator::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight )
|
||||
{
|
||||
Rect tr = rect = cvRect(x, y, _blockWidth, _blockHeight);
|
||||
CV_SUM_OFFSETS( p[0], p[1], p[4], p[5], tr, offset )
|
||||
tr.x += 2*rect.width;
|
||||
CV_SUM_OFFSETS( p[2], p[3], p[6], p[7], tr, offset )
|
||||
tr.y +=2*rect.height;
|
||||
CV_SUM_OFFSETS( p[10], p[11], p[14], p[15], tr, offset )
|
||||
tr.x -= 2*rect.width;
|
||||
CV_SUM_OFFSETS( p[8], p[9], p[12], p[13], tr, offset )
|
||||
}
|
||||
|
||||
void CvLBPEvaluator::Feature::write(FileStorage &fs) const
|
||||
{
|
||||
fs << CC_RECT << "[:" << rect.x << rect.y << rect.width << rect.height << "]";
|
||||
}
|
57
opencv-apps/traincascade/lbpfeatures.h
Normal file
57
opencv-apps/traincascade/lbpfeatures.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef _OPENCV_LBPFEATURES_H_
|
||||
#define _OPENCV_LBPFEATURES_H_
|
||||
|
||||
#include "traincascade_features.h"
|
||||
|
||||
#define LBPF_NAME "lbpFeatureParams"
|
||||
struct CvLBPFeatureParams : CvFeatureParams
|
||||
{
|
||||
CvLBPFeatureParams();
|
||||
|
||||
};
|
||||
|
||||
class CvLBPEvaluator : public CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
virtual ~CvLBPEvaluator() {}
|
||||
virtual void init(const CvFeatureParams *_featureParams,
|
||||
int _maxSampleCount, cv::Size _winSize );
|
||||
virtual void setImage(const cv::Mat& img, uchar clsLabel, int idx);
|
||||
virtual float operator()(int featureIdx, int sampleIdx) const
|
||||
{ return (float)features[featureIdx].calc( sum, sampleIdx); }
|
||||
virtual void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const;
|
||||
protected:
|
||||
virtual void generateFeatures();
|
||||
|
||||
class Feature
|
||||
{
|
||||
public:
|
||||
Feature();
|
||||
Feature( int offset, int x, int y, int _block_w, int _block_h );
|
||||
uchar calc( const cv::Mat& _sum, size_t y ) const;
|
||||
void write( cv::FileStorage &fs ) const;
|
||||
|
||||
cv::Rect rect;
|
||||
int p[16];
|
||||
};
|
||||
std::vector<Feature> features;
|
||||
|
||||
cv::Mat sum;
|
||||
};
|
||||
|
||||
inline uchar CvLBPEvaluator::Feature::calc(const cv::Mat &_sum, size_t y) const
|
||||
{
|
||||
const int* psum = _sum.ptr<int>((int)y);
|
||||
int cval = psum[p[5]] - psum[p[6]] - psum[p[9]] + psum[p[10]];
|
||||
|
||||
return (uchar)((psum[p[0]] - psum[p[1]] - psum[p[4]] + psum[p[5]] >= cval ? 128 : 0) | // 0
|
||||
(psum[p[1]] - psum[p[2]] - psum[p[5]] + psum[p[6]] >= cval ? 64 : 0) | // 1
|
||||
(psum[p[2]] - psum[p[3]] - psum[p[6]] + psum[p[7]] >= cval ? 32 : 0) | // 2
|
||||
(psum[p[6]] - psum[p[7]] - psum[p[10]] + psum[p[11]] >= cval ? 16 : 0) | // 5
|
||||
(psum[p[10]] - psum[p[11]] - psum[p[14]] + psum[p[15]] >= cval ? 8 : 0) | // 8
|
||||
(psum[p[9]] - psum[p[10]] - psum[p[13]] + psum[p[14]] >= cval ? 4 : 0) | // 7
|
||||
(psum[p[8]] - psum[p[9]] - psum[p[12]] + psum[p[13]] >= cval ? 2 : 0) | // 6
|
||||
(psum[p[4]] - psum[p[5]] - psum[p[8]] + psum[p[9]] >= cval ? 1 : 0)); // 3
|
||||
}
|
||||
|
||||
#endif
|
2043
opencv-apps/traincascade/old_ml.hpp
Normal file
2043
opencv-apps/traincascade/old_ml.hpp
Normal file
File diff suppressed because it is too large
Load Diff
2162
opencv-apps/traincascade/old_ml_boost.cpp
Normal file
2162
opencv-apps/traincascade/old_ml_boost.cpp
Normal file
File diff suppressed because it is too large
Load Diff
792
opencv-apps/traincascade/old_ml_data.cpp
Normal file
792
opencv-apps/traincascade/old_ml_data.cpp
Normal file
@ -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. */
|
1688
opencv-apps/traincascade/old_ml_inner_functions.cpp
Normal file
1688
opencv-apps/traincascade/old_ml_inner_functions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
376
opencv-apps/traincascade/old_ml_precomp.hpp
Normal file
376
opencv-apps/traincascade/old_ml_precomp.hpp
Normal file
@ -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, fills 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__ */
|
4148
opencv-apps/traincascade/old_ml_tree.cpp
Normal file
4148
opencv-apps/traincascade/old_ml_tree.cpp
Normal file
File diff suppressed because it is too large
Load Diff
129
opencv-apps/traincascade/traincascade.cpp
Normal file
129
opencv-apps/traincascade/traincascade.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "cascadeclassifier.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
/*
|
||||
traincascade.cpp is the source file of the program used for cascade training.
|
||||
User has to provide training input in form of positive and negative training images,
|
||||
and other data related to training in form of command line argument.
|
||||
*/
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
CvCascadeClassifier classifier;
|
||||
string cascadeDirName, vecName, bgName;
|
||||
int numPos = 2000;
|
||||
int numNeg = 1000;
|
||||
int numStages = 20;
|
||||
int numThreads = getNumThreads();
|
||||
int precalcValBufSize = 1024,
|
||||
precalcIdxBufSize = 1024;
|
||||
bool baseFormatSave = false;
|
||||
double acceptanceRatioBreakValue = -1.0;
|
||||
|
||||
CvCascadeParams cascadeParams;
|
||||
CvCascadeBoostParams stageParams;
|
||||
Ptr<CvFeatureParams> featureParams[] = { makePtr<CvHaarFeatureParams>(),
|
||||
makePtr<CvLBPFeatureParams>(),
|
||||
makePtr<CvHOGFeatureParams>()
|
||||
};
|
||||
int fc = sizeof(featureParams)/sizeof(featureParams[0]);
|
||||
if( argc == 1 )
|
||||
{
|
||||
cout << "Usage: " << argv[0] << endl;
|
||||
cout << " -data <cascade_dir_name>" << endl;
|
||||
cout << " -vec <vec_file_name>" << endl;
|
||||
cout << " -bg <background_file_name>" << endl;
|
||||
cout << " [-numPos <number_of_positive_samples = " << numPos << ">]" << endl;
|
||||
cout << " [-numNeg <number_of_negative_samples = " << numNeg << ">]" << endl;
|
||||
cout << " [-numStages <number_of_stages = " << numStages << ">]" << endl;
|
||||
cout << " [-precalcValBufSize <precalculated_vals_buffer_size_in_Mb = " << precalcValBufSize << ">]" << endl;
|
||||
cout << " [-precalcIdxBufSize <precalculated_idxs_buffer_size_in_Mb = " << precalcIdxBufSize << ">]" << endl;
|
||||
cout << " [-baseFormatSave]" << endl;
|
||||
cout << " [-numThreads <max_number_of_threads = " << numThreads << ">]" << endl;
|
||||
cout << " [-acceptanceRatioBreakValue <value> = " << acceptanceRatioBreakValue << ">]" << endl;
|
||||
cascadeParams.printDefaults();
|
||||
stageParams.printDefaults();
|
||||
for( int fi = 0; fi < fc; fi++ )
|
||||
featureParams[fi]->printDefaults();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for( int i = 1; i < argc; i++ )
|
||||
{
|
||||
bool set = false;
|
||||
if( !strcmp( argv[i], "-data" ) )
|
||||
{
|
||||
cascadeDirName = argv[++i];
|
||||
}
|
||||
else if( !strcmp( argv[i], "-vec" ) )
|
||||
{
|
||||
vecName = argv[++i];
|
||||
}
|
||||
else if( !strcmp( argv[i], "-bg" ) )
|
||||
{
|
||||
bgName = argv[++i];
|
||||
}
|
||||
else if( !strcmp( argv[i], "-numPos" ) )
|
||||
{
|
||||
numPos = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-numNeg" ) )
|
||||
{
|
||||
numNeg = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-numStages" ) )
|
||||
{
|
||||
numStages = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-precalcValBufSize" ) )
|
||||
{
|
||||
precalcValBufSize = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-precalcIdxBufSize" ) )
|
||||
{
|
||||
precalcIdxBufSize = atoi( argv[++i] );
|
||||
}
|
||||
else if( !strcmp( argv[i], "-baseFormatSave" ) )
|
||||
{
|
||||
baseFormatSave = true;
|
||||
}
|
||||
else if( !strcmp( argv[i], "-numThreads" ) )
|
||||
{
|
||||
numThreads = atoi(argv[++i]);
|
||||
}
|
||||
else if( !strcmp( argv[i], "-acceptanceRatioBreakValue" ) )
|
||||
{
|
||||
acceptanceRatioBreakValue = atof(argv[++i]);
|
||||
}
|
||||
else if ( cascadeParams.scanAttr( argv[i], argv[i+1] ) ) { i++; }
|
||||
else if ( stageParams.scanAttr( argv[i], argv[i+1] ) ) { i++; }
|
||||
else if ( !set )
|
||||
{
|
||||
for( int fi = 0; fi < fc; fi++ )
|
||||
{
|
||||
set = featureParams[fi]->scanAttr(argv[i], argv[i+1]);
|
||||
if ( !set )
|
||||
{
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setNumThreads( numThreads );
|
||||
classifier.train( cascadeDirName,
|
||||
vecName,
|
||||
bgName,
|
||||
numPos, numNeg,
|
||||
precalcValBufSize, precalcIdxBufSize,
|
||||
numStages,
|
||||
cascadeParams,
|
||||
*featureParams[cascadeParams.featureType],
|
||||
stageParams,
|
||||
baseFormatSave,
|
||||
acceptanceRatioBreakValue );
|
||||
return 0;
|
||||
}
|
101
opencv-apps/traincascade/traincascade_features.h
Normal file
101
opencv-apps/traincascade/traincascade_features.h
Normal file
@ -0,0 +1,101 @@
|
||||
#ifndef _OPENCV_FEATURES_H_
|
||||
#define _OPENCV_FEATURES_H_
|
||||
|
||||
#include "imagestorage.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define FEATURES "features"
|
||||
|
||||
#define CV_SUM_OFFSETS( p0, p1, p2, p3, rect, step ) \
|
||||
/* (x, y) */ \
|
||||
(p0) = (rect).x + (step) * (rect).y; \
|
||||
/* (x + w, y) */ \
|
||||
(p1) = (rect).x + (rect).width + (step) * (rect).y; \
|
||||
/* (x + w, y) */ \
|
||||
(p2) = (rect).x + (step) * ((rect).y + (rect).height); \
|
||||
/* (x + w, y + h) */ \
|
||||
(p3) = (rect).x + (rect).width + (step) * ((rect).y + (rect).height);
|
||||
|
||||
#define CV_TILTED_OFFSETS( p0, p1, p2, p3, rect, step ) \
|
||||
/* (x, y) */ \
|
||||
(p0) = (rect).x + (step) * (rect).y; \
|
||||
/* (x - h, y + h) */ \
|
||||
(p1) = (rect).x - (rect).height + (step) * ((rect).y + (rect).height);\
|
||||
/* (x + w, y + w) */ \
|
||||
(p2) = (rect).x + (rect).width + (step) * ((rect).y + (rect).width); \
|
||||
/* (x + w - h, y + w + h) */ \
|
||||
(p3) = (rect).x + (rect).width - (rect).height \
|
||||
+ (step) * ((rect).y + (rect).width + (rect).height);
|
||||
|
||||
float calcNormFactor( const cv::Mat& sum, const cv::Mat& sqSum );
|
||||
|
||||
template<class Feature>
|
||||
void _writeFeatures( const std::vector<Feature> features, cv::FileStorage &fs, const cv::Mat& featureMap )
|
||||
{
|
||||
fs << FEATURES << "[";
|
||||
const cv::Mat_<int>& featureMap_ = (const cv::Mat_<int>&)featureMap;
|
||||
for ( int fi = 0; fi < featureMap.cols; fi++ )
|
||||
if ( featureMap_(0, fi) >= 0 )
|
||||
{
|
||||
fs << "{";
|
||||
features[fi].write( fs );
|
||||
fs << "}";
|
||||
}
|
||||
fs << "]";
|
||||
}
|
||||
|
||||
class CvParams
|
||||
{
|
||||
public:
|
||||
CvParams();
|
||||
virtual ~CvParams() {}
|
||||
// from|to file
|
||||
virtual void write( cv::FileStorage &fs ) const = 0;
|
||||
virtual bool read( const cv::FileNode &node ) = 0;
|
||||
// from|to screen
|
||||
virtual void printDefaults() const;
|
||||
virtual void printAttrs() const;
|
||||
virtual bool scanAttr( const std::string prmName, const std::string val );
|
||||
std::string name;
|
||||
};
|
||||
|
||||
class CvFeatureParams : public CvParams
|
||||
{
|
||||
public:
|
||||
enum { HAAR = 0, LBP = 1, HOG = 2 };
|
||||
CvFeatureParams();
|
||||
virtual void init( const CvFeatureParams& fp );
|
||||
virtual void write( cv::FileStorage &fs ) const;
|
||||
virtual bool read( const cv::FileNode &node );
|
||||
static cv::Ptr<CvFeatureParams> create( int featureType );
|
||||
int maxCatCount; // 0 in case of numerical features
|
||||
int featSize; // 1 in case of simple features (HAAR, LBP) and N_BINS(9)*N_CELLS(4) in case of Dalal's HOG features
|
||||
};
|
||||
|
||||
class CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
virtual ~CvFeatureEvaluator() {}
|
||||
virtual void init(const CvFeatureParams *_featureParams,
|
||||
int _maxSampleCount, cv::Size _winSize );
|
||||
virtual void setImage(const cv::Mat& img, uchar clsLabel, int idx);
|
||||
virtual void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const = 0;
|
||||
virtual float operator()(int featureIdx, int sampleIdx) const = 0;
|
||||
static cv::Ptr<CvFeatureEvaluator> create(int type);
|
||||
|
||||
int getNumFeatures() const { return numFeatures; }
|
||||
int getMaxCatCount() const { return featureParams->maxCatCount; }
|
||||
int getFeatureSize() const { return featureParams->featSize; }
|
||||
const cv::Mat& getCls() const { return cls; }
|
||||
float getCls(int si) const { return cls.at<float>(si, 0); }
|
||||
protected:
|
||||
virtual void generateFeatures() = 0;
|
||||
|
||||
int npos, nneg;
|
||||
int numFeatures;
|
||||
cv::Size winSize;
|
||||
CvFeatureParams *featureParams;
|
||||
cv::Mat cls;
|
||||
};
|
||||
|
||||
#endif
|
5
opencv-apps/version/CMakeLists.txt
Normal file
5
opencv-apps/version/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
ocv_add_application(opencv_version MODULES opencv_core SRCS opencv_version.cpp)
|
||||
if(WIN32)
|
||||
ocv_add_application(opencv_version_win32 MODULES opencv_core SRCS opencv_version.cpp)
|
||||
target_compile_definitions(opencv_version_win32 PRIVATE "OPENCV_WIN32_API=1")
|
||||
endif()
|
84
opencv-apps/version/opencv_version.cpp
Normal file
84
opencv-apps/version/opencv_version.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/core/utils/trace.hpp>
|
||||
|
||||
#include <opencv2/core/opencl/opencl_info.hpp>
|
||||
|
||||
#ifdef OPENCV_WIN32_API
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static void dumpHWFeatures(bool showAll = false)
|
||||
{
|
||||
std::cout << "OpenCV's HW features list:" << std::endl;
|
||||
int count = 0;
|
||||
for (int i = 0; i < CV_HARDWARE_MAX_FEATURE; i++)
|
||||
{
|
||||
cv::String name = cv::getHardwareFeatureName(i);
|
||||
if (name.empty())
|
||||
continue;
|
||||
bool enabled = cv::checkHardwareSupport(i);
|
||||
if (enabled)
|
||||
count++;
|
||||
if (enabled || showAll)
|
||||
{
|
||||
printf(" ID=%3d (%s) -> %s\n", i, name.c_str(), enabled ? "ON" : "N/A");
|
||||
}
|
||||
}
|
||||
std::cout << "Total available: " << count << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
CV_TRACE_ARG(argc);
|
||||
CV_TRACE_ARG_VALUE(argv0, "argv0", argv[0]);
|
||||
CV_TRACE_ARG_VALUE(argv1, "argv1", argv[1]);
|
||||
|
||||
#ifndef OPENCV_WIN32_API
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | show this help message }"
|
||||
"{ verbose v | | show build configuration log }"
|
||||
"{ opencl | | show information about OpenCL (available platforms/devices, default selected device) }"
|
||||
"{ hw | | show detected HW features (see cv::checkHardwareSupport() function). Use --hw=0 to show available features only }"
|
||||
);
|
||||
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parser.has("verbose"))
|
||||
{
|
||||
std::cout << cv::getBuildInformation().c_str() << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << CV_VERSION << std::endl;
|
||||
}
|
||||
|
||||
if (parser.has("opencl"))
|
||||
{
|
||||
cv::dumpOpenCLInformation();
|
||||
}
|
||||
|
||||
if (parser.has("hw"))
|
||||
{
|
||||
dumpHWFeatures(parser.get<bool>("hw"));
|
||||
}
|
||||
#else
|
||||
std::cout << cv::getBuildInformation().c_str() << std::endl;
|
||||
cv::dumpOpenCLInformation();
|
||||
dumpHWFeatures();
|
||||
MessageBoxA(NULL, "Check console window output", "OpenCV(" CV_VERSION ")", MB_ICONINFORMATION | MB_OK);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
3
opencv-apps/visualisation/CMakeLists.txt
Normal file
3
opencv-apps/visualisation/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
ocv_add_application(opencv_visualisation
|
||||
MODULES opencv_core opencv_highgui opencv_imgproc opencv_videoio opencv_imgcodecs
|
||||
SRCS opencv_visualisation.cpp)
|
364
opencv-apps/visualisation/opencv_visualisation.cpp
Normal file
364
opencv-apps/visualisation/opencv_visualisation.cpp
Normal file
@ -0,0 +1,364 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*****************************************************************************************************
|
||||
|
||||
Software for visualising cascade classifier models trained by OpenCV and to get a better
|
||||
understanding of the used features.
|
||||
|
||||
USAGE:
|
||||
./opencv_visualisation --model=<model.xml> --image=<ref.png> --data=<video output folder>
|
||||
|
||||
Created by: Puttemans Steven - April 2016
|
||||
*****************************************************************************************************/
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
struct rect_data{
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
float weight;
|
||||
};
|
||||
|
||||
static void printLimits(){
|
||||
cerr << "Limits of the current interface:" << endl;
|
||||
cerr << " - Only handles cascade classifier models, trained with the opencv_traincascade tool, containing stumps as decision trees [default settings]." << endl;
|
||||
cerr << " - The image provided needs to be a sample window with the original model dimensions, passed to the --image parameter." << endl;
|
||||
cerr << " - ONLY handles HAAR and LBP features." << endl;
|
||||
}
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | show this message }"
|
||||
"{ image i | | (required) path to reference image }"
|
||||
"{ model m | | (required) path to cascade xml file }"
|
||||
"{ data d | | (optional) path to video output folder }"
|
||||
);
|
||||
// Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
printLimits();
|
||||
return 0;
|
||||
}
|
||||
string model(parser.get<string>("model"));
|
||||
string output_folder(parser.get<string>("data"));
|
||||
string image_ref = (parser.get<string>("image"));
|
||||
if (model.empty() || image_ref.empty()){
|
||||
parser.printMessage();
|
||||
printLimits();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Value for timing
|
||||
// You can increase this to have a better visualisation during the generation
|
||||
int timing = 1;
|
||||
|
||||
// Value for cols of storing elements
|
||||
int cols_prefered = 5;
|
||||
|
||||
// Open the XML model
|
||||
FileStorage fs;
|
||||
bool model_ok = fs.open(model, FileStorage::READ);
|
||||
if (!model_ok){
|
||||
cerr << "the cascade file '" << model << "' could not be loaded." << endl;
|
||||
return -1;
|
||||
}
|
||||
// Get a the required information
|
||||
// First decide which feature type we are using
|
||||
FileNode cascade = fs["cascade"];
|
||||
string feature_type = cascade["featureType"];
|
||||
bool haar = false, lbp = false;
|
||||
if (feature_type.compare("HAAR") == 0){
|
||||
haar = true;
|
||||
}
|
||||
if (feature_type.compare("LBP") == 0){
|
||||
lbp = true;
|
||||
}
|
||||
if ( feature_type.compare("HAAR") != 0 && feature_type.compare("LBP")){
|
||||
cerr << "The model is not an HAAR or LBP feature based model!" << endl;
|
||||
cerr << "Please select a model that can be visualized by the software." << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We make a visualisation mask - which increases the window to make it at least a bit more visible
|
||||
int resize_factor = 10;
|
||||
int resize_storage_factor = 10;
|
||||
Mat reference_image = imread(image_ref, IMREAD_GRAYSCALE );
|
||||
if (reference_image.empty()){
|
||||
cerr << "the reference image '" << image_ref << "'' could not be loaded." << endl;
|
||||
return -1;
|
||||
}
|
||||
Mat visualization;
|
||||
resize(reference_image, visualization, Size(reference_image.cols * resize_factor, reference_image.rows * resize_factor), 0, 0, INTER_LINEAR_EXACT);
|
||||
|
||||
// First recover for each stage the number of weak features and their index
|
||||
// Important since it is NOT sequential when using LBP features
|
||||
vector< vector<int> > stage_features;
|
||||
FileNode stages = cascade["stages"];
|
||||
FileNodeIterator it_stages = stages.begin(), it_stages_end = stages.end();
|
||||
int idx = 0;
|
||||
for( ; it_stages != it_stages_end; it_stages++, idx++ ){
|
||||
vector<int> current_feature_indexes;
|
||||
FileNode weak_classifiers = (*it_stages)["weakClassifiers"];
|
||||
FileNodeIterator it_weak = weak_classifiers.begin(), it_weak_end = weak_classifiers.end();
|
||||
vector<int> values;
|
||||
for(int idy = 0; it_weak != it_weak_end; it_weak++, idy++ ){
|
||||
(*it_weak)["internalNodes"] >> values;
|
||||
current_feature_indexes.push_back( (int)values[2] );
|
||||
}
|
||||
stage_features.push_back(current_feature_indexes);
|
||||
}
|
||||
|
||||
// If the output option has been chosen than we will store a combined image plane for
|
||||
// each stage, containing all weak classifiers for that stage.
|
||||
bool draw_planes = false;
|
||||
stringstream output_video;
|
||||
output_video << output_folder << "model_visualization.avi";
|
||||
VideoWriter result_video;
|
||||
if( output_folder.compare("") != 0 ){
|
||||
draw_planes = true;
|
||||
result_video.open(output_video.str(), VideoWriter::fourcc('X','V','I','D'), 15, Size(reference_image.cols * resize_factor, reference_image.rows * resize_factor), false);
|
||||
}
|
||||
|
||||
if(haar){
|
||||
// Grab the corresponding features dimensions and weights
|
||||
FileNode features = cascade["features"];
|
||||
vector< vector< rect_data > > feature_data;
|
||||
FileNodeIterator it_features = features.begin(), it_features_end = features.end();
|
||||
for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){
|
||||
vector< rect_data > current_feature_rectangles;
|
||||
FileNode rectangles = (*it_features)["rects"];
|
||||
int nrects = (int)rectangles.size();
|
||||
for(int k = 0; k < nrects; k++){
|
||||
rect_data current_data;
|
||||
FileNode single_rect = rectangles[k];
|
||||
current_data.x = (int)single_rect[0];
|
||||
current_data.y = (int)single_rect[1];
|
||||
current_data.w = (int)single_rect[2];
|
||||
current_data.h = (int)single_rect[3];
|
||||
current_data.weight = (float)single_rect[4];
|
||||
current_feature_rectangles.push_back(current_data);
|
||||
}
|
||||
feature_data.push_back(current_feature_rectangles);
|
||||
}
|
||||
|
||||
// Loop over each possible feature on its index, visualise on the mask and wait a bit,
|
||||
// then continue to the next feature.
|
||||
// If visualisations should be stored then do the in between calculations
|
||||
Mat image_plane;
|
||||
Mat metadata = Mat::zeros(150, 1000, CV_8UC1);
|
||||
vector< rect_data > current_rects;
|
||||
for(int sid = 0; sid < (int)stage_features.size(); sid ++){
|
||||
if(draw_planes){
|
||||
int features_nmbr = (int)stage_features[sid].size();
|
||||
int cols = cols_prefered;
|
||||
int rows = features_nmbr / cols;
|
||||
if( (features_nmbr % cols) > 0){
|
||||
rows++;
|
||||
}
|
||||
image_plane = Mat::zeros(reference_image.rows * resize_storage_factor * rows, reference_image.cols * resize_storage_factor * cols, CV_8UC1);
|
||||
}
|
||||
for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){
|
||||
stringstream meta1, meta2;
|
||||
meta1 << "Stage " << sid << " / Feature " << fid;
|
||||
meta2 << "Rectangles: ";
|
||||
Mat temp_window = visualization.clone();
|
||||
Mat temp_metadata = metadata.clone();
|
||||
int current_feature_index = stage_features[sid][fid];
|
||||
current_rects = feature_data[current_feature_index];
|
||||
Mat single_feature = reference_image.clone();
|
||||
resize(single_feature, single_feature, Size(), resize_storage_factor, resize_storage_factor, INTER_LINEAR_EXACT);
|
||||
for(int i = 0; i < (int)current_rects.size(); i++){
|
||||
rect_data local = current_rects[i];
|
||||
if(draw_planes){
|
||||
if(local.weight >= 0){
|
||||
rectangle(single_feature, Rect(local.x * resize_storage_factor, local.y * resize_storage_factor, local.w * resize_storage_factor, local.h * resize_storage_factor), Scalar(0), FILLED);
|
||||
}else{
|
||||
rectangle(single_feature, Rect(local.x * resize_storage_factor, local.y * resize_storage_factor, local.w * resize_storage_factor, local.h * resize_storage_factor), Scalar(255), FILLED);
|
||||
}
|
||||
}
|
||||
Rect part(local.x * resize_factor, local.y * resize_factor, local.w * resize_factor, local.h * resize_factor);
|
||||
meta2 << part << " (w " << local.weight << ") ";
|
||||
if(local.weight >= 0){
|
||||
rectangle(temp_window, part, Scalar(0), FILLED);
|
||||
}else{
|
||||
rectangle(temp_window, part, Scalar(255), FILLED);
|
||||
}
|
||||
}
|
||||
imshow("features", temp_window);
|
||||
putText(temp_window, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
result_video.write(temp_window);
|
||||
// Copy the feature image if needed
|
||||
if(draw_planes){
|
||||
single_feature.copyTo(image_plane(Rect(0 + (fid%cols_prefered)*single_feature.cols, 0 + (fid/cols_prefered) * single_feature.rows, single_feature.cols, single_feature.rows)));
|
||||
}
|
||||
putText(temp_metadata, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
putText(temp_metadata, meta2.str(), Point(15,40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
imshow("metadata", temp_metadata);
|
||||
waitKey(timing);
|
||||
}
|
||||
//Store the stage image if needed
|
||||
if(draw_planes){
|
||||
stringstream save_location;
|
||||
save_location << output_folder << "stage_" << sid << ".png";
|
||||
imwrite(save_location.str(), image_plane);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(lbp){
|
||||
// Grab the corresponding features dimensions and weights
|
||||
FileNode features = cascade["features"];
|
||||
vector<Rect> feature_data;
|
||||
FileNodeIterator it_features = features.begin(), it_features_end = features.end();
|
||||
for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){
|
||||
FileNode rectangle = (*it_features)["rect"];
|
||||
Rect current_feature ((int)rectangle[0], (int)rectangle[1], (int)rectangle[2], (int)rectangle[3]);
|
||||
feature_data.push_back(current_feature);
|
||||
}
|
||||
|
||||
// Loop over each possible feature on its index, visualise on the mask and wait a bit,
|
||||
// then continue to the next feature.
|
||||
Mat image_plane;
|
||||
Mat metadata = Mat::zeros(150, 1000, CV_8UC1);
|
||||
for(int sid = 0; sid < (int)stage_features.size(); sid ++){
|
||||
if(draw_planes){
|
||||
int features_nmbr = (int)stage_features[sid].size();
|
||||
int cols = cols_prefered;
|
||||
int rows = features_nmbr / cols;
|
||||
if( (features_nmbr % cols) > 0){
|
||||
rows++;
|
||||
}
|
||||
image_plane = Mat::zeros(reference_image.rows * resize_storage_factor * rows, reference_image.cols * resize_storage_factor * cols, CV_8UC1);
|
||||
}
|
||||
for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){
|
||||
stringstream meta1, meta2;
|
||||
meta1 << "Stage " << sid << " / Feature " << fid;
|
||||
meta2 << "Rectangle: ";
|
||||
Mat temp_window = visualization.clone();
|
||||
Mat temp_metadata = metadata.clone();
|
||||
int current_feature_index = stage_features[sid][fid];
|
||||
Rect current_rect = feature_data[current_feature_index];
|
||||
Mat single_feature = reference_image.clone();
|
||||
resize(single_feature, single_feature, Size(), resize_storage_factor, resize_storage_factor, INTER_LINEAR_EXACT);
|
||||
|
||||
// VISUALISATION
|
||||
// The rectangle is the top left one of a 3x3 block LBP constructor
|
||||
Rect resized(current_rect.x * resize_factor, current_rect.y * resize_factor, current_rect.width * resize_factor, current_rect.height * resize_factor);
|
||||
meta2 << resized;
|
||||
// Top left
|
||||
rectangle(temp_window, resized, Scalar(255), 1);
|
||||
// Top middle
|
||||
rectangle(temp_window, Rect(resized.x + resized.width, resized.y, resized.width, resized.height), Scalar(255), 1);
|
||||
// Top right
|
||||
rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y, resized.width, resized.height), Scalar(255), 1);
|
||||
// Middle left
|
||||
rectangle(temp_window, Rect(resized.x, resized.y + resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
// Middle middle
|
||||
rectangle(temp_window, Rect(resized.x + resized.width, resized.y + resized.height, resized.width, resized.height), Scalar(255), FILLED);
|
||||
// Middle right
|
||||
rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y + resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
// Bottom left
|
||||
rectangle(temp_window, Rect(resized.x, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
// Bottom middle
|
||||
rectangle(temp_window, Rect(resized.x + resized.width, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
// Bottom right
|
||||
rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
|
||||
if(draw_planes){
|
||||
Rect resized_inner(current_rect.x * resize_storage_factor, current_rect.y * resize_storage_factor, current_rect.width * resize_storage_factor, current_rect.height * resize_storage_factor);
|
||||
// Top left
|
||||
rectangle(single_feature, resized_inner, Scalar(255), 1);
|
||||
// Top middle
|
||||
rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Top right
|
||||
rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Middle left
|
||||
rectangle(single_feature, Rect(resized_inner.x, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Middle middle
|
||||
rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), FILLED);
|
||||
// Middle right
|
||||
rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Bottom left
|
||||
rectangle(single_feature, Rect(resized_inner.x, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Bottom middle
|
||||
rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Bottom right
|
||||
rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
|
||||
single_feature.copyTo(image_plane(Rect(0 + (fid%cols_prefered)*single_feature.cols, 0 + (fid/cols_prefered) * single_feature.rows, single_feature.cols, single_feature.rows)));
|
||||
}
|
||||
|
||||
putText(temp_metadata, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
putText(temp_metadata, meta2.str(), Point(15,40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
imshow("metadata", temp_metadata);
|
||||
imshow("features", temp_window);
|
||||
putText(temp_window, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
result_video.write(temp_window);
|
||||
|
||||
waitKey(timing);
|
||||
}
|
||||
|
||||
//Store the stage image if needed
|
||||
if(draw_planes){
|
||||
stringstream save_location;
|
||||
save_location << output_folder << "stage_" << sid << ".png";
|
||||
imwrite(save_location.str(), image_plane);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user