2016年5月19日 星期四

Raspberry Pi: OSC for Python3

since: 2016/05/19
update: 2016/05/19
reference:
1. Osc4py3 documentation

A. 安裝 OSC for Python3
    $ sudo pip3 install osc4py3

-----------------------------------------------------------------------------------------------

B. OSC 接收端 (client)
     // osc_client.py

#!/usr/bin/python3

# Import needed modules from osc4py3
from osc4py3.as_eventloop import *
from osc4py3 import oscmethod as osm

print('start osc client....'+'\n')

def osc_receive_function(address, s, x, y):
    # Will receive message address, and message data flattened in s, x, y
    print("osc client receive:")
    print('address = '+address)
    print('s = ' + s)
    print('x = ' + str(x))
    print('y = ' + str(y))

# Start the system.
osc_startup()

# Make server channels to receive packets.
osc_udp_server("192.168.1.17", 2781, "aservername")

# Associate Python functions with message address patterns
osc_method("/test/*", osc_receive_function, argscheme=osm.OSCARG_ADDRESS + osm.OSCARG_DATAUNPACK)

# Periodically call osc4py3 processing method in your event loop.
finished = False
while not finished:
    # …
    osc_process()
    # …

# Properly close the system.
osc_terminate()


-----------------------------------------------------------------------------------------------

C. OSC 發送端 (server)
     // osc_server.py

#!/usr/bin/python3

# Import needed modules from osc4py3
from osc4py3.as_eventloop import *
from osc4py3 import oscbuildparse

print('start osc server....'+ '\n')
# Start the system.
osc_startup()

# Make client channels to send packets.
osc_udp_client("192.168.1.17", 2781, "aclientname")

# Build a simple message and send it.
s = 'text'
x = 672
y = 8.871
address = '/test/me'

msg = oscbuildparse.OSCMessage(address, ",sif", [s, x, y])
osc_send(msg, "aclientname")

print("osc server send:")
print('address = ' + address)
print('s = ' + s)
print('x = ' + str(x))
print('y = ' + str(y))

# Periodically call osc4py3 processing method in your event loop.
finished = False
while not finished:
    # You can send OSC messages from your event loop too…
    # …

    osc_process()
    # …
    finished = True # finish the process

# Properly close the system.
osc_terminate()

-----------------------------------------------------------------------------------------------

D. 測試:
     1. 啟動 OSC client:
         $ ./osc_client.py
         start osc client....

     2. 啟動 OSC server:
         $ ./osc_server.py
         start osc server....

         osc server send:
         address = /test/me
         s = text
         x = 672
         y = 8.871


     3. 查看 OSC client:
         start osc client....

         osc client receive:
         address = /test/me
         s = text
         x = 672
         y = 8.871000289916992

2016年5月8日 星期日

Raspberry Pi: Real-Time Face Detection With Camera Module

since: 2016/05/08
update: 2016/05/08
reference:
1. openframeworks
2. I touchs: openFrameworks: Control The Raspberry Pi Camera Module

A. 功能測試: opencvHaarFinderExample
    // 單張照片臉部辨識
    $ cd /home/pi/of_v0.9.3/examples/addons/opencvHaarFinderExample
    $ make
    $ ./bin/opencvHaarFinderExample    // 結果


-----------------------------------------------------------------------------------------------
B. 功能測試: example-texture-mode
     // Camera Module
     $ cd /home/pi/of_v0.9.3/addons/
     $ git clone https://github.com/jvcleave/ofxRPiCameraVideoGrabber
     $ cd /home/pi/of_v0.9.3/addons/ofxRPiCameraVideoGrabber/example-texture-mode
     $ make
     $ ./bin/example-texture-mode

-----------------------------------------------------------------------------------------------

C. Face Detection With Camera Module
     1. addons.make
         ofxOpenCv
         ofxRPiCameraVideoGrabber

***********************************************

     2. main.cpp
#include "ofMain.h"
#include "ofApp.h"
#include "ofGLProgrammableRenderer.h"

