前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 实现 图片 转 字符画 效果

Android 实现 图片 转 字符画 效果

作者头像
音视频开发进阶
发布2020-07-06 17:14:30
1.2K0
发布2020-07-06 17:14:30
举报

开门见山!先上效果图:

字符稍微密集了一点,不过放大来看大家应该能够看到确确实实是 字符画

Android 端实现

Android开发中对图片的操作,显示一般都是通过Bitmap进行的,我们可以通过图片路径获取Bitmap对象:

static public Bitmap getBitmapByUri(Context context, Uri uri) {
        Bitmap bit = null;
        try {
            bit = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri));
        } catch (Exception ex) {
            Log.i("utils", "" + ex.getMessage());
        }
        return bit;
    }

一个图片的每一个像素其实都是一个值,这个值代表着这个像素的颜色,我们可以通过位运算来获取这个像素的ARGB值。

在安卓开发中要获取一个图片的每一个像素值其实很简单:

//按照参数范围获取像素数组
bitmap.getPixels(...);
//或者获取单个位置像素
bitmap.getPixel(x,y);

当我们获取到了像素值,转换成ARGB值后,我们获取带了RGB三个值,要如何判断什么颜色用什么字?要知道调色轮盘的颜色数不胜数:

这么多的颜色我们应该用什么样的标准给这么多颜色归类?

灰度值获取

灰度值是个很好的办法,什么是灰度值?

灰度值的范围只有0到255,计算方式一般是RGB三个值的平均值(也可以通过对RGB值进行加权计算不同的灰度),在很多图像处理里面的图片灰度化步骤用的就是这种方法。

原理跟思路清楚了,我们实现下把Bitmap转化成灰度值数组的方法:

 static public int[][] getBitmap2GaryArray(Bitmap bitmap) {
        int width = bitmap.getWidth();            //获取位图的宽
        int height = bitmap.getHeight();        //获取位图的高
        int[][] datas = new int[width][height];    //通过位图的大小创建像素点数组
        //也可以使用getPixels方法来获取像素数组
        //bitmap.getPixels(datas, 0, width, 0, 0, width, height);
        int alpha = 0xFF << 24;
        bitmap.getPixels();
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                int grey = bitmap.getPixel(i, j);
                int red = (grey & 0x00ff0000) >> 16; //取高两位
                int green = (grey & 0x0000ff00) >> 8; //取中两位
                int blue = grey & 0x000000ff; //取低两位

                grey = (int) ((float) red * 0.4 + (float) green * 0.3 + (float) blue * 0.3);
                datas[i][j] = grey;
            }
        }
        return datas;
    }

在获取像素前我们还需要多做一步,为了防止图片过大(类似2K图/4K图),我们需要在获取像素前做一次统一标准化的压缩,我设置为宽为200,高等比例压缩。

...
//宽为200时,计算压缩比例是多少
float xScale = (float) 200 / bitmap.getWidth();
bitmap = BitmapUtils.compressBitmap(bitmap, xScale, xScale);
...

static public Bitmap compressBitmap(Bitmap bitmap, float sx, float sy) {
        Matrix matrix = new Matrix();
        matrix.setScale(sx, sy);
        Log.i("utils_compressBitmap", "" + sx + "," + sy);
        Bitmap bit = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                bitmap.getHeight(), matrix, true);

        Log.i("utils_compressBitmap", "" + bit.getWidth() + "," + bit.getHeight());
        //记得把不用的bitmap进行回收,以防止OOM
        bitmap.recycle();
        return bit;
    }

当我们通过压缩好的图片获取到了它的灰度值数组,现在我们就可以根据灰度值转换为对应的文字了,我给了灰度值15个等级,根据颜色的深度给对应的中文字:(0是黑色,255是白色)

static String[] arr = {"餮", "淼", "圆", "困", "品", "回", "田", "凸", "口", "王", "天", "干", "工", "十", "一"};

我们制定好字符等级,那么要怎么根据数组制作图片呢?

根据 字符 绘制 Bitmap

上面说过图片的操作在Android中一般都在Bitmap进行的,所以我们要想绘制一张新的图片,那么就创建一个新的Bitmap对象,绘制的事情交给万能的画布就好了,画布带有文字绘制接口完美的符合我们需求:

static public Bitmap array2Bitmap(int[][] garyDatas, int width, int height) {
        //绘制一个字对应一个像素,所以新绘制的Bitmap的大小应该乘上字体大小
        Bitmap whiteBgBitmap = Bitmap.createBitmap(width * 6 + 20, height * 6 + 20, Bitmap.Config.ARGB_8888);
        //在Bitmap上创建画布
        Canvas canvas = new Canvas(whiteBgBitmap);
        //绘制白色背景
        canvas.drawARGB(255, 255, 255, 255);
        //初始化画笔
        Paint mPaint = new Paint();
        mPaint.setStrokeWidth(1);
        mPaint.setColor(Color.BLACK);
        mPaint.setTextSize(6);

        int x = 0;
         //遍历灰度值数组
        for (int xIndex = 10; x < width; xIndex += 6) {
            int y = 0;
            for (int yIndex = 10; y < height; yIndex += 6) {
                //获取灰度值对应的字符
                int charIndex = garyDatas[x][y] / 18;
                String _char = arr[charIndex];
                //在对应的坐标绘制字符
                canvas.drawText(_char, xIndex, yIndex, mPaint);
                y++;
            }
            x++;
        }
        return whiteBgBitmap;
    }

绘制完成后输出Bitmap,下一步是把Bitmap保存为本地图片,关键代码如下:

...
File photo = new File(Environment.getExternalStorageDirectory() + "/" + dirName, String.format("CharPic_%d.jpg",System.currentTimeMillis()));

File dir = new File(photo.getParent());
if(!dir.exists()){
      dir.mkdirs();
 }
photo.createNewFile();
saveBitmapToJPG(bmp, photo);
...

static private void saveBitmapToJPG(Bitmap bitmap, File photo) throws IOException {
        OutputStream stream = new FileOutputStream(photo);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream);
        stream.close();
        bitmap.recycle();
    }

下一步我们为了在系统相册更好的找到我们的图片,我们可以把图片发送一个广播来通知系统相册:

Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(photo);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);

以上就是图片转成字符画的全部代码与讲解。可能有的人会问这样的功能,除了酷炫,有趣,牛逼之外,做出来有什么用?我只能问得好!乍一看好像用处不大,但是基于这个功能我们可以做短视频转换字符画视频。

奉上完整的源码,觉得有趣的请star一下呗。

完整项目源码地址:

  • https://github.com/452kinton/CharacterDance

作者:Kinton 来源:https://www.jianshu.com/p/16ef3bf9ac5c

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 音视频开发进阶 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Android 端实现
  • 灰度值获取
  • 根据 字符 绘制 Bitmap
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档