Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Distorting an image to a quadrangle fails in some cases on Android

I'm using matrix.setPolyToPoly function to transform a selected region (4 corners) of a bitmap into a rectangle and normally it works amazing. But in the next example:

4 corners selection image

The polyToPoly function fails the perspective transform:

Bad transformation image

I have drawn two lines for testing, the lines mark where I want the four selected points.

What I'm doing wrong? Thanks!

EDIT: I've solved the problem using canvas.drawBitmapMesh, Thanks pskink for your advice!!

This is the final code

private float[] generateVertices(int widthBitmap, int heightBitmap) {
    float[] vertices=new float[(WIDTH_BLOCK+1)*(HEIGHT_BLOCK+1)*2];

    float widthBlock = (float)widthBitmap/WIDTH_BLOCK;
    float heightBlock = (float)heightBitmap/HEIGHT_BLOCK;

    for(int i=0;i<=HEIGHT_BLOCK;i++)
        for(int j=0;j<=WIDTH_BLOCK;j++) {
            vertices[i * ((HEIGHT_BLOCK+1)*2) + (j*2)] = j * widthBlock;
            vertices[i * ((HEIGHT_BLOCK+1)*2) + (j*2)+1] = i * heightBlock;
        }
    return vertices;
}

private Bitmap perspectiveTransformation(Bitmap bitmap, ArrayList<Point> bitmapPoints) {

    Bitmap correctedBitmap;
    int maxX = (int) Math.max(Math.abs(bitmapPoints.get(0).x - bitmapPoints.get(1).x), Math.abs(bitmapPoints.get(2).x - bitmapPoints.get(3).x));
    int maxY = (int) Math.max(Math.abs(bitmapPoints.get(0).y - bitmapPoints.get(3).y), Math.abs(bitmapPoints.get(1).y - bitmapPoints.get(2).y));
    Log.d("max", "x=" + maxX + " y=" + maxY); //This is the desired final size

    Bitmap.Config conf = Bitmap.Config.ARGB_8888;
    correctedBitmap = Bitmap.createBitmap(maxX,maxY,conf); //the final bitmap
    float mVertices[] =generateVertices(bitmap.getWidth(),bitmap.getHeight());

    Point mLeftTop = bitmapPoints.get(0);
    Point mRightTop = bitmapPoints.get(1);
    Point mLeftBot = bitmapPoints.get(3);
    Point mRightBot = bitmapPoints.get(2);  //the points on the image where the user has clicked

    Canvas canvas = new Canvas(correctedBitmap);

    Matrix matrix = new Matrix();
    matrix.setPolyToPoly(
            new float[]{mLeftTop.x, mLeftTop.y,
                    mRightTop.x, mRightTop.y,
                    mRightBot.x, mRightBot.y,
                    mLeftBot.x, mLeftBot.y   //the user's points
            },
            0,
            new float[]{0, 0,
                    maxX - 1, 0,
                    maxX - 1, maxY - 1,
                    0, maxY - 1             //where I want the user points in the corrected image
            }
            , 0, 4);

    canvas.concat(matrix);

    Paint paint = new Paint();
    paint.setAntiAlias(true);       //testing parameters
    paint.setFilterBitmap(true);    //testing parameters

    paint.setColor(Color.BLUE);
    paint.setStyle(Paint.Style.STROKE);

    canvas.drawBitmapMesh(bitmap, WIDTH_BLOCK , HEIGHT_BLOCK, mVertices,0,null,0, paint);  //draw the original bitmap into the corrected bitmap with PolyToPoly transformation matrix

    canvas.drawLine(mLeftTop.x, mLeftTop.y, mRightBot.x, mRightBot.y, paint); //draw two lines for testing the transformation matrix
    canvas.drawLine(mLeftBot.x, mLeftBot.y, mRightTop.x, mRightTop.y, paint);

    //bitmap.recycle();  //just testing

    return correctedBitmap;
}
like image 626
Pep Santacruz Avatar asked Mar 30 '16 12:03

