使用Tensorflow实现图片风格迁移,圆梦名画
一,前期基础知识储备
1)Prisma — 图片风格迁移的鼻祖:
照片可以记录生活的瞬间,变成一幅幅的回忆;而 Prisma 则是可以让瞬间的回忆变成永恒的名画!我们平常用手机随意拍出来的照片效果看起来都很普通,而通过 Prisma 处理之后,你一定会惊叹于它的神奇!
Pisma 是一款来自俄罗斯的照片美化应用,和「彩云天气」那样,借助人工智能技术将自身的能力提升到另一个层次。“Pisma 运用了综合人工神经网络技术(neural networks)和人工智能技术,学习模仿各种著名绘画大师和主要流派的艺术风格,然后对你的照片进行全智能的风格化处理”。
也就是说,每个滤镜最后所呈现的照片艺术效果,都是 Pisma “模仿”过去那些世界伟大艺术家们的风格,对你的照片进行 AI 智能分析之后而重绘出来的。不仅在技术上让人惊叹,实际产出的照片效果之佳,也同样让人为之惊叹!!
2)Tensorflow — 谷歌开源,实现图片风格迁移:
官方Demo地址:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/android
谷歌利用tensorflow一共实现了Android相关的例子有三个 —— 物体定位,行人识别,风格迁移,如图:
谷歌这里给出的实例是处理相机获得实时预览图。本篇文章的目的,是将风格迁移的效果运用到图片上。
谷歌训练的模型文件中一共有26种风格迁移的效果,下面来一一实现。
二,上代码,具体实现
1)build.gradle中添加tensorflow的依赖:
implementation 'org.tensorflow:tensorflow-android:+'
2)assets文件夹下放置谷歌训练好的模型文件:
3)对TensorFlowInferenceInterface类进行实例化:
private static final String MODEL_FILE = "stylize_quantized.pb";... ...inferenceInterface = new TensorFlowInferenceInterface(getAssets(), MODEL_FILE);
这里使用assets文件下的模型文件完成该类的实例化。
4)正式处理一张图片:
private void execute(final int position) {handler = new Handler(getMainLooper());inferenceInterface = new TensorFlowInferenceInterface(getAssets(), MODEL_FILE);runInBackground(new Runnable() {@Overridepublic void run() {croppedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.beauty_1);croppedBitmap = Bitmap.createScaledBitmap(croppedBitmap, desiredSize, desiredSize, false);imageView.setImageBitmap(croppedBitmap);cropCopyBitmap = Bitmap.createBitmap(croppedBitmap);final long startTime = SystemClock.uptimeMillis();stylizeImage(croppedBitmap, position);lastProcessingTimeMs = SystemClock.uptimeMillis() - startTime;textureCopyBitmap = Bitmap.createBitmap(croppedBitmap);done();}});}private void done() {imageView.setImageBitmap(textureCopyBitmap);}private int desiredSize = 512;private final float[] styleVals = new float[NUM_STYLES];private int[] intValues = new int[desiredSize * desiredSize];private float[] floatValues = new float[desiredSize * desiredSize * 3];private void stylizeImage(Bitmap bitmap, int model) {bitmap.getPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());for (int i = 0; i < intValues.length; ++i) {final int val = intValues[i];floatValues[i * 3] = ((val >> 16) & 0xFF) / 255.0f;floatValues[i * 3 + 1] = ((val >> 8) & 0xFF) / 255.0f;floatValues[i * 3 + 2] = (val & 0xFF) / 255.0f;}for (int i = 0; i < NUM_STYLES; ++i) {styleVals[i] = 0f;}styleVals[model] = 1f;// Copy the input data into TensorFlow.Log.d("tensor", "Width: " + bitmap.getWidth() + ", Height: " + bitmap.getHeight());inferenceInterface.feed(INPUT_NODE, floatValues, 1, bitmap.getWidth(), bitmap.getHeight(), 3);inferenceInterface.feed(STYLE_NODE, styleVals, NUM_STYLES);inferenceInterface.run(new String[]{OUTPUT_NODE}, false);inferenceInterface.fetch(OUTPUT_NODE, floatValues);for (int i = 0; i < intValues.length; ++i) {intValues[i] =0xFF000000| (((int) (floatValues[i * 3] * 255)) << 16)| (((int) (floatValues[i * 3 + 1] * 255)) << 8)| ((int) (floatValues[i * 3 + 2] * 255));}bitmap.setPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());}
① 我们获取本地的一张图片,然后将其进行裁剪缩放做成一张正方形图片bitmap(512 * 512);
② stylizeImage()方法中,接收该bitmap,同时接收一个float类型的参数,用以指定不同的风格迁移效果;
③ 注意TensorFlowInferenceInterface实例接口的三个方法(查看源码,可知):
- 注入数据:
feed
()方法有很多,根据需要传入参数即可,必须传入的为intputName
和数据src
。必须要的是传入数据的类型是什么,不然是不成功的。 - 运行:
run
()方法也有好几个,是执行运行的,需要传入outputName
数组,这里的outputName
需要和fetch
相关函数中的一致。 - 取出结果:
fetch
()方法也有很多,也是需要传出的即可,必须传入的是outputName
和 要存储结果的数组dst
。必须要确定传出结果的数据类型。
得到的结果如下:
三,延伸,可运用于正式项目中
2016年,中国版的Prisma — “深黑”上线,效果和Prisma类似,但有一个致命的缺陷——只能处理正方形图片。
谷歌给出的示例中,由于模型文件本身的限制,也是只能处理一张正方形的图片。如果在上面代码中,去掉裁剪缩放的步骤,直接使用一张正方形图片,此时,跑出来的结果是报错:
java.nio.BufferOverflowException
解决方法是,将用于存储结果的数组人为扩大为两倍:
float[] floatValues1 = new float[floatValues.length * 2];
// inferenceInterface.fetch(OUTPUT_NODE, floatValues);inferenceInterface.fetch(OUTPUT_NODE, floatValues1);for (int i = 0; i < intValues.length; ++i) {intValues[i] =0xFF000000| (((int) (floatValues1[i * 3] * 255)) << 16)| (((int) (floatValues1[i * 3 + 1] * 255)) << 8)| ((int) (floatValues1[i * 3 + 2] * 255));}
这样处理之后,不会报错,但是得到的效果非常差:
受制于模型文件,所以不能直接使用上面的代码处理一张长方形图片,而用户大多数的图片都是长方形的,所以需要进一步的处理。
1)使用canvas——人为将长方形图片转换为正方形图片;
这里有一个非常关键的思想:谷歌的模型文件只能处理正方形图片,那我们就根据用户的图片宽高去手动构建一个正方形图片,然后在处理结果出来之后,显示之前,对图片进行裁剪,这样就可以得到一张经过正常处理的长方形图片。这一过程借助Canvas实现:
croppedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.beauty);croppedBitmap = reSizeBitmap(croppedBitmap);croppedBitmap = Bitmap.createScaledBitmap(croppedBitmap, desiredSize, desiredSize, false);imageView.setImageBitmap(croppedBitmap);... ...// 这个方法就是将一张长方形图片转换成一张正方形图片private Bitmap reSizeBitmap(Bitmap bitmap) {Bitmap b = null;width = bitmap.getWidth();height = bitmap.getHeight();if (width < height) {b = Bitmap.createBitmap(height, height, bitmap.getConfig());Canvas c = new Canvas(b);c.drawBitmap(bitmap, (height - width) / 2, 0, null);} else if (height < width) {b = Bitmap.createBitmap(width, width, bitmap.getConfig());Canvas c = new Canvas(b);c.drawBitmap(bitmap, 0, (width - height) / 2, null);} else if (width == height) {return bitmap;}return b;}
我们获取图片的宽高,根据长的那一边进行构建正方形。
2)处理完后,再将图片进行裁剪,裁剪完后进行显示:
private void done() {if (width < height) {float scale = desiredSize * 1.0f / height * 1.0f;textureCopyBitmap = ImageUtils.clip(textureCopyBitmap, (desiredSize - Math.round(width * scale)) / 2,0, Math.round(width * scale), desiredSize);imageView.setImageBitmap(textureCopyBitmap);} else {float scale = desiredSize * 1.0f / width * 1.0f;textureCopyBitmap = ImageUtils.clip(textureCopyBitmap, 0, (desiredSize - Math.round(height * scale)) / 2, desiredSize, Math.round(height * scale));imageView.setImageBitmap(textureCopyBitmap);}}
得到的结果如下:
第一张图,我们可以清晰的看到,真正处理的还是一张正方形图片(我们手动转换长方形图片得到的),而后进行裁剪,将多余的图片区域裁剪掉,就可以显示了。最终我们得到了想要的结果。
本项目地址(包含模型文件):https://github.com/shenbuqingyun/stylizedImage-tensorflow
PS:谷歌训练的模型文件非常合适,大小只有550KB,却实现了26种风格滤镜效果。我在github上找到的其他个人训练的模型文件,通常都以M为单位,非常大。另外,Tensorflow在Android中的库打包时也会非常大,arm32位和64位的到有8M,这是非常惊人的大库。所以实际使用时,需要考量效果和成本。
使用Tensorflow实现图片风格迁移,圆梦名画相关推荐
- TensorFlow从1到2(十三)图片风格迁移
风格迁移 <从锅炉工到AI专家(8)>中我们介绍了一个"图片风格迁移"的例子.因为所引用的作品中使用了TensorFlow 1.x的代码,算法也相对复杂,所以文中没有仔 ...
- TensorFlow实时任意风格迁移,送女朋友的创意礼物有了
TensorFlow实时任意风格迁移,送女朋友的创意礼物有了 前言 自适应实例规范化 风格迁移网络 编码器结构与实现 通过反射填充(reflection padding)减少块伪影 解码器结构与实现 ...
- Pytorch入门(6)-图片风格迁移和GAN
视频资源:https://www.bilibili.com/video/BV12741177Cu?p=6&spm_id_from=pageDriver from __future__ impo ...
- python图片风格迁移毕设_神经风格迁移是如何运作的概述及Python实现
神经风格迁移是如何运作的概述及Python实现 作者:PHPYuan 时间:2019-03-26 03:40:37 深度学习可以捕获一个图像的内容并将其与另一个图像的风格相结合,这种技术称为神经风格迁 ...
- 第十课.图片风格迁移和GAN
目录 Neural Style Transfer Neural Style Transfer原理 准备工作 定义模型并加载预训练的模型参数 训练target以及结果可视化 生成对抗网络GAN GAN原 ...
- 第六课 图片风格迁移和GAN
一.Neuarl Style Transfer 图片风格迁移 结合一张图片的内容和另一张图片的风格,生成一张新图片 图片表示 代码 %matplotlib inlinefrom __future__ ...
- 第六节 图片风格迁移和GAN
第六节 图片风格迁移 - 图片风格迁移 - 用GAN生成MNIST - 用DCGAN生成更复杂的图片## 图片风格迁移 Neural Style Transfer matplotlib inlinef ...
- Pytorch入门+实战系列七:图片风格迁移和GAN
Pytorch官方文档:https://pytorch.org/docs/stable/torch.html? 1. 写在前面 今天开始,兼顾Pytorch学习, 如果刚刚接触深度学习并且想快速搭建神 ...
- 用Python实现图片风格迁移,让你的图片更加的高逼格!
先来看下效果: 上图是老王在甘南合作的米拉日巴佛阁外面拍下的一张照片,采用风格迁移技术后的效果为: 一些其它效果图: 下面进入正题. 如果你依然在编程的世界里迷茫,可以加入我们的Python学习扣qu ...
最新文章
- ORB-SLAM2从理论到代码实现(八):Tracking.cc程序详解(下)
- mysql nan_mysql在工作中的积累
- C++编程【Visual Studio 2017 环境搭建教程】【附:软件安装包】
- pycharm里怎么关闭一个项目_【周末分享】一个完整的项目复盘到底要怎么做?...
- JAVA-JAVA WEB开发工具下载与安装
- 分表分库时机选择及策略
- 阿里云 ECS服务器 开放 8080 端口 -- 图解
- qt 对话框关闭以及自动释放内存
- 已知贝塞尔曲线上的点求控制点
- 【三支火把】--- 关于UEFIPCD的总结介绍
- 【Win 10 应用开发】手写识别
- C++ 4 C++变量及作用域
- 在玩图像分类和图像分割?来挑战基于 TensorFlow 的图像注解生成!
- hibernate-annotation
- ria技术_JavaFXpert RIA示例挑战截止日期已延长
- 课程作业记录3:瑞利衰落信道下的BPSK/QPSK/16QAM的Matlab仿真
- php洗车分销系统_全国首个PHP宝塔IDC分销系统
- 永中word页码怎么从第二页开始_如何在Word的任意一页插入页码?原来还有这么简单的方法...
- excel基础-note-4.25
- cad lisp 二次抛物线_cad画二次抛物线