[OpenCV]detectMultiScale

I met a problem when using the interface ‘detectMultiScale’ of OpenCV. The rectangles it gives out may not be fully inside the frame of the original image. As a result, if these rectangles are applied directly on the original image to crop out the detected objects, your programs crash.

These are the interfaces

virtual void detectMultiScale( const Mat& image,
                               CV_OUT vector& objects,
                               double scaleFactor=1.1,
                               int minNeighbors=3, int flags=0,
                               Size minSize=Size(),
                               Size maxSize=Size() );

and

virtual void detectMultiScale( const Mat& image,
                               CV_OUT vector& objects,
                               vector& rejectLevels,
                               vector& levelWeights,
                               double scaleFactor=1.1,
                               int minNeighbors=3, int flags=0,
                               Size minSize=Size(),
                               Size maxSize=Size(),
                               bool outputRejectLevels=false );

I am copying what I wrote for a pull request on Github, this ‘may-be issue’ can be fixed easily by modifying one line in the source file

modules/objdetect/src/cascadedetect.cpp

Replace this one

Size processingRectSize( scaledImageSize.width - originalWindowSize.width + 1, scaledImageSize.height - originalWindowSize.height + 1 );

with this line

Size processingRectSize( scaledImageSize.width - originalWindowSize.width , scaledImageSize.height - originalWindowSize.height);

My explanation goes here, ignore the line numbers if they look wrong to you.
“Actually, in the code, the workflow is more complicated. In the file cascadedetect.cpp

This is the line building the final detected rectangle

995 rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));

the winSize is assigned here

969 Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor), cvRound(classifier->data.origWinSize.height * scalingFactor));

while the maximum value of x and y can be find here, they are related to the processingRectSize.

