/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2013, Itseez Inc, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of 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*/ /* Haar features calculation */ #include "precomp.hpp" #include "cascadedetect.hpp" #include namespace cv { /* field names */ #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" namespace haar_cvt { struct HaarFeature { enum { RECT_NUM = 3 }; HaarFeature() { tilted = false; for( int i = 0; i < RECT_NUM; i++ ) { rect[i].r = Rect(0,0,0,0); rect[i].weight = 0.f; } } bool tilted; struct { Rect r; float weight; } rect[RECT_NUM]; }; struct HaarClassifierNode { HaarClassifierNode() { f = left = right = 0; threshold = 0.f; } int f, left, right; float threshold; }; struct HaarClassifier { std::vector nodes; std::vector leaves; }; struct HaarStageClassifier { HaarStageClassifier() : threshold(0) {} double threshold; std::vector weaks; }; bool convert(const FileNode& oldroot, FileStorage& newfs) { FileNode sznode = oldroot[ICV_HAAR_SIZE_NAME]; if( sznode.empty() ) return false; Size cascadesize; cascadesize.width = (int)sznode[0]; cascadesize.height = (int)sznode[1]; std::vector features; int i, j, k, n; FileNode stages_seq = oldroot[ICV_HAAR_STAGES_NAME]; int nstages = (int)stages_seq.size(); std::vector stages(nstages); for( i = 0; i < nstages; i++ ) { FileNode stagenode = stages_seq[i]; HaarStageClassifier& stage = stages[i]; stage.threshold = (double)stagenode[ICV_HAAR_STAGE_THRESHOLD_NAME]; FileNode weaks_seq = stagenode[ICV_HAAR_TREES_NAME]; int nweaks = (int)weaks_seq.size(); stage.weaks.resize(nweaks); for( j = 0; j < nweaks; j++ ) { HaarClassifier& weak = stage.weaks[j]; FileNode weaknode = weaks_seq[j]; int nnodes = (int)weaknode.size(); for( n = 0; n < nnodes; n++ ) { FileNode nnode = weaknode[n]; FileNode fnode = nnode[ICV_HAAR_FEATURE_NAME]; HaarFeature f; HaarClassifierNode node; node.f = (int)features.size(); f.tilted = (int)fnode[ICV_HAAR_TILTED_NAME] != 0; FileNode rects_seq = fnode[ICV_HAAR_RECTS_NAME]; int nrects = (int)rects_seq.size(); for( k = 0; k < nrects; k++ ) { FileNode rnode = rects_seq[k]; f.rect[k].r.x = (int)rnode[0]; f.rect[k].r.y = (int)rnode[1]; f.rect[k].r.width = (int)rnode[2]; f.rect[k].r.height = (int)rnode[3]; f.rect[k].weight = (float)rnode[4]; } features.push_back(f); node.threshold = nnode[ICV_HAAR_THRESHOLD_NAME]; FileNode leftValNode = nnode[ICV_HAAR_LEFT_VAL_NAME]; if( !leftValNode.empty() ) { node.left = -(int)weak.leaves.size(); weak.leaves.push_back((float)leftValNode); } else { node.left = (int)nnode[ICV_HAAR_LEFT_NODE_NAME]; } FileNode rightValNode = nnode[ICV_HAAR_RIGHT_VAL_NAME]; if( !rightValNode.empty() ) { node.right = -(int)weak.leaves.size(); weak.leaves.push_back((float)rightValNode); } else { node.right = (int)nnode[ICV_HAAR_RIGHT_NODE_NAME]; } weak.nodes.push_back(node); } } } int maxWeakCount = 0, nfeatures = (int)features.size(); for( i = 0; i < nstages; i++ ) maxWeakCount = std::max(maxWeakCount, (int)stages[i].weaks.size()); newfs << "cascade" << "{:opencv-cascade-classifier" << "stageType" << "BOOST" << "featureType" << "HAAR" << "width" << cascadesize.width << "height" << cascadesize.height << "stageParams" << "{" << "maxWeakCount" << (int)maxWeakCount << "}" << "featureParams" << "{" << "maxCatCount" << 0 << "}" << "stageNum" << (int)nstages << "stages" << "["; for( i = 0; i < nstages; i++ ) { int nweaks = (int)stages[i].weaks.size(); newfs << "{" << "maxWeakCount" << (int)nweaks << "stageThreshold" << stages[i].threshold << "weakClassifiers" << "["; for( j = 0; j < nweaks; j++ ) { const HaarClassifier& c = stages[i].weaks[j]; newfs << "{" << "internalNodes" << "[:"; int nnodes = (int)c.nodes.size(), nleaves = (int)c.leaves.size(); for( k = 0; k < nnodes; k++ ) newfs << c.nodes[k].left << c.nodes[k].right << c.nodes[k].f << c.nodes[k].threshold; newfs << "]" << "leafValues" << "[:"; for( k = 0; k < nleaves; k++ ) newfs << c.leaves[k]; newfs << "]" << "}"; } newfs << "]" << "}"; } newfs << "]" << "features" << "["; for( i = 0; i < nfeatures; i++ ) { const HaarFeature& f = features[i]; newfs << "{" << "rects" << "["; for( j = 0; j < HaarFeature::RECT_NUM; j++ ) { if( j >= 2 && fabs(f.rect[j].weight) < FLT_EPSILON ) break; newfs << "[:" << f.rect[j].r.x << f.rect[j].r.y << f.rect[j].r.width << f.rect[j].r.height << f.rect[j].weight << "]"; } newfs << "]"; if( f.tilted ) newfs << "tilted" << 1; newfs << "}"; } newfs << "]" << "}"; return true; } } bool CascadeClassifier::convert(const String& oldcascade, const String& newcascade) { FileStorage oldfs(oldcascade, FileStorage::READ); FileStorage newfs(newcascade, FileStorage::WRITE); if( !oldfs.isOpened() || !newfs.isOpened() ) return false; FileNode oldroot = oldfs.getFirstTopLevelNode(); bool ok = haar_cvt::convert(oldroot, newfs); if( !ok && newcascade.size() > 0 ) remove(newcascade.c_str()); return ok; } }