這是在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)
第 151 行是
xSum = list.get(i)[0];
第 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();
}
}
}
}
uj5u.com熱心網友回復:
好吧,您的執行緒同步有什么問題是您根本沒有。您嘗試ArrayList通過 using避免并發訪問join()失敗,因為您仍然擁有thread1并thread2同時運行。如果你阻止它們同時運行,任何問題ArrayList都會消失,但當然,你也不會從使用多個執行緒中獲得任何好處。
現在,您可以用執行緒安全實作替換串列或為訪問添加手動同步,但這不是您應該這樣做的方式。這種同步增加的開銷可能大于并發計算的任何潛在收益。畢竟,每個像素執行的算術并不復雜。
相反,您最好讓所有執行緒使用它們自己的串列,它們可以添加到其中而不會發生爭用。然后,在兩個執行緒完成作業后合并串列。
為了使這項任務更容易,您應該重構代碼,使其具有一個具有引數的方法來控制要處理的范圍,而不是使用相同的代碼四次。
此外,您可以替換Integer[]為int[], 以避免將原始值裝箱到物件中。
// 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, ExecutionException {
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;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
//stores locations
ArrayList <int[]> locations = processPixels(pixels, height, 0, pixels.length, 1,
redLower, redHigher, greenLower, greenHigher, blueLower, blueHigher);
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations);
}
private static ArrayList<int[]> processPixels(
int[] pixels, int height, int from, int to, int divide,
int redLower, int redHigher, int greenLower, int greenHigher,
int blueLower, int blueHigher)
throws InterruptedException, ExecutionException {
List<FutureTask<ArrayList<int[]>>> jobs = new ArrayList<>();
while(divide > 0 && to - from > 1) {
int divideF = --divide;
int mid = (from to) >>> 1;
int jobFrom = from, jobTo = mid;
FutureTask<ArrayList<int[]>> f = new FutureTask<>(() -> processPixels(
pixels, height, jobFrom, jobTo, divideF,
redLower, redHigher, greenLower, greenHigher, blueLower, blueHigher));
new Thread(f).start();
jobs.add(f);
from = mid;
}
ArrayList<int[]> locations = new ArrayList<>();
for (int i = from; i < to; 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) {
int[] point = {i % height, i/height};
locations.add(point);
}
}
for(FutureTask<ArrayList<int[]>> j: jobs) locations.addAll(j.get());
return locations;
}
//given 2D array of locations, finds average location of set and returns as point
public static Point avgLocation(ArrayList<int[]> 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, avgY = 100;
int xSum = 0, 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);
}
The processPixels will do the job of processing the specified range, but first divide the work by halving the range and spawn new threads to call the same method with one half while keeping the other. Since the method executed by the other threads will also create new threads according to the divide, there will be 2divide threads working on the job. So pass 0 for single threaded, 1 for two threads, 2 for four threads, 3 for eight, and so on.
All threads will perform their work, adding to a local list, and add the result of the sub tasks afterwards, if any.
But note that a similar concept has been implemented in Java already, plus dynamic adaptation to the number of available CPU cores and actual workload. You get it for free when using
// 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) {
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 function
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();
List<int[]> locations = IntStream.range(0, pixels.length).parallel()
.filter(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
return r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF
&& b >= blueLowerF && b <= blueHigherF;
})
.mapToObj(i -> new int[] {i % height, i / height})
.collect(Collectors.toList());
//stores locations
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(List<int[]> 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, avgY = 100;
int xSum = 0, 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);
}
We can improve the code even more, as we do not need to express the points as int[] arrays, when we know the relationship between indices and coordinates:
public static Point processImage(BufferedImage img, Color color, int error) {
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 function
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();
int[] locations = IntStream.range(0, pixels.length).parallel()
.filter(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
return r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF
&& b >= blueLowerF && b <= blueHigherF;
})
.toArray();
//stores locations
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations, height);
}
private static Point avgLocation(int[] locations, int height) {
if (locations.length == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100, avgY = 100;
int xSum = 0, ySum = 0;
//loop through array
for (int i: locations) {
int x = i % height, y = i / height;
//for each location, add up coordinates
xSum = x;
ySum = y;
}
avgX = xSum/locations.length;
avgY = ySum/locations.length;
return new Point(avgX, avgY);
}
uj5u.com熱心網友回復:
ArrayList 不是執行緒安全的
創建一個類似的方法
static private void addToList (ArrayList<Integer[]> list, Integer [] p) {
synchronized (list) {
list.add(p);
}
}
可以在您的執行緒中呼叫,例如
Image.addToList(locations, point);
uj5u.com熱心網友回復:
locations ArrayList執行緒通過添加新元素同時修改和更改。ArrayList不適合這種沒有同步的并發修改。但是,有CopyOnWriteArrayList可以支持不同步的并發修改。
嘗試替換行:
ArrayList<Integer[]> locations= new ArrayList<>();
使用以下行:
final List<Integer []> locations = new CopyOnWriteArrayList<>();
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/422753.html
標籤:
上一篇:JPASpring引導上的執行緒