int main()
{
    ofSetLogLevel(OF_LOG_VERBOSE);
   
    // read config file
    vector<string> _linesOfTheFile;
    ofBuffer _buffer = ofBufferFromFile("config.txt");
    for (auto line : _buffer.getLines()){
        _linesOfTheFile.push_back(line);
    }
   
    // local variables
    int _debug = ofToBool(_linesOfTheFile[1]);
    int _camWidth = ofToInt(_linesOfTheFile[4]);
    int _camHeight = ofToInt(_linesOfTheFile[7]);
    bool _fullScreen = ofToBool(_linesOfTheFile[10]);
   
    // show debug message
    if(_debug) {
        ofLog() << "****** main()";
        ofLog() << "debug: " << _debug;
        ofLog() << "OpenGL width: " << _camWidth;
        ofLog() << "OpenGL height: " << _camHeight;
        ofLog() << "fullScreen: " << _fullScreen;
    }
   
    ofGLESWindowSettings settings;
    //settings.width = 320;
    //settings.height = 240;

    settings.width = _camWidth;
    settings.height = _camHeight;
   
    settings.setGLESVersion(2);
    ofCreateWindow(settings);
   
    ofRunApp( new ofApp());
}


***********************************************

     3. ofApp.h
#pragma once

#include "ofMain.h"
#include "TerminalListener.h"
#include "RPiVideoGrabber.h"
#include "ofxCvHaarFinder.h"

class ofApp : public ofBaseApp, public KeyListener{

    public:

        void setup();
        void update();
        void draw();
        void keyPressed(int key);

    void onCharacterReceived(KeyListenerEventData& e);
    TerminalListener consoleListener;
   
    //wrapper class for drop-in replacement of ofVideoGrabber
    RPiVideoGrabber vidGrabber;
   
    //@add for CvHaarFinder
    ofxCvHaarFinder finder;
    ofImage grayImg;
    //ofImage img;

    //@add second record
    float countROI;
    long readSeconds; // read from totalSeconds record file
    long writeSeconds; // write to totalSeconds record file
    long latestSeconds; // latestSeconds
   
    // config file
    bool debug;
    int camWidth;
    int camHeight;
    bool fullScreen;
    int frameRate;
    bool concurrentVisit;
    float minROI;
    float maxROI;
    float weightROI;
    string recordFile;
};


***********************************************

    4. ofApp.cpp
#include "ofApp.h"

//--------------------------------------------------------------

void ofApp::setup()
{
    ofSetLogLevel(OF_LOG_VERBOSE);
   
    // read config file
    vector<string> linesOfTheFile;
    ofBuffer buffer = ofBufferFromFile("config.txt");
    for (auto line : buffer.getLines()){
        linesOfTheFile.push_back(line);
    }
    //for (int i = 0; i < linesOfTheFile.size(); i++) {
    //    ofLog(OF_LOG_VERBOSE, "cur.width = %s", linesOfTheFile[i].c_str());
    //}

   
    debug = ofToBool(linesOfTheFile[1]);
    camWidth = ofToInt(linesOfTheFile[4]);
    camHeight = ofToInt(linesOfTheFile[7]);
    fullScreen = ofToBool(linesOfTheFile[10]);
    frameRate = ofToInt(linesOfTheFile[13]);
    concurrentVisit = ofToBool(linesOfTheFile[16]);
    minROI = ofToFloat(linesOfTheFile[19]);
    maxROI = ofToFloat(linesOfTheFile[22]);
    weightROI = ofToFloat(linesOfTheFile[25]);
    recordFile = linesOfTheFile[28];
   
    if(debug)
    {
        ofLog() << "****** ofApp::setup()";
        ofLog() << "debug: " << debug;
        ofLog() << "camWidth: " << camWidth;
        ofLog() << "camHeight: " << camHeight;
        ofLog() << "fullScreen: " << fullScreen;
        ofLog() << "frameRate: " << frameRate;
        ofLog() << "concurrentVisit: " << concurrentVisit;
        ofLog() << "minROI: " << minROI;
        ofLog() << "maxROI: " << maxROI;
        ofLog() << "weightROI: " << weightROI;
        ofLog() << "recordFile: " << recordFile;
    }
   
    // read totalSeconds record file
    vector<string> totalSecondsFile;
    ofBuffer sbuffer = ofBufferFromFile(recordFile);
    for (auto line : sbuffer.getLines()){
        totalSecondsFile.push_back(line);
    }
   
    readSeconds = ofToFloat(totalSecondsFile[0]);
   
    if(debug)
    {
        ofLog() << "****** read totalSeconds record file";
        ofLog() << "readSeconds: " << readSeconds;
    }
   
    //ofSetFrameRate(5);
    ofSetFrameRate(frameRate);
   
    //allows keys to be entered via terminal remotely (ssh)
    consoleListener.setup(this);
   
    //vidGrabber.setDesiredFrameRate(30);
    vidGrabber.setDesiredFrameRate(frameRate);
    vidGrabber.initGrabber(camWidth, camHeight);
   
    //@add for CvHaarFinder
    //img.load("test.jpg");

    finder.setup("haarcascade_frontalface_default.xml");
    //finder.findHaarObjects(img);
}

