2014/06/28

Youtube 影片連結: 大眼仔, 人群追蹤.
今天要來為大眼仔系列文章做個完結. :-) 


靠著 Philip Zheng 的幫忙, 大眼仔總算做到人群偵測, 可以迎著人來人往了. 和人臉追蹤的情境 (用戶停駐在 webcam 前面觀看螢幕) 不太一樣, 人群偵測的情境比較會是在開放空間, 有複數的人群走來走去. 這邊問題就來了..

  •  大眼仔該盯著誰看呢?
萊恩大兵的設計, 是將空間分成五個區塊, 然後分類每個人像框落在哪一區塊裡 (考慮胖瘦的差異, 會以人像框的中線為基準線). 大眼仔的目光就投向人最多的區塊.


在講程式的實作前, 先聊一下心得與未來還可改善的地方. (怕寫在最後沒人看. XD)


電源線的改良

雖然萊恩大兵已尊奉這條線為神器了, 還是要講一下

以複數 Arduino 板子(透過 I2C 串聯在一起), 再揹複數大眼仔的使用情境來看, 這條電源線得做到:
  • 供應 (Arduino + 大眼仔*n)*m 所需的 5V 電源. 
  • 電源線的長度須能適應大眼仔的擺置. 
    再長一點, 再長一點.
  • 複數 Arduino 板子之間, 是用 I2C 串聯在一起的, 這會需要另一組的電源供應 (3.3V).


大眼仔機構改良

可考慮改良機構, 讓大眼仔彼此可以組合或推疊, 擺置起來會更有變化.
雖然有千軍萬馬的感覺, 可是, 沒有陣型啊..



程式 (老問題)


  •  偵測的敏感度 (both 人臉追蹤 and 人群偵測) 待提升.
  •  左右反相的問題.



實作細節

  1. 將大眼仔相對 web cam 的位置, 分成五種級距 (左二, 左一, 中, 右一, 右二).
  2. 將空間分五區塊 (左二, 左一, 中, 右一, 右二), 再把 Processing + OpenCV 傳回來的人像框分類在適當的區塊裡, 最後找出哪個區塊人最多. 
  3. 定義不同位置的大眼仔如何回應人臉框位置. 
    不同位置的大眼仔, 轉頭的角度會有不同.
  4. 線路的佈建, 也是用兩片 Arduino 板子, 每片板子接兩個大眼仔, 各定義其位置為左二, 左一與右一, 右二. (兩片板子則透過 I2C 溝通). 
    (Arduino + 大眼仔*2) * 2


程式碼

(Arduino/ master and slave)
請參考 108 大眼仔, 進化, (4) 看著我的臉 (Arduino, SG90, Animatronic Eye, OpenCV, Processing, I2C)

(Processing + OpenCV)


import processing.serial.*;
import processing.video.*;
 
import java.util.*;
import java.nio.*;
 
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.CvType;
import org.opencv.core.Scalar;
import org.opencv.objdetect.HOGDescriptor;
import org.opencv.core.MatOfRect;
import org.opencv.core.MatOfDouble;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
 
Serial myPort;  // Create object from Serial class
String cmdstring = "";
int timeInterval = 500;
int lastUpdate;

Capture cap;
PImage small;
HOGDescriptor hog;
 
byte [] bArray;
int [] iArray;
int pixCnt1, pixCnt2;
int w, h;
float ratio;
int [] zones = {0, 0, 0, 0, 0}; // for calculating # of people in a zone 
 
void setup() {
  size(640, 480);
  ratio = 0.5;
  w = int(width*ratio);
  h = int(height*ratio);
 
  background(0);
  // Define and initialise the default capture device.
  cap = new Capture(this, width, height);
  cap.start();
 
  // Load the OpenCV native library.
  System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  println(Core.VERSION);
 
  // pixCnt1 is the number of bytes in the pixel buffer.
  // pixCnt2 is the number of integers in the PImage pixels buffer.
  pixCnt1 = w*h*4;
  pixCnt2 = w*h;
 
  // bArray is the temporary byte array buffer for OpenCV cv::Mat.
  // iArray is the temporary integer array buffer for PImage pixels.
  bArray = new byte[pixCnt1];
  iArray = new int[pixCnt2];
 
  small = createImage(w, h, ARGB);
  hog = new HOGDescriptor();
  hog.setSVMDetector(HOGDescriptor.getDefaultPeopleDetector());
  noFill();
  stroke(255, 255, 0);
  
  myPort = new Serial(this, Serial.list()[5], 9600);
  lastUpdate = millis() - timeInterval;
}
 
