画像処理のサンプル(difference_circle)

人が動いた後にいろいろなエフェクトがかかるサンプル。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import processing.video.*;
 
Capture video;    //キャプチャ用変数
int[] previousFrame;    //一つ前のフレーム
int pixelNum;    //キャプチャ画面の総ピクセル数
int noiseFilter = 50;    //ノイズを拾わないためのフィルタ(値を増やすとフィルタが強くかかる)
int reduction = 8;    //画面に対してのキャプチャ映像の縮小度合い 8の場合は、キャプチャ画像が画面の1/8という意味になる
int movementSum; //一つのフレームでの動作量
float[] eSize;    //円のサイズ
float[] eSpeed;    //円の拡大のスピード
float[] eAlpha;    //円の透明度
float[] eAlphaSpeed;    //円の透明度のスピード
int[] eFlag;    //円の透明度が0になったかどうかを判断するためのフラッグ
 
void setup() {
  // 640 x 480が大きすぎて遅かったら、320 x 240に変更する
  size(640, 480);
 
  //画面サイズのキャプチャ画像を生成。画面がコマ落ちするようだったら、
  //一番右のフレームレートを上げる。
  //この場合は、80x60のキャプチャ画像、90フレーム/秒を設定
  video = new Capture(this, width/reduction, height/reduction, 90);
  pixelNum = video.width * video.height;    //キャプチャ画像の総ピクセル数
  previousFrame = new int[pixelNum];    //一つ前のフレーム(配列)
  eSize = new float[pixelNum];    //円の大きさ
  eSpeed = new float[pixelNum];    //円の拡大スピード
  eAlpha = new float[pixelNum];    //円の透明度
  eAlphaSpeed = new float[pixelNum];    //円の透明化のスピード
  eFlag = new int[pixelNum];    //円が表示されている状態かそうでないかを判断するフラッグ
 
  //円の属性の初期化
  for(int i = 0; i < pixelNum; i ++){
    eSize[i] = 0.0;
    eSpeed[i] = 1.0;
    eAlpha[i] = 200.0;
    eAlphaSpeed[i] = 2.0;
    eFlag[i] = 0;
  }
  loadPixels();    //画面に画像のピクセルを展開
}
 
void draw() {
  background(0);
  float m = millis();    //時間をカウント
  if (video.available()) {  //もしキャプチャができたら、
    video.read(); //ビデオフレームの読み込み
    video.loadPixels(); //ビデオのピクセルを操作できるようにする
 
    movementSum = 0; //一つのフレームでの動作量
 
    //1ピクセルごとに色を調べる。
    for (int y = 0; y < video.height; y ++) {
      for (int x = 0; x < video.width; x ++) {
        //ビデオのピクセルを抜き出す
        int currColor = video.pixels[y*video.width + x];
        int prevColor = previousFrame[y*video.width + x];
 
        //現在のピクセルのR, G, Bを抜き出す
        int currR = (currColor >> 16) & 0xFF;
        int currG = (currColor >> 8 ) & 0xFF;
        int currB = currColor & 0xFF;
 
        //一つ前のフレームの色を抜き出す
        int prevR = (prevColor >> 16) & 0xFF;
        int prevG = (prevColor >> 8 ) & 0xFF;
        int prevB = prevColor & 0xFF;
 
        //現在のピクセルから前のピクセルの色を引いた絶対値
        int diffR = abs(currR - prevR);
        int diffG = abs(currG - prevG);
        int diffB = abs(currB - prevB);
 
        //noiseFilterの値よりも大きかったらmovementSumに足していく
        //そして、現在の色に更新
        //以下の条件文は3秒後に有効になる
        if(diffR + diffG + diffB > noiseFilter && eFlag[y*video.width + x] == 0 && m > 3*1000){
          eFlag[y*video.width + x] = 1;    //フラッグを1にする(円が再生中)
          movementSum ++;
 
          //以下の4行のコードは動いたところにラインを発生させるコードである(左右を反転させている)
          float lineSize = (float)(diffR + diffG + diffB)/(255*3)*20.0;
          stroke(currR, 100, 255, 80);
          line(((video.width-x)-lineSize)*reduction, y*reduction, ((video.width-x)+lineSize)*reduction, y*reduction);
          stroke(255, 100, currB, 80);
          line((video.width-x)*reduction, (y-lineSize/2)*reduction, (video.width-x)*reduction, (y+lineSize/2)*reduction);
        }
 
        //透明度(Alpha)が0.0未満にならない限りは、円はひたすら大きくなり、透明度はひたすら下がる
        if(eFlag[y*video.width + x] == 1 && eAlpha[y*video.width + x] >= 0.0){
          eSize[y*video.width + x] += eSpeed[y*video.width + x];
          eAlpha[y*video.width + x] -= eAlphaSpeed[y*video.width + x];
 
          noStroke();
          fill(currR, currG, currB, (int)(eAlpha[y*video.width + x]));
          ellipse((video.width-x)*reduction, y*reduction, eSize[y*video.width + x],  eSize[y*video.width + x]);
 
        }    //透明度(Alpha)が0.0未満になったら、円の属性をリセットする
        else if(eFlag[y*video.width + x] == 1 && eAlpha[y*video.width + x] < 0.0){
          eSize[y*video.width + x] = 0.0;
          eAlpha[y*video.width + x] = 100.0;
          eFlag[y*video.width + x] = 0;
        }
 
        previousFrame[y*video.width + x] = currColor;    //現在の色を一つ前のフレームの色として保存
      }
    }
    textSize(12);  //テキストのサイズ
    fill(255);
    text(movementSum, 30,30);  //変化したピクセルの総数を画面にプリント
  }
}