我已经与EMGU + OpenCV合作了很长一段时间并遇到了这个AccessViolationException谜.
首先,代码:
class AVE_Simulation
{
public static int Width = 7500;
public static int Height = 7500;
public static Emgu.CV.Image<Rgb, float>[] Images;
static void Main(string[] args)
{
int N = 50;
int Threads = 5;
Images = new Emgu.CV.Image<Rgb, float>[N];
Console.WriteLine("Start");
ParallelOptions po = new ParallelOptions();
po.MaxDegreeOfParallelism = Threads;
System.Threading.Tasks.Parallel.For(0, N, po, new Action<int>((i) =>
{
Images[i] = GetRandomImage();
Console.WriteLine("Prossing image: " + i);
Images[i].SmoothBilatral(15, 50, 50);
GC.Collect();
}));
Console.WriteLine("End");
}
public static Emgu.CV.Image<Rgb, float> GetRandomImage()
{
Emgu.CV.Image<Rgb, float> im = new Emgu.CV.Image<Rgb, float>(Width, Height);
float[, ,] d = im.Data;
Random r = new Random((int)DateTime.Now.Ticks);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
d[y, x, 0] = (float)r.Next(255);
d[y, x, 1] = (float)r.Next(255);
d[y, x, 2] = (float)r.Next(255);
}
}
return im;
}
}
Run Code Online (Sandbox Code Playgroud)
代码很简单.分配一系列图像.生成随机图像并使用随机数填充它.对图像执行双边滤镜.而已.
如果我在一个线程中执行这个程序,(Threads = 1)一切似乎都正常工作没有问题.但是,如果我将并发线程数增加到5,我会很快得到一个AccessViolationException.
我已经浏览了OpenCV代码并验证了OpenCV方面没有分配,并且还检查了EMGU代码,搜索未固定的对象或其他问题,一切似乎都正确.
一些说明:
GC.Collect()你会得到AccessViolationException较少的,但它最终会发生.OutOfMemoryException在系统内存不足时会抛出' '.任何帮助,将不胜感激.我一直试图解决这个问题超过一个星期.
i7(没有超频),Win7 64bit,32GB RAM,VS 2010,Framework 4.0,OpenCV 2.4.3
堆:
Start
Prossing image: 20
Prossing image: 30
Prossing image: 40
Prossing image: 0
Prossing image: 10
Prossing image: 21
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Emgu.CV.CvInvoke.cvSmooth(IntPtr src, IntPtr dst, SMOOTH_TYPE type, Int32 param1, Int32 param2, Double param3, Double param4)
at TestMemoryViolationCrash.AVE_Simulation.<Main>b__0(Int32 i) in C:\branches\1.1\TestMemoryViolationCrash\Program.cs:line 32
at System.Threading.Tasks.Parallel.<>c__DisplayClassf`1.<ForWorker>b__c()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass10.<ExecuteSelfReplicating>b__f(Object param0)
at System.Threading.Tasks.Task.Execute()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
at System.Threading.Tasks.ThreadPoolTaskScheduler.TryExecuteTaskInline(Task task, Boolean taskWasPreviouslyQueued)
at System.Threading.Tasks.TaskScheduler.TryRunInline(Task task, Boolean taskWasPreviouslyQueued)
at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler, Boolean waitForCompletion)
at System.Threading.Tasks.Parallel.ForWorker[TLocal](Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Func`4 bodyWithLocal, Func`1 loc
alInit, Action`1 localFinally)
at System.Threading.Tasks.Parallel.For(Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action`1 body)
at TestMemoryViolationCrash.AVE_Simulation.Main(String[] args) in C:\branches\1.1\TestMemoryViolationCrash\Program.cs:line 35
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Emgu.CV.CvInvoke.cvSmooth(IntPtr src, IntPtr dst, SMOOTH_TYPE type, Int32 param1, Int32 param2, Double param3, Double param4)
at TestMemoryViolationCrash.AVE_Simulation.<Main>b__0(Int32 i) in C:\branches\1.1\TestMemoryViolationCrash\Program.cs:line 32
at System.Threading.Tasks.Parallel.<>c__DisplayClassf`1.<ForWorker>b__c()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass10.<ExecuteSelfReplicating>b__f(Object param0)
at System.Threading.Tasks.Task.Execute()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
at System.Threading.ThreadPoolWorkQueue.Dispatch()
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Emgu.CV.CvInvoke.cvSmooth(IntPtr src, IntPtr dst, SMOOTH_TYPE type, Int32 param1, Int32 param2, Double param3, Double param4)
at TestMemoryViolationCrash.AVE_Simulation.<Main>b__0(Int32 i) in C:\branches\1.1\TestMemoryViolationCrash\Program.cs:line 32
at System.Threading.Tasks.Parallel.<>c__DisplayClassf`1.<ForWorker>b__c()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass10.<ExecuteSelfReplicating>b__f(Object param0)
at System.Threading.Tasks.Task.Execute()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
at System.Threading.ThreadPoolWorkQueue.Dispatch()
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)
Pet*_*art 12
您的示例不保留对Image.SmoothBilatral 的结果图像的引用.输入图像以一个静态数组为根,所以很好.
一个Emgu.CV Image的数据数组被固定到实际图像中的GCHandle,这与图像包含数组并且不会阻止收集这一事实没有什么不同,而GCHandle的指针正被非托管代码使用(在图像的托管根).
因为Image.SmoothBilatral方法除了传递其指针并返回它之外没有对其临时结果图像做任何事情,我认为它会被优化,以便在平滑处理时可以收集结果图像.
因为这个类没有终结器,所以opencv不会被调用来释放它的非托管图像头(它有一个指向托管图像数据的指针),所以opencv仍然认为它有一个可用的图像结构.
你可以通过引用SmoothBilatral的结果并对它做一些事情来解决它(比如处理它).
这种扩展方法也可以工作(即允许在没有使用结果的情况下成功调用它进行基准测试):
public static class BilateralExtensionFix
{
public static Emgu.CV.Image<testchannels, testtype> SmoothBilateral(this Emgu.CV.Image<testchannels, testtype> image, int p1, int p2 , int p3)
{
var result = image.CopyBlank();
var handle = GCHandle.Alloc(result);
Emgu.CV.CvInvoke.cvSmooth(image.Ptr, result.Ptr, Emgu.CV.CvEnum.SMOOTH_TYPE.CV_BILATERAL, p1, p1, p2, p3);
handle.Free();
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
我认为EmguCV应该做的只是固定指针,在进行互操作时传递给opencv.
ps OpenCv双边滤波器在所有通道上以零变化(min()= max())传递的任何浮动图像上崩溃(产生与您的问题非常相似的错误).我认为因为它是如何构建它的binned exp()查找表.
这可以通过以下方式复制:
// create new blank image
var zeroesF1 = new Emgu.CV.Image<Rgb, float>(75, 75);
// uncomment next line for failure
zeroesF1.Data[0, 0, 0] += 1.2037063600E-035f;
zeroesF1.SmoothBilatral(15, 50, 50);
Run Code Online (Sandbox Code Playgroud)
这让我感到困惑,因为我的测试代码中的错误实际上有时会出现此错误...