在 Android 上的某些情况下,将图像扭曲为四边形会失败

Posted

技术标签:

【中文标题】在 Android 上的某些情况下,将图像扭曲为四边形会失败【英文标题】:Distorting an image to a quadrangle fails in some cases on Android 【发布时间】:2016-03-30 12:27:18 【问题描述】:

我正在使用 matrix.setPolyToPoly 函数将位图的选定区域(4 个角)转换为矩形,通常效果很好。但在下一个例子中:

polyToPoly 函数未能通过透视变换:

我已经画了两条线进行测试,这些线标记了我想要四个选定点的位置。

我做错了什么?谢谢!

编辑:我已经使用 canvas.drawBitmapMesh 解决了这个问题,感谢pskink 的建议!!

这是最终代码

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;

【问题讨论】:

你不能用setPolyToPoly做所有事情,试试Canvas.drawBitmapMesh 谢谢!我已经用最终代码更新了问题。 @PepSantacruz 感谢您提出一个清晰、明确的问题。 SO 的精神是,问题和答案可能会在原始帖子很久之后对其他人有所帮助。是这种精神吗,您能否将编辑还原为您的问题,以便代码是原始帖子的代码(正如您所做的那样,答案中有工作代码)?另外,您能否“接受”您的答案。通过做这些事情,未来的访问者将能够看到有问题的代码和解决问题的代码之间的明显区别。 “接受”将明确表明此答案解决了问题。 我正在尝试解决类似的问题!你能解释一下这个例子中的“WIDTH_BLOCK”和“HEIGHT_BLOCK”是什么意思吗?提前致谢 这些是每个网格块的所需大小。例如:图像大小=(1000px,500px),您可以将 WIDTH_BLOCK 和 HEIGHT_BLOCK 分别定义为 100 和 50,或者 200 和 100,或者 50 和 100,或者......你知道的;)。 【参考方案1】:
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;

【讨论】:

感谢您的精彩回答和问题 - 发送赏金表示感谢 谢谢!! @Fattie【参考方案2】:

drawBitmap 也有同样的问题。在 Skia 中打开了一个问题 - android 的 Canvas 后端

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

【讨论】:

以上是关于在 Android 上的某些情况下,将图像扭曲为四边形会失败的主要内容,如果未能解决你的问题,请参考以下文章

如何在不扭曲纵横比的情况下将 1000x1000 图像调整为 200x300 图像[重复]

Android ImageView 将较小的图像缩放到具有灵活高度的宽度,而不会裁剪或扭曲

如何将未扭曲图像的点投影到扭曲图像?

初学Android 图形图像之使用drawBitmapMesh扭曲图像 三十二

为啥使用相机意图捕获的图像会在 Android 上的某些设备上旋转?

如何在没有抗锯齿的情况下拉伸图像