2012-06-18 3 views
0

Я пытаюсь создать программу, которая, используя указатели, обнаруживает линии в изображении и удаляет эти строки. В настоящее время часть обнаруживающих линий работает очень хорошо, и по большей части также работает удаление части линий. Однако после примерно 150-200 изображений программа будет генерировать случайные AccessViolationExceptions в местах, которые не имеют ничего общего с небезопасными битами кода.Random AccessViolationException от работы с растровыми изображениями

Это бит, что делает удаление строки:

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

После этого кода, я сохранить полученный Bitmap, а также встраивание его в другой Bitmap для целей сравнения:

// ... 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) 
{ } 

Вызов DrawImage - это то, что вызывает ошибки, и это всегда исключение AccessViolationException, за которым следует исключение InvalidOperationException. Глядя на lineRemoved во время ошибки, видно, что большинство ее членов «выбрали исключение типа InvalidOperationException» вместо реальных значений, хотя одна строка до того, как один битмарт был сохранен просто отлично. Растровое изображение ввода остается неизменным во всем коде и всегда клонируется или рисуется в другое растровое изображение, когда мне нужно его каким-либо образом изменить.

Я пробовал комментировать строки после сохранения строкиРедактировал, но затем эта же ошибка появляется позже в коде. Более того, try/catch фактически не захватывает Exception - он всегда говорит о необработанном. Это должно быть чем-то связано с указателями, но в остальном я полностью потерял то, что вызывает это.

+2

для (int c = 0; c

+0

@KrisVandermotten Это своего рода замедление смущения. Возникла проблема, когда удаление линии не охватывало весь образ, но я поставил эту проблему в более низкий приоритет, чтобы выяснить ее. Конечно, решение было бы одинаковым для обоих вопросов ... – Abion47

+0

«замедленное смущение» :-) «Конечно, решение было бы одинаковым для обоих вопросов». Подтверждаете ли вы, что нарушения доступа ушли? –

ответ

3

Ваш код содержит тонкую односимвольную ошибку. Линия, которая читает

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

должен быть

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

Если изображение находится в альбомной ориентации, ваша ошибка приведет к правой части изображения не обрабатывается.

Если изображение имеет ориентацию protrait, это приведет к переполнению буфера, что приведет к исключению нарушения доступа (если вам повезет) или повреждению памяти (если вы этого не сделали).

Это, как говорится, ваш алгоритм не очень эффективен. Например, вы делаете расчет

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

для каждого пикселя вы рисунок, в то время как очевидно, (г * s) не меняется во внутреннем цикле, с * BPP можно заменить на что-то вроде currentPixel + = bpp.

На самом деле, вероятно, было бы более эффективно перебирать горизонтальные линии и вертикальные линии.

+0

Спасибо за ответ. Хорошая точка в эффективности, улучшающей изменения кода. – Abion47