如何转换Android位图以包装圆柱体并更改透视图 [英] How to transform an Android bitmap to wrap a cylinder and change perspective

查看:85
本文介绍了如何转换Android位图以包装圆柱体并更改透视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个示例应用程序,该应用程序允许Android用户拍照并从视图中获取文本内容作为图像的叠加层,并保存到图库相册中:

I wrote a sample app that allows the Android user to take a picture and have the text content from a view as an overlay on the image and saved to a gallery album:

我想要做的是在合并两个图像之前转换文本位图.具体来说,我想使文本在侧面向上弯曲(模拟围绕圆柱体的环绕),并使其顶部大于底部(模拟从顶向下的角度),如下所示:

What I would like to to is transform the text bitmap before joining the two images. Specifically, I'd like to make the text curve up on the sides (simulating wrapping around a cylinder), and make it larger at the top than the bottom (simulating a top down perspective), as illustrated here:

无需为了确定曲率或透视变化而解释摄像机图像.问题是如何操纵位图以便可以进行两个转换.

There is no need to interpret the camera image in order to determine the amount of curvature or perspective change. The question is how to manipulate the bitmap such that the two transforms can be made.

这是我用来将未转换的文本输入到相机图像和图库中的代码:

Here's the code I used to get the non-transformed text into the camera image and into the gallery:

private void combinePictureWithText(String fileName) {
    Log.v(TAG, "combinePictureWithText");

    int targetW = getWindowManager().getDefaultDisplay().getWidth();
    int targetH = getWindowManager().getDefaultDisplay().getHeight();

    /* Get the size of the image */
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(fileName, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    /* Figure out which way needs to be reduced less */
    int scaleFactor = 1;
    if ((targetW > 0) || (targetH > 0)) {
        scaleFactor = Math.min(photoW/targetW, photoH/targetH);
    }
    Log.v(TAG, "Scale Factor: " + scaleFactor);

    /* Set bitmap options to scale the image decode target */
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    mBeerLayout.setDrawingCacheEnabled(true);
    Bitmap mDrawingCache = mBeerLayout.getDrawingCache();

    Bitmap cameraBitmap = BitmapFactory.decodeFile(fileName, bmOptions);
    Bitmap textBitmap = Bitmap.createBitmap(mDrawingCache);
    Bitmap combinedBitmap = Bitmap.createBitmap(targetW, targetH, Bitmap.Config.ARGB_8888);
    Canvas comboImage = new Canvas(combinedBitmap);
    cameraBitmap = Bitmap.createScaledBitmap(cameraBitmap, targetW, targetH, true);
    comboImage.drawBitmap(cameraBitmap, 0, 0, null);
    comboImage.drawBitmap(textBitmap, 0, 0, null); // WAS: matrix (instead of 0, 0)

    /* Save to the file system */
    Log.v(TAG, "save combined picture to the file system");
    try {
        File aFile = new File(fileName);
        if (aFile.exists()) {
            Log.v(TAG, "File " + fileName + "  existed. Deleting it.");
            //aFile.delete();
        } else {
            Log.v(TAG, "File " + fileName + " did not exist.");
        }
        FileOutputStream out = new FileOutputStream(fileName);
        combinedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
        out.flush();
        out.close();
        Log.v(TAG, "Saved " + fileName);
    } catch (Exception e) {
        Log.v(TAG, "Failed in file output stream " + e.getMessage());
    }

    /* Associate the Bitmap to the ImageView */
    //mImageView.setImageBitmap(combinedBitmap); // DRS was "bitmap"
    //mImageView.setVisibility(View.VISIBLE);

    /* Add as a gallery picture */
    Log.v(TAG, "galleryAddPic");
    Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
    File f = new File(fileName);
    Uri contentUri = Uri.fromFile(f);

    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

This question/answer might provide details for how to do the perspective alteration, but I do not believe it answers the question for simulation of wrapping text around a cylinder.

推荐答案

此问题的一种解决方案是使用Canvas.drawBitmapMesh().此解决方案的优点是不需要附加的OpenGL复杂性.

One solution to this problem is to use Canvas.drawBitmapMesh(). This solution has the benefit of not requiring the added complexity of OpenGL.

使用mesh的想法是,将输入到函数中的任何位图都扭曲以适合定义的点.该图显示了每个椭圆只有5个点的情况: 因此,将对位图中位于矩形左侧区域的部分进行调整,以适合我们计算的顶点定义的区域的左侧多边形的形状.

The idea behind using a mesh is that whatever bitmap is fed into the function is warped to fit the defined points. The image shows what's happening with just 5 points per ellipse: So the portion of the bitmap that falls in the left, rectangular area will be manipulated to fit in the shape of the left polygon of the area defined by the vertices that we calculate.

两个椭圆的建模部分为网格提供值.

Modeling portions of two ellipses provide the values the mesh.

将有很多方法可以计算通常可以完成所需顶点的顶点,并且此实现不一定是最佳,最有效,最易理解的等,但是这样做的好处是可以完成原来的问题.

There are going to be many ways to compute vertices that would do generally what is required, and this implementation is not necessarily the best, most efficient, most understandable, etc, but it has the benefit of doing what is asked about in the original question.

private static float[] computeVerticesFromEllipse(int width, int height, int steps, float curveFactor, float topBottomRatio) {
    double p = width / 2d;
    double q = 0d;
    double a = width / 2d;
    double b = curveFactor * a;

    float[] verticies = new float[(steps-1) * 4];
    int j = 0;

    double increment = width / (double)steps;

    for (int i = 1; i < steps; i++, j=j+2) {
        verticies[j] = (float)(increment * (double)i);
        verticies[j+1] =-(float) (-Math.sqrt((1-Math.pow(((increment * (double)i)-p), 2)/Math.pow(a,2)) * Math.pow(b,2)) + q);
        Log.v(TAG, "Point, " + verticies[j] + ", " + verticies[j+1] + ", " + j + ", " +(j+1));
    }

    double width2 = topBottomRatio * width;
    p = width2 / 2d;
    q = (width - width2) / 2d;
    a = width2 / 2d;
    b = curveFactor * a;
    increment = width2 / (double)steps;

    double shift = width * (1d - topBottomRatio) / 2d;

    for (int i = 1; i < steps; i++, j=j+2) {
        verticies[j] = (float)(increment * (double)i) + (float)shift;
        verticies[j+1] =(float) -(-Math.sqrt((1-Math.pow(((increment * (double)i)-p), 2)/Math.pow(a,2)) * Math.pow(b,2)) + q)+ height;
        Log.v(TAG, "Point, " + verticies[j] + ", " + verticies[j+1] + ", " + j + ", " +(j+1));
    }

    return verticies;
}

要将这种新方法集成到原始问题的代码中,我们定义了一些项目来控制结果的外观(curveFactortopBottomRatio).可以调整steps变量以获得更平滑的结果.最后,我们定义了要输入drawBitmapMesh()方法的项目(columnsscaledVertices).然后,我们使用drawBitmapMesh()而不是使用drawBitmap().

To integrate this new method into the code of the original question, we define some items to control the look of the result (curveFactor, topBottomRatio). The steps variable can be adjusted to get a smoother looking result. Finally, we defined items to enter into the drawBitmapMesh() method (columns, scaledVertices). Then, rather than using drawBitmap(), we use drawBitmapMesh().

    // 2017 03 22 - Added 5
    float curveFactor = 0.4f;
    float topBottomRatio = 0.7f;
    int steps = 48;
    float[] scaledVertices = computeVerticesFromEllipse(textBitmap.getWidth(), textBitmap.getHeight(), steps, curveFactor, topBottomRatio);
    int columns = (scaledVertices.length / 4) - 1; // divide by 2 since for two points, divide by 2 again for two rows

    Bitmap combinedBitmap = Bitmap.createBitmap(targetW, targetH, Bitmap.Config.ARGB_8888);
    Canvas comboImage = new Canvas(combinedBitmap);
    cameraBitmap = Bitmap.createScaledBitmap(cameraBitmap, targetW, targetH, true);
    comboImage.drawBitmap(cameraBitmap, 0, 0, null);
    // 2017 03 22 - Commented 1, Added 1
    // comboImage.drawBitmap(textBitmap, 0, 0, null); 
    comboImage.drawBitmapMesh(textBitmap, columns, 1, scaledVertices, 0, null, 0, null);

这篇关于如何转换Android位图以包装圆柱体并更改透视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