【问题标题】:Counting unique pixels in a drawn image of shapes计算形状绘制图像中的唯一像素
【发布时间】:2011-12-05 13:58:43
【问题描述】:

我正在开发一个图形应用程序,用户可以在其中绘制任意数量的线(从 A 点到 B 点有一定的粗细)、矩形或椭圆在画布上。

完成后,我有一组形状数据,指示每个形状的位置和绘制的线条,我需要确定作为研究项目的一部分,它们着色了多少唯一像素。

我的简单算法是为每个形状实现 bool shape.Contains(x,y) 并为图像中每个像素的每个绘制形状调用它,以确定该像素是由直线、矩形还是椭圆绘制的。

换一种方式,我可以创建 void shape.SetPixels(bool[,] canvas) 并将每个形状设置为真,它包含的每个像素。这是我实际实现的,对于大型数据集,它非常缓慢。

我感觉有一种更直接的方法可以从原始形状数据到我需要的输出,而无需检查每个像素。所以我的问题是,给定一组形状数据,是否有一个 O(n) 函数 bool[,] IsColored(int x, int y) {} 可以更直接地为彩色像素生成真/假矩阵我给出的想法?

【问题讨论】:

  • 如果你画了呢?生成的位图本质上就是这样一个矩阵
  • O(n) 是什么意思?实现 shape.Contains(x,y) 并为每个形状调用一次是 O(n),其中 n 是形状的数量。
  • 为什么形状不能只返回它包含的像素集?
  • 您的SetPixels(bool[,]) 版本中的哪一部分“非常慢”?当然不是查找 - 它是在构建 bool 值数组吗?在这种情况下,您可能只需要为您的形状提供更好的渲染算法。
  • 一个警告:Bitmap 类上的 GetPixelSetPixel 方法非常慢。如果要处理多个像素,则需要使用直接的低级访问。

标签: c# algorithm graphics


【解决方案1】:

避免使用Bitmap.GetPixel 方法。这是非常非常慢。如果可能,您可以使用LockBits 或类似技术对位图数据进行低级别访问。

在我使用的一个项目中:

public void LoadFromBitmap(Bitmap bmp)
    {
        if (bmp.Width != Width || bmp.Height != Height)
            throw new ArgumentException("Size missmatch");
        unsafe
        {
            BitmapData bmpData = null;
            try
            {
                bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

                for (int y = 0; y < bmpData.Height; y++)
                {
                    uint* p = (uint*)((byte*)bmpData.Scan0 + y * bmpData.Stride);
                    for (int x = 0; x < bmpData.Width; x++)
                    {
                        this[x, y] = RawColor.FromARGB(*p);
                        p++;
                    }
                }
            }
            finally
            {
                if (bmpData != null)
                    bmp.UnlockBits(bmpData);
            }
        }
    }

https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Image/Pixels.cs

另一个优化是为包含像素的数组实现一个池。根据我的经验,经常在大对象堆上分配对象会给 gc 带来很大压力。

【讨论】:

    【解决方案2】:

    您所说的两种方法通常是您的两个主要选择。要么根据需要检查每个像素,要么预先构建某种数据结构以便快速查找。

    如果您有一个大画布,但只有几个形状(因此,相对较少的“on”像素),那么最好只记录被任何形状击中的每个像素,例如在哈希中。

    HashSet<KeyValuePair<int,int>> listOfPixelsHitByAnyShape = new HashSet()
    
    foreach(Shape s in allShapes)
    {
        s.Draw(listOfPixelsHitByAnyShape); // will update listOfPixelsHitByAnyShape
    }
    
    // Now we can easily query if a pixel is set
    bool isSet = listOfPixelsHitByAnyShape.Contains(new KeyValuePair(10,99))
    

    这对于查找来说应该很快,但会消耗内存和构建 HashSet 的时间。

    但它不会像您的SetPixels(bool[,] canvas) 版本那么快,也不会使用那么多内存(在我们谈论的稀疏情况下)。

    【讨论】:

      【解决方案3】:

      如果您不使用图形库,请使用!


      如果它是一个图形应用程序,大概您已经在绘制用户输入的内容。你不能直接查询吗?您始终可以绘制到 2 个不同的目标,一个用于漂亮的用户版本,另一个用于查询(每个对象都有一个独特的颜色)。

      您想如何处理重叠?


      我们所说的数据集有多大?渲染应该是“免费的”,因为位图可以在用户在应用程序中绘制时构建。

      如果你所做的事情真的很慢,你可以考虑使用 GPU 进行绘制和查询(可能使用花哨的着色器)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-11
        • 1970-01-01
        • 2018-07-23
        • 2014-01-12
        相关资源
        最近更新 更多