本篇是 Processing 速写100天计划 的第7天Day_007。Processing速写100天计划是小菜的一个命题作业,所谓速写,简单的在5-10分钟,复杂点的在1-2个小时,主要就是敦促小菜能够对 Processing 的基本功进行扎实练习。
平时工作一忙,可能会断更,不过后续有时间一定会接着拾起来。?
好了,话不多说。Day_007 的命题是摄像头识别颜色 物理世界,最后速写练习如上视频号的呈现。
小菜简单说下实现的思路。
摄像头的颜色识别
主要是两点
1)如何计算两个色值是否接近
2)如何寻找最接近目标识别色的那个色值或者位置
首先我添加了一个鼠标点击函数,获取了鼠标点击位置的目标颜色值,也就是我们要识别的颜色(后续要在这个颜色位置添加一个物理平台),并分别计算出了目标的RGB三个颜色通道值,之所以保存成全局的,是为了避免在 draw 函数中反复计算。
代码语言:javascript复制void mousePressed() {
int loc = mouseX mouseY * video.width;
targetColor = video.pixels[loc];
targetColorR = red(targetColor);
targetColorG = green(targetColor);
targetColorB = blue(targetColor);
}
尽量减少不必要的计算,是提升性能的一个常见手段。
有了目标颜色,下面就是遍历摄像头的数据,进行寻找最接近目标色值的位置。
代码语言:javascript复制void draw() {
if (video.available()) {
video.read();
}
image(video, 0, 0);
video.loadPixels();
float targetX = 0;
float targetY = 0;
float colorDistance = 300;
for (int x = 0; x < video.width; x ) {
for (int y = 0; y < video.height; y ) {
int loc = x y * video.width;
color videoColor = video.pixels[loc];
float r1 = red(videoColor);
float g1 = green(videoColor);
float b1 = blue(videoColor);
float d = dist(r1, g1, b1, targetColorR, targetColorG, targetColorB);
if (d < colorDistance) {
colorDistance = d;
targetX = x;
targetY = y;
}
}
}
if (colorDistance < 15) {
drawTargetColorShape(targetX, targetY);
}
}
颜色的距离计算:我们使用dist
函数来计算俩颜色的距离,dist
函数本来是计算二维、三维坐标点距离用的,刚好我们可以将颜色的RGB三个通道看成三维空间坐标,来计算两个颜色的距离。
寻找最接近的颜色位置:我们设定一个颜色距离的最小值,colorDistance初始值设置的比较随意,给的 300,随着一次又一次 for 循环的迭代遍历,如果计算出的颜色距离小于我们的colorDistance
那么我们便更新这个colorDistance
,同时更新目标像素的位置即 targetX 和 targetY,遍历结束后,最终的targetX、targetY就是最接近我们目标色的像素位置。
物理世界
谈到使用物理世界,不得不提到大名鼎鼎的Box2D。小菜之前从事游戏开发的时候,经常用到Box2D。Cocos2D引擎内置的物理引擎便是Box2D和Chipmunk。
Box2D 是用 C 语言编写的,但有多种语言的版本,比如 javascript,我们可以在浏览器中使用,也有 java 版的 jbox2d,Daniel Shiffman 基于 jbox2d,做了一层简单的封装,我们可以在三方库找到并添加 Box2D for Processing。
Box2D的话,今天这里就不详细的阐述了。我们主要看下这个速写中的实现用法。
物理世界中从上往下坠落的粒子球
每个粒子都是一个Particle
,Particle
中都有一个Body
,该 Body
负责物理世界的模拟,包括重力、碰撞等。
我们将粒子从画面上方随机生成,由于粒子是动态刚体,且受到重力作用,便会做自由落体运动。
Particle
的绘制display
函数,要注意的是绘制部分的坐标需要从物理模拟世界中查询,如Vec2 pos = box2d.getBodyPixelCoord(body);
。这点是使用 Box2D 时值得注意的一个点。
class Particle {
Body body;
float r;
color col;
Particle(float x, float y, float r_) {
r = r_;
makeBody(x, y, r);
body.setUserData(this);
col = color(random(255), random(255), random(255));
}
void killBody() {
box2d.destroyBody(body);
}
boolean done() {
Vec2 pos = box2d.getBodyPixelCoord(body);
if (pos.y > height r*2) {
killBody();
return true;
}
return false;
}
void display() {
Vec2 pos = box2d.getBodyPixelCoord(body);
float a = body.getAngle();
pushMatrix();
translate(pos.x, pos.y);
rotate(-a);
fill(col);
stroke(0);
strokeWeight(1);
ellipse(0, 0, r*2, r*2);
line(0, 0, r, 0);
popMatrix();
}
void makeBody(float x, float y, float r) {
BodyDef bd = new BodyDef();
bd.position = box2d.coordPixelsToWorld(x, y);
bd.type = BodyType.DYNAMIC;
body = box2d.world.createBody(bd);
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
FixtureDef fd = new FixtureDef();
fd.shape = cs;
fd.density = 2.0;
fd.friction = 0.01;
fd.restitution = 0.3;
body.createFixture(fd);
body.setAngularVelocity(random(-10, 10));
}
}
物理世界中随指尖颜色运动的平台
物理平台是一个静态刚体,BodyType
为STATIC
,我们想让他跟着识别出的颜色位置移动,可以直接用刚体的setTransform
改变它的位置,注意将目标位置用coordPixelsToWorld
转换到Box2D的物理世界坐标中。
void display(float targetX, float targetY, color c) {
body.setTransform(box2d.coordPixelsToWorld(targetX, targetY), 0);
Vec2 pos = box2d.getBodyPixelCoord(body);
rectMode(PConstants.CENTER);
pushMatrix();
translate(pos.x, pos.y);
fill(c);
stroke(0);
rect(0, 0, w, h);
popMatrix();
}
查看代码
github:https://github.com/xiaocai-laoniao/Processing100DaysSketch
我来笔记地址:https://www.wolai.com/childhoodandy/nFdnVWMHkYxtb6QgBxHDJx?theme=dark