2016-07-07 3 views
0

다음과 같은 문제가 있습니다. 동적로드 예약 방식을 사용하여 this paper에 기반한 병렬 사전 필터를 생성했습니다. 불행히도 직렬 필터가 표시하지 않는 유물이 발생하여 스레드에서 동기화 문제가 있다는 것을 암시하는 임의의 위치에 나타납니다. 그러나 나는 그것이 어디에 있는지 알 수 없다. 나는 지금까지 비슷한 문제를 보여주지 않았지만 지금은 그렇습니다. 매우 유사한 그레이 스케일 필터를 가지고 있습니다.동적로드 스케줄링을 사용하는 병렬 이미지 컨볼 루션 필터 : 아티팩트

desired resultresult from parallel filtering
왼쪽 그림은 원하는 결과 순차 알고리즘 달성 올바른 하단에 상기 아티팩트를 도시한다.
추가 테스트를 통해 이제는 스레드가 이미지의 특정 부분을 필터링하지 않고 건너 뜁니다. 나는 계속 조사 할 것이다.

내 코드의 구조는 다음과 같습니다. ParallelPrewittFilter는 ParallelSobelianFilter에서 상속을 받고 올바른 종류의 작업자, 즉 다음 인스턴스 (실행 가능한 인터페이스를 구현하는 클래스)를 만드는 팩터 리 메서드 만 구현합니다. 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 간단한 그레이 스케일 필터 때 다음과 같은 코드가 잘 작동으로 오류가, 위의 두 블록에 의심

병렬 필터 작업자 :

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

무엇이 잘못 되었나요? 동기화가 누락 되었습니까? 나는 내 스레드가 정확히 무엇을하고 있으며 왜이 유물이 나타나는 지에 대한 설명에 매우 흥미가있을 것입니다. 모양을 가져 주셔서 감사합니다!

+0

나는 도움을 얻으려고했다. 나쁘다. – JayEff

+0

코드에서 무엇이 잘못되었는지 파악하는 것이 약간 어렵습니다. 도움이되는 것은 단일 스레드를 사용할 때 이미지와 결과를 보여주는 것입니다. – FiReTiTi

+0

@JayElf 그래서 자기 연민에 빠지기 전에 문제를 해결하기 위해 인터넷에있는 사람들에게 고통스러운 한 시간을 성공적으로 기다렸습니다. 나는 당신의 코드가 무엇을 해야할지 알아 내기 위해 1 시간이 필요하다. 샘플 입력, 출력 및 실행 가능한 기본 방법을 제공하십시오. – Gilfoyle

답변

0

허 레카! 내 문제는 내가 생각했던 것보다 훨씬 간단했다. 처음에는 내 그레이 스케일 필터가 정상적으로 작동하는 것처럼 보였으 나 실제로는 그렇지 않았습니다. 해결책은 다음과 같습니다.

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의 실행 방법입니다. while 루프의 조건이 변경되었습니다.

일부 스레드의 경우 while 상태가 false로 바뀌 었습니다. 다른 스레드가 이미 마지막 남은 픽셀을 얻었으므로이 스레드가 여전히 사용 중일 때 발생했습니다. 이로 인해 스레드는 모든 것이 완료되었다고 생각할 때 그 픽셀을 버렸습니다. 어리석은 실수 야.모두의 답변과 시간을 가져 주셔서 대단히 감사합니다!

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

은 당신의 목적을 위해 당신은 당신의 필터링을 할 수있는 노동자를 구현할 수 있습니다.

+0

와우, 정말 정교한 답변입니다. 정말 고마워요. 나는 이것을보고 무엇이 진행되고 있는지 보게 될 것이다. (이후 두 번 통과 할 필요가 없다는 사실을 깨달았습니다. 한 번에 모두 할 수 있기 때문에 조금 더 간소화 할 수 있습니다) – JayEff

+0

특별히 문제를 해결하지는 않았지만 귀하의 대답은 매우 유익했으며 저에게 소개되었습니다. 내가 전에 알지 못했던 개념들. 고맙습니다! – JayEff