//--------------------------------------------------------------
void ofApp::update()
{
    ofBackground(100, 100, 100);
    vidGrabber.update();
   
    if(vidGrabber.isFrameNew())
    {
        //ofPixels & pixels = vidGrabber.getPixels();
        //finder.findHaarObjects(pixels);

        grayImg.setFromPixels(vidGrabber.getPixels());
        grayImg.setImageType(OF_IMAGE_GRAYSCALE);

       
        finder.findHaarObjects(grayImg);
    }
}


//--------------------------------------------------------------
void ofApp::draw(){

    ofSetHexColor(0xffffff);
    //ofSetColor(ofColor::white);
   
    vidGrabber.draw(0, 0);
   
    //@add for CvHaarFinder
    //img.draw(0, 0);

    ofNoFill();
   
    for(unsigned int i = 0; i < finder.blobs.size(); i++) {
       
        ofRectangle cur = finder.blobs[i].boundingRect;
       
        //if(debug){
        //    ofLog(OF_LOG_VERBOSE, "ROI width = %f", cur.width);
        //    ofLog(OF_LOG_VERBOSE, "ROI height = %f", cur.height);
        //}

       
        //if((cur.width >= 50.0 && cur.width <= 150.0) &&
        //   (cur.height >= 50.0 && cur.height <= 150.0)) {

        if((cur.width >= (minROI * camWidth) && cur.width <= (maxROI * camWidth)) &&
           (cur.height >= (minROI * camWidth) && cur.height <= (maxROI * camWidth))) {
            countROI++;
            ofSetColor(255, 0, 0); // red color
            ofDrawRectangle(cur.x, cur.y, cur.width, cur.height);
           
            if(debug) {
                ofSetColor(ofColor::yellow);
                ofDrawBitmapString("ROI = " + ofToString(cur.width) + " x " + ofToString(cur.height), camWidth/5, camHeight/1.02);
            }
            // not concurrent Visit
            if(!concurrentVisit){
                break;
            }
        }
        else {
            ofSetColor(0, 0, 255); // blue color
            ofDrawRectangle(cur.x, cur.y, cur.width, cur.height);
        }
    }
   
    // writeSeconds
    writeSeconds = readSeconds + (countROI / frameRate * weightROI); // weightROI: for calibrate detect missing
   

    if(writeSeconds > latestSeconds){
        // Write data
        if(debug) {
            // totalSeconds record file
            ofLog() << "****** write totalSeconds record file: " << writeSeconds;
        }
       
        ofBuffer secondsBuff;
        secondsBuff.set(ofToString(writeSeconds));
        bool fileWritten = ofBufferToFile(recordFile, secondsBuff);
       
        latestSeconds = writeSeconds;
    }
   
    if(debug){
        // show message
        ofSetColor(ofColor::white);
        ofDrawBitmapString("total seconds: " + ofToString(writeSeconds), camWidth/5, camHeight/1.2);
        ofDrawBitmapString(ofToString(minROI * camWidth) + " <= ROI <= " + ofToString(maxROI * camWidth), camWidth/5, camHeight/1.1);
    }
   
    ofSetColor(100, 100, 100);
}

//--------------------------------------------------------------
void ofApp::keyPressed  (int key)
{
    ofLog(OF_LOG_VERBOSE, "%c keyPressed", key);
    //if (key == 'e')
    //{
    //}
    //@add for VideoGrabber

    if(key == 'd' || key == 'D'){
        debug = !debug;
    }
}

void ofApp::onCharacterReceived(KeyListenerEventData& e)
{
    keyPressed((int)e.character);
}


***********************************************

   5. bin/data 資料夾
        a. haarcascade_frontalface_default.xml // from ofxOpenCv

        b. totalSeconds.txt // record second
            0

        c. config.txt // config file
# debug (true/false)
false

# cam width (int)
320

# cam height (int)
240

# full screen (true/false)
false

# frame rate (int)
5

# concurrent visit (true/false)

false

# minimum width ratio of ROI(region of interest) (float)
0.125

