简体   繁体   English

Java多线程对图像进行阈值处理

[英]Image thresholding by multiple threads java

I have a school project, where we should use paralel programing to make some alghorithm work faster. 我有一个学校项目,我们应该在其中使用并行编程来使某些算法更快地工作。 For example I have chosen Image Thresholding. 例如,我选择了“图像阈值”。

So I created Java program, that does it normaly (load image, loop through all the pixels, counts threshold value, again loop through all the pixels and set black or white color, if it's greater or lower than threshold value). 因此,我创建了一个Java程序,该程序可以正常执行(加载图像,循环遍历所有像素,计数阈值,再次遍历所有像素并设置黑色或白色(如果大于或小于阈值)。 This takes me ~5seconds on my notebook with a picture about 4000x3000 and ~49 seconds with image 11500x11500. 这需要花费我约5秒钟的时间在笔记本上拍摄约4000x3000的图片,并花费约49秒的图像(约11500x11500)。

Then I created another program, which should be using threads, to make them loops finish faster. 然后,我创建了另一个程序,该程序应该使用线程,以使它们的循环更快地完成。

Now I'm creating 4 threads, where each of them proceses 1/4 of the image. 现在,我正在创建4个线程,每个线程处理图像的1/4。 First they are adding threshold vlaues into synchronized arraylist and after all of them are finished, I calculate the threshold value. 首先,他们将阈值vlaues添加到同步的arraylist中,并且在所有这些完成之后,我计算阈值。 Then I create another 4 threads, and they are again procesing 1/4 of the image each and setting black or white into the picture. 然后创建另外4个线程,它们分别处理图像的1/4,并将黑色或白色设置为图片。

This tooks me 12seconds with the 4000x3000 image and throws java.lang.OutOfMemoryError: Java heap space (in all threads) with the 11500x11500 one. 我用4000x3000的图像花了12秒,并抛出了java.lang.OutOfMemoryError:Java堆空间(在所有线程中)是11500x11500。

 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;        
    }    
}

And the Threshold class: 和阈值类:

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());    
    }
}

So why is it that it is slower when multithreading? 那么,为什么在多线程时速度较慢? I'm not synchronizing here nothing except the list, since the threads shouldn't be using same indexes in the array of pixels. 除了列表之外,我这里没有同步任何东西,因为线程不应该在像素数组中使用相同的索引。

Profiler picture here: 探查器图片在这里:

Serial: 串行: 串行

Paralel: 相同常: 相同常

There are couple of things you need to consider in this parallelizing case. 在这种并行化情况下,需要考虑几件事。

  1. Your code only does asynchronous processing of image (Calculating threshold and creating threshold image) but your Image IO (Writing) is blocked until all threads finish processing. 您的代码仅执行图像的异步处理(计算阈值并创建阈值图像),但是在所有线程完成处理之前,将阻止图像IO(写入)。
  2. Other more important factor is that how did you come up with 4 Threads solution. 其他更重要的因素是您如何提出4线程解决方案。 What is the reason behind you choosing that 4 Threads is the ideal amount of threads. 选择4个线程是理想数量的线程的背后原因是什么? In a CPU and Memory Intensive multi-threaded programs like yours ideal no of threads = Number of CPUs + 1 . 在像您这样的CPU和内存密集型多线程程序中,理想的线程Number of CPUs + 1 = Number of CPUs + 1 Having more threads doesn't make your program perform faster in fact it will degrade performance. 拥有更多的线程并不能使您的程序执行得更快,实际上会降低性能。
  3. Image processing is of course memory intensive, you need to increase Heap Space when running your program with large images. 图像处理当然会占用大量内存,因此在运行带有大图像的程序时需要增加堆空间。

Please consider the above mentioned. 请考虑上述内容。

EDIT 编辑

You can start off by making your code more readable and less code duplicating. 您可以通过使代码更具可读性和减少代码重复来开始。 You can make use of a CyclicBarrier to achieve sequential execution of parallel tasks. 您可以利用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");

    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM