1.实例演示图片转素描效果
首先我们来看一下具体的效果,在项目中添加依赖
<repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories> <dependency> <groupId>com.github.liuyueyi</groupId> <artifactId>java-jhlabs</artifactId> <!-- replace by newest version --> <version>1.0</version> </dependency> 复制代码
实用姿势
private static void toSketch(String imgPath, String output) throws IOException { BufferedImage src = ImageIO.read(Objects.requireNonNull(Img2SketchTest.class.getClassLoader().getResourceAsStream(imgPath))); src = ImageUtils.convertImageToARGB(src); //图像灰度化 PointFilter grayScaleFilter = new GrayscaleFilter(); BufferedImage grayScale = new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); grayScaleFilter.filter(src, grayScale); //灰度图像反色 BufferedImage inverted = new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); PointFilter invertFilter = new InvertFilter(); invertFilter.filter(grayScale, inverted); //高斯模糊处理 GaussianFilter gaussianFilter = new GaussianFilter(20); BufferedImage gaussianFiltered = new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); gaussianFilter.filter(inverted, gaussianFiltered); // 灰度图像和高斯模糊反向图混合 ColorDodgeComposite cdc = new ColorDodgeComposite(1.0f); CompositeContext cc = cdc.createContext(inverted.getColorModel(), grayScale.getColorModel(), null); WritableRaster invertedR = gaussianFiltered.getRaster(); WritableRaster grayScaleR = grayScale.getRaster(); // 混合之后的就是我们希望的结果 BufferedImage composite = new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); WritableRaster colorDodgedR = composite.getRaster(); cc.compose(invertedR, grayScaleR, colorDodgedR); //输出做好的素描 File outputfile = new File(output); ImageIO.write(composite, "png", outputfile); } 复制代码
表现结果如下
2. 算法原理
作为一个图像处理小白,网上搜索了一下,素描算法比较多,通过多番比较,感觉整体思想相差不大,大致是以下几个步骤
- 原图灰度化
- 临界值处理(小于某个临界值的像素值设置为0)
- 第二步的图像反色处理
- 第二步与第三步的图片混合处理
接下来将以我自己的粗浅理解出发,尝试做一下算法的解析
2.1 灰度处理
图像灰度处理,主要是为了将最终图片的色彩控制在五彩斑斓的灰白中,保持和我们常见的素描风格保持一致
具体的像素灰度处理,有一个广为流传的公式 (我也没找到原始出处,不知道是哪位大神给出的)
avgColor = red * 0.299f + green * 0.587f + blue * 0.114f 复制代码
2.2 边界凸显
素描效果的一个核心要点在于对于物体的边缘轮廓进行定位,清洗的显示轮廓信息,对于之外的信息进行抽象模糊处理;简单来讲就是改凸显的地方凸显出来,不重要的地方模糊些处理
那么问题就是如何定位轮廓,如何模糊处理?
上面步骤中的第二、第三两步主要就是来干这个事情的;临界值 + 反向,主要就是为了定位边界轮廓(盲猜一波:轮廓边缘的像素差异较大,两次处理叠加之后,轮廓处像素信息受影响较小,和之前的值差别不大,有相关背景知识的大佬可以指点一下)
具体实现的方式呢,最容易想到的一点是
- 设置一个阈值,小于这个阈值的像素设置为0;然后反向,两个图像进行混合处理,从而凸显轮廓
更先进一点的做法:
- 傅里叶变换、高斯模糊、梯度算法等出现在各相关论文中的算法(至于为什么有效,我也不知道了...)
3. 快速使用
文章的最开始就给出了一个素描处理的demo,输出效果基本ok,但是使用姿势有点麻烦;其实现就是借助jh-labs的滤镜来实现各种操作,对于应用者而言(比如我),可能并不关心具体细节,只要结果,有更简单的使用姿势么?
当然也是有的,下面这个开源项目已经做好了封装
最新版本为3.0,可以到中央仓库直接获取(如果还没有找到,不要慌,我还没有提交,因为3.0还在内测中)
引入依赖
<!-- https://mvnrepository.com/artifact/com.github.liuyueyi.media/photo-plugin --> <dependency> <groupId>com.github.liuyueyi.media</groupId> <artifactId>photo-plugin</artifactId> </dependency> 复制代码
使用姿势
@Test public void testSketch() { BufferedImage out = PhotoOperateWrapper.of(OperatorEnum.SKETCH) .setImg("https://t7.baidu.com/it/u=4162611394,4275913936&fm=193&f=GIF") .build() .asImg(); System.out.println("----"); }