Multithreading
Nowadays, most personal computers, workstations and servers are sold equipped with a multiple core architecture. In order to explore the benefits of these architectures, one technique is to divide the application into multiple threads, thus each one can run in a different core. In the case of image processing, each thread can handle a different image or a different segment of the same image. In this tutorial is shown how to process a image using multiple threads. Novertheless, the principles are the same in the case of processing multiple images using different threads for them. MarvinThreadMarvinThread provides a transparent way to use Java threads and Marvin plug-ins to process images. The MarvinThread constructor is presented as follow:public MarvinThread(MarvinImagePlugin plg, MarvinImage imgIn, MarvinImage imgOut, MarvinImageMask mask)
plg is the plug-in used to process the image, imgIn is the input image, imgOut is the output image and mask contains the indexes of the pixels that must be processed. In the case of using a thread to process the entire image, this mask must be MarvinImageMask.NULL_MASK. On the other hand, when different threads process a different segment of the same image, the mask of each MarvinThread must contain the pixels of the image segment destined for each thread. In order to listen MarvinThread events is needed to implement the MarvinThreadListener interface. This interface specifies the method threadFinished(MarvinThreadEvent e). Implementing this method an application can know when a thread has finished its job. ExampleTo demonstrate the presented concepts in practice, lets start an example by its skeleton, as shown in Code 1.import java.awt.BorderLayout; import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import marvin.gui.MarvinImagePanel; import marvin.image.MarvinImage; import marvin.image.MarvinImageMask; import marvin.io.MarvinImageIO; import marvin.plugin.MarvinImagePlugin; import marvin.thread.MarvinThread; import marvin.thread.MarvinThreadEvent; import marvin.thread.MarvinThreadListener; import marvin.util.MarvinPluginLoader; /** * Processing images single and multi threaded. * @author Gabriel Ambrósio Archanjo */ public class Multithread extends JFrame implements MarvinThreadListener{ // Interface Components private JButton buttonSingleThread, buttonMultiThread, buttonResetImage; private JLabel labelPerformance; // MarvinImagePanel to display the image private MarvinImagePanel imagePanel; // MarvinImage objects used by the app private MarvinImage imageIn, imageOut, originalImage; private int threadsFinished; private long processStartTime; /** * Create the user interface and load the images. */ public Multithread(){ super("Multithread Sample"); // Buttons ButtonHandler buttonHandler = new ButtonHandler(); buttonSingleThread = new JButton("Single Thread"); buttonMultiThread = new JButton("Multi Thread"); buttonResetImage = new JButton("Reset Image"); buttonSingleThread.addActionListener(buttonHandler); buttonMultiThread.addActionListener(buttonHandler); buttonResetImage.addActionListener(buttonHandler); // Label labelPerformance = new JLabel("Performance:"); // Panels JPanel panelIntern = new JPanel(); panelIntern.add(buttonSingleThread); panelIntern.add(buttonMultiThread); panelIntern.add(buttonResetImage); JPanel panelBottom = new JPanel(); panelBottom.setLayout(new BorderLayout()); panelBottom.add(panelIntern, BorderLayout.NORTH); panelBottom.add(labelPerformance, BorderLayout.SOUTH); imagePanel = new MarvinImagePanel(); // Container Container con = getContentPane(); con.setLayout(new BorderLayout()); con.add(imagePanel, BorderLayout.NORTH); con.add(panelBottom, BorderLayout.SOUTH); // Load Image loadImages(); setSize(originalImage.getWidth()+20,690); setVisible(true); } /** * Load the image processed by the application. Create other two images (imageIn and imageOut) * to be the input and output images. The image panel is set to display the original image. */ private void loadImages(){ originalImage = MarvinImageIO.loadImage("./res/senna.jpg"); imageIn = new MarvinImage(originalImage.getWidth(), originalImage.getHeight()); imageOut = new MarvinImage(originalImage.getWidth(), originalImage.getHeight()); imagePanel.setImage(originalImage); } public void threadFinished(MarvinThreadEvent e){ } public static void main(String args[]){ Multithread t = new Multithread(); t.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } private void singleThread(){ } private void multiThread(){ } private class ButtonHandler implements ActionListener{ public void actionPerformed(ActionEvent e){ imageIn = originalImage.clone(); if(e.getSource() == buttonSingleThread){ singleThread(); } else if(e.getSource() == buttonMultiThread){ multiThread(); } else if(e.getSource() == buttonResetImage){ imagePanel.setImage(originalImage); } } } } This source code only loads the target image and creates the user interface, shown in the image below. Figure 1. Screenshot of the application skeleton shown in the Code 1. The idea is when the user clicks on the button "Single Thread" the application apply a plug-in to the image using only one thread. On the other hand, when the user clicks on the button "Multi Thread", the application uses multiple threads. The time spent by these two methods must be shown in a label. First, letīs start implementing the single threaded method. private void singleThread(){ processStartTime = System.currentTimeMillis(); MarvinImagePlugin plgImage = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.statistical.Maximum.jar"); plgImage.process(imageIn, imageOut); imageOut.update(); imagePanel.setImage(imageOut); labelPerformance.setText("Performance: "+ (System.currentTimeMillis()-processStartTime)+ " milliseconds (Single Thread)"); repaint(); } Now letīs implement the multithreaded method. For this, two plug-ins are loaded and two masks are created, one responsible for the top half of the image and the other for the lowe half. Two MarvinThreads are created too, each one receiving a reference to the same image, but with a different plug-in and mask. In this fashion, each MarvinThread uses a different plug-in to process the same image in a different region. private void multiThread(){ processStartTime = System.currentTimeMillis(); // Load Plug-ins MarvinImagePlugin plgImage1 = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.statistical.Maximum.jar"); MarvinImagePlugin plgImage2 = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.statistical.Maximum.jar"); // Create masks MarvinImageMask mask1 = new MarvinImageMask ( imageIn.getWidth(), // width imageIn.getHeight(), // height 0, // x-start 0, // y-start imageIn.getWidth(), // regionīs width imageIn.getHeight()/2 // regionīs height ); MarvinImageMask mask2 = new MarvinImageMask ( imageIn.getWidth(), // width imageIn.getHeight(), // height 0, // x-start imageIn.getHeight()/2, // y-start imageIn.getWidth(), // regionīs width imageIn.getHeight()/2 // regionīs height ); MarvinThread mvThread1 = new MarvinThread(plgImage1, imageIn, imageOut, mask1); MarvinThread mvThread2 = new MarvinThread(plgImage2, imageIn, imageOut, mask2); mvThread1.addThreadListener(this); mvThread2.addThreadListener(this); mvThread1.start(); mvThread2.start(); threadsFinished = 0; } Finally, we have the implementation of the method responsible to listen MarvinThread events. Everytime a MarvinThread fishishes its job, a MarvinThreadEvent is dispatched. In our example, the method threadFinished(...) will be invoked two times, one time after each thread finished its job. Therefore, in the second invokation, the application updates the image and show the spent time. The implementation of the method threadFinished(...) is presented below. public void threadFinished(MarvinThreadEvent e){ threadsFinished++; if(threadsFinished == 2){ imageOut.update(); imagePanel.setImage(imageOut); labelPerformance.setText("Performance: "+ (System.currentTimeMillis()-processStartTime)+ " milliseconds (Multi Thread)"); repaint(); } } The final source code you can check out here: Multithread.java
For any kind of support: Marvin Discussion Group
|
||||