2013-04-18 3 views
4

저는 C#의 가장자리 감지 프로그램에서 작업하고 있으며 더 빠르게 실행하도록 최근에 잠금 비트를 사용하도록했습니다. 그러나 lockBits는 여전히 실행 속도가 빠르지 않습니다. 문제는 일반적인 알고리즘 일 수 있지만 이미지 처리에 사용할 수있는 lockBits보다 나은 것이 있는지 궁금합니다.잠금 비트보다 빠른 이미지 처리

문제가 알고리즘 인 경우 다음은 기본적인 설명입니다. 색상 배열 (픽셀을 나타내는 lockbit를 사용하여 만든)을 통과하고 각 색상에 대해 해당 픽셀 주변의 8 픽셀의 색상을 확인합니다. 이러한 픽셀이 현재 픽셀을 충분히 가깝게 일치시키지 않으면 현재 픽셀을 가장자리로 간주합니다.

다음은 픽셀이 가장자리 인 경우 정의되는 기본 코드입니다. 그것은 9 색의 Color []를 취하는데, 첫 번째는 픽셀이 검사하는 색입니다.

public Boolean isEdgeOptimized(Color[] colors) 
{ 
    //colors[0] should be the checking pixel 
    Boolean returnBool = true; 
    float percentage = percentageInt; //the percentage used is set 
    //equal to the global variable percentageInt 

    if (isMatching(colors[0], colors[1], percentage) && 
      isMatching(colors[0], colors[2], percentage) && 
      isMatching(colors[0], colors[3], percentage) && 
      isMatching(colors[0], colors[4], percentage) && 
      isMatching(colors[0], colors[5], percentage) && 
      isMatching(colors[0], colors[6], percentage) && 
      isMatching(colors[0], colors[7], percentage) && 
      isMatching(colors[0], colors[8], percentage)) 
    { 
     returnBool = false; 
    } 
    return returnBool; 
} 

이 코드는 모든 픽셀에 적용되며 색상은 lockbits를 사용하여 가져옵니다.

근본적으로 질문은, 어떻게하면 프로그램을 더 빨리 실행할 수 있습니까? 그것은 내 알고리즘인가, 아니면 내가 lockBits보다 빠를 수있는 무언가가 존재 하는가? 그런데

이 프로젝트는 GitHub의에 here

+0

그리고 ... 문제는 무엇인가? – LightStriker

+1

편집하지 못했습니다. 죄송합니다. – vkoves

+1

코드를 게시하십시오. 우리는 Github에서 프로젝트를 탐색하여 관련 부분을 찾고 싶지 않습니다. – leonbloy

답변

5

실제로 부동 소수점 숫자를 백분율로으로 전달하고 있습니까?

저는 GitHub에서 isMatching을위한 코드를 보았습니다. 자바에서 이걸 이식 했지, 그렇지? C#은 bool이 아닌 Boolean을 사용하며, 확실하지는 않지만 많은 복싱과 언 박싱을하는 코드의 모습이 마음에 들지 않습니다.

public static bool IsMatching(Color a, Color b, int percent) 
{ 
    //this method is used to identify whether two pixels, 
    //of color a and b match, as in they can be considered 
    //a solid color based on the acceptance value (percent) 

    int thresh = (int)(percent * 255); 

    return Math.Abs(a.R - b.R) < thresh && 
      Math.Abs(a.G - b.G) < thresh && 
      Math.Abs(a.B - b.B) < thresh; 
} 

이 당신은 픽셀 당하고있는 작업의 양을 줄일 것에 필요하지 않은 경우 또한, 당신은 부동 소수점 곱셈과 비교의 톤을하고 있습니다. 저는 여전히 픽셀 당 루프, 특히 8x 픽셀 루프의 중간에서 메서드 호출을 피하려고하기 때문에 그것을 좋아하지 않습니다. 메서드를 사용하지 않는 인스턴스를 줄이기 위해 정적으로 만들었습니다. 우리가 단지 1 곱하기, 복싱을하고 있기 때문에 이러한 변화만으로도 아마도 두 배의 성능을 발휘할 것이고, 이제 &의 고유 한 단락 회로를 사용하여 작업을 줄일 수 있습니다.

내가이 일을한다면, 나는 이런 식으로 뭔가를 할 가능성이있을 것입니다 : 이제 배열 등등에 액세스하고 줄이기 위해 끔찍한 포인터 맹 글링 모든 종류의 작업을 수행

// assert: bitmap.Height > 2 && bitmap.Width > 2 
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), 
         ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 

int scaledPercent = percent * 255; 
unsafe { 
    byte* prevLine = (byte*)data.Scan0; 
    byte* currLine = prevLine + data.Stride; 
    byte* nextLine = currLine + data.Stride; 

    for (int y=1; y < bitmap.Height - 1; y++) { 

     byte* pp = prevLine + 3; 
     byte* cp = currLine + 3; 
     byte* np = nextLine + 3; 
     for (int x = 1; x < bitmap.Width - 1; x++) { 
      if (IsEdgeOptimized(pp, cp, np, scaledPercent)) 
      { 
       // do what you need to do 
      } 
      pp += 3; cp += 3; np += 3; 
     } 
     prevLine = currLine; 
     currLine = nextLine; 
     nextLine += data.Stride; 
    } 
} 

private unsafe static bool IsEdgeOptimized(byte* pp, byte* cp, byte* np, int scaledPecent) 
{ 
    return IsMatching(cp, pp - 3, scaledPercent) && 
      IsMatching(cp, pp, scaledPercent) && 
      IsMatching(cp, pp + 3, scaledPercent) && 
      IsMatching(cp, cp - 3, scaledPercent) && 
      IsMatching(cp, cp + 3, scaledPercent) && 
      IsMatching(cp, np - 3, scaledPercent) && 
      IsMatching(cp, np, scaledPercent) && 
      IsMatching(cp, np + 3, scaledPercent); 
} 

private unsafe static bool IsMatching(byte* p1, byte* p2, int thresh) 
{ 
    return Math.Abs(p1++ - p2++) < thresh && 
      Math.Abs(p1++ - p2++) < thresh && 
      Math.Abs(p1 - p2) < thresh; 
} 

합니다. 이 모든 포인터 작업으로 인해 불편 함을 느끼면 prevLine, currLine 및 nextLine에 바이트 배열을 할당하고 이동하면서 각 행에 대해 마샬링. 복사를 수행 할 수 있습니다.

알고리즘은 위와 왼쪽에서 한 픽셀 씩 시작하고 바깥 쪽 가장자리를 제외한 이미지의 모든 픽셀을 반복합니다 (가장자리 조건 없음!). 나는 각 줄의 시작, prevLine, currLine 및 nextLine에 대한 포인터를 유지합니다. 그런 다음 x 루프를 시작할 때 이전 픽셀, 현재 픽셀 및 다음 픽셀 인 pp, cp, np를 만듭니다. 현재 픽셀은 실제로 우리가 신경 쓰는 픽셀입니다. pp 바로 위에있는 픽셀, 바로 아래에있는 픽셀입니다. 그것들을 각각 IsMatching을 호출하는 cp 주위를 돌고있는 IsEdgeOptimized로 넘깁니다.

이제이 모든 것이 픽셀 당 24 비트라고 가정합니다. 픽셀 당 32 비트를보고 있다면, 거기에있는 모든 마술 3은 4가되어야하지만, 그 이외의 코드는 변경되지 않습니다. 원하는 경우 픽셀 당 바이트 수를 매개 변수화하여 처리 할 수 ​​있습니다.

참고로, 픽셀의 채널은 일반적으로 b, g, r, (a)입니다.

색상 이고 메모리에 바이트로 저장됩니다. 실제 비트 맵이 24 비트 이미지 인 경우 바이트 블록으로 저장됩니다. 주사선은 data.Stride 바이트이며 적어도 행의 픽셀 수는 3 * 3입니다 (주사선이 자주 채워지기 때문에 더 클 수 있습니다).

byte * 변수를 C#에서 선언 할 때 몇 가지 작업을 수행하고 있습니다. 첫째,이 변수는 메모리에있는 바이트 위치의 주소를 포함한다고 말하고 있습니다. 둘째, 저는 이제 .NET의 모든 안전 조치를 위반하려고합니다. 메모리에서 어떤 바이트를 읽고 쓸 수 있기 때문에 위험 할 수 있습니다.

그래서 내가 좋아하는 무언가가있을 때 : 그것이 말하는

Math.Abs(*p1++ - *p2++) < thresh 

