如何在Android中计算位图擦除区域的百分比?

4

我是一名初学者,正在开发一个可以使用手指擦除画布上位图的应用程序,类似于手指绘画橡皮擦。我想计算已擦除区域的百分比(例如,完整图像中已擦除60%)。请帮助我完成这个功能。谢谢!

我尝试了一些方法,但总是得到0%的结果,不起作用。请看代码底部的方法。

自定义视图

public class MyView extends View
{
    private final Paint mPaint;
    private Bitmap mBitmap;
    private Canvas mCanvas;
    private final Path mPath;
    private final Paint mBitmapPaint;
    private Bitmap eraseableBitmap;

    public MyView(Context context)
    {
        super(context);
        if (Build.VERSION.SDK_INT >= 11)
        {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
        mPaint = new Paint();
        mPath = new Path();
        mBitmapPaint = new Paint(Paint.DITHER_FLAG);
    }

    protected void PaintObjectInit()
    {
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(30);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);

        try
        {
            //mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            eraseableBitmap =
                BitmapFactory.decodeResource(this.getResources(), R.drawable.tharu_rena_over).copy(
                    Bitmap.Config.ARGB_8888, true);

            eraseableBitmap = getResizedBitmap(eraseableBitmap, h, w);

            mCanvas = new Canvas(eraseableBitmap );
        }
        catch (Exception e)
        {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

    public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth)
    {
        int width = bm.getWidth();
        int height = bm.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // CREATE A MATRIX FOR THE MANIPULATION
        Matrix matrix = new Matrix();
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight);

        // "RECREATE" THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
        return resizedBitmap;
    }

    @Override
    protected void onDraw(Canvas canvas)
    {

        //canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
        canvas.drawBitmap(eraseableBitmap, 0, 0, mBitmapPaint);
        canvas.drawPath(mPath, mPaint);
    }

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

    private void touch_start(float x, float y)
    {
        //        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
    }

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

    private void touch_up()
    {
        mPath.lineTo(mX, mY);
        // commit the path to our offscreen
        mCanvas.drawPath(mPath, mPaint);
        // kill this so we don't double draw
        Toast.makeText(getContext(), "Deleted: " + percentTransparent(eraseableBitmap, 10), Toast.LENGTH_SHORT).show();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }
        return true;
    }

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

    }

}

在Activity类的onCreate方法中

try
{
    MyView myView = new MyView(this);
    myView.requestFocus();
    myView.PaintObjectInit();
    // setContentView(myView);

    LinearLayout upper = (LinearLayout) findViewById(R.id.LinearLayout01);
    upper.addView(myView);
}
catch (Exception e)
{
    // TODO: handle exception
    e.printStackTrace();
}

我不知道为什么它不起作用,但你为什么不调试一下你的函数,看看所有的变量是否正确,比如 xStep,或者尝试使用1像素步长运行该函数,并检查它是否进入了for循环,如果是的话,你可以记录 bm.getPixel(x, y)。然后你可能可以自己找出为什么它不起作用的原因。 - Ivo
谢谢,我调试好了。它进入了循环。而且始终bm.getPixel(x, y)都不为零。由于我给它设置了缩放比例10,每次迭代100次。所以我大约进行了20次迭代。对于所有这些像素,我得到了非零值。但是它总是弹出“0.0%已删除”的提示。我不知道为什么。 - ssdehero
1
你在哪里将“erased”像素设置为Color.TRANSPARENT? - Martin
当触摸时,它会使路径上的像素Color.TRANSPARENT。我认为覆盖绘制路径的所有像素都将设置为Color.TRANSPARENT。我从SDK的手指绘画示例应用程序中获得了该代码。 - ssdehero
你知道如何计算被擦除区域的百分比吗?请分享一下!谢谢。 - Nguyen Minh Binh
@NguyenMinhBinh,在我的问题中,您可以看到percentTransparent(Bitmap bm,int scale)方法。它计算百分比。正如您所看到的,您必须传递具有比例的特定位图(将位图分成样本)。只需查看代码,您就可以理解它。我在重新调整位图大小时搞砸了。除非代码运行良好。 - ssdehero
1个回答

1
问题是您未能调用PaintObjectInit()方法,因此使用默认画笔进行绘制,因此在Color.BLACK中绘制而不是Color.TRANSPARENT。在构造函数底部添加对PaintObjectInit()的调用即可解决。

此外,以下内容创建了不可变位图!请参见createBitmap。因此,您的位图永远不会被修改。通过绘制路径和位图,您正在在用户界面上隐藏此事实。

// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;

尝试创建一个可变位图,像这样 -
// "RECREATE" THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(resizedBitmap);
        c.setMatrix(matrix);
        c.drawBitmap(bm, matrix, null);
        return resizedBitmap;

不是的。我是在我的活动类中调用它,在将该视图对象添加到布局之前。所以我的擦除部分是有效的。 - ssdehero
你是如何从活动类中调用PaintObjectInit()方法的,如果它是你的视图的受保护方法?你是否在这段代码之外对对象的状态进行了其他更改? - iagreen
最好让视图自己初始化,这样每次重用视图时都会执行此步骤。这是一件容易被忘记的事情,当我复制你的代码时就会出现明显的问题。你错误的根源在于你在getResizedBitmap方法中分配了一个不可变位图。编辑后的答案。 - iagreen
现在我有另一个问题。现在像素正在变成透明的。但是当我画线时,它会被复制(绘制了两条线)。当我触摸图像上面的区域时,底部区域也被删除了。我猜现在有2个位图。而我已经使用矩阵扩展了它们(就像你的答案一样),它只会拉伸图像而不改变原始位图的宽度和高度(这正是我想要的方法)。请帮助我解决这个问题。谢谢。 - ssdehero

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