iOS如何计算曲线包围的像素数/面积? [英] iOS how to calculate number of pixels/area enclosed by a curve?

查看:43
本文介绍了iOS如何计算曲线包围的像素数/面积?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我得到了一条任意形状的曲线,包围了一些区域.我想估计曲线在 iPhone/iPad 屏幕上包围的像素数.我该怎么做?

I got an arbitrary shaped curve, enclosing some area. I would like to approximate the number of pixels that the curve is enclosing on an iPhone/iPad screen. How can I do so?

  • 曲线被定义为点的连续 x/y 坐标.
  • 一条曲线是闭合的.
  • 一条曲线是由用户的触摸(touchesMoved方法)绘制的,我不知道它是什么样子

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

I was thinking of somehow filling the closed curve with color, then calculating the number of pixels of this color in a screenshot of a screen. This means I need to know how to programmatically fill a closed curve with color.

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

Is there some other way that I'm not thinking of?

谢谢!

推荐答案

让我们通过创建一个包含曲线的 Quartz 路径来做到这一点.然后我们将创建一个位图上下文并填充该上下文中的路径.然后我们可以检查位图并计算填充的像素.我们将把这一切都包装在一个方便的函数中:

Let's do this by creating a Quartz path enclosing your curve. Then we'll create a bitmap context and fill the path in that context. Then we can examine the bitmap and count the pixels that were filled. We'll wrap this all in a convenient function:

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

首先我们需要创建路径:

First we need to create the path:

    CGPathRef path = createClosedPathWithPoints(points, count);

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

Then we need to get the bounding box of the path. CGPoint coordinates don't have to be integers, but a bitmap has to have integer dimensions, so we'll get an integral bounding box at least as big as the path's bounding box:

    CGRect frame = integralFrameForPath(path);

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

We also need to decide how wide (in bytes) to make the bitmap:

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

现在我们可以创建位图了:

Now we can create the bitmap:

    CGContextRef gc = createBitmapContextWithFrame(frame, bytesPerRow);

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

The bitmap is filled with black when it's created. We'll fill the path with white:

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

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

Now we're done with the path so we can release it:

    CGPathRelease(path);

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

Next we'll compute the area that was filled:

    double area = areaFilledInBitmapContext(gc);

现在我们完成了位图上下文,所以我们可以释放它:

Now we're done with the bitmap context, so we can release it:

    CGContextRelease(gc);

最后,我们可以返回我们计算的面积:

Finally, we can return the area we computed:

    return area;
}

嗯,这很容易!但是我们必须编写所有这些辅助函数.让我们从顶部开始.创建路径很简单:

Well, that was easy! But we have to write all those helper functions. Let's start at the top. Creating the path is trivial:

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

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

Getting the integral bounding box of the path is also trivial:

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

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

To choose the bytes per row of the bitmap, we could just use width of the path's bounding box. But I think Quartz likes to have bitmaps that are multiples of a nice power of two. I haven't done any testing on this, so you might want to experiment. For now, we'll round up the width to the next smallest multiple of 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).

We create the bitmap context with the computed sizes. We also need to translate the origin of the coordinate system. Why? Because the origin of the path's bounding box might not be at (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 使用灰色像素填充路径时,它会消除路径边缘的锯齿.所以我们必须决定如何计算那些灰色像素.

Finally, we need to write the helper that actually counts the filled pixels. We have to decide how we want to count pixels. Each pixel is represented by one unsigned 8-bit integer. A black pixel is 0. A white pixel is 255. The numbers in between are shades of gray. Quartz anti-aliases the edge of the path when it fills it using gray pixels. So we have to decide how to count those gray pixels.

一种方法是定义一个阈值,比如 128.任何等于或高于阈值的像素都被视为已填充;其余的算作未填满.

One way is to define a threshold, like 128. Any pixel at or above the threshold counts as filled; the rest count as unfilled.

另一种方法是将灰色像素计算为部分填充,并将该部分填充相加.因此,两个完全半填充的像素组合在一起并算作一个完全填充的像素.让我们这样做:

Another way is to count the gray pixels as partially filled, and add up that partial filling. So two exactly half-filled pixels get combined and count as a single, entirely-filled pixel. Let's do it that way:

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;
}

您可以在本要点中找到捆绑的所有代码.

You can find all of the code bundled up in this gist.

这篇关于iOS如何计算曲线包围的像素数/面积?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