如何在Android画布上绘制路径时获取总覆盖面积?

15

我正在使用以下代码在位图画布上绘制线条,当手指滑动时...这里我发布了部分代码,它可以正常工作。

如下图所示,黑白位图在触摸拖动时被擦除。我使画布透明,所以父布局的背景(彩色图像)变得可见。

我想知道,擦除了多少区域(例如位图的50%或60%)...是否有办法找到?

enter image description here

//Erasing paint

         mDrawPaint = new Paint();
    mDrawPaint.setAntiAlias(true); 
    mDrawPaint.setDither(true);  
    mDrawPaint.setStyle(Paint.Style.STROKE); 
    mDrawPaint.setStrokeJoin(Paint.Join.ROUND);
    mDrawPaint.setStrokeCap(Paint.Cap.ROUND);
    mDrawPaint.setStrokeWidth(50); 
    mDrawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    BlurMaskFilter mBlur = new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL);
    mDrawPaint.setMaskFilter(mBlur);

private void doDraw(Canvas c) {

    c.drawBitmap(mBitmap, 0, 0,null );

}

private float mX, mY;
private static final float TOUCH_TOLERANCE = 1;

void touch_start(float x, float y) {
    mPath.reset();
    mPath.moveTo(x, y);
    mX = x;
    mY = y;
}
void touch_move(float x, float y) {
    float dx = Math.abs(x - mX);
    float dy = Math.abs(y - mY);
    if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
        mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
        mX = x;
        mY = y;
    }

     canvas.drawPath(mPath, mDrawPaint ); //Erasing Black and white image

}
void touch_up() {
    mPath.lineTo(mX, mY);
    // commit the path to our offscreen
    mCanvas.drawPath(mPath, mDrawPaint);
    // kill this so we don't double draw
    mPath.reset();
}

如果我理解你的问题,你需要检查mBitmap的每个像素并判断它是否为空白。 - Lumis
5个回答

8
尝试使用蒙特卡罗方法来估计透明区域的百分比。我认为这是一种最快最简单的方法。在透明度掩码上随机选取大约50个像素(取决于所需精度),并检查它们的颜色。然后计算ans = TransparentPixelsCount/TestPixelCount。
使用路径坐标计算用户绘图的面积非常困难。迭代所有像素也需要相当长的时间。因此,我的意见是选择蒙特卡罗方法。

1
请问您能否分享一个在Android/Java上实现的样例? - Nguyen Minh Binh

5
要获得准确(但较慢)的答案,您需要检查每个像素并计算透明像素数,然后将其除以总像素数。如果您的要求允许进行一些估计,最好对图像进行采样。您可以缩小图像并在较小的图像上运行上述过程。这种方法的缺点是缩放操作可能会遍历所有像素,使其变慢。我们建议使用网格采样法,类似于缩小操作,但会跳过像素。基本上,我们在图像上均匀地分布x个采样点。然后计算透明样本的数量。透明百分比的估计值为总透明样本数/透明样本数。您可以使用较少的样本数量(例如100个)获得合理的准确度(通常在5%内)。这里是一个实现此方法的代码函数 - bm是位图,scale是每个轴的样本数,因此设置scale = 10会在图像上产生100个总样本(10x10采样网格)。
static public float percentTransparent(Bitmap bm, int scale) {

        final int width = bm.getWidth();
        final int height = bm.getHeight();

        // size of sample rectangles
        final int xStep = width/scale;
        final int yStep = height/scale;

        // center of the first rectangle
        final int xInit = xStep/2;
        final int yInit = yStep/2;

        // center of the last rectangle
        final int xEnd = width - xStep/2;
        final int yEnd = height - yStep/2;

        int totalTransparent = 0;

        for(int x = xInit; x <= xEnd; x += xStep) {
            for(int y = yInit; y <= yEnd; y += yStep) {
                if (bm.getPixel(x, y) == Color.TRANSPARENT) {
                    totalTransparent++;
                }
            }
        }
        return ((float)totalTransparent)/(scale * scale);

    }

作为参考,通过计算每个像素来获得结果的慢方法如下。它可以用于测试上述估计器的参考。

static public float percentTransparent(Bitmap bm) {
        final int width = bm.getWidth();
        final int height = bm.getHeight();

        int totalTransparent = 0;
        for(int x = 0; x < width; x++) {
            for(int y = 0; y < height; y++) {
                if (bm.getPixel(x, y) == Color.TRANSPARENT) {
                    totalTransparent++;
                }
            }
        }
        return ((float)totalTransparent)/(width * height);

    }

如果位图大小等于设备大小,那么我需要给出什么比例尺? - vnshetty
它与设备大小无关,而是取样数量的多少。更多的样本=更好的准确性。比例10 = 100个样本,因此每个样本代表图像的1%。如果您想要更好的结果,可以尝试15或20(对于20,每个样本为0.25%)。我在scale = 10下使用640x480图像进行了测试,通常与实际值相差3-4%。 - iagreen
@vnshetty。之前的评论中忘记提到你了,我已经添加了完整的解决方案来测试估计值。 - iagreen
我尝试了你的代码,但是我得到了类似于0.7778E34%的值...我在on touch up上调用了这个函数...然而,我希望它在on touch move上完成,当涉及速度问题时,这将更加困难... - vnshetty
@vnshetty 你使用的是什么比例尺?至于速度,这就是为什么要采用较小的样本的原因,第二个程序将会非常缓慢,但可以给出精确的结果。第一个程序提供了一个估计值,但运行速度更快,特别是对于少量的样本。 - iagreen
嗨,我也在使用同一个例子。但每次我的百分比都是0%。这意味着“bm.getPixel(x,y)”始终具有非零值。请帮帮我。请参考我的问题 - ssdehero

1

您需要检测绘制的多边形内部的点。 以下是函数,它接受包含所有绘制点的数组作为第一个参数,第二个参数是点本身,即x,y。

// Return true if the dot { x,y } is within any of the polygons in the list

function pointInPolygons( polygons, dot )

      for (i=1, [polygons count] i++)
         {
           if (pointInPolygon( polygons[i], dot )) 
                    return true
         } 

      return false
end

// Returns true if the dot { x,y } is within the polygon 
//defined by points table { {x,y},-    --{x,y},{x,y},... }

function pointInPolygon( points, dot )
    local i, j = #points, #points
    local oddNodes = false

    for i=1, #points do
            if ((points[i].y < dot.y and points[j].y>=dot.y
                    or points[j].y< dot.y and points[i].y>=dot.y) and (points[i].x<=dot.x
                    or points[j].x<=dot.x)) then
                    if (points[i].x+(dot.y-points[i].y)/(points[j].y-points[i].y)*(points[j].x-points[i].x)<dot.x) then
                            oddNodes = not oddNodes
                    end
            end
            j = i
    end

    return oddNodes
   end

1
另一种方法是:您可以使用ComputeBounds计算每个路径的大小。然后,将其与视图的大小进行比较,并决定绘图的百分比。只需记住路径可能会重叠绘制,因此需要在计算中小心处理。

0

将所有点的x和y值存储在两个不同的排序集合中,一个用于点的x值,另一个用于点的y值。您的边界的最终值将是点(min_x,min_y)和点(max_x,max_y)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接