# maximum width ratio of ROI(region of interest) (float)
0.5

# weight of ROI (float)
1.0

# recordFile (string)
totalSeconds.txt

Raspberry Pi: Real-Time Face Detection With USB Webcam & GPIO(I2C)

since: 2016/05/08
update: 2016/08/11
reference:
1. openframeworks
2. GitHub - kashimAstro/ofxGPIO
3. I touchs: Raspberry Pi: Connected Arduino Using I2C


A. 功能測試: opencvHaarFinderExample

    // 單張照片臉部辨識
    $ cd /home/pi/of_v0.9.3/examples/addons/opencvHaarFinderExample
    $ make
    $ ./bin/opencvHaarFinderExample    // 結果

-----------------------------------------------------------------------------------------------

B. 功能測試: videoGrabberExample
    // USB Web Cam
    $ cd /home/pi/of_v0.9.3/examples/video/videoGrabberExample
    $ make
    $ ./bin/videoGrabberExample

    // 結果

-----------------------------------------------------------------------------------------------

C. Face Detection With USB Webcam
    1. addons.make
        ofxOpenCv

***********************************************

    2. main.cpp
#include "ofMain.h"
#include "ofApp.h"

//========================================================================
int main( ){
    ofSetLogLevel(OF_LOG_VERBOSE);
   
    // read config file
    vector<string> _linesOfTheFile;
    ofBuffer _buffer = ofBufferFromFile("config.txt");
    for (auto line : _buffer.getLines()){
        _linesOfTheFile.push_back(line);
    }
   
    // local variables
    int _debug = ofToBool(_linesOfTheFile[1]);
    int _camWidth = ofToInt(_linesOfTheFile[4]);
    int _camHeight = ofToInt(_linesOfTheFile[7]);
    bool _fullScreen = ofToBool(_linesOfTheFile[10]);
   
    // show debug message
    if(_debug) {
        ofLog() << "****** main()";
        ofLog() << "debug: " << _debug;
        ofLog() << "OpenGL width: " << _camWidth;
        ofLog() << "OpenGL height: " << _camHeight;
        ofLog() << "fullScreen: " << _fullScreen;
    }

    // fullScreen
    if(_fullScreen) {
        // set camWidth & camHeight as window's
        ofSetupOpenGL(_camWidth, _camHeight, OF_FULLSCREEN);
    }
    // window
    else {
        // set camWidth & camHeight as window's
        ofSetupOpenGL(_camWidth, _camHeight, OF_WINDOW);
    }
   
    // this kicks off the running of my app
    // can be OF_WINDOW or OF_FULLSCREEN
    // pass in width and height too:

    ofRunApp(new ofApp());
}


***********************************************

    3. ofApp.h
#pragma once

#include "ofMain.h"
#include "ofxCvHaarFinder.h"

class ofApp : public ofBaseApp{

    public:
        void setup();
        void update();
        void draw();

        void keyPressed(int key);
        void keyReleased(int key);
        void mouseMoved(int x, int y );
        void mouseDragged(int x, int y, int button);
        void mousePressed(int x, int y, int button);
        void mouseReleased(int x, int y, int button);
        void mouseEntered(int x, int y);
        void mouseExited(int x, int y);
        void windowResized(int w, int h);
        void dragEvent(ofDragInfo dragInfo);
        void gotMessage(ofMessage msg);
   
        //@add for VideoGrabber
        ofVideoGrabber vidGrabber;

        //@add for CvHaarFinder
        //ofImage img;

        ofxCvHaarFinder finder;
   
        //@add second record
        float countROI;
        long readSeconds; // read from totalSeconds record file
        long writeSeconds; // write to totalSeconds record file
        long latestSeconds; // latestSeconds
   
        // config file
        bool debug;
        int camWidth;
        int camHeight;
        bool fullScreen;
        int frameRate;
        bool concurrentVisit;
        float minROI;
        float maxROI;
        float weightROI;
        string recordFile;
};


***********************************************

    4. ofApp.cpp
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    ofSetLogLevel(OF_LOG_VERBOSE);
   
    // read config file
    vector<string> linesOfTheFile;
    ofBuffer buffer = ofBufferFromFile("config.txt");
    for (auto line : buffer.getLines()){
        linesOfTheFile.push_back(line);
    }
    //for (int i = 0; i < linesOfTheFile.size(); i++) {
    //    ofLog(OF_LOG_VERBOSE, "cur.width = %s", linesOfTheFile[i].c_str());
    //}

   
    debug = ofToBool(linesOfTheFile[1]);
    camWidth = ofToInt(linesOfTheFile[4]);
    camHeight = ofToInt(linesOfTheFile[7]);
    fullScreen = ofToBool(linesOfTheFile[10]);
    frameRate = ofToInt(linesOfTheFile[13]);
    concurrentVisit = ofToBool(linesOfTheFile[16]);
    minROI = ofToFloat(linesOfTheFile[19]);
    maxROI = ofToFloat(linesOfTheFile[22]);
    weightROI = ofToFloat(linesOfTheFile[25]);
    recordFile = linesOfTheFile[28];
   
    if(debug)
    {
        ofLog() << "****** ofApp::setup()";
        ofLog() << "debug: " << debug;
        ofLog() << "camWidth: " << camWidth;
        ofLog() << "camHeight: " << camHeight;
        ofLog() << "fullScreen: " << fullScreen;
        ofLog() << "frameRate: " << frameRate;
        ofLog() << "concurrentVisit: " << concurrentVisit;
        ofLog() << "minROI: " << minROI;
        ofLog() << "maxROI: " << maxROI;
        ofLog() << "weightROI: " << weightROI;
        ofLog() << "recordFile: " << recordFile;
    }
   
    // read totalSeconds record file
    vector<string> totalSecondsFile;
    ofBuffer sbuffer = ofBufferFromFile(recordFile);
    for (auto line : sbuffer.getLines()){
        totalSecondsFile.push_back(line);
    }
   
    readSeconds = ofToFloat(totalSecondsFile[0]);
   
    if(debug)
    {
        ofLog() << "****** read totalSeconds record file";
        ofLog() << "readSeconds: " << readSeconds;
    }
   
    //ofSetFrameRate(5);
    ofSetFrameRate(frameRate);
   
    //@add for VideoGrabber
    //we can now get back a list of devices.

    vector<ofVideoDevice> devices = vidGrabber.listDevices();
   
    for(int i = 0; i < devices.size(); i++){
        if(devices[i].bAvailable){
            ofLogNotice() << devices[i].id << ": " << devices[i].deviceName;
        }else{
            ofLogNotice() << devices[i].id << ": " << devices[i].deviceName << " - unavailable ";
        }
    }
   
    vidGrabber.setDeviceID(0);
    //vidGrabber.setDesiredFrameRate(60);
    vidGrabber.setDesiredFrameRate(frameRate);
    vidGrabber.initGrabber(camWidth, camHeight);
    ofSetVerticalSync(true);
   
    //@add for CvHaarFinder
    //img.load("test.jpg");

    finder.setup("haarcascade_frontalface_default.xml");
    //finder.findHaarObjects(img);
}

