加工 |程序滞后
Processing | Program is lagging
我是 Processing 的新手,我需要制作一个程序,捕获主显示器,在第二个屏幕上显示平均颜色,并使用函数获得的另一种颜色(感知主色)制作螺旋线。
问题是程序太慢了(滞后,1FPS)。我想是因为每次截图的时候有太多事情要做,但是我不知道如何让它更快。
可能还有很多其他问题,但最主要的是。
非常感谢!
代码如下:
import java.awt.Robot;
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
PImage screenshot;
float a = 0;
int blockSize = 20;
int avg_c;
int per_c;
void setup() {
fullScreen(2); // 1920x1080
noStroke();
frame.removeNotify();
}
void draw() {
screenshot();
avg_c = extractColorFromImage(screenshot);
per_c = extractAverageColorFromImage(screenshot);
background(avg_c); // Average color
spiral();
}
void screenshot() {
try{
Robot robot_Screenshot = new Robot();
screenshot = new PImage(robot_Screenshot.createScreenCapture
(new Rectangle(0, 0, displayWidth, displayHeight)));
}
catch (AWTException e){ }
frame.setLocation(displayWidth/2, 0);
}
void spiral() {
fill (per_c);
for (int i = blockSize; i < width; i += blockSize*2)
{
ellipse(i, height/2+sin(a+i)*100, blockSize+cos(a+i)*5, blockSize+cos(a+i)*5);
a += 0.001;
}
}
color extractColorFromImage(PImage screenshot) { // Get average color
screenshot.loadPixels();
int r = 0, g = 0, b = 0;
for (int i = 0; i < screenshot.pixels.length; i++) {
color c = screenshot.pixels[i];
r += c>>16&0xFF;
g += c>>8&0xFF;
b += c&0xFF;
}
r /= screenshot.pixels.length; g /= screenshot.pixels.length; b /= screenshot.pixels.length;
return color(r, g, b);
}
color extractAverageColorFromImage(PImage screenshot) { // Get lab average color (perceptual)
float[] average = new float[3];
CIELab lab = new CIELab();
int numPixels = screenshot.pixels.length;
for (int i = 0; i < numPixels; i++) {
color rgb = screenshot.pixels[i];
float[] labValues = lab.fromRGB(new float[]{red(rgb),green(rgb),blue(rgb)});
average[0] += labValues[0];
average[1] += labValues[1];
average[2] += labValues[2];
}
average[0] /= numPixels;
average[1] /= numPixels;
average[2] /= numPixels;
float[] rgb = lab.toRGB(average);
return color(rgb[0] * 255,rgb[1] * 255, rgb[2] * 255);
}
public class CIELab extends ColorSpace {
@Override
public float[] fromCIEXYZ(float[] colorvalue) {
double l = f(colorvalue[1]);
double L = 116.0 * l - 16.0;
double a = 500.0 * (f(colorvalue[0]) - l);
double b = 200.0 * (l - f(colorvalue[2]));
return new float[] {(float) L, (float) a, (float) b};
}
@Override
public float[] fromRGB(float[] rgbvalue) {
float[] xyz = CIEXYZ.fromRGB(rgbvalue);
return fromCIEXYZ(xyz);
}
@Override
public float getMaxValue(int component) {
return 128f;
}
@Override
public float getMinValue(int component) {
return (component == 0)? 0f: -128f;
}
@Override
public String getName(int idx) {
return String.valueOf("Lab".charAt(idx));
}
@Override
public float[] toCIEXYZ(float[] colorvalue) {
double i = (colorvalue[0] + 16.0) * (1.0 / 116.0);
double X = fInv(i + colorvalue[1] * (1.0 / 500.0));
double Y = fInv(i);
double Z = fInv(i - colorvalue[2] * (1.0 / 200.0));
return new float[] {(float) X, (float) Y, (float) Z};
}
@Override
public float[] toRGB(float[] colorvalue) {
float[] xyz = toCIEXYZ(colorvalue);
return CIEXYZ.toRGB(xyz);
}
CIELab() {
super(ColorSpace.TYPE_Lab, 3);
}
private double f(double x) {
if (x > 216.0 / 24389.0) {
return Math.cbrt(x);
} else {
return (841.0 / 108.0) * x + N;
}
}
private double fInv(double x) {
if (x > 6.0 / 29.0) {
return x*x*x;
} else {
return (108.0 / 841.0) * (x - N);
}
}
private final ColorSpace CIEXYZ =
ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
private final double N = 4.0 / 29.0;
}
是的,在我的机器上大约 1 FPS:
优化代码可能真的很难,所以我没有阅读所有东西寻找可以改进的东西,而是从测试你失去这么多处理能力的地方开始。答案是在这一行:
per_c = extractAverageColorFromImage(screenshot);
extractAverageColorFromImage
方法写得很好,但是低估了它要做的工作量。屏幕尺寸与屏幕像素数之间存在二次关系,因此屏幕越大情况越差。而且这种方法一直在处理屏幕截图的每个像素,每个屏幕截图几次。
对于平均颜色来说,这需要大量工作。现在,如果有办法减少一些角落……也许是更小的屏幕,或者更小的屏幕截图……哦!有!让我们调整屏幕截图的大小。毕竟,我们不需要为了平均而深入到单个像素这样的细节。在 screenshot
方法中,添加这一行:
void screenshot() {
try {
Robot robot_Screenshot = new Robot();
screenshot = new PImage(robot_Screenshot.createScreenCapture(new Rectangle(0, 0, displayWidth, displayHeight)));
// ADD THE NEXT LINE
screenshot.resize(width/4, height/4);
}
catch (AWTException e) {
}
frame.setLocation(displayWidth/2, 0);
}
我将工作量除以 4,但我鼓励您调整这个数字,直到您以最快的速度获得令人满意的结果。这只是一个概念证明:
如您所见,调整屏幕截图大小并将其缩小 4 倍可使我的速度提高 10 倍。这不是奇迹,但好多了,我看不出最终结果有什么不同——但关于那部分,你必须使用自己的判断,因为你是知道你的项目是关于什么的人.希望对您有所帮助!
玩得开心!
很遗憾,我无法像 laancelot (+1) 那样提供详细的答案,但希望我能提供一些提示:
- 调整图片大小绝对是个不错的方向。请记住,您也可以跳过一些像素而不是递增每个像素。 (如果你正确处理像素索引,你可以在不调用调整大小的情况下获得类似的调整大小效果,尽管这不会为你节省很多 CPU 时间)
- 不要每秒多次创建新的
Robot
实例。在设置中创建一次并重新使用它。 (这是一个更好的习惯)
- 使用 CPU 探查器,例如 VisualVM 中的探查器,看看到底什么是慢的,旨在首先优化最慢的东西。
点 1 示例:
for (int i = 0; i < numPixels; i+= 100)
点 2 示例:
Robot robot_Screenshot;
...
void setup() {
fullScreen(2); // 1920x1080
noStroke();
frame.removeNotify();
try{
robot_Screenshot = new Robot();
}catch(AWTException e){
println("error setting up screenshot Robot instance");
e.printStackTrace();
}
}
...
void screenshot() {
screenshot = new PImage(robot_Screenshot.createScreenCapture
(new Rectangle(0, 0, displayWidth, displayHeight)));
frame.setLocation(displayWidth/2, 0);
}
第 3 点示例:
注意最慢的位实际上是 AWT 的 fromRGB 和 Math.cbrt()
我建议寻找另一种 RGB -> XYZ -> L*a*b*
转换方法,它更简单(主要是函数,较少 类,具有 AWT 或其他依赖项)并且希望更快。
有很多事情可以做,甚至超出了已经提到的范围。
迭代与线程
截取屏幕截图后,立即迭代缓冲图像的每 1/N 个像素(可能每 4 或 8 个)。然后,在此迭代期间,计算每个像素的 LAB 值(因为每个像素通道都直接可用),同时增加每个 RGB 通道的 运行ning 总数。
这避免了我们对相同像素进行两次迭代,并避免了不必要的转换(BufferedImage
→ PImage
;然后从 PImage
像素合成然后分解像素通道)。
同样,我们避免了 Processing 的昂贵 resize()
调用(如另一个答案中所建议的),这不是我们想要在每一帧调用的东西(即使它确实加快了程序速度,但它不是一种有效的方法).
现在,在迭代更改之上,我们可以将迭代包装在 Callable
中,以轻松地 运行 同时跨多个系统线程的工作负载(毕竟,像素迭代是 令人尴尬的并行);下面的示例使用 2 个线程执行此操作,每个线程截屏并处理显示器像素的一半。
优化RGB→XYZ→LAB转换
我们不太关心向后转换,因为每帧只为一个值完成
看起来您已经自己实现了 XYZ→LAB,并且正在使用来自 java.awt.color
的 RGB→XYZ 转换器。
正如已经确定的那样,前向转换 XYZ→LAB 使用 cbrt()
作为瓶颈。我还想象 RGB→XYZ 实现对 Math.Pow(x, 2.4)
进行了 3 次调用——每个像素 3 个非整数指数大大增加了计算量。解决方案是更快的数学...
贾法玛
Jafama 是 java.math
的替代品——只需导入库并将任何 Math.__()
调用替换为 FastMath.__()
即可免费加速(您可以去通过将 Jafama 的 E-15 精度与更不准确但更快的基于 LUT 的专用 类).
进行交易,更进一步
所以至少,将 Math.cbrt()
换成 FastMath.cbrt()
。然后考虑自己实现 RGB→XYZ (example),再次使用 Jafama 代替 java.math
.
您甚至可能会发现,对于这样的项目,仅转换为 XYZ 颜色就足够了 space 可以用来克服众所周知的 RGB 弱点(从而避免 XYZ→LAB 转换).
缓存 LAB 计算
除非大多数像素在每帧发生变化,然后考虑缓存每个像素的LAB值,只有当像素有变化时才重新计算它当前之前的帧之间发生变化。这里的权衡是检查每个像素与其先前值的开销,以及积极检查将节省多少计算。鉴于 LAB 计算的成本要高得多,这里非常值得。下面的示例使用了这种技术。
屏幕截图
无论程序的其余部分优化得多么好,一个相当大的瓶颈是 AWT 机器人的 createScreenCapture()
。在足够大的显示器上,它很难超过 30FPS。我无法提供任何确切的建议,但值得看看 Java.
中的其他屏幕捕获方法
通过迭代更改和线程重新编写代码
此代码实现了上面讨论的减去对 LAB 计算的任何更改。
float a = 0;
int blockSize = 20;
int avg_c;
int per_c;
java.util.concurrent.ExecutorService threadPool = java.util.concurrent.Executors.newFixedThreadPool(4);
List<java.util.concurrent.Callable<Boolean>> taskList;
float[] averageLAB;
int totalR = 0, totalG = 0, totalB = 0;
CIELab lab = new CIELab();
final int pixelStride = 8; // look at every 8th pixel
void setup() {
size(800, 800, FX2D);
noStroke();
frame.removeNotify();
taskList = new ArrayList<java.util.concurrent.Callable<Boolean>>();
Compute thread1 = new Compute(0, 0, width, height/2);
Compute thread2 = new Compute(0, height/2, width, height/2);
taskList.add(thread1);
taskList.add(thread2);
}
void draw() {
totalR = 0; // re init
totalG = 0; // re init
totalB = 0; // re init
averageLAB = new float[3]; // re init
final int numPixels = (width*height)/pixelStride;
try {
threadPool.invokeAll(taskList); // run threads now and block until completion of all
}
catch (Exception e) {
e.printStackTrace();
}
// calculate average LAB
averageLAB[0]/=numPixels;
averageLAB[1]/=numPixels;
averageLAB[2]/=numPixels;
final float[] rgb = lab.toRGB(averageLAB);
per_c = color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255);
// calculate average RGB
totalR/=numPixels;
totalG/=numPixels;
totalB/=numPixels;
avg_c = color(totalR, totalG, totalB);
background(avg_c); // Average color
spiral();
fill(255, 0, 0);
text(frameRate, 10, 20);
}
class Compute implements java.util.concurrent.Callable<Boolean> {
private final Rectangle screenRegion;
private Robot robot_Screenshot;
private final int[] previousRGB;
private float[][] previousLAB;
Compute(int x, int y, int w, int h) {
screenRegion = new Rectangle(x, y, w, h);
previousRGB = new int[w*h];
previousLAB = new float[w*h][3];
try {
robot_Screenshot = new Robot();
}
catch (AWTException e1) {
e1.printStackTrace();
}
}
@Override
public Boolean call() {
BufferedImage rawScreenshot = robot_Screenshot.createScreenCapture(screenRegion);
int[] ssPixels = new int[rawScreenshot.getWidth()*rawScreenshot.getHeight()]; // screenshot pixels
rawScreenshot.getRGB(0, 0, rawScreenshot.getWidth(), rawScreenshot.getHeight(), ssPixels, 0, rawScreenshot.getWidth()); // copy buffer to int[] array
for (int pixel = 0; pixel < ssPixels.length; pixel+=pixelStride) {
// get invididual colour channels
final int pixelColor = ssPixels[pixel];
final int R = pixelColor >> 16 & 0xFF;
final int G = pixelColor >> 8 & 0xFF;
final int B = pixelColor & 0xFF;
if (pixelColor != previousRGB[pixel]) { // if pixel has changed recalculate LAB value
float[] labValues = lab.fromRGB(new float[]{R/255f, G/255f, B/255f}); // note that I've fixed this; beforehand you were missing the /255, so it was always white.
previousLAB[pixel] = labValues;
}
averageLAB[0] += previousLAB[pixel][0];
averageLAB[1] += previousLAB[pixel][1];
averageLAB[2] += previousLAB[pixel][2];
totalR+=R;
totalG+=G;
totalB+=B;
previousRGB[pixel] = pixelColor; // cache last result
}
return true;
}
}
800x800px;像素步幅 = 4;相当静态的屏幕背景
我是 Processing 的新手,我需要制作一个程序,捕获主显示器,在第二个屏幕上显示平均颜色,并使用函数获得的另一种颜色(感知主色)制作螺旋线。
问题是程序太慢了(滞后,1FPS)。我想是因为每次截图的时候有太多事情要做,但是我不知道如何让它更快。
可能还有很多其他问题,但最主要的是。
非常感谢!
代码如下:
import java.awt.Robot;
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
PImage screenshot;
float a = 0;
int blockSize = 20;
int avg_c;
int per_c;
void setup() {
fullScreen(2); // 1920x1080
noStroke();
frame.removeNotify();
}
void draw() {
screenshot();
avg_c = extractColorFromImage(screenshot);
per_c = extractAverageColorFromImage(screenshot);
background(avg_c); // Average color
spiral();
}
void screenshot() {
try{
Robot robot_Screenshot = new Robot();
screenshot = new PImage(robot_Screenshot.createScreenCapture
(new Rectangle(0, 0, displayWidth, displayHeight)));
}
catch (AWTException e){ }
frame.setLocation(displayWidth/2, 0);
}
void spiral() {
fill (per_c);
for (int i = blockSize; i < width; i += blockSize*2)
{
ellipse(i, height/2+sin(a+i)*100, blockSize+cos(a+i)*5, blockSize+cos(a+i)*5);
a += 0.001;
}
}
color extractColorFromImage(PImage screenshot) { // Get average color
screenshot.loadPixels();
int r = 0, g = 0, b = 0;
for (int i = 0; i < screenshot.pixels.length; i++) {
color c = screenshot.pixels[i];
r += c>>16&0xFF;
g += c>>8&0xFF;
b += c&0xFF;
}
r /= screenshot.pixels.length; g /= screenshot.pixels.length; b /= screenshot.pixels.length;
return color(r, g, b);
}
color extractAverageColorFromImage(PImage screenshot) { // Get lab average color (perceptual)
float[] average = new float[3];
CIELab lab = new CIELab();
int numPixels = screenshot.pixels.length;
for (int i = 0; i < numPixels; i++) {
color rgb = screenshot.pixels[i];
float[] labValues = lab.fromRGB(new float[]{red(rgb),green(rgb),blue(rgb)});
average[0] += labValues[0];
average[1] += labValues[1];
average[2] += labValues[2];
}
average[0] /= numPixels;
average[1] /= numPixels;
average[2] /= numPixels;
float[] rgb = lab.toRGB(average);
return color(rgb[0] * 255,rgb[1] * 255, rgb[2] * 255);
}
public class CIELab extends ColorSpace {
@Override
public float[] fromCIEXYZ(float[] colorvalue) {
double l = f(colorvalue[1]);
double L = 116.0 * l - 16.0;
double a = 500.0 * (f(colorvalue[0]) - l);
double b = 200.0 * (l - f(colorvalue[2]));
return new float[] {(float) L, (float) a, (float) b};
}
@Override
public float[] fromRGB(float[] rgbvalue) {
float[] xyz = CIEXYZ.fromRGB(rgbvalue);
return fromCIEXYZ(xyz);
}
@Override
public float getMaxValue(int component) {
return 128f;
}
@Override
public float getMinValue(int component) {
return (component == 0)? 0f: -128f;
}
@Override
public String getName(int idx) {
return String.valueOf("Lab".charAt(idx));
}
@Override
public float[] toCIEXYZ(float[] colorvalue) {
double i = (colorvalue[0] + 16.0) * (1.0 / 116.0);
double X = fInv(i + colorvalue[1] * (1.0 / 500.0));
double Y = fInv(i);
double Z = fInv(i - colorvalue[2] * (1.0 / 200.0));
return new float[] {(float) X, (float) Y, (float) Z};
}
@Override
public float[] toRGB(float[] colorvalue) {
float[] xyz = toCIEXYZ(colorvalue);
return CIEXYZ.toRGB(xyz);
}
CIELab() {
super(ColorSpace.TYPE_Lab, 3);
}
private double f(double x) {
if (x > 216.0 / 24389.0) {
return Math.cbrt(x);
} else {
return (841.0 / 108.0) * x + N;
}
}
private double fInv(double x) {
if (x > 6.0 / 29.0) {
return x*x*x;
} else {
return (108.0 / 841.0) * (x - N);
}
}
private final ColorSpace CIEXYZ =
ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
private final double N = 4.0 / 29.0;
}
是的,在我的机器上大约 1 FPS:
优化代码可能真的很难,所以我没有阅读所有东西寻找可以改进的东西,而是从测试你失去这么多处理能力的地方开始。答案是在这一行:
per_c = extractAverageColorFromImage(screenshot);
extractAverageColorFromImage
方法写得很好,但是低估了它要做的工作量。屏幕尺寸与屏幕像素数之间存在二次关系,因此屏幕越大情况越差。而且这种方法一直在处理屏幕截图的每个像素,每个屏幕截图几次。
对于平均颜色来说,这需要大量工作。现在,如果有办法减少一些角落……也许是更小的屏幕,或者更小的屏幕截图……哦!有!让我们调整屏幕截图的大小。毕竟,我们不需要为了平均而深入到单个像素这样的细节。在 screenshot
方法中,添加这一行:
void screenshot() {
try {
Robot robot_Screenshot = new Robot();
screenshot = new PImage(robot_Screenshot.createScreenCapture(new Rectangle(0, 0, displayWidth, displayHeight)));
// ADD THE NEXT LINE
screenshot.resize(width/4, height/4);
}
catch (AWTException e) {
}
frame.setLocation(displayWidth/2, 0);
}
我将工作量除以 4,但我鼓励您调整这个数字,直到您以最快的速度获得令人满意的结果。这只是一个概念证明:
如您所见,调整屏幕截图大小并将其缩小 4 倍可使我的速度提高 10 倍。这不是奇迹,但好多了,我看不出最终结果有什么不同——但关于那部分,你必须使用自己的判断,因为你是知道你的项目是关于什么的人.希望对您有所帮助!
玩得开心!
很遗憾,我无法像 laancelot (+1) 那样提供详细的答案,但希望我能提供一些提示:
- 调整图片大小绝对是个不错的方向。请记住,您也可以跳过一些像素而不是递增每个像素。 (如果你正确处理像素索引,你可以在不调用调整大小的情况下获得类似的调整大小效果,尽管这不会为你节省很多 CPU 时间)
- 不要每秒多次创建新的
Robot
实例。在设置中创建一次并重新使用它。 (这是一个更好的习惯) - 使用 CPU 探查器,例如 VisualVM 中的探查器,看看到底什么是慢的,旨在首先优化最慢的东西。
点 1 示例:
for (int i = 0; i < numPixels; i+= 100)
点 2 示例:
Robot robot_Screenshot;
...
void setup() {
fullScreen(2); // 1920x1080
noStroke();
frame.removeNotify();
try{
robot_Screenshot = new Robot();
}catch(AWTException e){
println("error setting up screenshot Robot instance");
e.printStackTrace();
}
}
...
void screenshot() {
screenshot = new PImage(robot_Screenshot.createScreenCapture
(new Rectangle(0, 0, displayWidth, displayHeight)));
frame.setLocation(displayWidth/2, 0);
}
第 3 点示例:
注意最慢的位实际上是 AWT 的 fromRGB 和 Math.cbrt()
我建议寻找另一种 RGB -> XYZ -> L*a*b*
转换方法,它更简单(主要是函数,较少 类,具有 AWT 或其他依赖项)并且希望更快。
有很多事情可以做,甚至超出了已经提到的范围。
迭代与线程
截取屏幕截图后,立即迭代缓冲图像的每 1/N 个像素(可能每 4 或 8 个)。然后,在此迭代期间,计算每个像素的 LAB 值(因为每个像素通道都直接可用),同时增加每个 RGB 通道的 运行ning 总数。
这避免了我们对相同像素进行两次迭代,并避免了不必要的转换(BufferedImage
→ PImage
;然后从 PImage
像素合成然后分解像素通道)。
同样,我们避免了 Processing 的昂贵 resize()
调用(如另一个答案中所建议的),这不是我们想要在每一帧调用的东西(即使它确实加快了程序速度,但它不是一种有效的方法).
现在,在迭代更改之上,我们可以将迭代包装在 Callable
中,以轻松地 运行 同时跨多个系统线程的工作负载(毕竟,像素迭代是 令人尴尬的并行);下面的示例使用 2 个线程执行此操作,每个线程截屏并处理显示器像素的一半。
优化RGB→XYZ→LAB转换
我们不太关心向后转换,因为每帧只为一个值完成
看起来您已经自己实现了 XYZ→LAB,并且正在使用来自 java.awt.color
的 RGB→XYZ 转换器。
正如已经确定的那样,前向转换 XYZ→LAB 使用 cbrt()
作为瓶颈。我还想象 RGB→XYZ 实现对 Math.Pow(x, 2.4)
进行了 3 次调用——每个像素 3 个非整数指数大大增加了计算量。解决方案是更快的数学...
贾法玛
Jafama 是 java.math
的替代品——只需导入库并将任何 Math.__()
调用替换为 FastMath.__()
即可免费加速(您可以去通过将 Jafama 的 E-15 精度与更不准确但更快的基于 LUT 的专用 类).
所以至少,将 Math.cbrt()
换成 FastMath.cbrt()
。然后考虑自己实现 RGB→XYZ (example),再次使用 Jafama 代替 java.math
.
您甚至可能会发现,对于这样的项目,仅转换为 XYZ 颜色就足够了 space 可以用来克服众所周知的 RGB 弱点(从而避免 XYZ→LAB 转换).
缓存 LAB 计算
除非大多数像素在每帧发生变化,然后考虑缓存每个像素的LAB值,只有当像素有变化时才重新计算它当前之前的帧之间发生变化。这里的权衡是检查每个像素与其先前值的开销,以及积极检查将节省多少计算。鉴于 LAB 计算的成本要高得多,这里非常值得。下面的示例使用了这种技术。
屏幕截图
无论程序的其余部分优化得多么好,一个相当大的瓶颈是 AWT 机器人的 createScreenCapture()
。在足够大的显示器上,它很难超过 30FPS。我无法提供任何确切的建议,但值得看看 Java.
通过迭代更改和线程重新编写代码
此代码实现了上面讨论的减去对 LAB 计算的任何更改。
float a = 0;
int blockSize = 20;
int avg_c;
int per_c;
java.util.concurrent.ExecutorService threadPool = java.util.concurrent.Executors.newFixedThreadPool(4);
List<java.util.concurrent.Callable<Boolean>> taskList;
float[] averageLAB;
int totalR = 0, totalG = 0, totalB = 0;
CIELab lab = new CIELab();
final int pixelStride = 8; // look at every 8th pixel
void setup() {
size(800, 800, FX2D);
noStroke();
frame.removeNotify();
taskList = new ArrayList<java.util.concurrent.Callable<Boolean>>();
Compute thread1 = new Compute(0, 0, width, height/2);
Compute thread2 = new Compute(0, height/2, width, height/2);
taskList.add(thread1);
taskList.add(thread2);
}
void draw() {
totalR = 0; // re init
totalG = 0; // re init
totalB = 0; // re init
averageLAB = new float[3]; // re init
final int numPixels = (width*height)/pixelStride;
try {
threadPool.invokeAll(taskList); // run threads now and block until completion of all
}
catch (Exception e) {
e.printStackTrace();
}
// calculate average LAB
averageLAB[0]/=numPixels;
averageLAB[1]/=numPixels;
averageLAB[2]/=numPixels;
final float[] rgb = lab.toRGB(averageLAB);
per_c = color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255);
// calculate average RGB
totalR/=numPixels;
totalG/=numPixels;
totalB/=numPixels;
avg_c = color(totalR, totalG, totalB);
background(avg_c); // Average color
spiral();
fill(255, 0, 0);
text(frameRate, 10, 20);
}
class Compute implements java.util.concurrent.Callable<Boolean> {
private final Rectangle screenRegion;
private Robot robot_Screenshot;
private final int[] previousRGB;
private float[][] previousLAB;
Compute(int x, int y, int w, int h) {
screenRegion = new Rectangle(x, y, w, h);
previousRGB = new int[w*h];
previousLAB = new float[w*h][3];
try {
robot_Screenshot = new Robot();
}
catch (AWTException e1) {
e1.printStackTrace();
}
}
@Override
public Boolean call() {
BufferedImage rawScreenshot = robot_Screenshot.createScreenCapture(screenRegion);
int[] ssPixels = new int[rawScreenshot.getWidth()*rawScreenshot.getHeight()]; // screenshot pixels
rawScreenshot.getRGB(0, 0, rawScreenshot.getWidth(), rawScreenshot.getHeight(), ssPixels, 0, rawScreenshot.getWidth()); // copy buffer to int[] array
for (int pixel = 0; pixel < ssPixels.length; pixel+=pixelStride) {
// get invididual colour channels
final int pixelColor = ssPixels[pixel];
final int R = pixelColor >> 16 & 0xFF;
final int G = pixelColor >> 8 & 0xFF;
final int B = pixelColor & 0xFF;
if (pixelColor != previousRGB[pixel]) { // if pixel has changed recalculate LAB value
float[] labValues = lab.fromRGB(new float[]{R/255f, G/255f, B/255f}); // note that I've fixed this; beforehand you were missing the /255, so it was always white.
previousLAB[pixel] = labValues;
}
averageLAB[0] += previousLAB[pixel][0];
averageLAB[1] += previousLAB[pixel][1];
averageLAB[2] += previousLAB[pixel][2];
totalR+=R;
totalG+=G;
totalB+=B;
previousRGB[pixel] = pixelColor; // cache last result
}
return true;
}
}
800x800px;像素步幅 = 4;相当静态的屏幕背景