图像阈值由多个线程java

时间:2015-06-08 11:12:25

标签: java multithreading image-processing

我有一个学校项目,我们应该使用paralel编程来使一些算法工作更快。例如,我选择了Image Thresholding。

所以我创建了Java程序,它正常地执行它(加载图像,遍历所有像素,计算阈值,再次遍历所有像素并设置黑色或白色,如果它大于或小于门槛值)。 这需要我在我的笔记本上约5秒,图片大约4000x3000,大约49秒,图片11500x11500。

然后我创建了另一个应该使用线程的程序,使它们的循环更快完成。

现在我创建4个线程,每个线程执行图像的1/4。首先,他们将阈值值添加到同步arraylist中,并在完成所有这些后,我计算阈值。然后我创建另外4个线程,他们再次处理图像的1/4,并在图片中设置黑色或白色。

使用4000x3000图像花了我12秒,并抛出了java.lang.OutOfMemoryError:带有11500x11500的Java堆空间(在所有线程中)。

 public class PprPrahovaniParalelne{

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException {
        final Threshold image = new Threshold(nactiObrazek("ryba.jpg"));


        final int width = image.getImage().getWidth();
        final int height = image.getImage().getHeight();


        Thread t1 = new Thread(){
            int threshold;
            public void run(){
                System.out.println("Thread 1 - Started");
                for(int y = 0; y < height/4;y++){
                    for(int x = 0; x < width;x++){
                        Color color = new Color(image.getImage().getRGB(x,y));
                        threshold = (color.getRed()+color.getGreen()+color.getBlue())/3;
                        image.addThreshold(threshold); 
                    }
                }
                System.out.println("Thread 1 - finished");
            }
        };

        Thread t2 = new Thread(){
            int threshold;
            @Override
            public void run(){
                for(int y = height/4; y < height/4*2;y++){
                    for(int x = 0; x < width;x++){
                        Color barva = new Color(image.getImage().getRGB(x,y));
                        threshold = (barva.getRed()+barva.getGreen()+barva.getBlue())/3;
                        image.addThreshold(threshold); 
                    }
                }
                System.out.println("Thread 2 - finished");
            }
        };

        Thread t3 = new Thread(){
            int threshold;
            @Override
            public void run(){
                for(int y = height/4*2; y < height/4*3;y++){
                    for(int x = 0; x < width;x++){
                        Color barva = new Color(image.getImage().getRGB(x,y));
                        threshold = (barva.getRed()+barva.getGreen()+barva.getBlue())/3;
                        image.addThreshold(threshold); 
                    }
                }
                System.out.println("Thread 3 - finished");
            }
        };

        Thread t4 = new Thread(){
            int threshold;
            @Override
            public void run(){
                for(int y = height/4*3; y < height;y++){
                    for(int x = 0; x < width;x++){
                        Color barva = new Color(image.getImage().getRGB(x,y));
                        threshold = (barva.getRed()+barva.getGreen()+barva.getBlue())/3;
                        image.addThreshold(threshold); 
                    }
                }
                System.out.println("Thread 4 - finished");
            }
        };

        t1.start();
        t2.start();
        t4.start();
        t3.start();

        try{
            t1.join();
            t2.join();
            t3.join();
            t4.join();
        }catch(InterruptedException e){
            e.printStackTrace();
        }


        image.countThreshold();
        System.out.println("Threshold je: " + image.getThreshold());

        Thread t5 = new Thread(){
            Color cerna = new Color(255,255,255);
            Color bila = new Color(0,0,0);
            int threshold;
            @Override
            public void run(){
                for(int y = 0; y < height/4;y++){
                    for(int x = 0; x < width;x++){
                        Color barva = new Color(image.getImage().getRGB(x,y));
                        threshold = (barva.getRed()+barva.getGreen()+barva.getBlue())/3;
                        if(threshold > image.getThreshold()){
                            image.getImage().setRGB(x, y, cerna.getRGB());
                        }else{
                            image.getImage().setRGB(x, y, bila.getRGB());
                        }
                    }
                }                
                System.out.println("Thread 5 - finished");
            }
        };

        Thread t6 = new Thread(){
            Color cerna = new Color(255,255,255);
            Color bila = new Color(0,0,0);
            int threshold;
            @Override
            public void run(){
                for(int y = height/4; y < height/4*2;y++){
                    for(int x = 0; x < width;x++){
                        Color color = new Color(image.getImage().getRGB(x,y));
                        threshold = (color.getRed()+color.getGreen()+color.getBlue())/3;
                        if(threshold > image.getThreshold()){
                            image.getImage().setRGB(x, y, cerna.getRGB());
                        }else{
                            image.getImage().setRGB(x, y, bila.getRGB());
                        }
                    }
                }                
                System.out.println("Thread 6 - finished");
            }
        };

        Thread t7 = new Thread(){
            Color cerna = new Color(255,255,255);
            Color bila = new Color(0,0,0);
            int threshold;
            @Override
            public void run(){
                for(int y = height/4*2; y < height/4*3;y++){
                    for(int x = 0; x < width;x++){
                        Color color = new Color(image.getImage().getRGB(x,y));
                        threshold = (color.getRed()+color.getGreen()+color.getBlue())/3;
                        if(threshold > image.getThreshold()){
                            image.getImage().setRGB(x, y, cerna.getRGB());
                        }else{
                            image.getImage().setRGB(x, y, bila.getRGB());
                        }
                    }
                }                
                System.out.println("Thread 7 - finished");
            }
        };

        Thread t8 = new Thread(){
            Color cerna = new Color(255,255,255);
            Color bila = new Color(0,0,0);
            int threshold;
            @Override
            public void run(){
                for(int y = height/4*3; y < height;y++){
                    for(int x = 0; x < width;x++){
                        Color barva = new Color(image.getImage().getRGB(x,y));
                        threshold = (barva.getRed()+barva.getGreen()+barva.getBlue())/3;
                        if(threshold > image.getThreshold()){
                            image.getImage().setRGB(x, y, cerna.getRGB());
                        }else{
                            image.getImage().setRGB(x, y, bila.getRGB());
                        }
                    }
                }                
                System.out.println("Thread 8 - finished");
            }
        };

        t5.start();
        t6.start();
        t7.start();
        t8.start();

        try{
            t5.join();
            t6.join();
            t7.join();
            t8.join();
        }catch(InterruptedException e){
            e.printStackTrace();
        }

        File hotovo = new File("ryba_prahovanej.jpg");
        ImageIO.write(image.getImage(), "jpg", hotovo);

    }


    public static BufferedImage nactiObrazek(String nazev){
        BufferedImage img = null;
        try {
            img = ImageIO.read(new File(nazev));
        } catch (IOException e) {
        }
        return img;        
    }    
}