971         int y1 = range.start * stripSize;
972         int y2 = min(range.end * stripSize, processingRectSize.height);
973         for( int y = y1; y < y2; y += yStep )
974         {
975             for( int x = 0; x < processingRectSize.width; x += yStep )

Say the original image size is O, the original window size is W, scaling factor is F. O and W is integer and F is a decimal usually larger than 1. The width and height are assumed to be the same for example.

If we calculate the right-most point of the detected rectangle, it should be:

the maximum x is: (cvRound(O/F) - W), current winSize is W*F, following the line 995 we get:

cvRound( (cvRound(O/F) - W) * F ) + W*F

This can be larger than O, say O is 600, F is 4.177250, W is 24, the number we can get above is 601.254 which is larger than 600."

Hope these help.

libstdc++ 4.6 type_traits 的一个bug

如果你的编译环境和我一样,然后又在用C++11的时候,不小心直接或者间接用到了<chrono>这个头文件,应该就会遇到这个bug。

完整的错误信息如下:

|| In file included from /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../include/c++/4.6/thread:37:
/usr/include/c++/4.6/chrono|240 col 10| error: cannot cast from lvalue of type 'const long' to rvalue reference type 'rep' (aka 'long &&'); types are not compatible
||           : __r(static_cast(__rep)) { }
||                 ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|128 col 13| note: in instantiation of function template specialization 'std::chrono::duration >::duration' requested here
||             return _ToDur(static_cast(__d.count()));
||                    ^
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, true, true>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|247 col 10| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||           : __r(duration_cast(__d).count()) { }
||                 ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|128 col 13| error: call to implicitly-deleted copy constructor of 'std::chrono::duration >'
||             return _ToDur(static_cast(__d.count()));
||                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, true, true>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|247 col 10| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||           : __r(duration_cast(__d).count()) { }
||                 ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|233 col 12| note: explicitly defaulted function was implicitly deleted here
||         constexpr duration(const duration&) = default;
||                   ^
/usr/include/c++/4.6/chrono|349 col 6| note: copy constructor of 'duration >' is implicitly deleted because field '__r' is of rvalue reference type 'rep' (aka 'long &&')
||         rep __r;
||             ^
/usr/include/c++/4.6/chrono|182 col 9| error: call to implicitly-deleted copy constructor of 'typename enable_if<__is_duration<duration > >::value, duration > >::type' (aka 'std::chrono::duration >')
||         return __dc::__cast(__d);
||                ^~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|247 col 10| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||           : __r(duration_cast(__d).count()) { }
||                 ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|233 col 12| note: explicitly defaulted function was implicitly deleted here
||         constexpr duration(const duration&) = default;
||                   ^
/usr/include/c++/4.6/chrono|349 col 6| note: copy constructor of 'duration >' is implicitly deleted because field '__r' is of rvalue reference type 'rep' (aka 'long &&')
||         rep __r;
||             ^
/usr/include/c++/4.6/chrono|255 col 11| error: rvalue reference to type 'long' cannot bind to lvalue of type 'long'
||         { return __r; }
||                  ^~~
/usr/include/c++/4.6/chrono|247 col 39| note: in instantiation of member function 'std::chrono::duration >::count' requested here
||           : __r(duration_cast(__d).count()) { }
||                                              ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|666 col 21| error: static_assert expression is not an integral constant expression
||       static_assert(system_clock::duration::min()
||                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|666 col 21| note: undefined function 'operator<, long, std::ratio >' cannot be used in a constant expression
/usr/include/c++/4.6/chrono|460 col 7| note: declared here
||       operator& __lhs,
||       ^
/usr/include/c++/4.6/chrono|141 col 40| error: cannot cast from lvalue of type 'const intmax_t' (aka 'const long') to rvalue reference type 'long &&'; types are not compatible
||               static_cast(__d.count()) / static_cast(_CF::den)));
||                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, true, false>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|679 col 21| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||         return std::time_t(duration_cast
||                            ^
/usr/include/c++/4.6/chrono|154 col 40| error: cannot cast from lvalue of type 'const intmax_t' (aka 'const long') to rvalue reference type 'long &&'; types are not compatible
||               static_cast(__d.count()) * static_cast(_CF::num)));
||                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, false, true>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|577 col 22| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||         return __time_point(duration_cast(__t.time_since_epoch()));
||                             ^
/usr/include/c++/4.6/chrono|687 col 9| note: in instantiation of function template specialization 'std::chrono::time_point_cast<std::chrono::duration >, std::chrono::system_clock, std::chrono::duration > >' requested here
||         return time_point_cast
||                ^
|| 7 errors generated.

Google搜到了这个帖子 http://urfoex.blogspot.com/2012/06/c11-fixing-bug-to-use-chrono-with-clang.html
和我的环境略有不同,但是出错的地方是一样的。

我自己的环境是

Ubuntu 12.04, clang 3.1, libstdc++ 4.6

解决方法是改

/usr/include/c++/4.6/type_traits

这个文件的第1113行

{ typedef decltype(true ? declval<_Tp>() : declval<_Up>()) type; };

改成

{ typedef typename decay() : declval<_Up>())>::type type; }

出错的原因,大概是因为没有正确的做类型转换。decay这个函数我从没用过,看文档是C++11引入的函数,用做类型转换。对此不理解,不能在这里误导人,文档原文如下

Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer implicit conversions to the type T, removes cv-qualifiers, and defines the resulting type as the member typedef type. This is the type conversion applied to all function arguments when passed by value.

总之,把那行改掉再重新编译自己的工程就没有问题了。

如果你是和我一样没有root权限去改/usr/include下的文件,也可以这么做:
1. 把/usr/include/c++/4.6/type_traits拷贝到自己的目录下面,比如

$cp /usr/include/c++/4.6/type_traits $HOME/local/include/c++/4.6

2. 改自己目录下的版本,然后编译的时候把这个目录放到编译选项里,clang会优先使用这个目录下的版本

$clang++ blahblah.cpp -I$HOME/local/include/c++/4.6/

好吧,虽然没有完全搞清楚这个bug是怎么回事,但是解决掉一大堆报错的感觉挺好 :]