本站可出售
有意者可联系

抖音上很火的字符画 用Android来实现的一些尝试

扫一扫以下二维码下载体验APP

抖音上炫代码的不少,有些真的让人叹为观止,作为一个androider,当我看到下面这段舞蹈的时候,终于忍不住了,想要通过android实现一样的效果。

这么好玩的东西,为啥就没有大佬做呢,原因可能有两个,一是真的难,二是出力不讨好,难以达到最终效果,一番尝试后,技术问题都解决了,但并没有达到电脑端美感,手机屏幕还是太小了。。

这是电脑端的静态图

这是手机端的

猜猜下面这张是谁

下面开始分析代码,首先根据图片像素灰度转为ascii字符,这在网上有现成的java代码,android上只需要改一点api就可以,代码如下

  1. public static Bitmap createAsciiPic(final String path, Context context) {
  2.         final String base = “#8XOHLTI)i=+;:,.”;// 字符串由复杂到简单
  3. //        final String base = “#,.0123456789:;@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz”;// 字符串由复杂到简单
  4.         StringBuilder text = new StringBuilder();
  5.         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  6.         DisplayMetrics dm = new DisplayMetrics();
  7.         wm.getDefaultDisplay().getMetrics(dm);
  8.         int width = dm.widthPixels;
  9.         int height = dm.heightPixels;
  10.         Bitmap image = BitmapFactory.decodeFile(path);  //读取图片
  11.         int width0 = image.getWidth();
  12.         int height0 = image.getHeight();
  13.         int width1, height1;
  14.         int scale = 7;
  15.         if (width0 <= width / scale) {
  16.             width1 = width0;
  17.             height1 = height0;
  18.         } else {
  19.             width1 = width / scale;
  20.             height1 = width1 * height0 / width0;
  21.         }
  22.         image = scale(path, width1, height1);  //读取图片
  23.         //输出到指定文件中
  24.         for (int y = 0; y < image.getHeight(); y += 2) {
  25.             for (int x = 0; x < image.getWidth(); x++) {
  26.                 final int pixel = image.getPixel(x, y);
  27.                 final int r = (pixel & 0xff0000) >> 16, g = (pixel & 0xff00) >> 8, b = pixel & 0xff;
  28.                 final float gray = 0.299f * r + 0.578f * g + 0.114f * b;
  29.                 final int index = Math.round(gray * (base.length() + 1) / 255);
  30.                 String s = index >= base.length() ? ” “ : String.valueOf(base.charAt(index));
  31.                 text.append(s);
  32.             }
  33.             text.append(“\n”);
  34.         }
  35.         return textAsBitmap(text, context);
  36.     }

这样处理完得到的ascii文本,但我们需要的是ascii图片,那我们需要怎么做呢,截屏?

请读者思考10秒钟,想想自己的解决方案。

我这里通过TextPanit和StaticLayout实现的,也可以new一个TextView,写入文本,然后把Textview的缓冲区转换为图片,但是这种StaticLayout的方式更底层,更有效,代码如下:

  1. public static Bitmap textAsBitmap(StringBuilder text, Context context) {
  2.         TextPaint textPaint = new TextPaint();
  3.         textPaint.setColor(Color.BLACK);
  4.         textPaint.setAntiAlias(true);
  5.         textPaint.setTypeface(Typeface.MONOSPACE);
  6.         textPaint.setTextSize(12);
  7.         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  8.         DisplayMetrics dm = new DisplayMetrics();
  9.         wm.getDefaultDisplay().getMetrics(dm);
  10.         int width = dm.widthPixels;
  11.         StaticLayout layout = new StaticLayout(text, textPaint, width,
  12.        Layout.Alignment.ALIGN_CENTER, 1f, 0.0f, true);
  13.         Bitmap bitmap = Bitmap.createBitmap(layout.getWidth() + 20,
  14.                 layout.getHeight() + 20, Bitmap.Config.ARGB_8888);
  15.         Canvas canvas = new Canvas(bitmap);
  16.         canvas.translate(10, 10);
  17.         canvas.drawColor(Color.WHITE);
  18.         layout.draw(canvas);
  19.         Log.d(“textAsBitmap”,
  20.                 String.format(“1:%d %d”, layout.getWidth(), layout.getHeight()));
  21.         return bitmap;
  22.     }

相对于电脑端有无边无际的txt编辑框,android里text是有字数限制的,所以原始图片如果像素过多的话就要进行尺寸压缩。

而且textPaint的这个设置特别重要textPaint.setTypeface(Typeface.MONOSPACE);字体对效果的影响太大了,失之毫厘谬以千里,这是一个大坑,说多了都是时间。

我在项目里集成了一个图片选择库,可以直接把拍的照片转化为ascii图,碰到一个问题就是拍照图片拿到后都会自动旋转90度,很是困惑,虽然找到了处理方法,但系统为啥要作旋转处理,还请知道的大神告知原因。

处理代码如下:

  1. public static String amendRotatePhoto(String originpath, Context context) {
  2.     // 取得图片旋转角度
  3.     int angle = readPictureDegree(originpath);
  4.     // 把原图压缩后得到Bitmap对象
  5.     if (angle != 0) {
  6.         Bitmap bmp = getCompressPhoto(originpath);
  7.         Bitmap bitmap = rotaingImageView(angle, bmp);
  8.         return savePhotoToSD(bitmap, context);
  9.     } else {
  10.         return originpath;
  11.     }
  12. }

用到的方法:

  1. public static int readPictureDegree(String path) {
  2.     int degree = 0;
  3.     try {
  4.         ExifInterface exifInterface = new ExifInterface(path);
  5.         int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
  6.         switch (orientation) {
  7.             case ExifInterface.ORIENTATION_ROTATE_90:
  8.                 degree = 90;
  9.                 break;
  10.             case ExifInterface.ORIENTATION_ROTATE_180:
  11.                 degree = 180;
  12.                 break;
  13.             case ExifInterface.ORIENTATION_ROTATE_270:
  14.                 degree = 270;
  15.                 break;
  16.         }
  17.     } catch (IOException e) {
  18.         e.printStackTrace();
  19.     }
  20.     return degree;
  21. }
  22. public static Bitmap rotaingImageView(int angle, Bitmap bitmap) {
  23.     Bitmap returnBm = null;
  24.     // 根据旋转角度,生成旋转矩阵
  25.     Matrix matrix = new Matrix();
  26.     matrix.postRotate(angle);
  27.     try {
  28.         // 将原始图片按照旋转矩阵进行旋转,并得到新的图片
  29.         returnBm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  30.     } catch (OutOfMemoryError e) {
  31.     }
  32.     if (returnBm == null) {
  33.         returnBm = bitmap;
  34.     }
  35.     if (bitmap != returnBm) {
  36.         bitmap.recycle();
  37.     }
  38.     return returnBm;
  39. }

这些代码都在文末的项目里。

按说拿到ascii图后,想要把整个视频转换成ascii字符视频就很简单了。只要把视频逐帧抽成图片,图片转换后,再合成为视频播放出来,但我视频库用的不多,希望有能力的朋友可以帮助完成最后一步。

最后,也希望朋友们能把一些有趣的想法实践到android项目中来,让搬砖之余,有更多的乐趣。

赞(1)
未经允许不得转载:抖音培训网 » 抖音上很火的字符画 用Android来实现的一些尝试
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

抖音培训网 抖音热门技术培训 更专业 更方便

关于我们联系我们