//--------------------------------------------------------------
void ofApp::update(){
    //@add for VideoGrabber
    ofBackground(100, 100, 100);
    vidGrabber.update();
   
    //@add for CvHaarFinder
    if(vidGrabber.isFrameNew()){
        ofPixels & pixels = vidGrabber.getPixels();
        finder.findHaarObjects(pixels);
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
   
    //@add for VideoGrabber
    ofSetHexColor(0xffffff);
    vidGrabber.draw(0, 0);
   
    //@add for CvHaarFinder
    //img.draw(0, 0);

   
    ofNoFill();
    for(unsigned int i = 0; i < finder.blobs.size(); i++) {
       
        ofRectangle cur = finder.blobs[i].boundingRect;
       
        //if(debug){
        //    ofLog(OF_LOG_VERBOSE, "ROI width = %f", cur.width);
        //    ofLog(OF_LOG_VERBOSE, "ROI height = %f", cur.height);
        //}
       
        //if((cur.width >= 50.0 && cur.width <= 150.0) &&
        //   (cur.height >= 50.0 && cur.height <= 150.0)) {

        if((cur.width >= (minROI * camWidth) && cur.width <= (maxROI * camWidth)) &&
          (cur.height >= (minROI * camWidth) && cur.height <= (maxROI * camWidth))) {
           
            countROI++;
            ofSetColor(255, 0, 0); // red color
            ofDrawRectangle(cur.x, cur.y, cur.width, cur.height);
           
            if(debug) {
                ofSetColor(ofColor::yellow);
                ofDrawBitmapString("ROI = " + ofToString(cur.width) + " x " + ofToString(cur.height), camWidth/5, camHeight/1.02);
            }
           
            // not concurrent Visit
            if(!concurrentVisit){
                break;
            }
        }
        else {
            ofSetColor(0, 0, 255); // blue color
            ofDrawRectangle(cur.x, cur.y, cur.width, cur.height);
        }
    }
   
    // writeSeconds
    writeSeconds = readSeconds + (countROI / frameRate * weightROI); // weightROI: for calibrate detect missing
   
    if(writeSeconds > latestSeconds){
        // Write data
        if(debug) {
            // totalSeconds record file
            ofLog() << "****** write totalSeconds record file: " << writeSeconds;
        }
       
        ofBuffer secondsBuff;
        secondsBuff.set(ofToString(writeSeconds));
        bool fileWritten = ofBufferToFile(recordFile, secondsBuff);
       
        latestSeconds = writeSeconds;
    }

    if(debug){
        // show message
        ofSetColor(ofColor::white);
        ofDrawBitmapString("total seconds: " + ofToString(writeSeconds), camWidth/5, camHeight/1.2);
        ofDrawBitmapString(ofToString(minROI * camWidth) + " <= ROI <= " + ofToString(maxROI * camWidth), camWidth/5, camHeight/1.1);
    }
   
    ofSetColor(100, 100, 100);
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    //@add for VideoGrabber
    if(key == 'd' || key == 'D'){
        debug = !debug;
    }
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){

}


***********************************************

    5. bin/data 資料夾
        a. haarcascade_frontalface_default.xml // from ofxOpenCv

        b. totalSeconds.txt // record second
            0

        c. config.txt // config file
# debug (true/false)
false

# cam width (int)
320

# cam height (int)
240

# full screen (true/false)
false

# frame rate (int)
5

# concurrent visit (true/false)

false

# minimum width ratio of ROI(region of interest) (float)
0.125

# maximum width ratio of ROI(region of interest) (float)
0.5

# weight of ROI (float)
1.0

# recordFile (string)
totalSeconds.txt

-----------------------------------------------------------------------------------------------

* 無法正常作用(可能要配合 ofx 0.9.2) *
D. 利用 GPIO( or I²C: Inter-Integrated Circuit) 傳送資料給 Arduino
     1. 參考: I touchs: Raspberry Pi: Connected Arduino Using I2C
         > 讓 Raspberry Pi 的 I2C 功能啟用, 並且記下以下的資料

           $ ls /dev/i2c*
           /dev/i2c-1

           以及 Arduino 裡的定義:
          
#define SLAVE_ADDRESS 0x04
 
     2. 安裝 ofxGPIO
         $ cd /home/pi/of_v0.9.3/addons 
         $ git clone https://github.com/kashimAstro/ofxGPIO

     3. 修改專案的 addons.make 檔案如下:
         /home/pi/of_v0.9.3/apps/myApps/DPHeadTrackingUSB/addons.make
         ofxOpenCv
         ofxGPIO


     4.
修改 ofApp.h 如下:
/home/pi/of_v0.9.3/apps/myApps/DPHeadTrackingUSB/src/
ofApp.h
 
#pragma once

#include "ofMain.h"
#include "ofxCvHaarFinder.h"
//@add for GPIO
#include "ofxGPIO.h"

class ofApp : public ofBaseApp{

    public:
        void setup();
        void update();
        void draw();
        //@add for GPIO 
        //void exit();

        void keyPressed(int key);
        void keyReleased(int key);
        void mouseMoved(int x, int y );
        void mouseDragged(int x, int y, int button);
        void mousePressed(int x, int y, int button);
        void mouseReleased(int x, int y, int button);
        void mouseEntered(int x, int y);
        void mouseExited(int x, int y);
        void windowResized(int w, int h);
        void dragEvent(ofDragInfo dragInfo);
        void gotMessage(ofMessage msg);
   
        //@add for VideoGrabber
        ofVideoGrabber vidGrabber;

        //@add for CvHaarFinder
        //ofImage img;
        ofxCvHaarFinder finder;
   
        //@add second record
        float countROI;
        long readSeconds; // read from totalSeconds record file
        long writeSeconds; // write to totalSeconds record file
        long latestSeconds; // latestSeconds
   
        // config file
        bool debug;
        int camWidth;
        int camHeight;
        bool fullScreen;
        int frameRate;
        bool concurrentVisit;
        float minROI;
        float maxROI;
        float weightROI;
        string recordFile;

        //@add for I2C
        //I2CBus * bus;

        //@add for GPIO
        //GPIO ofxGPIO;
        GPIO* ofxGPIO;

};

 
     5.
修改 ofApp.cpp 如下:
/home/pi/of_v0.9.3/apps/myApps/DPHeadTrackingUSB/src/ofApp.cpp
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    ofSetLogLevel(OF_LOG_VERBOSE);

    // read config file
    vector<string> linesOfTheFile;
    ofBuffer buffer = ofBufferFromFile("config.txt");
    for (auto line : buffer.getLines()){
        linesOfTheFile.push_back(line);
    }
    //for (int i = 0; i < linesOfTheFile.size(); i++) {
    //    ofLog(OF_LOG_VERBOSE, "cur.width = %s", linesOfTheFile[i].c_str());
    //}
   
    debug = ofToBool(linesOfTheFile[1]);
    camWidth = ofToInt(linesOfTheFile[4]);
    camHeight = ofToInt(linesOfTheFile[7]);
    fullScreen = ofToBool(linesOfTheFile[10]);
    frameRate = ofToInt(linesOfTheFile[13]);
    concurrentVisit = ofToBool(linesOfTheFile[16]);
    minROI = ofToFloat(linesOfTheFile[19]);
    maxROI = ofToFloat(linesOfTheFile[22]);
    weightROI = ofToFloat(linesOfTheFile[25]);
    recordFile = linesOfTheFile[28];
   
    if(debug)
    {
        ofLog() << "****** ofApp::setup()";
        ofLog() << "debug: " << debug;
        ofLog() << "camWidth: " << camWidth;
        ofLog() << "camHeight: " << camHeight;
        ofLog() << "fullScreen: " << fullScreen;
        ofLog() << "frameRate: " << frameRate;
        ofLog() << "concurrentVisit: " << concurrentVisit;
        ofLog() << "minROI: " << minROI;
        ofLog() << "maxROI: " << maxROI;
        ofLog() << "weightROI: " << weightROI;
        ofLog() << "recordFile: " << recordFile;
    }
   
    // read totalSeconds record file
    vector<string> totalSecondsFile;
    ofBuffer sbuffer = ofBufferFromFile(recordFile);
    for (auto line : sbuffer.getLines()){
        totalSecondsFile.push_back(line);
    }
   
    readSeconds = ofToFloat(totalSecondsFile[0]);
   
    if(debug)
    {
        ofLog() << "****** read totalSeconds record file";
        ofLog() << "readSeconds: " << readSeconds;
    }
   
    //ofSetFrameRate(5);
    ofSetFrameRate(frameRate);
   
    //@add for VideoGrabber
    //we can now get back a list of devices.
    vector<ofVideoDevice> devices = vidGrabber.listDevices();
   
    for(int i = 0; i < devices.size(); i++){
        if(devices[i].bAvailable){
            ofLogNotice() << devices[i].id << ": " << devices[i].deviceName;
        }else{
            ofLogNotice() << devices[i].id << ": " << devices[i].deviceName << " - unavailable ";
        }
    }
   
    vidGrabber.setDeviceID(0);
    //vidGrabber.setDesiredFrameRate(60);
    vidGrabber.setDesiredFrameRate(frameRate);
    vidGrabber.initGrabber(camWidth, camHeight);
    ofSetVerticalSync(true);
   
    //@add for CvHaarFinder
    //img.load("test.jpg");
    finder.setup("haarcascade_frontalface_default.xml");
    //finder.findHaarObjects(img);

    //@add for GPIO
    //bus = new I2CBus("/dev/i2c-1");
    //bus->addressSet(0x04);


    //@add for GPIO (pin) 
    //ofxGPIO.setup("7");
    //ofxGPIO.export_gpio();
    //ofxGPIO.setdir_gpio("out");

    ofxGPIO = new GPIO("7");
    ofxGPIO->export_gpio();
    ofxGPIO->setdir_gpio("out");

}

