C# 连通元件标记算法优化
我需要一些帮助来优化我的CCL算法实现。我用它来检测图像上的黑色区域。在2000x2000上,它需要11秒,这相当于。我需要将运行时间减少到可能达到的最低值。另外,我很高兴知道是否有其他算法可以让你做同样的事情,但比这个算法更快。这是我的代码:C# 连通元件标记算法优化,c#,algorithm,optimization,image-processing,C#,Algorithm,Optimization,Image Processing,我需要一些帮助来优化我的CCL算法实现。我用它来检测图像上的黑色区域。在2000x2000上,它需要11秒,这相当于。我需要将运行时间减少到可能达到的最低值。另外,我很高兴知道是否有其他算法可以让你做同样的事情,但比这个算法更快。这是我的代码: //The method returns a dictionary, where the key is the label //and the list contains all the pixels with that label
//The method returns a dictionary, where the key is the label
//and the list contains all the pixels with that label
public Dictionary<short, LinkedList<Point>> ProcessCCL()
{
Color backgroundColor = this.image.Palette.Entries[1];
//Matrix to store pixels' labels
short[,] labels = new short[this.image.Width, this.image.Height];
//I particulary don't like how I store the label equality table
//But I don't know how else can I store it
//I use LinkedList to add and remove items faster
Dictionary<short, LinkedList<short>> equalityTable = new Dictionary<short, LinkedList<short>>();
//Current label
short currentKey = 1;
for (int x = 1; x < this.bitmap.Width; x++)
{
for (int y = 1; y < this.bitmap.Height; y++)
{
if (!GetPixelColor(x, y).Equals(backgroundColor))
{
//Minumum label of the neighbours' labels
short label = Math.Min(labels[x - 1, y], labels[x, y - 1]);
//If there are no neighbours
if (label == 0)
{
//Create a new unique label
labels[x, y] = currentKey;
equalityTable.Add(currentKey, new LinkedList<short>());
equalityTable[currentKey].AddFirst(currentKey);
currentKey++;
}
else
{
labels[x, y] = label;
short west = labels[x - 1, y], north = labels[x, y - 1];
//A little trick:
//Because of those "ifs" the lowest label value
//will always be the first in the list
//but I'm afraid that because of them
//the running time also increases
if (!equalityTable[label].Contains(west))
if (west < equalityTable[label].First.Value)
equalityTable[label].AddFirst(west);
if (!equalityTable[label].Contains(north))
if (north < equalityTable[label].First.Value)
equalityTable[label].AddFirst(north);
}
}
}
}
//This dictionary will be returned as the result
//I'm not proud of using dictionary here too, I guess there
//is a better way to store the result
Dictionary<short, LinkedList<Point>> result = new Dictionary<short, LinkedList<Point>>();
//I define the variable outside the loops in order
//to reuse the memory address
short cellValue;
for (int x = 0; x < this.bitmap.Width; x++)
{
for (int y = 0; y < this.bitmap.Height; y++)
{
cellValue = labels[x, y];
//If the pixel is not a background
if (cellValue != 0)
{
//Take the minimum value from the label equality table
short value = equalityTable[cellValue].First.Value;
//I'd like to get rid of these lines
if (!result.ContainsKey(value))
result.Add(value, new LinkedList<Point>());
result[value].AddLast(new Point(x, y));
}
}
}
return result;
}
//该方法返回一个字典,其中键是标签
//该列表包含所有带有该标签的像素
公共字典进程CCL()
{
Color backgroundColor=this.image.palete.Entries[1];
//用于存储像素标签的矩阵
short[,]labels=新的short[this.image.Width,this.image.Height];
//我特别不喜欢我存储标签相等表的方式
//但我不知道我还能怎么储存它
//我使用LinkedList可以更快地添加和删除项目
Dictionary equalityTable=新字典();
//当前标签
短电流键=1;
对于(int x=1;x
提前谢谢 您可以将图片拆分为多个子图片,并行处理,然后合并结果。 1次通过:4个任务,每个任务处理一个1000x1000子图片 2传递:2个任务,每个任务处理传递1中的2个子图片 3通过:1个任务,处理通过2的结果
对于C#,我建议使用,它允许轻松定义相互依赖和等待的任务。下面的代码项目Artiel为您提供了TPL的基本介绍:。我将一次处理一条扫描线,跟踪每次黑色像素运行的开始和结束 然后,我会在每一个扫描行上,将其与前一行上的运行进行比较。如果当前行上有一个运行与前一行上的运行不重叠,则表示一个新blob。如果前一行上有一个梯段与当前行上的梯段重叠,则该梯段将获得与前一行相同的blob标签。等等,等等,你明白了 我会尽量不使用字典之类的东西。 根据我的经验,随机停止程序表明,这些事情可能会使编程变得越来越容易,但由于
new
-ing,它们可能会导致严重的性能损失。问题是关于GetPixelColor(x,y),访问图像数据需要很长时间。
Set/GetPixel函数在C#中的速度非常慢,所以如果需要经常使用它们,应该使用Bitmap.lockBits
private void ProcessUsingLockbits(Bitmap ProcessedBitmap)
{
BitmapData bitmapData = ProcessedBitmap.LockBits(new Rectangle(0, 0, ProcessedBitmap.Width, ProcessedBitmap.Height), ImageLockMode.ReadWrite, ProcessedBitmap.PixelFormat);
int BytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(ProcessedBitmap.PixelFormat) / 8;
int ByteCount = bitmapData.Stride * ProcessedBitmap.Height;
byte[] Pixels = new byte[ByteCount];
IntPtr PtrFirstPixel = bitmapData.Scan0;
Marshal.Copy(PtrFirstPixel, Pixels, 0, Pixels.Length);
int HeightInPixels = bitmapData.Height;
int WidthInBytes = bitmapData.Width * BytesPerPixel;
for (int y = 0; y < HeightInPixels; y++)
{
int CurrentLine = y * bitmapData.Stride;
for (int x = 0; x < WidthInBytes; x = x + BytesPerPixel)
{
int OldBlue = Pixels[CurrentLine + x];
int OldGreen = Pixels[CurrentLine + x + 1];
int OldRed = Pixels[CurrentLine + x + 2];
// Transform blue and clip to 255:
Pixels[CurrentLine + x] = (byte)((OldBlue + BlueMagnitudeToAdd > 255) ? 255 : OldBlue + BlueMagnitudeToAdd);
// Transform green and clip to 255:
Pixels[CurrentLine + x + 1] = (byte)((OldGreen + GreenMagnitudeToAdd > 255) ? 255 : OldGreen + GreenMagnitudeToAdd);
// Transform red and clip to 255:
Pixels[CurrentLine + x + 2] = (byte)((OldRed + RedMagnitudeToAdd > 255) ? 255 : OldRed + RedMagnitudeToAdd);
}
}
// Copy modified bytes back:
Marshal.Copy(Pixels, 0, PtrFirstPixel, Pixels.Length);
ProcessedBitmap.UnlockBits(bitmapData);
}
private void ProcessUsingLockbits(位图处理位图)
{
BitmapData BitmapData=ProcessedBitmap.LockBits(新矩形(0,0,ProcessedBitmap.Width,ProcessedBitmap.Height),ImageLockMode.ReadWrite,ProcessedBitmap.PixelFormat);
int BytesPerPixel=System.Drawing.Bitmap.GetPixelFormatSize(ProcessedBitmap.PixelFormat)/8;
int ByteCount=bitmapData.Stride*ProcessedBitmap.Height;
字节[]像素=新字节[字节计数];
IntPtr PtrFirstPixel=bitmapData.Scan0;
复制(PtrFirstPixel,像素,0,像素,长度);
int HeightInPixels=bitmapData.Height;
int WidthInBytes=位图数据。宽度*字节/像素;
用于(int y)=
private void bitmap_to_matrix()
{
unsafe
{
bitmapData = ProcessedBitmap.LockBits(new Rectangle(0, 0, ProcessedBitmap.Width, ProcessedBitmap.Height), ImageLockMode.ReadWrite, ProcessedBitmap.PixelFormat);
int BytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(ProcessedBitmap.PixelFormat) / 8;
int HeightInPixels = ProcessedBitmap.Height;
int WidthInPixels = ProcessedBitmap.Width;
int WidthInBytes = ProcessedBitmap.Width * BytesPerPixel;
byte* PtrFirstPixel = (byte*)bitmapData.Scan0;
Parallel.For(0, HeightInPixels, y =>
{
byte* CurrentLine = PtrFirstPixel + (y * bitmapData.Stride);
for (int x = 0; x < WidthInBytes; x = x + BytesPerPixel)
{
// Conversion in grey level
double rst = CurrentLine[x] * 0.0721 + CurrentLine[x + 1] * 0.7154 + CurrentLine[x + 2] * 0.2125;
// Fill the grey matix
TG[x / 3, y] = (int)rst;
}
});
}
}