使用动态负载调度的并行图像卷积滤波器:工件

时间:2016-07-07 14:35:55

标签: java multithreading image-processing convolution

我遇到了以下问题:我使用动态负载调度方法创建了基于this paper的并行化prewitt过滤器。不幸的是,我遇到了我的序列化过滤器没有显示的工件,出现在看似随机的地方,这意味着我在线程中有同步问题。但是,我无法弄清楚它在哪里。我有一个非常相似的灰度过滤器......到目前为止,没有类似的问题,但现在确实如此。

desired result result from parallel filtering
左图是期望的结果,用顺序算法实现,右图显示底部的上述伪像 通过进一步测试,我现在相当确定我的线程跳过图像的某些部分而不过滤它们。我会继续调查。

我的代码结构如下:ParallelPrewittFilter继承自ParallelSobelianFilter,只实现一个工厂方法,创建正确类型的worker,即下一个(实现runnable接口的类)。 PrewittFilterWorker继承自SobelianFilterWorker(后者继承自ParallelFilterWorker)并仅实现返回卷积内核的方法。因此,我现在将发布ParallelSobelianFilter和SobelianFilter worker的相关代码。最后一个代码块是加载调度代码。

ParallelSobelianFilter:

public BufferedImage applyFilter(BufferedImage image) {
  //taking the red and alpha channels from image and placing them
  //in the arrays red[width*height] and alpha[width*height]

  ParallelFilterWorker.resetDynamicLoadCounter();
  for (SobelianFilterWorker worker : workers) {
    worker.setSourceArrays(width, height, alpha, red, green, blue, hasAlpha);
    worker.setDestImage(result);
  }

  for (Thread thread : threads) {
    System.out.println("starting thread ");
    thread.start();
  }

  for (Thread thread : threads) {
    try {
      thread.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  return result;
}

SobelianFilterWorker:

protected void filterPixel(int index) {
  //[..]
  if (x < 1 || y < 1 || x > width - 2 || y > height - 2) {
    //do nothing
    color = red[index];
  } else {
    firstPass = red[index - 1] * kernel[0][1] + red[index + 1] * kernel[2][1]
        + red[index - width - 1] * kernel[0][0] + red[index - width] * kernel[1][0] 
        + red[index - width + 1] * kernel[2][0] + red[index + width - 1] * kernel[0][2]
        + red[index + width] * kernel[1][2] + red[index + width + 1] * kernel[2][2];

    //transposed kernel
    secondPass = red[index - 1] * kernel[1][0] + red[index + 1] * kernel[1][2]
        + red[index - width - 1] * kernel[0][0] + red[index - width] * kernel[0][1] 
        + red[index - width + 1] * kernel[0][2] + red[index + width - 1] * kernel[2][0]
        + red[index + width] * kernel[2][1] + red[index + width + 1] * kernel[2][2];
    color = (int) Math.floor(Math.sqrt(firstPass * firstPass + secondPass * secondPass));
  }
  if (color > 255) {
    color = 255;
  }
  // ... color turned into an ARGB integer argb
    destImage.setRGB(x, y, argb);      
  }

}

我怀疑错误是在上面两个块中,因为当filterPixel是一个简单的灰度过滤器时,下面的代码工作正常:

ParallelFilterWorker:

private static final int loadPerInterval = 500;

private static volatile int dynamicLoadCounter = 0;

public static synchronized void resetDynamicLoadCounter() {
  dynamicLoadCounter = -loadPerInterval;
}

public void run() {
  if (checkNull()) {
    return;
  }

  int localCounter = loadPerInterval - 1;
  int start = 0;
  while (dynamicLoadCounter < width * height) {
    localCounter++;
    if (localCounter == loadPerInterval) {
      //fetch a package of pixels to work on and mark them as being worked on
      start = syncCounterUp();
      System.out.println("#" + threadID + " starting at " + start);
      localCounter = 0;
    }
    if (start + localCounter < width * height) {
      filterPixel(start + localCounter);
    } else {
      return;
    }
  }
}

private static synchronized int syncCounterUp() {
  dynamicLoadCounter += loadPerInterval;
  return dynamicLoadCounter;
}

出了什么问题,我错过了同步吗?我会非常感兴趣地解释我的线程究竟在做什么以及这些工件出现的原因。谢谢你看看!

2 个答案:

答案 0 :(得分:1)

这可以通过使用ThreadPool并使用Callable进行解决来解决。由于锁定,这种接缝有点过度设计,但是即使一个图像没有通过第一个过滤器通过完成并且在第二次通过中使用,它也会保持线程分开。 这里有一个关于如何工作的例子:

import java.awt.image.BufferedImage;
import java.util.*;
import java.util.concurrent.*;

public class Filter {
    int lockcount = 0;
    Worker worker = new Worker();
    List<Worker> fpThreads = new ArrayList<Worker>();
    List<Worker> spThreads = new ArrayList<Worker>();
    ExecutorService executor = Executors.newCachedThreadPool();
    Map<Integer, Object> lockMap = Collections.synchronizedMap(new Hashtable<Integer, Object>());


    public static void main(String[] args) {
        Filter filter = new Filter();

        for (int i = 0; i < 1000; i++) {
            Worker w1 = new Worker();
            filter.fpThreads.add(w1);
        }
        for (int i = 0; i < 1000; i++) {
            Worker w1 = new Worker();
            filter.spThreads.add(w1);
        }

        filter.filer();
    }


    public void filer() {
        runPass(lockMap, fpThreads);
        runPass(lockMap, spThreads);
    }

    private BufferedImage runPass(Map<Integer, Object> lockMap, List<Worker> threads) {
        Future<BufferedImage> future = null;
        Object lock = null;
        for (Worker thread : threads) {
            lock = lockMap.get(worker.hashCode());
            if (lock == null) {
                lock = thread;
                lockMap.put(thread.hashCode(), lock);
                future = executor.submit(thread);
            } else { //we have a lock
                waitOnLock(thread, lock);
            }
        }
        try {
            //get() waits until it gets an result
            return future.get();

        } catch (InterruptedException | ExecutionException ex) {
            // TODO Auto-generated catch block
            ex.printStackTrace();
        }
        synchronized (lock) {
            lockMap.remove(lock.hashCode());
            lock.notifyAll();
            System.out.println("Notify: " + lock.hashCode() + " with " + lockcount + " locks in use.");
        }
        return null;
    }

    private void waitOnLock(Worker thread, Object lock) {
        synchronized (lock) {
            try {
                lockcount++;
                System.out.println("Wait: " + thread.hashCode() + " with " + lockcount + " locks in use.");
                lock.wait();
                lockcount--;
                System.out.println("Continuing: " + thread.hashCode() + " with " + lockcount + " locks in use.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Worker implements Callable<BufferedImage> {

    public BufferedImage call() throws Exception {
        //Here you do you filtering
        System.out.println("Thread called: " + this.hashCode());
        Thread.sleep(1000);
        return null;
    }
}

出于您的目的,您可以实施工作人员来进行过滤。

答案 1 :(得分:0)

Heureka!我的问题比我想象的要简单得多。最初看起来我的灰度过滤器工作正常,实际上,它并没有让我感到沮丧。这是解决方案:

public void run() {
  if (checkNull()) {
    return;
  }

  int localCounter = loadPerInterval - 1;
  int start = 0;
  while (start + localCounter < width * height) {
    localCounter++;
    if (localCounter == loadPerInterval) {
      start = syncCounterUp();
      localCounter = 0;
    }
    if (start + localCounter < width * height) { 
      filterPixel(start + localCounter);
    }
  }
}

这是ParallelFilterworker中的run方法。我已经改变了while循环的条件。

发生了什么,是因为某些线程,while条件变为false,因为其他线程已经获得了最后剩余的像素,而这个线程仍然忙于一些。这导致线程放弃它所担保的像素,因为它认为一切都已完成。多么愚蠢的错误。非常感谢大家的答案和时间!