//--------------------------------------------------------------
void ofApp::update(){
    //@add for VideoGrabber
    ofBackground(100, 100, 100);
    vidGrabber.update();
   
    //@add for CvHaarFinder
    if(vidGrabber.isFrameNew()){
        ofPixels & pixels = vidGrabber.getPixels();
        finder.findHaarObjects(pixels);
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
   
    //@add for VideoGrabber
    ofSetHexColor(0xffffff);
    vidGrabber.draw(0, 0);
   
    //@add for CvHaarFinder
    //img.draw(0, 0);
   
    ofNoFill();
    for(unsigned int i = 0; i < finder.blobs.size(); i++) {
       
        ofRectangle cur = finder.blobs[i].boundingRect;
       
        //if(debug){
        //    ofLog(OF_LOG_VERBOSE, "ROI width = %f", cur.width);
        //    ofLog(OF_LOG_VERBOSE, "ROI height = %f", cur.height);
        //}
       
        //if((cur.width >= 50.0 && cur.width <= 150.0) &&
        //   (cur.height >= 50.0 && cur.height <= 150.0)) {
        if((cur.width >= (minROI * camWidth) && cur.width <= (maxROI * camWidth)) &&
          (cur.height >= (minROI * camWidth) && cur.height <= (maxROI * camWidth))) {
           
            countROI++;
            ofSetColor(255, 0, 0); // red color
            ofDrawRectangle(cur.x, cur.y, cur.width, cur.height);
           
            if(debug) {
                ofSetColor(ofColor::yellow);
                ofDrawBitmapString("ROI = " + ofToString(cur.width) + " x " + ofToString(cur.height), camWidth/5, camHeight/1.02);
            }
           
            // not concurrent Visit
            if(!concurrentVisit){
                break;
            }
        }
        else {
            ofSetColor(0, 0, 255); // blue color
            ofDrawRectangle(cur.x, cur.y, cur.width, cur.height);
        }
    }
   
    // writeSeconds
    writeSeconds = readSeconds + (countROI / frameRate * weightROI); // weightROI: for calibrate detect missing
   
    if(writeSeconds > latestSeconds){
        // Write data
        if(debug) {
            // totalSeconds record file
            ofLog() << "****** write totalSeconds record file: " << writeSeconds;
        }
       
        ofBuffer secondsBuff;
        secondsBuff.set(ofToString(writeSeconds));
        bool fileWritten = ofBufferToFile(recordFile, secondsBuff);
       
        latestSeconds = writeSeconds;

        //@add for GPIO
        //bus->writeByte(0x04, writeSeconds);
        //usleep(50000); // sleep for 0.05 second

        //@add for test

        /*
        bus->writeByte(0x04,1);
        usleep(500000); // sleep for 0.5 second
        bus->writeByte(0x04,0);
        usleep(500000); // sleep for 0.5 second  

        */  

        //@add for GPIO
        //ofxGPIO.setval_gpio("1");
        //usleep(50000); // sleep for 0.05 second
        //ofxGPIO.setval_gpio("0");
        //usleep(50000); // sleep for 0.05 second

        ofxGPIO->setval_gpio("1");
        usleep(50000); // sleep for 0.05 second
        ofxGPIO->setval_gpio("0");
        usleep(50000); // sleep for 0.05 second

    }

    if(debug){
        // show message
        ofSetColor(ofColor::white);
        ofDrawBitmapString("total seconds: " + ofToString(writeSeconds), camWidth/5, camHeight/1.2);
        ofDrawBitmapString(ofToString(minROI * camWidth) + " <= ROI <= " + ofToString(maxROI * camWidth), camWidth/5, camHeight/1.1);
    }
   
    ofSetColor(100, 100, 100);
}

/*
//--------------------------------------------------------------
void ofApp::exit(){
   
    //@add for GPIO
    ofxGPIO.unexport_gpio();
}

*/

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    //@add for VideoGrabber
    if(key == 'd' || key == 'D'){
        debug = !debug;
    }
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){
}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){
}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){
}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){
}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){
}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){
}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){
}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){
}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){
}

 
     6. 編譯與執行:
         $ cd /home/pi/of_v0.9.3/apps/myApps/DPHeadTrackingUSB
         $ make         

         $ ./bin/DPHeadTrackingUSB

     7. 開機後自動執行: (console 的訊息會出不來, 建議改成在 .bashrc 裡執行程式)
         $ cd
         $ touch startHeadTracking.sh
         $ chmod 755 startHeadTracking.sh

         $ sudo nano startHeadTracking.sh
#!/bin/bash
sleep 5
cd /home/pi/of_v0.9.3/apps/myApps/DPHeadTrackingUSB
./bin/DPHeadTrackingUSB


         $ cat startHeadTracking.sh

         $ sudo crontab -e
         ....
         @reboot /home/pi/startHeadTracking.sh

         $ sudo crontab -l