실제로 부동 소수점 숫자를 백분율로으로 전달하고 있습니까?
저는 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
을 (이 긴 것) :
- 에 포인트를 P1 바이트를 가지고 개최 위에 그것을
- p1에 1을 더합니다 (이것은 ++입니다 - 포인터가 다음 바이트를 가리키게합니다).
- p2가 가리키는 바이트를 가져 가서 붙잡습니다.
- 1에 p2를 더함
- 1 단계에서 3 단계를 뺍니다.
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 행 이상에서는 읽을 수 없습니다"라는 경우를 처리하지 않으려 고 의도적인데 의도적으로 많은 런타임 조건에서 좋지 않은 합법적 인 메모리 블록 외부에서 읽는 것과 관련된 잠재적 인 버그를 제거합니다.
그리고 ... 문제는 무엇인가? – LightStriker
편집하지 못했습니다. 죄송합니다. – vkoves
코드를 게시하십시오. 우리는 Github에서 프로젝트를 탐색하여 관련 부분을 찾고 싶지 않습니다. – leonbloy