Pep Santacruz


Video Answer


2 Answers

Had the same issue with drawBitmap. Opened an issue in Skia - Android's Canvas backend

https://bugs.chromium.org/p/skia/issues/detail?id=8675#c4

like image 168
valka Avatar answered Oct 10 '22 03:10

valka


private float[] generateVertices(int widthBitmap, int heightBitmap) {
    float[] vertices=new float[(WIDTH_BLOCK+1)*(HEIGHT_BLOCK+1)*2];

    float widthBlock = (float)widthBitmap/WIDTH_BLOCK;
    float heightBlock = (float)heightBitmap/HEIGHT_BLOCK;

    for(int i=0;i<=HEIGHT_BLOCK;i++)
        for(int j=0;j<=WIDTH_BLOCK;j++) {
            vertices[i * ((HEIGHT_BLOCK+1)*2) + (j*2)] = j * widthBlock;
            vertices[i * ((HEIGHT_BLOCK+1)*2) + (j*2)+1] = i * heightBlock;
        }
    return vertices;
}

private Bitmap perspectiveTransformation(Bitmap bitmap, ArrayList<Point> bitmapPoints) {

    Bitmap correctedBitmap;
    int maxX = (int) Math.max(Math.abs(bitmapPoints.get(0).x - bitmapPoints.get(1).x), Math.abs(bitmapPoints.get(2).x - bitmapPoints.get(3).x));
    int maxY = (int) Math.max(Math.abs(bitmapPoints.get(0).y - bitmapPoints.get(3).y), Math.abs(bitmapPoints.get(1).y - bitmapPoints.get(2).y));
    Log.d("max", "x=" + maxX + " y=" + maxY); //This is the desired final size

    Bitmap.Config conf = Bitmap.Config.ARGB_8888;
    correctedBitmap = Bitmap.createBitmap(maxX,maxY,conf); //the final bitmap
    float mVertices[] =generateVertices(bitmap.getWidth(),bitmap.getHeight());

    Point mLeftTop = bitmapPoints.get(0);
    Point mRightTop = bitmapPoints.get(1);
    Point mLeftBot = bitmapPoints.get(3);
    Point mRightBot = bitmapPoints.get(2);  //the points on the image where the user has clicked

    Canvas canvas = new Canvas(correctedBitmap);

    Matrix matrix = new Matrix();
    matrix.setPolyToPoly(
            new float[]{mLeftTop.x, mLeftTop.y,
                    mRightTop.x, mRightTop.y,
                    mRightBot.x, mRightBot.y,
                    mLeftBot.x, mLeftBot.y   //the user's points
            },
            0,
            new float[]{0, 0,
                    maxX - 1, 0,
                    maxX - 1, maxY - 1,
                    0, maxY - 1             //where I want the user points in the corrected image
            }
            , 0, 4);

    canvas.concat(matrix);

    Paint paint = new Paint();
    paint.setAntiAlias(true);       //testing parameters
    paint.setFilterBitmap(true);    //testing parameters

    paint.setColor(Color.BLUE);
    paint.setStyle(Paint.Style.STROKE);

    canvas.drawBitmapMesh(bitmap, WIDTH_BLOCK , HEIGHT_BLOCK, mVertices,0,null,0, paint);  //draw the original bitmap into the corrected bitmap with PolyToPoly transformation matrix

    canvas.drawLine(mLeftTop.x, mLeftTop.y, mRightBot.x, mRightBot.y, paint); //draw two lines for testing the transformation matrix
    canvas.drawLine(mLeftBot.x, mLeftBot.y, mRightTop.x, mRightTop.y, paint);

    //bitmap.recycle();  //just testing

    return correctedBitmap;
}
like image 34
Pep Santacruz Avatar answered Oct 10 '22 04:10

Pep Santacruz