Tuesday, November 27, 2012

How to train your classifier (on a mac with opencv)

Or rather, how to experiment with using OpenCV to train a classifier and ultimately fail.

The following two links were my guides. They might make better guides than what follows here, but I'm going to write this stuff down for myself.

I wanted to try training my own OpenCV Haar Cascaded Classifier. OpenCV comes with some pre-trained ones for faces and body parts and whatnot. For fun, I tried to train it to recognize my cat Jpeg, which Picasa humorously recognized as a person in a handful of photos last month:

picasa now apparently detects cat faces


Step 1: Download pictures from my own Flickr account

I use this python program to get Flickr image urls: https://code.google.com/p/flickrpy/
I added my flickr API key and changed the main of that python file to look through MY pictures for things tagged 'cat'. 

if __name__ == '__main__':
    pix = photos_search('16977922@N00', False, '', '', 'cat')
    for p in pix:
        print p.getLarge()


Then, because bash is one of my best friends these days:

python flickr.py > cat_pix.txt && for i in `cat cat_pix.txt`; do wget $i; done

Actually the first time I downloaded the pictures, I wanted to resize them with Imagemagick's mogrify (in-place image editing) command so I ran:

for i in *.jpg; do mogrify -resize 1024x1024 $i; done

Step 2: Mark "objects" to make positive examples

One of the links above has an object marking program for windows. This link has one that I was able to compile on my mac. It's kind of amateur code and I have vague plans to rewrite it.
http://achuwilson.wordpress.com/2011/07/01/create-your-own-haar-classifier-for-detecting-objects-in-opencv/

I compiled it with the following command after commenting out two useless includes that were complaining at me. BRIEF INTERJECTION: I just installed OpenCV with Macports. If you do that, your OpenCV stuff will probably be in the same place. Installation of OpenCV didn't work properly until I made sure my macports was up to date.

g++ -I/opt/local/include -L/opt/local/lib -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_objdetect objectmarker.cpp -o objectmarker


For context, I'm inside a directory of cat images. Running that objectmarker program is as so:

../objectmarker positive_examples.txt ./

Right, so, run that program from the directory with your images in it because the code doesn't actually build the right path to the image file if you run it from a different directory, even though it looks like it intended to. Then click boxes and press the appropriate SPACE and Capital B keys until all the objects/cats are marked.

Step 3: Get negative examples by making a list of images that don't have the object in them

I wound up using images from flickr tagged "eddie", which is the name of my mom's chubby chihuahua. Pictures of other dogs named Eddie were also included, but that didn't matter. The only thing that mattered was that Jpeg was not in any of the images. Which is true because he's never met any dogs.

The command went something like this:

ls eddie/ > negative_examples.txt

Step 4: Fix the paths

My images were in folders called "jpeg/" and "eddie/" and some of the jpeg images didn't actually have jpeg's face clearly shown. I had to change the image paths in positive_examples.txt and negative_examples.txt from "eddie_4787878333_4dd7bcd3d8_o.jpg" to "eddie/eddie_4787878333_4dd7bcd3d8_o.jpg". I did that with vim and the mass insert thing!

Step 5: Run opencv_createsamples

Finally! Getting to the running of the actual training code. Almost.
So I had these two files:
  • positive_examples.txt
  • negative_examples.txt
Negative examples just had image paths in it. Positive examples had image paths and # of objects in the image and the bounding rectangles for each image. For some reason, you have to convert your handy list to a binary representation to feed to the next step. Actually that reason is that you can use the createsamples program to take one image showing off your object and warp and distort it a whole bunch to create artificial training data. But if you actually have many positive examples of your data, you need to run this to just process it and get it in the right form.

I had 19 positive examples and 17 negative images so I ran this:

/opt/local/bin/opencv_createsamples -vec jpeg_pos.vec -info postive_examples.txt -show -w 32 -h 32

One very important thing I learned is that you should not put a number like 100 into the height and width fields because that will make your computer freak out (have a lot to process) at the next step.

Step 6: Run opencv_traincascade

Finally, for reals. Let's do it. 

/opt/local/bin/opencv_traincascade -data output_jpeg_cascade/ -vec jpeg_pos.vec -bg negative_examples.txt -numPos 19 -numNeg 17 -w 32 -h 32

Your -w and -h numbers have to be the same. When I tried them at 100, the program was trying to use 6GB of ram and making very slow progress. When I knocked it down to 32x32, it finished in under 30 seconds. I hear from those other links on the internet that training can take 5 days with 2000 positive and negative examples. Maybe that'll be me some day, and maybe it wont.

Step 7: Detect cat

I hacked some code online that used the face detector classifier and replaced "haarcascade_frontalface_alt2.xml" or whatever with "output_jpeg_cascade/cascade.xml". I don't remember where the code came from so I don't have a good link that particular resource.

In many cases, it completely failed to detect Jpeg. Even though this image was part of the training:

In another instance, it got different ends of the cat confused: 

Voodoo magic

This was the first time I ever tried anything like this. There's probably a lot I could learn about how to make it work better. At the same time, I wish I could teach the classifier more specific things to look for, or have it tell me exactly why it thinks the cat butt is the face. Feeding lots of data into a black box and crossing my fingers doesn't seem like a fun approach.

3 comments:

  1. Hai, can you share your objectmarker executable file after you compiled? I've compiled on mac os x but the executable file can't work. thanks

    ReplyDelete
  2. I'm working on a similar project. The quality of the classifier depends on the training. Perhaps you should have included many (1-5 thousand) positive and negative. Also the sample size w/h don't have to be equal, just scaled to match the original scale.

    ReplyDelete
  3. Hi, I'm working with similiar project with you, I'm using opencv with python to detect car, I already found converted program to python in google, and I run it, it doesn't work get stuck in here:
    if (len(sys.argv) != 3):
    sys.stderr.write("%s output_info.txt raw/data/directory\n"
    % sys.argv[0])
    return -1
    could you help me??

    ReplyDelete