Som*_*Guy 0 java multithreading image-processing thread-safety
这是在java中。我的程序的目的是每秒多次捕获计算机屏幕的屏幕截图,并找到具有特殊红色阴影的所有像素。然后,它找到所有红色像素的平均位置。
为了提高图像处理的效率,我创建了 3 个线程,每个线程处理 1/4 的像素。这些线程加上原始线程将因此处理所有像素。但是,我的 avgLocation() 方法中不断出现错误。这是一个空指针异常,我认为这是因为其他线程正在更改包含所有红色像素的列表的大小,这导致程序访问不存在的像素的数据。为了解决这个问题,我在 Thread2 的代码之后在 Thread1 和 Thread2 上使用了 .join,然后在 Thread3 的代码段之后使用了 .join。因此,在调用 avgLocation 方法之前应该连接所有线程,但每当屏幕上出现特定的红色阴影时,它仍然会给出 NullPointerException。这是堆栈跟踪
Exception in thread "main" java.lang.NullPointerException
at Images.avgLocation(Images.java:151)
at Images.processImage(Images.java:133)
at Images.main(Images.java:169)
Run Code Online (Sandbox Code Playgroud)
151 号线是
xSum += list.get(i)[0];
Run Code Online (Sandbox Code Playgroud)
第 133 行是这是我的代码:
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.ArrayList;
public class Images {
//takes in an input image and a target color and a bound to check within, returns Point to target
public static Point processImage(BufferedImage img, Color color, int error) throws InterruptedException {
long start = System.nanoTime();
//bounds to check on color components, make sure all within [0,255]
int redLower = color.getRed() - error;
int redHigher = color.getRed() + error;
int greenLower = color.getGreen() - error;
int greenHigher = color.getGreen() + error;
int blueLower = color.getBlue() - error;
int blueHigher = color.getBlue() + error;
//place all components within [0,255]
if (redLower < 0) redLower = 0;
if (redHigher > 255) redHigher = 255;
if (greenLower < 0) greenLower = 0;
if (greenHigher > 255) greenHigher = 255;
if (blueLower < 0) blueLower = 0;
if (blueHigher > 255) blueHigher = 255;
//create final variables to use for thread
int redLowerF = redLower;
int redHigherF = redHigher;
int greenLowerF = greenLower;
int greenHigherF = greenHigher;
int blueLowerF = blueLower;
int blueHigherF = blueHigher;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
//stores locations
ArrayList <Integer[]> locations = new ArrayList <>();
for (int i = 0; i < pixels.length/4; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLower && r<=redHigher && g>=greenLower && g <= greenHigher && b >= blueLower && b <= blueHigher) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
Thread thread1 = new Thread( () -> {
for (int i = pixels.length/4; i < pixels.length/2; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF && b >= blueLowerF && b <= blueHigherF) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
});
thread1.start();
Thread thread2 = new Thread( () -> {
for (int i = pixels.length/2; i < pixels.length*3/4; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF && b >= blueLowerF && b <= blueHigherF) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
});
thread2.start();
thread1.join();
thread2.join();
Thread thread3 = new Thread( () -> {
for (int i = pixels.length*3/4; i < pixels.length; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF && b >= blueLowerF && b <= blueHigherF) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
});
thread3.start();
thread3.join();
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations);
}
//given 2D array of locations, finds average location of set and returns as point
public static Point avgLocation (ArrayList<Integer[]> list) {
//if no points in list, return an impossible point on screen
if (list.size() == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100;
int avgY = 100;
int xSum = 0;
int ySum = 0;
//loop through array
for (int i = 0; i < list.size(); i++) {
//for each location, add up coordinates
xSum += list.get(i)[0];
ySum += list.get(i)[1];
}
avgX = xSum/list.size();
avgY = ySum/list.size();
return new Point (avgX, avgY);
}
public static void main (String[] args) {
Robot robot = null;
try {
robot = new Robot();
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
while (true) {
try {
processImage(robot.createScreenCapture(new Rectangle(0,0,d.width,d.height)), new Color (255, 0, 0), 10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
好吧,你的线程同步的问题在于你根本没有同步。ArrayList您尝试通过使用避免并发访问join()失败,因为您仍然同时thread1运行thread2。如果您阻止它们同时运行,则任何问题都会ArrayList消失,但是当然,您也不会从使用多个线程中获得任何好处。
现在,您可以使用线程安全实现替换该列表,或者添加手动访问同步,但是\xe2\x80\x99s 不应该这样做。这种同步增加的开销可能大于并发计算的任何潜在收益。毕竟,每个像素执行的算术并不复杂。
\n相反,您最好让所有线程使用它们自己的列表,它们可以无争用地添加到这些列表中。然后,在两个线程完成其工作后合并列表。
\n为了使这项任务变得更容易,您应该重构代码,以使用一种具有参数的方法来控制要处理的范围,而不是使用四次相同的代码。
\n此外,您可以替换Integer[]为int[], 以避免将原始值装箱到对象中。
// takes in an input image and a target color and a bound to check within,\n// returns Point to target\npublic static Point processImage(BufferedImage img, Color color, int error)\n throws InterruptedException, ExecutionException {\n\n long start = System.nanoTime();\n //bounds to check on color components, make sure all within [0,255]\n int redLower = color.getRed() - error;\n int redHigher = color.getRed() + error;\n int greenLower = color.getGreen() - error;\n int greenHigher = color.getGreen() + error;\n int blueLower = color.getBlue() - error;\n int blueHigher = color.getBlue() + error;\n \n //place all components within [0,255]\n if (redLower < 0) redLower = 0;\n if (redHigher > 255) redHigher = 255;\n if (greenLower < 0) greenLower = 0;\n if (greenHigher > 255) greenHigher = 255;\n if (blueLower < 0) blueLower = 0;\n if (blueHigher > 255) blueHigher = 255;\n\n //data of image\n int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();\n int width = img.getWidth();\n int height = img.getHeight();\n \n //stores locations\n ArrayList <int[]> locations = processPixels(pixels, height, 0, pixels.length, 1,\n redLower, redHigher, greenLower, greenHigher, blueLower, blueHigher);\n long end = System.nanoTime();\n System.out.println((end-start)/1000000);\n return avgLocation(locations); \n}\nprivate static ArrayList<int[]> processPixels(\n int[] pixels, int height, int from, int to, int divide,\n int redLower, int redHigher, int greenLower, int greenHigher,\n int blueLower, int blueHigher)\n throws InterruptedException, ExecutionException {\n\n List<FutureTask<ArrayList<int[]>>> jobs = new ArrayList<>();\n while(divide > 0 && to - from > 1) {\n int divideF = --divide;\n int mid = (from + to) >>> 1;\n int jobFrom = from, jobTo = mid;\n FutureTask<ArrayList<int[]>> f = new FutureTask<>(() -> processPixels(\n pixels, height, jobFrom, jobTo, divideF,\n redLower, redHigher, greenLower, greenHigher, blueLower, blueHigher));\n new Thread(f).start();\n jobs.add(f);\n from = mid;\n }\n ArrayList<int[]> locations = new ArrayList<>();\n for (int i = from; i < to; i++) {\n int p = pixels[i];\n // get red\n int r = (p >> 16) & 0xff;\n // get green\n int g = (p >> 8) & 0xff;\n // get blue\n int b = p & 0xff;\n //check if all components within bounds\n if (r >= redLower && r<=redHigher && g>=greenLower && g <= greenHigher\n && b >= blueLower && b <= blueHigher) { \n int[] point = {i % height, i/height};\n locations.add(point);\n }\n }\n for(FutureTask<ArrayList<int[]>> j: jobs) locations.addAll(j.get());\n return locations;\n}\n//given 2D array of locations, finds average location of set and returns as point\npublic static Point avgLocation(ArrayList<int[]> list) {\n //if no points in list, return an impossible point on screen\n if (list.size() == 0) {\n return new Point (-100, -100);\n }\n //coordinates of average location (set to 100 to reveal bug easily)\n int avgX = 100, avgY = 100;\n \n int xSum = 0, ySum = 0;\n \n //loop through array\n for (int i = 0; i < list.size(); i++) {\n //for each location, add up coordinates\n xSum += list.get(i)[0];\n ySum += list.get(i)[1];\n }\n avgX = xSum/list.size();\n avgY = ySum/list.size();\n return new Point(avgX, avgY);\n}\nRun Code Online (Sandbox Code Playgroud)\n它将processPixels完成处理指定范围的工作,但首先divide通过将范围减半并生成新线程来调用相同的方法,同时保留另一半。由于其他线程执行的方法也会根据 来创建新线程divide,因此将有 2 个divide线程在执行该作业。因此,0可以选择单线程、1两线程、2四线程、3八线程,依此类推。
所有线程都将执行其工作,添加到本地列表,然后添加子任务的结果(如果有)。
\n但请注意,类似的概念已经在 Java 中实现,并且动态适应可用 CPU 核心的数量和实际工作负载。使用时免费获得
\n// takes in an input image and a target color and a bound to check within,\n// returns Point to target\npublic static Point processImage(BufferedImage img, Color color, int error) {\n long start = System.nanoTime();\n //bounds to check on color components, make sure all within [0,255]\n int redLower = color.getRed() - error;\n int redHigher = color.getRed() + error;\n int greenLower = color.getGreen() - error;\n int greenHigher = color.getGreen() + error;\n int blueLower = color.getBlue() - error;\n int blueHigher = color.getBlue() + error;\n \n //place all components within [0,255]\n if (redLower < 0) redLower = 0;\n if (redHigher > 255) redHigher = 255;\n if (greenLower < 0) greenLower = 0;\n if (greenHigher > 255) greenHigher = 255;\n if (blueLower < 0) blueLower = 0;\n if (blueHigher > 255) blueHigher = 255;\n\n //create final variables to use for function\n int redLowerF = redLower;\n int redHigherF = redHigher;\n int greenLowerF = greenLower;\n int greenHigherF = greenHigher;\n int blueLowerF = blueLower;\n int blueHigherF = blueHigher;\n\n //data of image\n int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();\n int width = img.getWidth();\n int height = img.getHeight();\n\n List<int[]> locations = IntStream.range(0, pixels.length).parallel()\n .filter(i -> {\n int p = pixels[i];\n // get red\n int r = (p >> 16) & 0xff;\n // get green\n int g = (p >> 8) & 0xff;\n // get blue\n int b = p & 0xff;\n //check if all components within bounds\n return r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF\n && b >= blueLowerF && b <= blueHigherF;\n })\n .mapToObj(i -> new int[] {i % height, i / height})\n .collect(Collectors.toList());\n\n //stores locations\n long end = System.nanoTime();\n System.out.println((end-start)/1000000);\n return avgLocation(locations); \n}\n//given 2D array of locations, finds average location of set and returns as point\npublic static Point avgLocation(List<int[]> list) {\n //if no points in list, return an impossible point on screen\n if (list.size() == 0) {\n return new Point (-100, -100);\n }\n //coordinates of average location (set to 100 to reveal bug easily)\n int avgX = 100, avgY = 100;\n int xSum = 0, ySum = 0;\n\n //loop through array\n for (int i = 0; i < list.size(); i++) {\n //for each location, add up coordinates\n xSum += list.get(i)[0];\n ySum += list.get(i)[1];\n }\n avgX = xSum/list.size();\n avgY = ySum/list.size();\n return new Point(avgX, avgY);\n}\nRun Code Online (Sandbox Code Playgroud)\nint[]我们可以进一步改进代码,因为当我们知道索引和坐标之间的关系时,我们不需要将点表示为数组:
public static Point processImage(BufferedImage img, Color color, int error) {\n long start = System.nanoTime();\n //bounds to check on color components, make sure all within [0,255]\n int redLower = color.getRed() - error;\n int redHigher = color.getRed() + error;\n int greenLower = color.getGreen() - error;\n int greenHigher = color.getGreen() + error;\n int blueLower = color.getBlue() - error;\n int blueHigher = color.getBlue() + error;\n \n //place all components within [0,255]\n if (redLower < 0) redLower = 0;\n if (redHigher > 255) redHigher = 255;\n if (greenLower < 0) greenLower = 0;\n if (greenHigher > 255) greenHigher = 255;\n if (blueLower < 0) blueLower = 0;\n if (blueHigher > 255) blueHigher = 255;\n\n //create final variables to use for function\n int redLowerF = redLower;\n int redHigherF = redHigher;\n int greenLowerF = greenLower;\n int greenHigherF = greenHigher;\n int blueLowerF = blueLower;\n int blueHigherF = blueHigher;\n\n //data of image\n int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();\n int width = img.getWidth();\n int height = img.getHeight();\n\n int[] locations = IntStream.range(0, pixels.length).parallel()\n .filter(i -> {\n int p = pixels[i];\n // get red\n int r = (p >> 16) & 0xff;\n // get green\n int g = (p >> 8) & 0xff;\n // get blue\n int b = p & 0xff;\n //check if all components within bounds\n return r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF\n && b >= blueLowerF && b <= blueHigherF;\n })\n .toArray();\n\n //stores locations\n long end = System.nanoTime();\n System.out.println((end-start)/1000000);\n return avgLocation(locations, height); \n}\nprivate static Point avgLocation(int[] locations, int height) {\n if (locations.length == 0) {\n return new Point (-100, -100);\n }\n //coordinates of average location (set to 100 to reveal bug easily)\n int avgX = 100, avgY = 100;\n int xSum = 0, ySum = 0;\n\n //loop through array\n for (int i: locations) {\n int x = i % height, y = i / height;\n //for each location, add up coordinates\n xSum += x;\n ySum += y;\n }\n avgX = xSum/locations.length;\n avgY = ySum/locations.length;\n return new Point(avgX, avgY);\n}\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
145 次 |
| 最近记录: |