使用位图的随机AccessViolationExceptions

时间:2012-06-18 20:02:11

标签: c# pointers bitmap access-violation unsafe

我正在尝试制作一个程序,使用指针检测图像中的线条并删除这些线条。目前,检测线部分工作得非常好,并且在大多数情况下,移除线部分也是有效的。但是,在大约150-200个图像之后,程序将在与代码的不安全位无关的位置抛出随机的AccessViolationExceptions。

这是删除行的位:

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'类型的异常而不是实际值,即使同一个Bitmap之前的一行保存得很好。输入位图在整个代码中保持不变,并且当我需要以任何方式更改它时,始终克隆或绘制到不同的位图。

我在保存lineRemoved之后尝试将这些行注释掉,但之后会在代码中弹出相同的错误。更重要的是,try / catch实际上并没有捕获异常 - 它总是说未处理。它必须与指针有关,但除此之外我完全迷失了造成这种情况的原因。

1 个答案:

答案 0 :(得分:3)

您的代码包含一个微妙的单字符错误。读取的行

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

应该是

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

如果图像处于横向,则您的错误会导致图像的右侧部分无法处理。

如果图像处于protrait方向,则会导致缓冲区溢出,导致访问冲突异常(如果幸运)或内存损坏(如果不是)。

话虽这么说,你的算法效率不高。例如,您正在进行计算

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

对于你正在绘制的每个像素,而显然(r * s)在内部循环中没有变化,而c * bpp可以被像currentPixel + = bpp这样的东西替换。

实际上,循环horizo​​ntalLines和verticalLines可能更有效。