11

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;
}
Community
  • 1
  • 1
Pep Santacruz
  • 487
  • 3
  • 11
  • 1
    you cannot do everything with `setPolyToPoly`, try `Canvas.drawBitmapMesh` instead – pskink Mar 30 '16 at 13:04
  • Thanks! I've updated the question with the final code. – Pep Santacruz Mar 30 '16 at 16:11
  • @PepSantacruz Thank you for a clear, well expressed question. The spirit of SO is that questions & answers may be helpful to others long after the original post. Is this spirit, could you revert the edit to your question so that the code is that of the original post (with the working code in the answer as you have done)? Also, could you 'Accept' your answer. By doing these things, future visitors will be able to see a clear difference between the code that had a problem & the code that fixed the problem. 'Accept' will make an obvious indication that this answer fixed the problem. – Gary99 Apr 26 '17 at 14:32
  • I'm trying to solve a similar problem !! can you please explain what do you mean by "WIDTH_BLOCK" and "HEIGHT_BLOCK" in this example ? thanks in advance – user 007 Apr 25 '18 at 12:39
  • These are the desired size of each block of the mesh. Ex: image size=(1000px,500px), you can define WIDTH_BLOCK and HEIGHT_BLOCK as 100 and 50 respectively, or 200 and 100, or 50 and 100, or... you know ;). – Pep Santacruz Apr 26 '18 at 13:42

2 Answers2

2
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;
}
eli-k
  • 10,898
  • 11
  • 40
  • 44
Pep Santacruz
  • 487
  • 3
  • 11
2

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

valka
  • 116
  • 4