Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[traincascade] add type_id to the old save format #7887

Merged

Conversation

kevinhughes27
Copy link
Contributor

This pullrequest changes

This PR adds the type_id of opencv-haar-classifier to the saved model when using the latest traincascade program with the -baseFormatSave option.

Background

I recently trained a cascade classifier using the traincascade program with the -baseFormatSave option because I needed to use the GPU version of the classifier for detection. After training when I went to load my cascade.yml file I encountered the following error:

OpenCV Error: Unspecified error (The node does not represent a user object (unknown type?)) in cvRead, file /home/kevin/src/opencv/modules/core/src/persistence.cpp, line 6628
terminate called after throwing an instance of 'cv::Exception'
  what():  /home/kevin/src/opencv/modules/core/src/persistence.cpp:6628: error: (-2) The node does not represent a user object (unknown type?) in function cvRead

I started investigating by looking at the cascades provided by opencv:

new format (from data/haarcascades):

<opencv_storage>
<cascade type_id="opencv-cascade-classifier"><stageType>BOOST</stageType>
  <featureType>HAAR</featureType>
  <height>20</height>
  <width>20</width>
  <stageParams>
  ...
</opencv_storage>

gpu format (from data/haarcascades_gpu):

<opencv_storage>
<haarcascade_frontaleye type_id="opencv-haar-classifier">
  <size>
    20 20</size>
  <stages>
    <_>
      <!-- stage 0 -->
      <trees>
        <_>
          <!-- tree 0 -->
          <_>
            <!-- root node -->
    ...
<opencv_storage>

and compared them to the file that was saved after my classifier was trained:

traincascade with -baseFormatSave option:

<?xml version="1.0"?>
<opencv_storage>
<cascade>
  <size>
    100 40</size>
  <stages>
    <_>
      <trees>
        <_>
          <_>
  ...
<opencv_storage>

This is when I noticed that my output was missing the type_id. Adding this manually fixed the issue and I was able to load and run my classifier. Note that this only affected the old file format and if I saved my classifier in the new format I was able to load it.

From here I started looking into how the classifier is saved to see if I could fix the problem. I am including the notes I took while exploring the FileStorage code in case others find it useful.

  • The traincascade program writes to FileStorage using a hash like DSL starting here
  • The persistence module converts the hash like structure into NODE types. This is how this module is able to support multiple output formats (yml and xml).
  • Here is where the { char is converted to a node by calling cvStartWriteStruct with anything trailing the : as type_name
  • Which writes to type_id here

Testing

There doesn't seem to be any tests for the traincascade app so I wrote a small program to confirm my fix:

#include <opencv2/core/core.hpp>

using namespace cv;

int main() {
  string filename = "cascade.xml";
  FileStorage fs( filename, FileStorage::WRITE );
  fs << FileStorage::getDefaultObjectName(filename) << "{:opencv-haar-classifier";
  fs << "}";
}

which when ran produces:

<?xml version="1.0"?>
<opencv_storage>
<cascade type_id="opencv-haar-classifier"></cascade>
</opencv_storage>

@vpisarev vpisarev self-assigned this Dec 19, 2016
@@ -450,6 +452,7 @@ void CvCascadeClassifier::save( const string filename, bool baseFormat )
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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was my understanding that the : character was required but I just tried with my test program and it isn't. It does affect the output though - one line vs two lines.

#include <opencv2/core/core.hpp>

using namespace cv;

int main() {
  std::string filename = "cascade.xml";
  FileStorage fs( filename, FileStorage::WRITE );
  fs << FileStorage::getDefaultObjectName(filename) << "{opencv-haar-classifier";
  fs << "}";
}
<?xml version="1.0"?>
<opencv_storage>
<cascade type_id="opencv-haar-classifier">
  </cascade>
</opencv_storage>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked it one more time. fs << "{:" << ICV_HAAR_TYPE_ID; is probably not what you want. You'd want something like fs << "{:" ICV_HAAR_TYPE_ID; to make sure that "{:opencv-haar-classifier" is streamed to fs and parsed as a single "token"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense I was wondering about that as well since one of my earlier tests did't work as expected but then they all started to. I can make the change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, please

@vpisarev
Copy link
Contributor

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants