欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

互动油画——Processing和Kinect联合实现

程序员文章站 2022-04-16 21:29:44
...

项目总结

作为大二上学期的合作项目,总结一下制作过程和代码
我们完成了一个模拟笔刷画油画的效果,并最终用投影的形式表现出来。其中笔刷的频率会根据人的位置(Kinect捕捉)和音乐的频率变化。
我主要参与了创意策划和负责代码中笔刷的优化。
主要过程是,先找了一些现成的代码,按照代码分析功能,学习Processing,然后不断修改得到了我们想要的效果。
在此过程中,遗憾的是也有一些效果无法实现,部分是由于Processing的自身限制。所以想要达到更好的效果,还需要配合其他软件,如TouchDesigner。

主要参考网站:
有很多开源的项目 https://www.openprocessing.org/
p5.js的官方指南 https://p5js.org/reference/

效果图

互动油画——Processing和Kinect联合实现

代码部分

说明:

使用processing3.5.3直接打开,需要安装minim和Kinect库

结构描述:
读取图片(放在同一个文件夹下,需要名字和String[] imgNames里面对应)-> 启动Kinect/播放音乐 -> Kinect获取脸部坐标数据/获取音乐音量 -> 根据脸部坐标数据切换底层图片/根据音量大小确定笔触大小 -> 根据笔触算法(paintStroke函数)随机在屏幕上生成笔触 -> 等待画面逐渐完成

代码如下:

String[] imgNames = {"portrait_1.png", "portrait_2.png", "portrait_3.png"};
PImage img;
int imgIndex = 1;

int headX;
int headY;
int headZ;
int headXdis;

int headXmax = 900;
int headXmin = 900;
int headYmax = 500;
int headYmin = 500;
int time = 0;
int preTime = 0;

import ddf.minim.*;
import ddf.minim.effects.*;

Minim minim;
AudioPlayer air;

import KinectPV2.KJoint;
import KinectPV2.*;
KinectPV2 kinect;

void nextImage() {
  loop();
  frameCount = 0;
  
  img = loadImage(imgNames[imgIndex]);
  //img.resize(width / 2 / 4 * 3, height / 4 * 3);
  //img.resize(width, height);
  img.loadPixels();
  
  imgIndex += 1;
  if (imgIndex >= imgNames.length) {
    imgIndex = 0;
  }
}


void paintStroke(float strokeLength, color strokeColor, int strokeThickness) {
  float stepLength = strokeLength/4.0;
  
  // Determines if the stroke is curved. A straight line is 0.
  float tangent1 = 0;
  float tangent2 = 0;
  
  float odds = random(1.0);
  
  if (odds < 0.9) {
    //tangent1 = random(-hue(strokeColor), hue(strokeColor));
    //tangent2 = random(-hue(strokeColor), hue(strokeColor));
    tangent1 = random(-1, 1);
    tangent2 = random(-1, 1);
  } 
  
  // Draw a big stroke
  noFill();
  stroke(strokeColor);
  //strokeWeight(strokeThickness);
  //bezier(tangent1, -stepLength*2, 0, -stepLength, 0, stepLength, tangent2, stepLength*2);
  
  int z = 1;
  
  // Draw stroke's details
  for (int num = strokeThickness; num > 0; num --) {
    float offset = random(-10, 10);
    color newColor = color(hue(strokeColor)+offset, saturation(strokeColor)+offset, brightness(strokeColor)+offset, random(50, 150));
    
    stroke(newColor);
    strokeWeight((int)random(0, 10));
    float curveT=random(-2, 2);
    curveTightness(curveT);
    curve(tangent1, -stepLength*2, z-strokeThickness/2, -stepLength*random(0.9, 1.1), z-strokeThickness/2, stepLength*random(0.9, 1.1), tangent2, stepLength*2);
    //bezier(tangent1, -stepLength*2, z-strokeThickness/2, -stepLength*random(0.9, 1.1), z-strokeThickness/2, stepLength*random(0.9, 1.1), tangent2, stepLength*2);
    z += 1;
  }
}


void setup() {
  //size(1280, 720);
  fullScreen();
  background(0);
  frameRate(30);
  noCursor();
  nextImage();
  
  colorMode(HSB);
  
  minim = new Minim(this);
  air = minim.loadFile("air.mp3", 2048);
  air.loop();
  
  kinect = new KinectPV2(this);

  kinect.enableSkeletonColorMap(true);
  kinect.enableColorImg(true);

  kinect.init();
}


void draw() {
  //translate(width/4, height/2);
  //background(img);
  translate(width / 2, height / 2);
  
  ArrayList<KSkeleton> skeletonArray =  kinect.getSkeletonColorMap();
  for (int i = 0; i < skeletonArray.size(); i++) {
    KSkeleton skeleton = (KSkeleton) skeletonArray.get(i);
    if (skeleton.isTracked()) {
      KJoint[] joints = skeleton.getJoints();

      color col  = skeleton.getIndexColor();
      fill(col);
      stroke(col);
     
      //draw different color for each hand state
      drawHandState(joints[KinectPV2.JointType_Head]);
       
    } 
  }
  
  int index = 0;
  for (int y = 0; y < height; y+=1) {
    for (int x = 0; x < width; x+=1) {
      int odds = (int)random(80000);
      //int odds = 0 ;
      if (odds < 1) {
        //println(odds);
        color pixelColor = img.pixels[index];
        headXdis = Math.abs(headX-1075);
        pixelColor = color(hue(pixelColor), saturation(pixelColor), brightness(pixelColor), 10);
        
        pushMatrix();
        translate(x-img.width/2, y-img.height/2);
        println("headX:" + headX);
        println("headY:" + headY);
        println("headXdis:" + headXdis);
        rotate(radians(hue(pixelColor) + headX));
        
        // Paint by layers from rough strokes to finer details
        int sound = (int)(500 * air.left.get((int)random(2048)));
        if(sound < 0){
          sound = -sound;
        }
        
        paintStroke((int)random(sound + 1, sound + 5), pixelColor, (int)random(sound / 10 + 1, sound / 10 + 5));
        //paintStroke((int)random(headXdis / 5 + 2, headXdis / 5 + 10), pixelColor, (int)random(headXdis / 50 + 1, headXdis / 50 + 5));
        popMatrix();
      }
      
      index += 1;
    }
  }
  
  if(frameCount % 60 == 0){
    frameCount = 0;
    if (headXdis > 150) {
      if (headX < 1075){
        time = 0;
        if(time != preTime){
          img = loadImage(imgNames[0]);
          //img.resize(width / 2 / 4 * 3, height / 4 * 3);
          //img.resize(width, height);
          img.loadPixels();
          time = 0;
          preTime = time;
        }
      
      }
      else if(headX > 1075){
        time = 2;
        if(time != preTime){
          img = loadImage(imgNames[2]);
          //img.resize(width / 2 / 4 * 3, height / 4 * 3);
          //img.resize(width, height);
          img.loadPixels();
          time = 2;
          preTime = time;
        }
      
      }
    }
    else{
        time = 1;
        if(time != preTime){
          img = loadImage(imgNames[1]);
          //img.resize(width / 2 / 4 * 3, height / 4 * 3);
          //img.resize(width, height);
          img.loadPixels();
          time = 1;
          preTime = time;
        }
      
    }
  }
  
}


void mousePressed() {
  nextImage();
}

void drawHandState(KJoint joint) {
  headX = int(joint.getX());
  headY = int(joint.getY());
}