Threshold类:

public class Threshold {
    private BufferedImage image;
    final private List<Integer> list;
    private int threshold;

    public int getThreshold() {
        return threshold;
    }

    public List<Integer> getList(){
        return list;
    }

    public Threshold(BufferedImage obrazek) {
        this.list = Collections.synchronizedList(new ArrayList<Integer>());
        this.image = obrazek;
    }

    public void setObrazek(BufferedImage obrazek){
        this.image = obrazek;
    }

    public BufferedImage getImage(){
        return this.image;
    }

    public void addThreshold(int threshold){
        list.add(threshold);
    }

    public void countThreshold(){
        long sum = 0;
        for (Iterator<Integer> it = list.iterator(); it.hasNext();) {
            int item = it.next();
            sum += item;
        }
        this.threshold = (int) (sum/list.size());    
    }
}

那么为什么多线程时它会变慢?除了列表之外,我没有在这里同步,因为线程不应该在像素数组中使用相同的索引。

Profiler图片在这里:

串行: Serial

相同常: Paralel

1 个答案:

答案 0 :(得分:1)

在此并行化案例中,您需要考虑几件事情。

  1. 您的代码仅对图像进行异步处理(计算阈值并创建阈值图像),但在所有线程完成处理之前,您的图像IO(写入)将被阻止。
  2. 其他更重要的因素是你是如何提出4线程解决方案的。你选择4个线程是理想的线程数量背后的原因是什么。在像你这样的CPU和内存密集型多线程程序中,理想的线程数为Number of CPUs + 1。拥有更多线程并不会使您的程序执行速度更快,实际上会降低性能。
  3. 图像处理当然是内存密集型的,您需要在使用大图像运行程序时增加堆空间。
  4. 请考虑上述情况。

    修改

    您可以从使代码更易读,减少代码重复开始。您可以使用CyclicBarrier来实现并行任务的顺序执行。

    import java.awt.image.*;
    import java.io.*;
    import java.awt.*;
    import javax.imageio.*;
    import java.util.concurrent.*;
    
    public class PprPrahovaniParalelne {
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) throws IOException {
            final Threshold image = new Threshold(nactiObrazek("DSC03691.jpg"));
    
    
            final int width = image.getImage().getWidth();
            final int height = image.getImage().getHeight();
    
        final int nCpu = Runtime.getRuntime().availableProcessors() + 1;
    
        ExecutorService threadPool = Executors.newFixedThreadPool(nCpu);
    
        System.out.println("Number of CPUs : "+nCpu);
    
        CyclicBarrier cyclicBarrier = new CyclicBarrier(4, new Runnable() {
                private int count = 1;
    
                public void run() {
                    if(count == 1) {
                image.countThreshold();
                    System.out.println("Threshold je: " + image.getThreshold());
    
            } else {
                try {
                    File hotovo = new File("ryba_prahovanej.jpg");
                        ImageIO.write(image.getImage(), "jpg", hotovo);
                } catch(IOException e) {
                    System.err.println("Error while writing : " + e);
                }
                threadPool.shutdownNow();
            }
            count++;
                }
            });
    
        threadPool.submit(new ImageProcessingTask(0, height/4, width, image, cyclicBarrier));
        threadPool.submit(new ImageProcessingTask(height/4, height/4*2, width, image, cyclicBarrier));
        threadPool.submit(new ImageProcessingTask(height/4*2, height/4*3, width, image, cyclicBarrier));
        threadPool.submit(new ImageProcessingTask(height/4*3, height, width, image, cyclicBarrier));
    
        }
    
    
        public static BufferedImage nactiObrazek(String nazev){
            BufferedImage img = null;
            try {
                img = ImageIO.read(new File(nazev));
            } catch (IOException e) {
            }
            return img;        
        }    
    }
    
    class ImageProcessingTask implements Runnable {
    
        private int start;
        private int height;
        private int width;
        private Threshold image;
        private CyclicBarrier barrier;
    
        public ImageProcessingTask(int start, int height, int width, Threshold image, CyclicBarrier barrier) {
            this.start = start;
            this.height = height;
            this.width = width;
            this.image = image;
            this.barrier = barrier;
        }
    
            public void run(){
                    int threshold;
                    System.out.println(Thread.currentThread().getName()+" - Started");
                    for(int y = start; y < height;y++){
                        for(int x = 0; x < width;x++){
                            Color color = new Color(image.getImage().getRGB(x,y));
                            threshold = (color.getRed()+color.getGreen()+color.getBlue())/3;
                            image.addThreshold(threshold); 
                        }
                    }
            try {
                int count = barrier.await();
                if(count == 0) {
                    barrier.reset();
                    System.out.println("Resetting Cyclic Barrier");
                }
            } catch(InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch(Exception e) {
                e.printStackTrace();
            }
            Color cerna = new Color(255,255,255);
                    Color bila = new Color(0,0,0);
            for(int y = start; y < height;y++){
                        for(int x = 0; x < width;x++){
                            Color barva = new Color(image.getImage().getRGB(x,y));
                            threshold = (barva.getRed()+barva.getGreen()+barva.getBlue())/3;
                            if(threshold > image.getThreshold()){
                                image.getImage().setRGB(x, y, cerna.getRGB());
                            }else{
                                image.getImage().setRGB(x, y, bila.getRGB());
                            }
                        }
                    }    
            try {
                barrier.await();
            } catch(InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch(Exception e) {
                e.printStackTrace();
            }
                    System.out.println(Thread.currentThread().getName()+" - finished");
    
        }
    }