Permalink
Browse files

refactor training and collection scripts and add readme detailing tra…

…ining process
  • Loading branch information...
1 parent 778d6fb commit 9de1239826951ce276cfbb7a9c8ea6959110eb1d @harthur committed Oct 15, 2012
View
@@ -0,0 +1,19 @@
+var hog = require("hog-descriptor"),
+ utils = require("./utils");
+
+var defaultParams = {
+ "cellSize": 6,
+ "blockSize": 2,
+ "blockStride": 1,
+ "bins": 6,
+ "norm": "L2"
+}
+
+var size = 48;
+
+exports.extractFeatures = function(canvas, params) {
+ canvas = utils.resizeCanvas(canvas, size, size);
+
+ var descriptor = hog.extractHOG(canvas, params || defaultParams);
+ return descriptor;
+}
View
@@ -2,58 +2,41 @@ var brain = require("brain"),
hog = require("hog-descriptor"),
nms = require("./nms");
-var network = require("./network.js");
-
-var net = new brain.NeuralNetwork().fromJSON(network);
-
-if (process.arch) {
- // in node
+if (process.arch) { // in node
var Canvas = (require)('canvas');
}
-function createCanvas (width, height) {
- if (typeof Canvas !== 'undefined') {
- // have node-canvas
- return new Canvas(width, height);
- }
- else {
- // in browser
- var canvas = document.createElement('canvas');
- canvas.setAttribute('width', width);
- canvas.setAttribute('height', height);
- return canvas;
- }
-}
+var network = require("./network.json");
+var net = new brain.NeuralNetwork().fromJSON(network);
-var kittydar = {
+var params = {
patchSize: 48, // size of training images in px
-
minSize: 48, // starting window size
-
resize: 360, // initial image resize size in px
-
- threshold: 0.9999, // probablity threshold for classifying
-
+ threshold: 0.995, // probablity threshold for classifying
scaleStep: 6, // scaling step size in px
-
shiftBy: 6, // px to slide window by
-
- overlapThresh: 0.3, // min overlap ratio to classify as an overlap
-
+ overlapThresh: 0.5, // min overlap ratio to classify as an overlap
minOverlaps: 2, // minumum overlapping rects to classify as a head
-
HOGparams: { // parameters for HOG descriptor
cellSize: 6,
blockSize: 2,
blockStride: 1,
bins: 6,
norm: "L2"
- },
+ }
+}
+var kittydar = {
detectCats: function(canvas, options) {
- this.setOptions(options || {});
+ if (options) {
+ for (var opt in options) {
+ params[opt] = options[opt];
+ }
+ }
- var resizes = this.getAllSizes(canvas, this.minSize);
+ // get canvases of the image at different scales
+ var resizes = this.getAllSizes(canvas, params.minSize);
var cats = [];
resizes.forEach(function(resize) {
@@ -65,25 +48,18 @@ var kittydar = {
return cats;
},
- setOptions: function(options) {
- for (var opt in options) {
- this[opt] = options[opt];
- }
- },
-
getAllSizes: function(canvas, minSize) {
// For use with Worker threads, return canvas ImageDatas
// resized to accomodate various window sizes
- minSize = minSize || this.minSize;
+ minSize = minSize || params.minSize;
// resize canvas to cut down on number of windows to check
- var resize = this.resize;
var max = Math.max(canvas.width, canvas.height)
- var scale = Math.min(max, resize) / max;
+ var scale = Math.min(max, params.resize) / max;
var resizes = [];
- for (var size = minSize; size < max; size += this.scaleStep) {
+ for (var size = minSize; size < max; size += params.scaleStep) {
var winScale = (minSize / size) * scale;
var imagedata = this.scaleCanvas(canvas, winScale);
@@ -108,7 +84,7 @@ var kittydar = {
},
isCat: function(vectors) {
- var features = hog.extractHOGFromVectors(vectors, this.HOGparams);
+ var features = hog.extractHOGFromVectors(vectors, params.HOGparams);
var prob = net.runInput(features)[0];
return prob;
@@ -122,14 +98,14 @@ var kittydar = {
var width = imagedata.width,
height = imagedata.height;
- var size = this.patchSize;
+ var size = params.patchSize;
- for (var y = 0; y + size < height; y += this.shiftBy) {
- for (var x = 0; x + size < width; x += this.shiftBy) {
+ for (var y = 0; y + size < height; y += params.shiftBy) {
+ for (var x = 0; x + size < width; x += params.shiftBy) {
var win = getRect(vectors, x, y, size, size);
var prob = this.isCat(win);
- if (prob > this.threshold) {
+ if (prob > params.threshold) {
cats.push({
x: Math.floor(x / scale),
y: Math.floor(y / scale),
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -20,9 +20,13 @@ function runTest() {
if (err) throw err;
var images = files.filter(function(file) {
- return path.extname(file) == ".png";
+ return path.extname(file) == ".jpg";
})
+ images = images.slice(0, 3);
+
+ console.log(images);
+
async.forEach(images, function(file, done) {
file = dir + file;
View
@@ -0,0 +1,93 @@
+var fs = require("fs"),
+ path = require("path"),
+ Canvas = require("canvas"),
+ utils = require("../utils");
+
+exports.collectImages = collectImages;
+exports.getDir = getDir;
+
+/*
+ * Collect the canvas representations of the images in the positive and
+ * negative directories and return
+ * an array of objects that look like:
+ * {
+ * canvas: <Canvas object>,
+ * file: 'test.jpg',
+ * iscat: true
+ * }
+ */
+function collectImages(posDir, negDir, samples, limit) {
+ // number of samples to extract from each negative, 0 for whole image
+ samples = samples || 0;
+
+ // max number of images to collect per directory
+ limit = limit || 1000;
+
+ var pos = getDir(posDir, true, 0, limit);
+ var neg = getDir(negDir, false, samples, limit);
+ return pos.concat(neg);
+}
+
+function getDir(dir, isCat, samples, limit) {
+ var files = fs.readdirSync(dir);
+
+ var images = files.filter(function(file) {
+ return (path.extname(file) == ".png"
+ || path.extname(file) == ".jpg");
+ });
+
+ images = images.slice(0, limit);
+
+ var data = [];
+ for (var i = 0; i < images.length; i++) {
+ var file = dir + "/" + images[i];
+ try {
+ var canvas = utils.drawImgToCanvasSync(file);
+ }
+ catch(e) {
+ console.log(e, file);
+ continue;
+ }
+
+ var canvases = extractSamples(canvas, samples);
+
+ for (var j = 0; j < canvases.length; j++) {
+ data.push({
+ canvas: canvases[j],
+ file: file,
+ isCat: isCat ? 1 : 0
+ });
+ }
+ }
+
+ return data;
+}
+
+
+function extractSamples(canvas, num) {
+ if (num == 0) {
+ // 0 means "don't sample"
+ return [canvas];
+ }
+
+ var min = 48;
+ var max = Math.min(canvas.width, canvas.height);
+
+ var canvases = [];
+ for (var i = 0; i < num; i++) {
+ var length = Math.max(min, Math.ceil(Math.random() * max));
+
+ var x = Math.floor(Math.random() * (max - length));
+ var y = Math.floor(Math.random() * (max - length));
+
+ canvases.push(cropCanvas(canvas, x, y, length, length));
+ }
+ return canvases;
+}
+
+function cropCanvas(canvas, x, y, width, height) {
+ var cropCanvas = new Canvas(width, height);
+ var context = cropCanvas.getContext("2d");
+ context.drawImage(canvas, x, y, width, height, 0, 0, width, height);
+ return cropCanvas;
+}
@@ -0,0 +1,22 @@
+## collection
+
+the goal of collection is to get a folder of positive (cat head) images and a folder of negative (non-cat) images to train the classifier with.
+
+### creating the positives
+
+To get the positives, first download this [dataset of cat pictures](http://137.189.35.203/WebUI/CatDatabase/catData.html). There should be folders called CAT_00, CAT_01, etc. Take the images from all of these and combine into one directory. Also remove the file "00000003_019.jpg.cat" and add [00000003_015.jpg.cat](http://137.189.35.203/WebUI/CatDatabase/Data/00000003_015.jpg.cat).
+
+Run the script to rotate and the crop out the cat head from each image. If you put the cat dataset in a folder called "CATS" and you want to put the cropped images in a folder called "POSITIVES":
+
+`node make-positives.js CATS POSITIVES`
+
+### creating the negatives
+
+If you don't already have a bunch of non-cat pictures you can fetch recent images from Flickr and save them in a folder called "FLICKR" by running:
+
+`ruby fetch-negatives.rb NEGATIVES`
+
+You'll need at least 10,000 images.
+
+If you're getting images from Flickr, some will contain cats for sure, so you'll need to weed those out by taking a close look at your hard negatives.
+
@@ -5,34 +5,34 @@
FlickRaw.api_key="0cc11cffc8a238efef4dfa6dca255a44"
FlickRaw.shared_secret="5f76a97053f99673"
-$count = 0
-
$fetched = Hash.new
+$dir = ARGV[0]
+
def getPage(page)
list = flickr.photos.getRecent :per_page => 500, :page => page
list.each do |photo|
- url = "http://farm#{photo.farm}.staticflickr.com/#{photo.server}/#{photo.id}_#{photo.secret}_c.jpg"
+ url = "http://farm#{photo.farm}.staticflickr.com/#{photo.server}/#{photo.id}_#{photo.secret}_-.jpg"
if $fetched[url] != 1
$fetched[url] = 1
name = rand(100000000000)
- file = "NEGS_FLICKR/#{name}.jpg"
+ file = "#{$dir}/#{name}.jpg"
puts file
open(file, 'wb') do |file|
file << open(url).read
end
puts "saved to #{file}"
- $count+=1
end
end
end
+# gets 120 x 500 = 60,000 images
120.times do |i|
getPage(i)
end
Oops, something went wrong.

0 comments on commit 9de1239

Please sign in to comment.