{"id":2660,"date":"2012-08-09T10:20:00","date_gmt":"2012-08-09T17:20:00","guid":{"rendered":"https:\/\/pkmital.com\/home\/?post_type=pkm_teaching&#038;p=2660"},"modified":"2023-08-22T18:00:23","modified_gmt":"2023-08-23T01:00:23","slug":"workshops-in-creative-coding-computer-vision","status":"publish","type":"pkm_teaching","link":"https:\/\/pkmital.com\/home\/teaching\/workshops-in-creative-coding-computer-vision\/","title":{"rendered":"Goldsmiths, University of London | Workshops in Creative Coding:  Computer Vision"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">Introduction<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the homepage for the Master\u2019s course \u2013 Workshops in Creative Coding: Computer Vision @ Goldsmiths, University of London\u2019s Department of Computing. We will go over the basics of Computer Vision within the openFrameworks environment, using the ofxOpenCV addon, as well as using OpenCV directly. The course will be taught over 3 weeks and will end with a final project due at the end of Reading week.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Labs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">On this page, you will find your lab sheets and example solutions to the labs by the end of the week.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Please make sure you already have a coding environment setup to use openFrameworks v007:&nbsp;<a href=\"http:\/\/openframeworks.cc\/download\" target=\"_blank\" rel=\"noreferrer noopener\">openFrameworks<\/a>. openFrameworks v007 includes an addon for OpenCV version 2.2.0 which we will explore extensively. We will also use some features of the OpenCV library directly. Keep in mind links for the OpenCV&nbsp;<a href=\"http:\/\/opencv.willowgarage.com\/documentation\/cpp\/\" target=\"_blank\" rel=\"noreferrer noopener\">documentation<\/a>&nbsp;and&nbsp;<a href=\"http:\/\/opencv.willowgarage.com\/wiki\/FullOpenCVWiki\" target=\"_blank\" rel=\"noreferrer noopener\">Wiki<\/a>&nbsp;pages. Actually,&nbsp;<a href=\"http:\/\/public.cranfield.ac.uk\/c5354\/teaching\/dip\/opencv\/manual\/\" target=\"_blank\" rel=\"noreferrer noopener\">this page<\/a>&nbsp;is a good compendium of different resources, and also includes a link to a&nbsp;<a href=\"http:\/\/public.cranfield.ac.uk\/c5354\/teaching\/dip\/opencv\/manual\/opencv2refman.pdf\" target=\"_blank\" rel=\"noreferrer noopener\">PDF Manual of OpenCV 2.3.1<\/a>. More information is also in each of the lab sheets below.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Final Project<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Information regarding your final project can also be found here for&nbsp;<a href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2012\/01\/computational-arts-final-project.pdf\" target=\"_blank\" rel=\"noreferrer noopener\">[Computational Arts]<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2012\/01\/games-final-project.pdf\" target=\"_blank\" rel=\"noreferrer noopener\">[Games]<\/a>&nbsp;students.&nbsp;&nbsp;<strong>You are expected to have formed groups no larger than 6 ready with an idea for discussion by the start of the second week\u2019s lab (30th\/31st of January)&nbsp;<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You will be asked to show your finished work to us in the week after reading week (details to follow).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You should also submit a short (500 words) written discussion of your work by midnight on the 19th Feb. This should include a brief description of your work, your role in the work and the contribution of your different team members. In the last category you should state whether you feel the contribution was roughly equal in which case marks will be allocated equally, or whether any team members contributed significantly more or less.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Other Computer Vision courses<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Bob Fisher\u2019s&nbsp;<a href=\"http:\/\/www.inf.ed.ac.uk\/teaching\/courses\/ivr\/\" target=\"_blank\" rel=\"noreferrer noopener\">IVR Introduction to Vision and Robotics<\/a>&nbsp;&amp;&nbsp;<a href=\"http:\/\/www.inf.ed.ac.uk\/teaching\/courses\/av\/\" target=\"_blank\" rel=\"noreferrer noopener\">AV Advanced Vision<\/a>&nbsp;classes at University of Edinburgh<br>James Hays\u2019s&nbsp;<a href=\"http:\/\/www.cs.brown.edu\/courses\/cs143\/\" target=\"_blank\" rel=\"noreferrer noopener\">CS 143 Introduction to Computer Vision<\/a>&nbsp;class at Brown<br>Trevor Darrell\u2019s&nbsp;<a href=\"http:\/\/www.eecs.berkeley.edu\/~trevor\/CS280.html\" target=\"_blank\" rel=\"noreferrer noopener\">CS 280 Computer Vision<\/a>&nbsp;class at University of California, Berkeley<br>Antonio Torralba\u2019s&nbsp;<a href=\"http:\/\/groups.csail.mit.edu\/vision\/courses\/6.869\/\" target=\"_blank\" rel=\"noreferrer noopener\">6.869 Advances in Computer Vision<\/a>&nbsp;class at MIT<br>Kristen Grauman\u2019s&nbsp;<a href=\"http:\/\/www.cs.utexas.edu\/~grauman\/courses\/fall2009\/main.htm\" target=\"_blank\" rel=\"noreferrer noopener\">CS 378 Computer Vision<\/a>&nbsp;class at University of Texas, Austin<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Recommended Books<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Forsyth &amp; Ponce \u2013 Computer Vision: a Modern Approach<br>Norvig \u2013 Artificial Intelligence: a Modern Approach<br>Bishop \u2013 Pattern Recognition and Machine Learning<br>Ballard &amp; Brown \u2013 Computer Vision [<a href=\"http:\/\/homepages.inf.ed.ac.uk\/rbf\/BOOKS\/BANDB\/bandb.htm\" target=\"_blank\" rel=\"noreferrer noopener\">free eBook<\/a>]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Week 1<\/h3>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"has-text-align-left wp-block-paragraph\"><strong>Introduction<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<p class=\"wp-block-paragraph\">This lab will orient you with using some features of openFrameworks\u2019 ofxOpenCv addon, which builds wrappers for OpenCV, and also get you started with using OpenCV itself using some of its most basic functions.<\/p>\n<\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Lecture Slides<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<div data-wp-interactive=\"core\/file\" class=\"wp-block-file\"><object data-wp-bind--hidden=\"!state.hasPdfPreview\" hidden class=\"wp-block-file__embed\" data=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/lecture-1-introduction-to-computer-vision-opt.pdf\" type=\"application\/pdf\" style=\"width:100%;height:600px\" aria-label=\"Embed of lecture-1-introduction-to-computer-vision-opt.\"><\/object><a id=\"wp-block-file--media-9f647504-2981-4d46-b1a3-e62129355377\" href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/lecture-1-introduction-to-computer-vision-opt.pdf\" target=\"_blank\" rel=\"noreferrer noopener\">lecture-1-introduction-to-computer-vision-opt<\/a><\/div>\n<\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Lab Sheet<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<div data-wp-interactive=\"core\/file\" class=\"wp-block-file\"><object data-wp-bind--hidden=\"!state.hasPdfPreview\" hidden class=\"wp-block-file__embed\" data=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/labsheet-1.pdf\" type=\"application\/pdf\" style=\"width:100%;height:600px\" aria-label=\"Embed of labsheet-1.\"><\/object><a id=\"wp-block-file--media-5cf07687-927c-4331-be3c-06eef91aec4b\" href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/labsheet-1.pdf\">labsheet-1<\/a><\/div>\n<\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Interactive RGB Colorspace<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<p class=\"wp-block-paragraph\">testApp.h<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#pragma once\n \n#include &quot;ofMain.h&quot;\n#include &quot;ofxCvMain.h&quot;\n \nclass testApp : public ofBaseApp{\n \npublic:\n         \n    \/\/ redeclaration of functions (declared in base class)\n    void setup();\n    void update();\n    void draw();\n \n    void keyPressed(int key);\n \n    ofVideoGrabber camera;\n     \n    ofxCvColorImage im_color;\n    ofxCvGrayscaleImage im_red, im_green, im_blue;\n    ofxCvGrayscaleImage im_gray;\n    ofxCvGrayscaleImage im_value;\n     \n \n    int imgWidth, imgHeight;\n     \n    bool bShowBlended;\n};\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">testApp.cpp<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &quot;testApp.h&quot;\n \n\/\/ here we &quot;define&quot; the methods we &quot;declared&quot; in the &quot;testApp.h&quot; file\n \n\/\/ i get called once\nvoid testApp::setup(){\n     \n    \/\/ do some initialization\n    imgWidth = 320;\n    imgHeight = 240;\n     \n    bShowBlended = false;\n     \n    \/\/ set the size of the window\n    ofSetWindowShape(imgWidth * 6, imgHeight);\n     \n    \/\/ the rate at which the program runs (FPS)\n    ofSetFrameRate(30);\n     \n    \/\/ setup the camera\n    camera.initGrabber(imgWidth, imgHeight);\n    im_color.allocate(imgWidth, imgHeight);\n    im_red.allocate(imgWidth, imgHeight);\n    im_green.allocate(imgWidth, imgHeight);\n    im_blue.allocate(imgWidth, imgHeight);\n    im_gray.allocate(imgWidth, imgHeight);\n    im_value.allocate(imgWidth, imgHeight);\n}\n \n\/\/ i get called in a loop that runs until the program ends\nvoid testApp::update(){\n    camera.update();\n     \n    if(camera.isFrameNew())\n    {\n        \/\/ copy the pixels from the camera object into an ofxCvColorImage object\n        im_color.setFromPixels(camera.getPixels(), imgWidth, imgHeight);\n         \n        im_gray = im_color;\n         \n        \/\/ get each color channel\n        im_color.convertToGrayscalePlanarImages(im_red, im_green, im_blue);\n         \n        im_color.convertRgbToHsv();\n        im_color.convertToGrayscalePlanarImage(im_value, 2);\n    }\n}\n \n\/\/ i also get called in a loop that runs until the program ends\nvoid testApp::draw(){\n    \/\/ background values go to 0\n    ofBackground(0);\n     \n    \/\/ draw the camera\n    ofSetColor(255, 255, 255);\n    camera.draw(imgWidth * 0,0);\n     \n    if(bShowBlended)\n    {\n        \/\/ blending mode for adding pictures together\n        ofEnableAlphaBlending();\n        ofEnableBlendMode(OF_BLENDMODE_ADD);\n         \n        \/\/ full red energy\n        ofSetColor(255, 0, 0);\n        im_red.draw(imgWidth * 1,0);\n         \n        \/\/ full green energy\n        ofSetColor(0, 255, 0);\n         \n        \/\/ draw using an offset from the center of the screen determined by the mouse position\n        im_green.draw(imgWidth * 1 + (mouseX - ofGetScreenWidth()\/2) \/ 10.0,0);\n         \n        \/\/ full blue energy\n        ofSetColor(0, 0, 255);\n         \n        \/\/ offset just like above, but 2x as much\n        im_blue.draw(imgWidth * 1 + (mouseX - ofGetScreenWidth()\/2) \/ 5.0,0);\n         \n        ofDisableAlphaBlending();\n    }\n    else\n    {\n        ofSetColor(255, 0, 0);\n        im_red.draw(imgWidth * 1,0);\n         \n        ofSetColor(0, 255, 0);\n        im_green.draw(imgWidth * 2,0);\n         \n        ofSetColor(0, 0, 255);\n        im_blue.draw(imgWidth * 3,0);\n    }\n     \n    ofSetColor(255, 255, 255);\n    im_gray.draw(imgWidth * 4, 0);\n    im_value.draw(imgWidth * 5, 0);\n}\n \nvoid testApp::keyPressed(int key)\n{\n    switch (key) {\n        case &#039;s&#039;:\n            camera.videoSettings();\n            break;\n             \n        \/\/ press space to switch between modes    \n        case &#039; &#039;:\n            bShowBlended = !bShowBlended;\n            break;\n        default:\n            break;\n    }\n}\n<\/pre><\/div><\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Motion Tracking + Interactive Video Player<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<p class=\"wp-block-paragraph\">testApp.h<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\n#pragma once\n \n#include &quot;ofMain.h&quot;\n#include &quot;ofxOpenCv.h&quot;\n \nclass testApp : public ofBaseApp{\n \npublic:\n    void setup();\n    void update();\n    void draw();    \n      \n    float                   alpha;\n    float                   sum;\n    ofVideoGrabber          camera;\n \n    ofxCvColorImage         color_img;\n \n    ofxCvGrayscaleImage     gray_img;\n    ofxCvGrayscaleImage     gray_previous_img;\n    ofxCvGrayscaleImage     gray_diff;\n \n    ofVideoPlayer           video;\n \n    vector&lt;ofxCvGrayscaleImage&gt; previous_imgs;\n \n    int                     img_width, img_height;\n};\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">testApp.cpp<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\n#include &quot;testApp.h&quot;\n \nusing namespace cv;\n \n\/\/--------------------------------------------------------------\nvoid testApp::setup(){\n \n    \/\/ keep variables for our image size\n    img_width = 320;\n    img_height = 240;\n     \n    \/\/ value for our first order linear filter\n    \/\/ this controls how much we mix in previous results\n    \/\/ the larger the value, the larger the weight of the \n    \/\/ previous result.  this is a very essential and basic\n    \/\/ technique in digital signal processing also known as a \n    \/\/ low pass filter.\n    alpha = 0.5;\n     \n    \/\/ change the window to hold enough space for 2 movies (1 row x 2 columns of movies)\n    ofSetWindowShape(img_width * 2, img_height);\n     \n    ofSetFrameRate(30);\n     \n    \/\/ initialize our camera with a resolution of 320z240\n    camera.initGrabber(img_width, img_height);\n     \n    \/\/ load a movie in and set it to loop, and then start it (play())\n    video.loadMovie(&quot;sunra_pink.mov&quot;);\n    video.setLoopState(OF_LOOP_NORMAL);\n    video.play();\n     \n    sum = 0;\n     \n    \/\/ these are (wrappers for) opencv image containers \n    \/\/ we&#039;ll use for image processing\n    \/\/ we are going to find the difference between successive frames\n    color_img.allocate(img_width, img_height);\n    gray_img.allocate(img_width, img_height);\n    gray_previous_img.allocate(img_width, img_height);\n    gray_diff.allocate(img_width, img_height);\n    previous_imgs.push_back(gray_previous_img);\n    previous_imgs.push_back(gray_previous_img);\n    previous_imgs.push_back(gray_previous_img);\n     \n}\n \n\/\/--------------------------------------------------------------\nvoid testApp::update(){\n    \/\/ background to black\n    ofBackground(0);\n     \n    \/\/ update the camera\n    camera.update();\n     \n    if (camera.isFrameNew()) {\n        \/\/ set the color image (opencv container) to the camera image\n        color_img.setFromPixels(camera.getPixels(), img_width, img_height);\n        \/\/ convert to grayscale\n        gray_img = color_img;\n        \/\/ calculate the difference image\n        gray_diff = gray_img;\n        \/\/ compute the absolute difference with the previous frame&#039;s grayscale image\n        gray_diff.absDiff(previous_imgs&#x5B;0]);\n         \n        Mat diff_mat(gray_diff.getCvImage());\n        Scalar s1 = mean(diff_mat);\n         \n         \n        \/\/ store the current grayscale image for the next iteration of update()\n        previous_imgs.push_back(gray_img);\n        if (previous_imgs.size() &gt; 10) {\n            previous_imgs.erase(previous_imgs.begin());\n        }\n         \n        \/\/ let&#039;s threshold the difference image,\n        \/\/ all values less than 10 are 0, all values above 10 become 255\n        \/\/gray_diff.threshold(10);\n         \n        \/\/ here we will find the sum and then average of all the pixels in the difference image\n        \/\/ this will be used for a simple measure of &quot;motion&quot; \n        \/\/ we use a low-pass filter, a first order filter which combines the current \n        \/\/ value with the previous one, using a linear weighting.\n        sum = (alpha) * sum + \n        (1 - alpha) * s1&#x5B;0] \/ 10.0f;\/\/cvSum(gray_diff.getCvImage()).val&#x5B;0] \/ (float)img_width \/ (float)img_height \/ 10.0;\n         \n         \n        \/\/ let&#039;s change the speed of our movie based on the motion value we calculated\n        video.setSpeed(sum);\n        video.update();\n    }\n     \n     \n}\n \n\/\/--------------------------------------------------------------\nvoid testApp::draw(){\n    ofEnableAlphaBlending();\n    ofEnableBlendMode(OF_BLENDMODE_ADD);\n     \n    color_img.draw(0, 0, img_width, img_height);\n    gray_diff.draw(0, 0, img_width, img_height);\n \n    ofDisableAlphaBlending();\n     \n    video.draw(img_width, 0);\n     \n    \/\/ draw the sum of the motion pixels divided by the number of motion pixels \n    \/\/ (average of difference values)\n    char buf&#x5B;256];\n    sprintf(buf, &quot;%f&quot;, sum);\n    ofDrawBitmapString(buf, 20, 20);\n}\n<\/pre><\/div><\/div>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Week 2<\/h3>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Introduction<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<figure class=\"wp-block-embed is-type-video is-provider-vimeo wp-block-embed-vimeo wp-embed-aspect-4-3 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/REC-html40\/loose.dtd\">\n<html><body><iframe loading=\"lazy\" title=\"Real time Object Tracking\" src=\"https:\/\/player.vimeo.com\/video\/29734948?dnt=1&amp;app_id=122963&amp;autoplay=0&amp;loop=1&amp;autopause=0&amp;muted=1\" width=\"320\" height=\"240\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write\"><\/iframe><\/body><\/html>\n\n<\/div><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Last week we learned about colorspaces and how to represent our input image as luminance and a single value representing motion for doing simple motion tracking. This week, we will detect and display image features, and then use those features for detecting any planar object. Get the additional files required for the second part of the lab here:&nbsp;<a href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/pkmImageFeatureDetector.zip\" target=\"_blank\" rel=\"noreferrer noopener\">[pkmImageFeatureDetector.zip]<\/a>.<\/p>\n<\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Lecture Slides<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<div data-wp-interactive=\"core\/file\" class=\"wp-block-file\"><object data-wp-bind--hidden=\"!state.hasPdfPreview\" hidden class=\"wp-block-file__embed\" data=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/lecture-2-image-features-opt.pdf\" type=\"application\/pdf\" style=\"width:100%;height:600px\" aria-label=\"Embed of lecture-2-image-features-opt.\"><\/object><a id=\"wp-block-file--media-5896c885-71fa-4fa4-a1a6-b6f588af47d7\" href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/lecture-2-image-features-opt.pdf\" target=\"_blank\" rel=\"noreferrer noopener\">lecture-2-image-features-opt<\/a><\/div>\n<\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Lab Sheet<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<div data-wp-interactive=\"core\/file\" class=\"wp-block-file\"><object data-wp-bind--hidden=\"!state.hasPdfPreview\" hidden class=\"wp-block-file__embed\" data=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/labsheet-2.pdf\" type=\"application\/pdf\" style=\"width:100%;height:600px\" aria-label=\"Embed of labsheet-2.\"><\/object><a id=\"wp-block-file--media-85e9659d-4c27-415d-903d-4e6bc482308c\" href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/labsheet-2.pdf\" target=\"_blank\" rel=\"noreferrer noopener\">labsheet-2<\/a><\/div>\n<\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Detecting and Drawing Image Features<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<p class=\"wp-block-paragraph\">testApp.h<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\n#pragma once\n \n#include &quot;ofMain.h&quot;\n#include &quot;ofxCvMain.h&quot;\n \nusing namespace cv;\n \nclass testApp : public ofBaseApp{\n \npublic:\n         \n    \/\/ redeclaration of functions (declared in base class)\n    void setup();\n    void setupFeatures();\n     \n    void update();\n    void draw();\n \n    void keyPressed(int key);\n     \n    int width, height;\n    float scalar;\n     \n    ofVideoGrabber camera;\n \n    ofxCvColorImage cv_color_img;\n    ofxCvGrayscaleImage cv_luminance_img;\n     \n    Mat mat_image;\n     \n    unsigned int current_detector; \n    vector&lt;string&gt; feature_detectors;\n     \n    cv::Ptr&lt;FeatureDetector&gt; feature_detector;\n    vector&lt;KeyPoint&gt; keypoints;\n};\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">testApp.cpp<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\n#include &quot;testApp.h&quot;\n \n\/\/ here we &quot;define&quot; the methods we &quot;declared&quot; in the &quot;testApp.h&quot; file\n \n\/\/ i get called once\nvoid testApp::setup(){\n     \n    scalar = 1.0f;\n    width = 640;\n    height = 480;\n     \n    camera.initGrabber(width, height);\n     \n    cv_color_img.allocate(width, height);\n    cv_luminance_img.allocate(width, height);\n     \n    feature_detectors.push_back(&quot;SURF&quot;);\n    feature_detectors.push_back(&quot;DynamicSURF&quot;);\n    feature_detectors.push_back(&quot;PyramidSURF&quot;);\n    feature_detectors.push_back(&quot;GridSURF&quot;);\n    feature_detectors.push_back(&quot;SIFT&quot;);\n    feature_detectors.push_back(&quot;STAR&quot;);\n    feature_detectors.push_back(&quot;DynamicSTAR&quot;);\n    feature_detectors.push_back(&quot;PyramidSTAR&quot;);\n    feature_detectors.push_back(&quot;GridSTAR&quot;);\n    feature_detectors.push_back(&quot;FAST&quot;);\n    feature_detectors.push_back(&quot;DynamicFAST&quot;);\n    feature_detectors.push_back(&quot;PyramidFAST&quot;);\n    feature_detectors.push_back(&quot;GridFAST&quot;);\n    feature_detectors.push_back(&quot;GFTT&quot;);\n    feature_detectors.push_back(&quot;PyramidGFTT&quot;);\n    feature_detectors.push_back(&quot;MSER&quot;);\n    feature_detectors.push_back(&quot;PyramidMSER&quot;);\n    feature_detectors.push_back(&quot;HARRIS&quot;);\n    feature_detectors.push_back(&quot;PyramidHARRIS&quot;);\n     \n    current_detector = 0;\n    feature_detector = FeatureDetector::create(feature_detectors&#x5B;current_detector]);\n     \n    ofSetFrameRate(60.0f);\n    ofSetWindowShape(width * scalar, height * scalar);\n}\n \n\/\/ i get called in a loop that runs until the program ends\nvoid testApp::update(){\n    camera.update();\n    if (camera.isFrameNew()) {\n        cv_color_img.setFromPixels(camera.getPixelsRef());\n        cv_color_img.convertRgbToHsv();\n        cv_color_img.convertToGrayscalePlanarImage(cv_luminance_img, 2);\n         \n        mat_image = Mat(cv_luminance_img.getCvImage());\n         \n        keypoints.clear();\n        feature_detector-&gt;detect(mat_image, keypoints);\n    }\n}\n \n\/\/ i also get called in a loop that runs until the program ends\nvoid testApp::draw(){\n    ofBackground(0);\n     \n    ofPushMatrix();\n    ofScale(scalar, scalar);\n     \n    ofSetColor(255, 255, 255);\n    camera.draw(0, 0);\n     \n    ofNoFill();\n    ofSetColor(200, 100, 100);\n    vector&lt;KeyPoint&gt;::iterator it = keypoints.begin();\n    while(it != keypoints.end())\n    {\n        ofPushMatrix();\n        float radius = it-&gt;size\/2;\n        ofTranslate(it-&gt;pt.x - radius, it-&gt;pt.y - radius, 0);\n        ofRotate(it-&gt;angle, 0, 0, 1);\n        ofRect(0, 0, radius, radius);\n        ofPopMatrix();\n        it++;\n    }\n    ofPopMatrix();\n     \n    ofSetColor(255, 255, 255);\n    string draw_string = ofToString(current_detector+1) + string(&quot;\/&quot;) + \n                        ofToString(feature_detectors.size()) + string(&quot;: &quot;) +\n                        feature_detectors&#x5B;current_detector];\n    ofDrawBitmapString(draw_string, 20, 20);\n    draw_string = string(&quot;# of features: &quot;) + ofToString(keypoints.size());\n    ofDrawBitmapString(draw_string, 20, 35);\n    draw_string = string(&quot;fps: &quot;) + ofToString(ofGetFrameRate());\n    ofDrawBitmapString(draw_string, 20, 50);\n}\n \nvoid testApp::keyPressed(int key){\n    if(key == &#039;n&#039;)\n    {\n        current_detector = (current_detector + 1) % feature_detectors.size();\n        feature_detector = FeatureDetector::create(feature_detectors&#x5B;current_detector]);\n    }\n}\n<\/pre><\/div><\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Detecting Planar Objects<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<p class=\"wp-block-paragraph\">testApp.h<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\n\/*\n*  Created by Parag K. Mital - http:\/\/pkmital.com \n*  Contact: parag@pkmital.com\n*\n*  Copyright 2011 Parag K. Mital. All rights reserved.\n* \n*   Permission is hereby granted, free of charge, to any person\n*   obtaining a copy of this software and associated documentation\n*   files (the &quot;Software&quot;), to deal in the Software without\n*   restriction, including without limitation the rights to use,\n*   copy, modify, merge, publish, distribute, sublicense, and\/or sell\n*   copies of the Software, and to permit persons to whom the\n*   Software is furnished to do so, subject to the following\n*   conditions:\n*   \n*   The above copyright notice and this permission notice shall be\n*   included in all copies or substantial portions of the Software.\n*\n*   THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, \n*   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n*   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n*   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n*   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n*   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n*   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n*   OTHER DEALINGS IN THE SOFTWARE.\n*\/\n#ifndef _TEST_APP\n#define _TEST_APP\n \n#include &quot;ofMain.h&quot;\n \n#include &quot;pkmImageFeatureDetector.h&quot;\n \n#include &quot;ofxOpenCv.h&quot;\n \nconst int CAM_WIDTH = 320;\nconst int CAM_HEIGHT = 240;\nconst int SCREEN_WIDTH = CAM_WIDTH*2;\nconst int SCREEN_HEIGHT = CAM_HEIGHT + 75;\n \nclass testApp : public ofBaseApp {\n \n    public:\n \n    ~testApp();\n    void setup();\n      \n    void update();\n    void draw();\n    void drawKeypoints(vector&lt;KeyPoint&gt; keypts);\n \n    void keyPressed  (int key);\n    void mouseMoved(int x, int y );\n    void mouseDragged(int x, int y, int button);\n    void mousePressed(int x, int y, int button);\n    void mouseReleased(int x, int y, int button);\n    void windowResized(int w, int h);\n     \n    ofVideoGrabber          camera;\n     \n    ofxCvColorImage         color_img, color_roi_img;\n    ofxCvGrayscaleImage     gray_search_img, \n                            gray_template_img;\n     \n    float                   x_start, \n                            x_end, \n                            y_start, \n                            y_end;\n     \n    cv::Point2f             low_pass_bounding_box&#x5B;4],\n                            prev_pass_bounding_box&#x5B;4];\n     \n    float                   alpha;\n     \n    bool                    choosing_img, \n                            chosen_img;\n     \n    pkmImageFeatureDetector detector;\n     \n    vector&lt;cv::KeyPoint&gt;    img_template_keypoints,\n                            img_search_keypoints;\n     \n};\n#endif\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">testApp.cpp<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\n\/*\n *  Created by Parag K. Mital - http:\/\/pkmital.com \n *  Contact: parag@pkmital.com\n *\n *  Copyright 2011 Parag K. Mital. All rights reserved.\n * \n *  Permission is hereby granted, free of charge, to any person\n *  obtaining a copy of this software and associated documentation\n *  files (the &quot;Software&quot;), to deal in the Software without\n *  restriction, including without limitation the rights to use,\n *  copy, modify, merge, publish, distribute, sublicense, and\/or sell\n *  copies of the Software, and to permit persons to whom the\n *  Software is furnished to do so, subject to the following\n *  conditions:\n *  \n *  The above copyright notice and this permission notice shall be\n *  included in all copies or substantial portions of the Software.\n *\n *  THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, \n *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n *  OTHER DEALINGS IN THE SOFTWARE.\n *\/\n \n#include &quot;testApp.h&quot;\n\/\/--------------------------------------------------------------\ntestApp::~testApp(){\n}\nvoid testApp::setup(){\n     \n    \/\/ init video input\n    camera.initGrabber(CAM_WIDTH,CAM_HEIGHT);\n    camera.setUseTexture(true);\n     \n    \/\/ window setup\n    ofSetWindowShape(SCREEN_WIDTH, SCREEN_HEIGHT);\n    ofSetVerticalSync(true);\n    ofSetFrameRate(60);\n    ofBackground(0,0,0);\n     \n    \/\/ allocate stuff\n    color_img.allocate(CAM_WIDTH, CAM_HEIGHT);\n    gray_search_img.allocate(CAM_WIDTH, CAM_HEIGHT);\n     \n    alpha = 0.6;\n     \n    choosing_img = false;\n    chosen_img = false;\n     \n}\n \n\/\/--------------------------------------------------------------\nvoid testApp::update(){\n     \n    camera.update();\n    if(camera.isFrameNew())\n    {\n        \/\/ get camera img into iplimage\n        color_img.setFromPixels(camera.getPixels(), CAM_WIDTH, CAM_HEIGHT);\n        color_img.convertRgbToHsv();\n        if (chosen_img) {\n            color_img.convertToGrayscalePlanarImage(gray_search_img, 2);\n            detector.setImageSearch(gray_search_img.getCvImage());\n            detector.update();\n             \n            img_search_keypoints = detector.getImageSearchKeypoints();\n             \n            ofCircle(detector.dst_corners&#x5B;0].x, detector.dst_corners&#x5B;0].y, 10);\n             \n            low_pass_bounding_box&#x5B;0] = detector.dst_corners&#x5B;0] * (1-alpha) + prev_pass_bounding_box&#x5B;0] * alpha;\n            low_pass_bounding_box&#x5B;1] = detector.dst_corners&#x5B;1] * (1-alpha) + prev_pass_bounding_box&#x5B;1] * alpha;\n            low_pass_bounding_box&#x5B;2] = detector.dst_corners&#x5B;2] * (1-alpha) + prev_pass_bounding_box&#x5B;2] * alpha;\n            low_pass_bounding_box&#x5B;3] = detector.dst_corners&#x5B;3] * (1-alpha) + prev_pass_bounding_box&#x5B;3] * alpha;\n             \n            prev_pass_bounding_box&#x5B;0] = low_pass_bounding_box&#x5B;0];\n            prev_pass_bounding_box&#x5B;1] = low_pass_bounding_box&#x5B;1];\n            prev_pass_bounding_box&#x5B;2] = low_pass_bounding_box&#x5B;2];\n            prev_pass_bounding_box&#x5B;3] = low_pass_bounding_box&#x5B;3];\n        }\n    } \n}\n \n\/\/--------------------------------------------------------------\nvoid testApp::draw(){\n    ofBackground(0,0,0);\n     \n    ofSetColor(255, 255, 255);\n    \/\/ camera image\n    camera.draw(0, 0);\n     \n    if (chosen_img) {\n        ofSetColor(200, 100, 100);\n        drawKeypoints(img_search_keypoints);\n         \n         \n        ofPushMatrix();\n        ofTranslate(CAM_WIDTH, 0, 0);\n        ofSetColor(255, 255, 255);\n        gray_template_img.draw(0, 0);\n        ofSetColor(200, 100, 100);\n        drawKeypoints(img_template_keypoints);\n        ofPopMatrix();\n         \n        ofSetColor(200, 200, 200);\n         \n        ofLine(low_pass_bounding_box&#x5B;0].x, low_pass_bounding_box&#x5B;0].y,\n               low_pass_bounding_box&#x5B;1].x, low_pass_bounding_box&#x5B;1].y);\n         \n        ofLine(low_pass_bounding_box&#x5B;2].x, low_pass_bounding_box&#x5B;2].y,\n               low_pass_bounding_box&#x5B;1].x, low_pass_bounding_box&#x5B;1].y);\n         \n        ofLine(low_pass_bounding_box&#x5B;2].x, low_pass_bounding_box&#x5B;2].y,\n               low_pass_bounding_box&#x5B;3].x, low_pass_bounding_box&#x5B;3].y);\n         \n        ofLine(low_pass_bounding_box&#x5B;0].x, low_pass_bounding_box&#x5B;0].y,\n               low_pass_bounding_box&#x5B;3].x, low_pass_bounding_box&#x5B;3].y);\n         \n    }\n     \n     \n    \/\/ draw a rectanlge around the current selection\n    if (choosing_img) {\n        int x = mouseX;\n        int y = mouseY;\n         \n        ofNoFill();\n        ofRect(x_start &lt; x ? x_start : x, \n               y_start &lt; y ? y_start : y, \n               abs(x_start - x), \n               abs(y_start - y));\n         \n    }\n     \n     \n}\n \nvoid testApp::drawKeypoints(vector&lt;KeyPoint&gt; keypts)\n{\n    vector&lt;KeyPoint&gt;::iterator it = keypts.begin();\n    while(it != keypts.end())\n    {\n        ofPushMatrix();\n        float radius = it-&gt;size\/2;\n        ofTranslate(it-&gt;pt.x - radius, it-&gt;pt.y - radius, 0);\n        ofRotate(it-&gt;angle, 0, 0, 1);\n        ofRect(0, 0, radius, radius);\n        ofPopMatrix();\n        it++;\n    }\n \n}\n \n \n\/\/--------------------------------------------------------------\nvoid testApp::keyPressed  (int key){\n     \n    switch (key){           \n        case &#039;s&#039;:\n            camera.videoSettings();\n            break;\n        case &#039;n&#039;:\n        {\n            detector.changeDetector();\n            if(chosen_img)\n               img_template_keypoints = detector.getImageTemplateKeypoints();\n            break;\n        }\n        case &#039;1&#039;:\n            break;\n        case &#039;2&#039;:\n            break;\n             \n        case &#039;b&#039;:\n            break;\n             \n    }\n}\n \n\/\/--------------------------------------------------------------\nvoid testApp::mouseMoved(int x, int y ){\n}\n \n\/\/--------------------------------------------------------------\nvoid testApp::mouseDragged(int x, int y, int button){\n}\n \n\/\/--------------------------------------------------------------\nvoid testApp::mousePressed(int x, int y, int button){\n     \n    \/\/ start a rectangle selection\n    if(!choosing_img)\n    {\n        choosing_img = true;\n        x_start = x;\n        y_start = y;\n    }\n}\n \n\/\/--------------------------------------------------------------\nvoid testApp::mouseReleased(int x, int y, int button){\n     \n    \/\/ end the rectangle selection\n    if (choosing_img) {\n        choosing_img = false;\n        x_end = x;\n        y_end = y;\n         \n        if(x_start &gt; x_end)\n            std::swap(x_start, x_end);\n        if(y_start &gt; y_end)\n            std::swap(y_start, y_end);\n         \n        int w = x_end - x_start;\n        int h = y_end - y_start;\n         \n         \n        cvSetImageROI(color_img.getCvImage(), \n                      cvRect(x_start, \n                             y_start, \n                             w, h));\n         \n        if (color_roi_img.bAllocated) {\n            gray_template_img.clear();\n            color_roi_img.clear();\n        }\n        gray_template_img.allocate(w, h);\n        color_roi_img.allocate(w, h);\n        color_roi_img = color_img;\n        color_roi_img.convertToGrayscalePlanarImage(gray_template_img, 2);\n        cvResetImageROI(color_img.getCvImage());\n \n        detector.setImageTemplate(gray_template_img.getCvImage());\n         \n        img_template_keypoints = detector.getImageTemplateKeypoints();\n         \n        chosen_img = true;\n    }\n     \n}\n \n\/\/--------------------------------------------------------------\nvoid testApp::windowResized(int w, int h){\n     \n}\n<\/pre><\/div><\/div>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Week 3<\/h3>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Introduction<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<figure class=\"wp-block-embed is-type-video is-provider-vimeo wp-block-embed-vimeo\"><div class=\"wp-block-embed__wrapper\">\n<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/REC-html40\/loose.dtd\">\n<html><body><iframe loading=\"lazy\" title=\"Overhead Tracking w\/ Orientation\" src=\"https:\/\/player.vimeo.com\/video\/22054133?dnt=1&amp;app_id=122963&amp;autoplay=0&amp;loop=1&amp;autopause=0&amp;muted=1\" width=\"500\" height=\"163\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write\"><\/iframe><\/body><\/html>\n\n<\/div><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This week we will develop a system for blob detection and tracking.<\/p>\n<\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Lecture Slides<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<div data-wp-interactive=\"core\/file\" class=\"wp-block-file\"><object data-wp-bind--hidden=\"!state.hasPdfPreview\" hidden class=\"wp-block-file__embed\" data=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/lecture-3-blob-detection.pdf\" type=\"application\/pdf\" style=\"width:100%;height:600px\" aria-label=\"Embed of lecture-3-blob-detection.\"><\/object><a id=\"wp-block-file--media-264b31db-4353-42d9-967b-d820f378968d\" href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/lecture-3-blob-detection.pdf\" target=\"_blank\" rel=\"noreferrer noopener\">lecture-3-blob-detection<\/a><\/div>\n<\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<p class=\"wp-block-paragraph\"><strong>Lab Sheet<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<p class=\"wp-block-paragraph\">Make sure you download the additional files required for Part 2 of the lab here:&nbsp;<a href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/pkmBlobTracker.zip\" target=\"_blank\" rel=\"noreferrer noopener\">[pkmBlobTracker.zip]<\/a>.<\/p>\n\n\n\n<div data-wp-interactive=\"core\/file\" class=\"wp-block-file\"><object data-wp-bind--hidden=\"!state.hasPdfPreview\" hidden class=\"wp-block-file__embed\" data=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/labsheet-3.pdf\" type=\"application\/pdf\" style=\"width:100%;height:600px\" aria-label=\"Embed of labsheet-3.\"><\/object><a id=\"wp-block-file--media-36499d3a-2c0e-447c-b370-c5ccccefe2b3\" href=\"https:\/\/pkmital.com\/home\/wp-content\/uploads\/2023\/08\/labsheet-3.pdf\" target=\"_blank\" rel=\"noreferrer noopener\">labsheet-3<\/a><\/div>\n<\/div>\n<\/div>\n","protected":false},"featured_media":2922,"template":"","categories":[15],"teaching-type":[395,396],"class_list":["post-2660","pkm_teaching","type-pkm_teaching","status-publish","has-post-thumbnail","hentry","category-teaching","teaching-type-computer-vision","teaching-type-creative-coding"],"acf":[],"_links":{"self":[{"href":"https:\/\/pkmital.com\/home\/wp-json\/wp\/v2\/pkm_teaching\/2660","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pkmital.com\/home\/wp-json\/wp\/v2\/pkm_teaching"}],"about":[{"href":"https:\/\/pkmital.com\/home\/wp-json\/wp\/v2\/types\/pkm_teaching"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pkmital.com\/home\/wp-json\/wp\/v2\/media\/2922"}],"wp:attachment":[{"href":"https:\/\/pkmital.com\/home\/wp-json\/wp\/v2\/media?parent=2660"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pkmital.com\/home\/wp-json\/wp\/v2\/categories?post=2660"},{"taxonomy":"teaching-type","embeddable":true,"href":"https:\/\/pkmital.com\/home\/wp-json\/wp\/v2\/teaching-type?post=2660"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}