iOS如何计算曲线所包含的像素数/面积?

我有一条任意形状的曲线,包围了一些区域。 我想估计曲线在iPhone / iPad屏幕上的像素数。 我怎么能这样做?

  • 曲线定义为点的连续x / y坐标。
  • 曲线已关闭。
  • 用户的触摸(touchesMoved方法)绘制曲线,我不知道它看起来像什么

在此处输入图像描述

我想用某种方式用颜色填充闭合曲线,然后在屏幕截图中计算这种颜色的像素数。 这意味着我需要知道如何以编程方式用颜色填充闭合曲线。

还有其他一些我没想过的方法吗?

谢谢!

让我们通过创建一个包围曲线的Quartz路径来完成此操作。 然后我们将创建一个位图上下文并填充该上下文中的路径。 然后我们可以检查位图并计算填充的像素。 我们将这一切包装在一个方便的function中:

static double areaOfCurveWithPoints(const CGPoint *points, size_t count) { 

首先,我们需要创建路径:

  CGPathRef path = createClosedPathWithPoints(points, count); 

然后我们需要得到路径的边界框。 CGPoint坐标不必是整数,但是位图必须具有整数尺寸,因此我们将获得至少与路径边界框一样大的整数边界框:

  CGRect frame = integralFrameForPath(path); 

我们还需要确定制作位图的宽度(以字节为单位):

  size_t bytesPerRow = bytesPerRowForWidth(frame.size.width); 

现在我们可以创建位图:

  CGContextRef gc = createBitmapContextWithFrame(frame, bytesPerRow); 

位图在创建时用黑色填充。 我们将用白色填充路径:

  CGContextSetFillColorWithColor(gc, [UIColor whiteColor].CGColor); CGContextAddPath(gc, path); CGContextFillPath(gc); 

现在我们完成了路径,以便我们可以释放它:

  CGPathRelease(path); 

接下来我们将计算填充的区域:

  double area = areaFilledInBitmapContext(gc); 

现在我们已经完成了位图上下文,所以我们可以发布它:

  CGContextRelease(gc); 

最后,我们可以返回我们计算的区域:

  return area; } 

嗯,这很容易! 但是我们必须编写所有这些辅助函数。 让我们从顶部开始吧。 创建路径是微不足道的:

 static CGPathRef createClosedPathWithPoints(const CGPoint *points, size_t count) { CGMutablePathRef path = CGPathCreateMutable(); CGPathAddLines(path, NULL, points, count); CGPathCloseSubpath(path); return path; } 

获取路径的整数边界框也很简单:

 static CGRect integralFrameForPath(CGPathRef path) { CGRect frame = CGPathGetBoundingBox(path); return CGRectIntegral(frame); } 

要选择位图的每行字节数,我们可以使用路径边界框的宽度。 但我认为Quartz喜欢将位图设置为两倍的优势的倍数。 我没有对此进行任何测试,因此您可能想要进行实验。 现在,我们将宽度四舍五入为64的下一个最小倍数:

 static size_t bytesPerRowForWidth(CGFloat width) { static const size_t kFactor = 64; // Round up to a multiple of kFactor, which must be a power of 2. return ((size_t)width + (kFactor - 1)) & ~(kFactor - 1); } 

我们使用计算的大小创建位图上下文。 我们还需要翻译坐标系的原点。 为什么? 因为路径边界框的原点可能不在(0,0)。

 static CGContextRef createBitmapContextWithFrame(CGRect frame, size_t bytesPerRow) { CGColorSpaceRef grayscale = CGColorSpaceCreateDeviceGray(); CGContextRef gc = CGBitmapContextCreate(NULL, frame.size.width, frame.size.height, 8, bytesPerRow, grayscale, kCGImageAlphaNone); CGColorSpaceRelease(grayscale); CGContextTranslateCTM(gc, -frame.origin.x, -frame.origin.x); return gc; } 

最后,我们需要编写实际计算填充像素的辅助对象。 我们必须决定我们想要计算像素的方式。 每个像素由一个无符号8位整数表示。 黑色像素为0.白色像素为255.中间的数字为灰色阴影。 使用灰色像素填充时,Quartz会对路径的边缘进行反锯齿处理。 所以我们必须决定如何计算这些灰色像素。

一种方法是定义阈值,如128.任何等于或高于阈值的像素都计为填充; 剩下的数量是未填充的。

另一种方法是将灰色像素计数为部分填充,并将该部分填充加起来。 因此,两个完全半填充的像素被组合并计为单个完全填充的像素。 我们这样做:

 static double areaFilledInBitmapContext(gc) { size_t width = CGBitmapContextGetWidth(gc); size_t height = CGBitmapContextGetHeight(gc); size_t stride = CGBitmapContextGetBytesPerRow(gc); uint8_t *pixels = CGBitmapContextGetData(gc); uint64_t coverage = 0; for (size_t y = 0; y < height; ++y) { for (size_t x = 0; x < width; ++x) { coverage += pixels[y * stride + x]; } } return (double)coverage / UINT8_MAX; } 

你可以找到这个要点中捆绑的所有代码。

我会把这幅画作为CGIMage抓住……

(CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());

然后,如上所述,使用“Flood Fill”方法计算像素。 (Google Flood Fill)