void draw() {
  if (cap.available()) {
    cap.read();
  } 
  else {
    return;
  }
  image(cap, 0, 0);
  // Resize the video to a smaller PImage.
  small.copy(cap, 0, 0, width, height, 0, 0, w, h);
  // Copy the webcam image to the temporary integer array iArray.
  arrayCopy(small.pixels, iArray);
 
  // Define the temporary Java byte and integer buffers. 
  // They share the same storage.
  ByteBuffer bBuf = ByteBuffer.allocate(pixCnt1);
  IntBuffer iBuf = bBuf.asIntBuffer();
 
  // Copy the webcam image to the byte buffer iBuf.
  iBuf.put(iArray);
 
  // Copy the webcam image to the byte array bArray.
  bBuf.get(bArray);
 
  // Create the OpenCV cv::Mat.
  Mat m1 = new Mat(h, w, CvType.CV_8UC4);
 
  // Initialise the matrix m1 with content from bArray.
  m1.put(0, 0, bArray);
  // Prepare the grayscale matrix.
  Mat m3 = new Mat(h, w, CvType.CV_8UC1);
  Imgproc.cvtColor(m1, m3, Imgproc.COLOR_BGRA2GRAY);
 
  MatOfRect found = new MatOfRect();
  MatOfDouble weight = new MatOfDouble();
 
  hog.detectMultiScale(m3, found, weight, 0, new Size(8, 8), new Size(32, 32), 1.05, 2, false);
 
  int idx = 0;
  Rect [] rects = found.toArray();
  int num = rects.length;
  if (num > 0) {
    if (millis()-lastUpdate >= timeInterval) {
      for (int i=0; i<num; i  ) {   

        // which zone the people belongs to?
        float x = (rects[i].x   rects[i].width/2) / ratio;   
        if (x <= 128)
          zones[0]  ;
        else if (x > 128 && x <= 256)
          zones[1]  ;
        else if (x > 256 && x <= 384)
          zones[2]  ;
        else if (x > 384 && x <= 512)
          zones[3]  ;
        else
          zones[4]  ;
      }

      // which zone has the most people
      for (int i=0; i<5; i  ) {
        if (zones[i] >= zones[idx]) 
          idx = i;
      }

      // inform Arduino
      cmdstring = "s,1,p,"   (idx-2)   "\n";
      print(cmdstring);
      myPort.write(cmdstring);

      // reset zones[]
      for (int i=0; i<5; i  )
        zones[i] = 0;

      lastUpdate = millis();
    }

    for (int i=0; i<rects.length; i  )      
      rect(rects[i].x/ratio, rects[i].y/ratio, rects[i].width/ratio, rects[i].height/ratio);
  }
  text("Frame Rate: "   round(frameRate), 500, 50);
}


Okay, 就是這些了. 期待有一天大眼仔能重登舞台.


[萊恩大兵的其它文章]

自製大四軸

自製大四軸, (1) 零組件篇, 遙控器 (Drone, Quadcopter, Futaba, Maker, Arduino, Animatronic Eye)
自製大四軸, (2) 零組件篇, 飛控板 (Drone, Quadcopter, MultiWii, Arduino, Futaba, Maker)
自製大四軸, (3) 零組件篇, 自行雷切木質機架 (Drone, Quadcopter, Maker, Laser Cut)
自製大四軸, (4) 零組件篇, 馬達與電變調整 (Drone, Quadcopter, Maker, Electric Speed Control, Motor)
自製大四軸, (5) 組裝篇, 四軸飛行器成形 (Drone, Quadcopter, MultiWii, Arduino, Maker, Electric Speed Control, Motor)
自製大四軸, (6) 調整篇, 飛行前兩三事 (Drone, Quadcopter, Maker, Futaba, Arduino, MultiWii)
自製大四軸, (7) 充電篇, iMax B6 充電器操作記要 (Charger, Battery)

自動報球速的棒球



CC2540 Bluetooth Low Energy
筆記, CC2540 Bluetooth Low Energy, (1) 開發環境 架設 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (2) 跑第一個範例程式 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (3) SimpleBLEPeripheral 簡單介紹 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (4) 在智慧手機上執行範例程式 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (5) 偵測與發送 iBeacon 訊號 (Bluetooth, CC2540, iBeacon)
實作, iBeacon 發訊器 x 防丟器 (Bluetooth, CC2540, iBeacon)
實作, iBeacon 尋寶遊戲 (Bluetooth, CC2540, iBeacon, iOS app)
實作, BLE + iOS app, 遙控燈泡君 (Bluetooth, CC2540, iOS app)
做實驗, 用 iBeacon 做自動控制的可行性 (Bluetooth, iBeacon, CC2540, Automation, URL Scheme, iOS app)

藍色小鋪一起來做

藍色小鋪一起來做, (1) 用 beacon 控制開關的枱燈
藍色小鋪一起來做, (2) 講解 BLE CC2540 UART 通訊範例程式 (Bluetooth, CC2540, UART)
藍色小鋪一起來做, (3) 藍牙枱燈專案實作 (上) (Bluetooth, CC2540)
藍色小鋪一起來做, (4) 藍牙枱燈專案實作 (下) (Bluetooth, CC2540)
藍色小鋪一起來做, (5) iBeacon scanner 專案示範與解說 (Bluetooth, CC2540, iBeacon)
藍色小鋪一起來做, (6) 完成, 用 iBeacon 控制開關的枱燈 (Bluetooth, CC2540, iBeacon)

小惡魔 無線溫度感測器

108 大眼仔
Plot Clock
體驗, 原住民互動故事書@宜蘭大同鄉泰雅生活館
體驗, 蛋生音互動裝置@兒童美術館 (Arduino, 3D Printing, HC-SR04, Interactive)



實作, 電容感應音樂樹

0 意見:

張貼留言