을 (이 긴 것) :

  1. 에 포인트를 P1 바이트를 가지고 개최 위에 그것을
  2. p1에 1을 더합니다 (이것은 ++입니다 - 포인터가 다음 바이트를 가리키게합니다).
  3. p2가 가리키는 바이트를 가져 가서 붙잡습니다.
  4. 1에 p2를 더함
  5. 1 단계에서 3 단계를 뺍니다.
  6. Math.Abs으로 전달하십시오.

역사적으로 한 바이트의 내용을 읽고 앞으로 나아가는 것은 매우 일반적인 작업이며 많은 CPU가 단일 사이클로 파이프 라인하는 몇 가지 명령어의 단일 작업을 구성하거나 그래서.

우리는 픽셀 1-IsMatching, p1 점, 화소 (2)에 p2 포인트를 입력하고 메모리에 그들이 이런 식으로 배치하는 경우 : 메모리을 통과 할 때

p1 : B 
p1 + 1: G 
p1 + 2: R 

p2 : B 
p2 + 1: G 
p2 + 2: R 

그래서 IsMatching 단지 절대 차이를한다.

귀하의 후속 질문은 귀하가 실제로 포인터를 이해하지 못한다고 알려줍니다. 괜찮습니다. 아마도 배울 수 있습니다.솔직히 컨셉은 그다지 어렵지는 않지만 그 문제는 많은 경험없이 발을 쏠 가능성이 높다는 것입니다. 아마도 코드 작성 및 냉각에 프로파일 링 도구를 사용해보십시오. 최악의 핫스팟을 내리고 그것을 좋게 부르십시오.

예를 들어, 첫 번째 행에서 끝에서 두 번째 행까지, 첫 번째 열에서 끝에서 두 번째 열까지를 보게 될 것입니다. 이는 "제 0 행 이상에서는 읽을 수 없습니다"라는 경우를 처리하지 않으려 고 의도적인데 의도적으로 많은 런타임 조건에서 좋지 않은 합법적 인 메모리 블록 외부에서 읽는 것과 관련된 잠재적 인 버그를 제거합니다.

+0

별표의 사용뿐만 아니라 각 변수의 의미를 설명 할 수 있습니까? 어떻게 든 색을 바이트로 저장하고 있습니까? – vkoves

+0

포인터는 확실히 C#에서 이미지 처리를 수행하는 방법이지만, 중첩 루프에서 함수를 호출하는 것을 피할 수 있습니다. 이것은 C#에서는 효율적이지 않습니다. 일반적으로 나는 C#에서 이미지 조작을하는 것이 동등한 C++ 함수에서 적어도 두 배의 시간이 걸린다는 것을 발견했다. 여기에 효율성이 정말로 필요하다면 C#에서 네이티브 C++ 함수를 호출하거나 opencv 용 C# 래퍼 중 하나를 사용하는 것이 좋습니다 - 이것은 매우 빠릅니다. –

+0

귀하의 isMatching 문구가 나에게 적합하지 않습니다. Math.Abs ​​(* p1 ++ - * p2 ++) vkoves

5

대신 색을 설정할 SetPixel를 사용하여 각 화소마다 다른 온도 Color[9]를 생성하는 Color[]에 복사 후, byte[] 각 화상을 복사 및 당신이 prope 설정으로와 setPixel 전화를 교체해야합니다

using (byte* bytePtr = ptr) 
{ 
    //code goes here 
} 

:, 안전하지 않은 등의 방법을 표시의 /unsafe 플래그를 사용하여 컴파일 Marshal.Copy에와 byte[]에 복사를 교체 r 바이트. 이것은 LockBits에서 문제가되지 않습니다. LockBits가 필요합니다. 문제는 당신이 이미지 처리와 관련된 모든 작업에 비효율적이라는 것입니다.

+0

구현 내용을 자세히 설명 할 수 있습니까? 내가 보여주는 코드를 사용하여 코드를 어떻게 바꿀 수 있는지 정확히 알지 못합니다. – vkoves

+0

Marshal.Copy를 사용하여 전체 이미지를 별도로 할당 된 byte []로 복사 한 다음 byte []를 별도로 할당 된 Color []에 복사합니다. 원본 이미지의 메모리를 3 배 사용하여 많은 양을 차지합니다. 시간 복사. 대신에'IntPtr'을'byte *'로 변환하고 포인터를 직접 조작해야합니다. 이로 인해 이미지의 사본이 남지 않으며 이미지 데이터에 액세스하는 가장 빠른 방법입니다. –

+0

바이트가 * 무엇인지 설명 할 수 있겠습니까? 별표는 특수 변경자입니까? – vkoves