紅外線體感互動感溫平台

by 7 月 8, 2021開發筆記0 comments

硬體:

  • 紅外線攝影機 (波長850NM)
  • 紅外線投射燈 (波長850NM)
  • 紅外線濾光片
  • 具有無線功能的電腦主機

軟體:

  • Processing 3
  • VSCode

在新冠疫情之下紅外線的感測功能幾乎是各大活動場所必備的道具,利用感測元件可以即時的測量體溫,於此同時也改變了傳統FLIR多用於工程相關感測的環境。但一般市面上紅外線攝影感測方式會延伸出隱私權,被測者觀感不佳等相關問題

利用紅外線感測元件結合即時影像處理平台,可製作一具有即時互動效果且自動馬塞克無隱私疑慮之紅外線人流過濾系統。

一般紅外線攝影模組

更換850nm濾光片

效果示意:
利用紅外線偵測模組感測出高溫處並將溫度顯示,設定溫度閘道以過濾人流。

結合實作雲端平台API,可利用此API與使用者作即時互動,如留言版功能

影像處理原始碼

// 視傳系 37.5 畢展
// 使用紅外線攝影機,利用 brighness track 抓取出最白區塊
// 在最白區塊上亂數溫度
// 像素化處理
// 

//import http.requests.*;


import processing.video.*;


PFont f, sloganFont, msgFont;
int videoScale = 10;
int cols, rows, contrast, brightnessThrehold;
Capture video;
float textX, textY, sloganX, sloganY;
float aimX =0;
float aimY =0;
int timer = 0, sloganIndex = 0;
int countDown = 2000;
float degree = 0;

boolean showText, showRect, invert = false;

String degreeNF = "0", msg = "初始化";
String[] get10msg;

float posY = 0.0;

int cameraIndex = 0;
void setup() { 
  
  smooth();
  msg = getMsg();
  get10msg = get10msg();
  //GetRequest linhas = new GetRequest("http://localhost:8000/api/GetLastContent");
  //linhas.send();
  
  f = createFont("digitalism.ttf", 50);
  sloganFont = createFont("NotoSansCJKtc-Medium.otf", 35);
  msgFont = createFont("NotoSansCJKtc-Medium.otf", 20);
  textFont(f);
  textX = 0;
  textY = 0;
  sloganX= 0;
  sloganY = 0;
  contrast = 0;
  brightnessThrehold = 2;
  showText= true;


  background(0);
  //size(1366, 768); 
  fullScreen();

  cols = width / videoScale;  
  rows = height / videoScale;  

  printArray(Capture.list());
  //video = new Capture(this, Capture.list()[1]);
  //video = new Capture(this, cols, rows); 
  println("camera count: "+Capture.list().length);
  if(Capture.list().length > 1){
    cameraIndex = 1;
  }else{
    cameraIndex = 0;
  }
  video = new Capture(this, cols, rows, Capture.list()[cameraIndex]);
  video.start();

}

void captureEvent(Capture video) {  
  video.read();
}

void keyPressed() {
  if (key == '1') {
    brightnessThrehold = 2;
  }
  if (key == '2') {
    brightnessThrehold = 3;
  }
  if (key == '3') {
    brightnessThrehold = 4;
  }
  if (key == 't') {
    showText = !showText;
  }
  if (key == 'i') {
    invert = !invert;
  }
  if (key == 'r') {
    showRect = !showRect;
  }
  
  if(key == 'l'){
    if(countDown <= 10000){
      countDown += 2000;
    }else{
      countDown = 2000;     
    }
    text("更新時間: "+countDown, 0,0);
  }


  if (key == 'c') {
    if (contrast < 120) {
      contrast += 20;
    } else {
      contrast = 0;
    }
  }
  if (key == CODED) {

    video.stop();
    if (keyCode == UP) {
      if (videoScale < 100) {
        videoScale += 5;
      }
    } else if (keyCode == DOWN) {
      if (videoScale > 5) {
        videoScale -= 5;
      }
    }
    cols = width / videoScale;  
    rows = height / videoScale;  
    // Construct the Capture object  
    video = new Capture(this, cols, rows, Capture.list()[cameraIndex]);  
    video.start();
  } else {
  }
}




void drawText(float x, float y)
{ 
  if (millis()-timer > countDown) {
    degree = random(35, 38.5);
    degreeNF = nf(degree, 0, 1);
    timer = millis();
    msg = getMsg();
    get10msg = get10msg();
  }
  textFont(f);
 fill(0);
  text(degreeNF+".C", x+50+2, y-20+2);
  if (degree > 37.5) {
    fill(255, 0, 0);
  } else {
    fill(255, 255, 255);
  }
  
  text(degreeNF+".C", x+50, y-20);
 
}

void slogan(float x, float y) {
  textFont(sloganFont);
  fill(0, 0, 0, 100);
  text(msg, x+50+2, y+20+2);
  fill(255, 255, 255);
  text(msg, x+50, y+20);
}




void mosaicVideo() {
  video.loadPixels(); 
  //int index = 0;
  int brightestX = 0; // X-coordinate of the brightest video pixel
  int brightestY = 0; // Y-coordinate of the brightest video pixel
  float brightestValue = 0;

  for (int i = 1; i < cols; i++) {    
    // Begin loop for rows    
    for (int j = 0; j < rows; j++) {      

      int x = i*videoScale;      
      int y = j*videoScale;

      //reverse
      int loc = (video.width - i - 1) + j * video.width;   

      //normal
      // int loc = i + j*video.width;   

      //color c = video.pixels[i + j*video.width];
      color c = video.pixels[loc];

      //handel Brightness
      int pixelValue = video.pixels[loc];
      float pixelBrightness = brightness(pixelValue);
      if (pixelBrightness > brightestValue) {
        brightestValue = pixelBrightness;
        brightestY = y;
        brightestX = x;
      }


      int leftLoc = loc;
      color leftPix = video.pixels[leftLoc];
      if (red(c) + green(c) + blue(c) < 120) {
        c = color(red(c)-contrast, green(c)-contrast, blue(c)-contrast);
      } else {
        c = color(red(c)+contrast, green(c)+contrast, blue(c)+contrast);
      }

      float diff;
      if (invert) {
        diff = abs(brightness(c*brightnessThrehold) - brightness(leftPix));
      } else {
        diff = abs(brightness(c));
      }


      float threshold = 1;
      //float red2white = red(c);
      color bwC = color(diff*threshold, diff*threshold, diff*threshold, 50);

      fill(bwC);   
      noStroke();
      strokeWeight(0.5);
      stroke(diff*1.2, diff*1.2, diff*1.2, 70);      
      rect(x, y, videoScale, videoScale);
    }
  }
  //draw brightness Track
  //noFill();
  //textX = brightestX;
  textX = lerp(textX, brightestX, 0.2);
  textY = lerp(textY, brightestY, 0.2);
  strokeWeight(3);
  stroke(255, 0, 0);
  rectMode(CENTER);
  //rect(brightestX, brightestY, 100, 100);
}


void draw() {  
  logo();
  cursorPos(textX, textY);
  
  mosaicVideo();
  if (showText)
  {
    drawText(textX, textY);
    slogan(textX, textY);
    cursor(textX, textY);

    aimX = lerp(aimX, textX, 0.8);
    aimY = lerp(aimY, textY, 0.8);
    aim(aimX, aimY);
    
  }
  if (showRect)
  {
    drawRect(textX, textY);
  }
  //filter(ERODE);
  if(textX < 100 || textY < 100){
    showText = false;
  }else{
    showText = true;
  }
  //posY = 0;
  //text(posY);
  
  leftMsg(get10msg);


  if(posY > -600){
    posY -= random(10, 30);
  }else{
    posY = 0;
  }
  
  

}