2012-06-18 2 views
0

포인터를 사용하여 이미지의 줄을 감지하고 해당 줄을 제거하는 프로그램을 만들려고합니다. 현재, 감지 라인 부분은 정말로 잘 작동하고 있으며, 대부분은 라인 부분을 제거하는 것이 효과적입니다. 그러나 약 150-200 개 이미지 이후에이 프로그램은 코드의 안전하지 않은 비트와 관련이없는 장소에서 무작위로 AccessViolationExceptions를 throw합니다.비트 맵 작업에서 무작위 AccessViolationExceptions

// ... Detect lines and such 
Bitmap export = new Bitmap(bitmap.Width * 3, bitmap.Height, PixelFormat.Format24bppRgb); 
Graphics fg = Graphics.FromImage(export); 
fg.DrawImage(bitmap, 0, 0); // Draw the original input bitmap 
fg.DrawImage(edited, bitmap.Width, 0); // Draw the input after processing (Line Detection) 
try 
{ 
    Bitmap lineRemoved = RemoveLines(bitmap, horizontalLines.ToArray(), verticalLines.ToArray()); // Remove lines based on earlier detection 
    lineRemoved.Save(cellDirectory + "\\Lines\\cell_lr_" + i.ToString("D2") + j.ToString("D2") + ".gif", ImageFormat.Gif); // Save image after removal 
    fg.DrawImage(lineRemoved, bitmap.Width * 2, 0); // Add image to composite for comparison; This line is what throws the error most of the time 
    lineRemoved.Dispose(); 
    export.Save(cellDirectory + "\\Lines\\cell" + i.ToString("D2") + j.ToString("D2") + ".gif", ImageFormat.Gif); 
} 
catch (Exception ex) 
{ } 

을 :이 코드 후

static unsafe Bitmap RemoveLines(Bitmap input, int[] horizontalLines, int[] verticalLines) 
{ 
    Bitmap output; 

    if (input.PixelFormat == PixelFormat.Format24bppRgb) 
    { 
     output = (Bitmap) input.Clone(); 
    } 
    else 
    { 
     output = ConvertTo24bpp((Bitmap)input.Clone()); 
    } 

    BitmapData bitmapData = output.LockBits(new Rectangle(0, 0, output.Width, output.Height), ImageLockMode.ReadWrite, output.PixelFormat); 

    int w = output.Width; 
    int h = output.Height; 
    int bpp = 3; 

    int s = bitmapData.Stride; 

    byte* p = (byte*) bitmapData.Scan0; 

    for (int r = 0; r < h; r++) 
    { 
     for (int c = 0; c < h; c++) 
     { 
      if (horizontalLines.Contains(r) || verticalLines.Contains(c)) 
      { 
       int i = (r * s) + c * bpp; 

       p[i + 0] = 255; 
       p[i + 1] = 255; 
       p[i + 2] = 255; 
      } 
     } 
    } 

    output.UnlockBits(bitmapData); 

    return output; 
} 

, 내가 비교를 위해 다른 비트 맵을 내장뿐만 아니라 결과 비트 맵을 저장

라인 제거를 수행하는 비트가 DrawImage 호출은 오류를 발생시키고 항상 AccessViolationException 다음에 InvalidOperationException이옵니다. 오류가 발생한 동안 lineRemoved를 보면 대부분의 멤버가 "InvalidOperationException '유형의 예외를 던졌습니다. 실제 값이 아닌 동일한 비트 맵 앞에있는 한 줄만 있으면 문제가 없습니다. 입력 비트 맵은 코드 전체에서 변경되지 않고 항상 어떤 방식 으로든 변경해야 할 때 복제되거나 다른 비트 맵에 그려집니다.

lineRemoved를 저장 한 후에 행을 주석 처리하려고했지만 나중에 동일한 오류가 코드에서 나타납니다. 게다가 try/catch는 실제로 Exception을 잡아 내지는 않습니다. 항상 처리되지 않는다고 말합니다. 그것은 포인터와 관련이 있어야합니다, 그렇지 않으면 나는 이것을 일으키는 것에 관해서는 완전히 잃어버린 것입니다.

+2

(int c = 0; c

+0

에 대한 것이어야합니다. 줄 제거가 전체 이미지에 미치지 못하는 문제가 있었지만 그 문제를 파악하기 위해 우선 순위를 낮추었습니다. 물론이 솔루션은 두 가지 문제 모두에서 동일 할 것입니다. – Abion47

+0

"지연된 embarassing":-) "물론 해결책은 두 가지 문제 모두 동일합니다." 액세스 위반이 사라진 것을 확인합니까? –

답변

3

코드에 미묘한 한 문자 버그가 있습니다. 이미지가 가로 방향에있는 경우

for (int c = 0; c < h; c++) 

를 읽어 줄은

for (int c = 0; c < w; c++) 

해야한다, 버그 이미지의 오른쪽 부분이 아닌 처리되는 원인이됩니다.

이미지가 원형으로 표시되면 오버플로가 발생하여 액세스 위반 예외 (행운의 경우) 또는 메모리 손상 (그렇지 않은 경우)이 발생합니다.

귀하의 알고리즘은 그리 효율적이지 않습니다. 예를 들어, 당신은 분명히 (r은 * s의)는 내부 루프에서 변경되지 않습니다, 및 C *의 BPP가 currentPixel 같은 의해 대체 될 수 있지만, 당신이 그리고있는 모든 픽셀에 대한 계산

int i = (r * s) + c * bpp; 

을하고있다 + = bpp.

실제로 horizontalLines 및 verticalLines을 루프하는 것이 더 효율적입니다.

+0

답변 해 주셔서 감사합니다. 효율성에 대한 좋은 지적은 코드의 변경을 강화합니다. – Abion47