📄 backgroundsubtraction.pde
字号:
/**
* Advanced Background Subtraction
*
* by Jorge Cardoso http://jorgecardoso.eu
* January 2008
*/
import JMyron.*;
public class BackgroundSubtraction {
int width, height;
// the list of glob points from jmyron
int[][][] list;
// the reference background pixels
int backgroundPixels[];
// the result of subtraction
int []subtraction;
// the subtraction binarized and possible inverted to be used as an image mask
int alphaChannel[];
// the quantity of movement
int presenceSum;
// the current jmyron frame
PImage currentFrame;
JMyron m;
// indicates that we need an inverted mask
boolean invertAlphaChannel = false;
// the length of segments in the silhouette. passed to jmyron
int silhuetteSegmentLength = 10;
// we do a blur before asking jmyron for globs. this is the blur radius
int blurRadius = 2;
// the color threshold passed to jmyron. not really important because the image is binarized
int jMyronColorThreshold = 255;
// our subtraction threshold. differences bellow this are considered black.
int subtractionThreshold = 100;
Vector masks = new Vector();
public BackgroundSubtraction(int width, int height) {
this.width = width;
this.height = height;
backgroundPixels = new int[width*height];
alphaChannel = new int[width*height];
subtraction = new int[width*height];
presenceSum = 0;
m = new JMyron();
m.start(width, height);
m.trackColor(255, 255, 255, jMyronColorThreshold);
m.minDensity(100);
}
public void update() {
m.update();
currentFrame = new PImage(this.width, this.height);
arraycopy(m.image(), currentFrame.pixels);
}
// returns the current frame from jmyron
public PImage currentFrame() {
return currentFrame;
}
// forward to jmyron for camera settings
public void settings() {
m.settings();
}
public void invertAlphaChannel(boolean invert) {
invertAlphaChannel = invert;
}
// we will mask this area no matter what. usefull to clean
// small parts of the image that don't interest
public void maskThis(int x, int y, int width, int height) {
//Rectangle r = new Rectangle(x, y, width, height);
//masks.add(r);
}
public void setBackground() {
setBackground(m.image());
}
public void setWhiteBackground() {
subtractionThreshold = 400;
for (int i = 0; i < backgroundPixels.length; i++) {
backgroundPixels[i] = color(255, 255);
}
}
public void setBackground(int []frame) {
arraycopy(frame, backgroundPixels);
presenceSum = 0;
}
public long getQuantityOfMovement() {
return presenceSum;
}
public int[] getSubtraction() {
return subtraction;
}
public PImage getSubtractionImage() {
PImage img = new PImage(this.width, this.height);
arraycopy(subtraction, img.pixels);
return img;
}
public int[] getAlphaChannel() {
return alphaChannel;
}
public void subtract() {
subtract(m.image());
}
/*
* Subtract with default values.
*/
public void subtract(int []frame) {
subtract(frame, subtractionThreshold, true, true, true, true, false);
}
/**
* General purpose method.
* Based on example by Golan Levin
*/
private void subtract(int []frame, int threshold, boolean binarize, boolean cRed, boolean cGreen, boolean cBlue, boolean onlyBackground) {
for (int i = 0; i < frame.length; i++) { // For each pixel in the video frame...
// Fetch the current color in that location, and also the color
// of the background in that spot
color currColor = frame[i];
color bkgdColor = backgroundPixels[i];
// Extract the red, green, and blue components of the current pixel誷 color
int currR = (currColor >> 16) & 0xFF;
int currG = (currColor >> 8) & 0xFF;
int currB = currColor & 0xFF;
// Extract the red, green, and blue components of the background pixel誷 color
int bkgdR = (bkgdColor >> 16) & 0xFF;
int bkgdG = (bkgdColor >> 8) & 0xFF;
int bkgdB = bkgdColor & 0xFF;
// Compute the difference of the red, green, and blue values
int diffR = abs(currR - bkgdR);
int diffG = abs(currG - bkgdG);
int diffB = abs(currB - bkgdB);
// Add these differences to the running tally
int sub = ( (cRed?diffR:0) + (cGreen?diffG:0) + (cBlue?diffB:0));
presenceSum += sub;
// Render the difference image to the screen
//subtraction[i] = color(diffR, diffG, diffB);
// The following line does the same thing much faster, but is more technical
int c = (0xFF000000 | ((cRed?diffR:0) << 16) | ((cGreen?diffG:0) << 8) | (cBlue?diffB:0));
subtraction[i] = sub < threshold ? 0 : (binarize ? 0xffffffff : (onlyBackground?frame[i]:c));
if (invertAlphaChannel) {
alphaChannel[i] = sub < threshold ? 0xffffff : 0;
}
else {
alphaChannel[i] = sub < threshold ? 0 : 0xffffff;
}
}
PImage alpha = new PImage(this.width, this.height);
arraycopy(alphaChannel, alpha.pixels);
fastblur(alpha, blurRadius);
m.hijack(this.width, this.height, alpha.pixels);
m.update();
list = m.globEdgePoints(silhuetteSegmentLength);//get the outlines
//println(presenceSum); // Print out the total amount of movement
}
/**
* Returns the biggest silhouette.
*/
public Silhouette getSilhouette() {
return getSilhouette(0);
}
/**
* Returns the nth biggest sillhouette
*/
public Silhouette getSilhouette(int n) {
// doesn't exist
if (n >= list.length) {
return null;
}
// Sort by lengths
int lengths[] = new int[list.length];
for (int i = 0; i < list.length; i++) {
if (list[i] != null) {
lengths[i] = list[i].length;
}
else {
lengths[i] = 0;
}
}
lengths = sort(lengths);
int index = -1;
// find out which one has the desired length
for (int i = 0; i < list.length; i++) {
if (list[i] != null && list[i].length == lengths[lengths.length-1-n]) {
index = i;
break;
}
}
if (index != -1) {
float [][] points = new float[list[index].length][2];
for (int i = 0; i < list[index].length; i++) {
points[i][0] = (float)list[index][i][0]/this.width;
points[i][1] = (float)list[index][i][1]/this.height;
// jmyron sometimes makes polygons with shooting vertexes... let's try to remove them
// by making them the same value as the previous vertex.
if (i> 1 && dist(points[i-1][0], points[i-1][1], points[i][0],points[i][1]) > 0.2) {
points[i][0] = points[i-1][0];
points[i][1] = points[i-1][1];
}
}
return new Silhouette(points);
}
else {
return null;
}
}
public void setSilhouetteSegmentLength(int segLength) {
silhuetteSegmentLength = segLength;
}
public void setBlurRadius(int radius) {
blurRadius = radius;
}
public void setJMyronColorThreshold(int threshold) {
jMyronColorThreshold = threshold;
}
public void setSubtractionThreshold(int threshold) {
subtractionThreshold = threshold;
}
// ==================================================
// Super Fast Blur v1.1
// by Mario Klingemann <http://incubator.quasimondo.com>
// ==================================================
void fastblur(PImage img,int radius){
if (radius<1){
return;
}
int w=img.width;
int h=img.height;
int wm=w-1;
int hm=h-1;
int wh=w*h;
int div=radius+radius+1;
int r[]=new int[wh];
int g[]=new int[wh];
int b[]=new int[wh];
int rsum,gsum,bsum,x,y,i,p,p1,p2,yp,yi,yw;
int vmin[] = new int[max(w,h)];
int vmax[] = new int[max(w,h)];
int[] pix=img.pixels;
int dv[]=new int[256*div];
for (i=0;i<256*div;i++){
dv[i]=(i/div);
}
yw=yi=0;
for (y=0;y<h;y++){
rsum=gsum=bsum=0;
for(i=-radius;i<=radius;i++){
p=pix[yi+min(wm,max(i,0))];
rsum+=(p & 0xff0000)>>16;
gsum+=(p & 0x00ff00)>>8;
bsum+= p & 0x0000ff;
}
for (x=0;x<w;x++){
r[yi]=dv[rsum];
g[yi]=dv[gsum];
b[yi]=dv[bsum];
if(y==0){
vmin[x]=min(x+radius+1,wm);
vmax[x]=max(x-radius,0);
}
p1=pix[yw+vmin[x]];
p2=pix[yw+vmax[x]];
rsum+=((p1 & 0xff0000)-(p2 & 0xff0000))>>16;
gsum+=((p1 & 0x00ff00)-(p2 & 0x00ff00))>>8;
bsum+= (p1 & 0x0000ff)-(p2 & 0x0000ff);
yi++;
}
yw+=w;
}
for (x=0;x<w;x++){
rsum=gsum=bsum=0;
yp=-radius*w;
for(i=-radius;i<=radius;i++){
yi=max(0,yp)+x;
rsum+=r[yi];
gsum+=g[yi];
bsum+=b[yi];
yp+=w;
}
yi=x;
for (y=0;y<h;y++){
pix[yi]=0xff000000 | (dv[rsum]<<16) | (dv[gsum]<<8) | dv[bsum];
if(x==0){
vmin[y]=min(y+radius+1,hm)*w;
vmax[y]=max(y-radius,0)*w;
}
p1=x+vmin[y];
p2=x+vmax[y];
rsum+=r[p1]-r[p2];
gsum+=g[p1]-g[p2];
bsum+=b[p1]-b[p2];
yi+=w;
}
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -