{"id":189,"date":"2021-03-14T17:05:36","date_gmt":"2021-03-14T17:05:36","guid":{"rendered":"http:\/\/aslak.nu\/?page_id=189"},"modified":"2021-03-14T20:19:14","modified_gmt":"2021-03-14T20:19:14","slug":"object-detection","status":"publish","type":"page","link":"http:\/\/aslak.nu\/index.php\/portfolio\/object-detection\/","title":{"rendered":"object detection"},"content":{"rendered":"\n<h4 class=\"wp-block-heading\"><span class=\"has-inline-color has-accent-color\">How does it work<\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">An object detection algorithm is given a set of images containing the object of interest, and a set of images not containing the object of interest. Given enough images, the detection algorithm learns to recognize the underlying features of the object. Any new image can then be assessed by the algorithm, which will return positive matches of the object, if it exists.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With OpenCV, quite an extensive set of utility tools for object detection exist. An introductory example is given below in steps. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"has-inline-color has-accent-color\">Building a dataset<\/span><\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code># find a collection of images without the object of interest\n# place them in a folder of your own choice. Each image must\n# be labeled with an index, and the given folder name.\n\nnegatives\/\n  \/negatives1.png\n  \/negatives2.png\n  ...\n\n# create a file.txt file referencing this folder\n# file.txt content\n\nnegatives\/negatives1.png\nnegatives\/negatives1.png\n...\n\n# place the file in the same folder level as the negatives\/\n# folder from earlier\n\n# find a representing image of the object save it as e.g.\n# positives.png\n\n# create a new folder called classifier at the same level.\n# the final structure is now\n\nsome_root_folder\/\n  classifier\/\n  negatives\/\n     negatives1.png\n     negatives2.png\n     ...\n  file.txt\n  positives.png<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"has-inline-color has-accent-color\">Creating data samples<\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">From the image containing the object, we synthesize 1000 new ones with a utility function. The results are saved in a special vectorized file format specified with the -vec argument.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Spin up a terminal - make sure opencv is installed\nopencv_createsamples \\\n  -img positives.png \\\n  -vec vec-positives \\\n  -num 1000 \\\n  -bg file.txt<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"has-inline-color has-accent-color\">Train the classifier<\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Next up, training uses the recently created vectorized file The training result i.e. the classifier is stored under the classifier\/ folder. THe number of cascading levels of the algorithm is specified in the -numStages argument. -numNeg is the number of images without the object, located in the negatives folder. -numPos is the 1000 images we created in the vectorized file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n# basic parameters - don't expect a good result\n\nopencv_traincascade \\\n  -data classifier\/ \\\n  -vec vec-positives \\\n  -bg file.txt \\\n  -numPos 1000 \\\n  -numNeg 5 \\\n  -numStages 4<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"has-inline-color has-accent-color\">Test the classifer<\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">After training a couple of files are locted in the classifier\/ folder, but one of them is the actual trained classifier. It contains a collection of features based on vector metrics, which defines the object after training.<br>By providing a test image and the classifier .xml file, we can run the following python script and see if any green bounding boxes appear in the image. If so, the classifier has identified what it believes to be or object.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># python 3.x\n\nimport cv2\nimport sys\n\n# Get user supplied values\nimagePath = sys.argv&#91;1]\n\n# provide full path to *.xml\ncascPath = sys.argv&#91;2]\n\n# show the paths used\nprint(imagePath)\nprint(cascPath)\n\n# Create the haar cascade\ncascade = cv2.CascadeClassifier(cascPath)\n\n# Read the image\nimage = cv2.imread(imagePath)\n\n# make it grayscale for 2D representation\ngray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n\n# Detect objects in the image\nfaces = cascade.detectMultiScale(\n    gray,\n    scaleFactor=1.1,\n    minNeighbors=5,\n    minSize=(30, 30)\n)\n\n# Draw a rectangle around the objects\nfor (x, y, w, h) in faces:\n    cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)\n\ncv2.imshow(\"objects found\", image)\ncv2.waitKey(0)<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"has-inline-color has-accent-color\">How it really works<\/span> <\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The algorithm used is an optimized version of the original algorithms presented in &#8220;<em>Rapid Object Detection using a Boosted Cascade of Simple Features<\/em>&#8221; by Paul Viola and Michael Jones<br><br>It uses Haar features to extract information about the object when training and searches for them later when testing. A Haar feature, popularized by Alfred Haar back in the early 1900&#8217;s, is similar to a convolutional kernel, where a set of pixel values in a grid formation, slides over an image. Haar features can be used to find e.g. edges\/lines in an image. The grid used is variable in size but contain 2 pixel groupings. A simple seperation strategy of a square grid, is dividing it in the middle either vertically (creating a left\/right grouping) or horizontally (up\/down grouping). There might be more, but the grid is always divided into two groups. The groups does not have to be adjacently connected in the grid. As an example, the top left pixel could be assigned to the same group as the lower right pixel in the grid.<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># pixel groupings\n# 0 and 1 indicates which group the pixel belongs to\n\n# a 2x2 grid split into 2 groups vertically\n\n&#91; \n  0, 1\n  0, 1\n]\n\n# a 2x2 grid split into 2 groups in a cross fashion\n\n&#91;\n  0, 1\n  1, 0\n]\n\n# a 2x3 grid split into 2 groups by seperating the middle out\n&#91;\n  0, 0\n  1, 1\n  0, 1\n]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each feature represents the sum of the pixels from the first group subtracted from the sum of pixels in the second group. If we use a 24&#215;24 image, there are more than 160.000 features available given multiple grid sizes from 2&#215;2 and up to 24&#215;24. In order to speed up the process a summed area table or an integral image is created instead. It&#8217;s a 2D lookup table which allows for finding the sum of sub grid in an image, vastly reducing the number of computations needed for subtracting pixels from each other when calculating features.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># an example of the summed area table\n\n# here an 'image' of pixel intensities is given\n&#91;\n  1, 2, 5\n  1, 1, 1\n]\n\n# so we run over the image once from the top left corner to create the table given the following information\n\nSUM(x,y) = i(x,y) + SUM(x-1,y) + SUM(x,y-1) - SUM(x-1,y-1)\n\nGiven a rectangle with 4 corners (A,B,C,D) the sum of the rectangle is \nD - C - B + A\nwhere A is top left, B is top right, C is bottom left, and D is bottom right.\n\nIf we have the summed area table, its a matter of lookups for the values and a simple subtraction, rather than re-iteratively calculating the numbers over and over, if A is fixed and D increases.\n\n# the summed area table\n&#91;\n  {1},       {1+0+2-0}, {3+0+5-0},\n  {1+1+0+0}, {1+2+3-1}, {1+5+8-3},\n]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Most features are unimportant, and thus a filtering approach is therefore applied. Here a feature is weighted based on the training images is classifies correctly. A collection of features given the optimal classification of the training data is therefore a combined feature, which is good, while each individual feature might be less good on its own. By doing this, the amount of features below a certain error threshold, i.e. always classifying incorrectly can be fully discarded for the combined feature, drastically reducing the total number of features to use for testing. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However this reduction is still to slow. In order to speed up the process,  features are ordered and applied in stages which is known here as cascading. A preliminary stages suggest that there might be an object which some level of uncertainty, and each stage gradually becomes more confident that the object exists. If a stage fails, we stop trying, and identify no object in the tested image. A failed stage is a stage where a combined sum of weights for the features of the stage, drops below a threshold. Now the testing is really fast, compared to the initial check of 160.000 features. We can sometimes end up using just 10 &#8211; 30 filtered features.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How does it work An object detection algorithm is given a set of images containing the object of interest, and a set of images not containing the object of interest. Given enough images, the detection algorithm learns to recognize the underlying features of the object. Any new image can then be assessed by the algorithm, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":104,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-189","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"http:\/\/aslak.nu\/index.php\/wp-json\/wp\/v2\/pages\/189","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/aslak.nu\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/aslak.nu\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/aslak.nu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/aslak.nu\/index.php\/wp-json\/wp\/v2\/comments?post=189"}],"version-history":[{"count":3,"href":"http:\/\/aslak.nu\/index.php\/wp-json\/wp\/v2\/pages\/189\/revisions"}],"predecessor-version":[{"id":194,"href":"http:\/\/aslak.nu\/index.php\/wp-json\/wp\/v2\/pages\/189\/revisions\/194"}],"up":[{"embeddable":true,"href":"http:\/\/aslak.nu\/index.php\/wp-json\/wp\/v2\/pages\/104"}],"wp:attachment":[{"href":"http:\/\/aslak.nu\/index.php\/wp-json\/wp\/v2\/media?parent=189"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}