sclass G22BurnIn { settable double alphaStep = 0.1; settable double tolerance = 0.1; settable double colorMorph = 0.1; // speed to update color within tolerance settable Color backgroundColor = Color.black; int toleranceSquaredInt; int w, h; int[] mask; // how much motion is there in the image overall? (0 to 1) double motionFactor = Double.NaN; void processFrame(BufferedImage frame) { toleranceSquaredInt = iround(sqr(tolerance*255)*3); int w = frame.getWidth(), h = frame.getHeight(); if (mask == null || this.w != w || this.h != h) { this.w = w; this.h = h; this.mask = new int[w*h]; } // We assume the frame has no transparency var gp = grabbableIntPixels_fastOrSlow(frame); int n = w*h, iMask = 0, iFrame = gp.offset; int[] pixels = gp.data; int[] mask = this.mask; for y to h: { for x to w: { int mpix = mask[iMask]; int fcol = pixels[iFrame++] & 0xFFFFFF; if (mpix == 0) mpix = differentColor(mpix, fcol); else { int mcol = mpix & 0xFFFFFF; int diff = rgbDistanceSquaredInt(mcol, fcol); if (diff <= toleranceSquaredInt) mpix = sameColor(mpix, fcol); else mpix = differentColor(mpix, fcol); } mask[iMask++] = mpix; } iFrame += gp.scanlineStride-w; } motionFactor = Double.NaN; } bool isSameColor(int col1, int col2) { ret toleranceSquaredInt == 0 ? col1 == col2 : rgbDistanceSquaredInt(col1, col2) <= tolerance; } int sameColor(int mpix, int fcol) { double newAlpha = rgbAlphaZeroToOne(mpix)+alphaStep; int newColor = blendRGBInts(mpix, fcol, colorMorph); ret withAlpha(newAlpha, newColor); } int differentColor(int mpix, int fcol) { ret withAlpha(alphaStep, fcol); } BufferedImage stillImage aka image() { if (mask == null) null; var img = bufferedImage(w, h, mask); ret renderImageOnBackground(backgroundColor, img); } BufferedImage motionImage() { if (mask == null) null; int[] mask2 = pixelsWithInvertedAlpha(mask); var img = bufferedImage(w, h, mask2); ret renderImageOnBackground(backgroundColor, img); } BWImage motionDetectionImage() { ret BWImage(w, h, alphaChannelFromPixels(mask)); } double motionFactor() { if (isNaN(motionFactor) && mask != null) motionFactor = 1-alphaChannelAverage(mask)/255.0; ret motionFactor; } }