JavaCV 实现黑客帝国风格的照片——20220126

大家好,我是指北君。

前不久,黑客帝国系列最新的 《矩阵重启》 上映了。黑客帝国是早期科幻类型的翘楚。但是这次有点垮了。豆瓣评分不到6分。

小的时候,看到黑客帝国的那些照片,一串串数字从上而下, 感觉特别酷炫。 今天我们就来看看怎么制作类似的效果。

准备工作

一张基努里维斯的照片

在这里插入图片描述

在 maven 中,配置好 opencv

1
2
3
4
5
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>opencv-platform</artifactId>
    <version>4.5.3-1.5.6</version>
</dependency>

开始写代码

开始写读取文件的代码

1
2
 	val path = "path/to/jinu.jpg"
    val img = opencv_imgcodecs.imread(path)

获取到图像的宽高

1
2
3
4
5
6
	val size = img.size()
    val height = size.height()
    val width = size.width()

    val cellHeight = 20
    val cellWidth = 20

做一次图片的缩放,然后进行图片的取色操作

1
2
3
4
5
6
7
8
9
	val img2 = img.clone()
    opencv_imgproc.resize(
        img,
        img2,
        Size(width / cellWidth, height / cellHeight),
        0.0,
        0.0,
        opencv_imgproc.INTER_NEAREST
    )

新建一个图片,用于放置 0 1 数字,需要注意的是,这里建立的新图片需要和原图的尺寸,颜色通道数等等都是相同的才行,否则后续合并图片的时候,会有问题。

1
val newImg = Mat.zeros(height, width, opencv_core.CV_8UC3).asMat()

对新图片进行涂色添加文字处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    val indexer = img2.createIndexer<UByteIndexer>()
    val bgr = IntArray(3)
    for (i in 0 until indexer.rows()) {
        for (j in 0 until indexer.cols()) {
            indexer.get(i, j, bgr)
            val b = bgr[0]
            val g = bgr[1]
            val r = bgr[2]
            val k = (b + g + r) / 3
            if (k < 128) {
                opencv_imgproc.putText(newImg,"1", Point(j.toInt()*cellWidth, i.toInt()* cellHeight), opencv_imgproc.FONT_HERSHEY_COMPLEX_SMALL, 0.7, Scalar(0.0,g.toDouble(),0.0, 0.0))
            } else {
                opencv_imgproc.putText(newImg,"0", Point(j.toInt()*cellWidth, i.toInt()* cellHeight), opencv_imgproc.FONT_HERSHEY_COMPLEX_SMALL, 0.7, Scalar(0.0,g.toDouble(),0.0, 0.0))
            }
        }
    }

到这一步我们的工作基本已经完成了。 再进行一次图片合成,就OK了。

1
addWeighted(img, 0.2, newImg, 0.8, 0.0, img)

最终效果图

在这里插入图片描述

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import org.bytedeco.javacpp.indexer.UByteIndexer
import org.bytedeco.opencv.global.opencv_core
import org.bytedeco.opencv.global.opencv_core.addWeighted
import org.bytedeco.opencv.global.opencv_highgui
import org.bytedeco.opencv.global.opencv_imgcodecs
import org.bytedeco.opencv.global.opencv_imgproc
import org.bytedeco.opencv.opencv_core.Mat
import org.bytedeco.opencv.opencv_core.Point
import org.bytedeco.opencv.opencv_core.Scalar
import org.bytedeco.opencv.opencv_core.Size


fun main() {
    val path = "/Users/wbf/Desktop/jinu.jpg"
    val img = opencv_imgcodecs.imread(path)

    val size = img.size()
    val height = size.height()
    val width = size.width()

    val cellHeight = 20
    val cellWidth = 20
    val img2 = img.clone()
    opencv_imgproc.resize(
        img,
        img2,
        Size(width / cellWidth, height / cellHeight),
        0.0,
        0.0,
        opencv_imgproc.INTER_NEAREST
    )

    val newWidth = img2.size().width()
    val newHeight = img2.size().height()

    val newImg = Mat.zeros(height, width, opencv_core.CV_8UC3).asMat()

    val indexer = img2.createIndexer<UByteIndexer>()
    val bgr = IntArray(3)
    for (i in 0 until indexer.rows()) {
        for (j in 0 until indexer.cols()) {
            indexer.get(i, j, bgr)
            val b = bgr[0]
            val g = bgr[1]
            val r = bgr[2]
            val k = (b + g + r) / 3
            if (k < 128) {
                opencv_imgproc.putText(newImg,"1", Point(j.toInt()*cellWidth, i.toInt()* cellHeight), opencv_imgproc.FONT_HERSHEY_COMPLEX_SMALL, 0.7, Scalar(0.0,g.toDouble(),0.0, 0.0))
            } else {
                opencv_imgproc.putText(newImg,"0", Point(j.toInt()*cellWidth, i.toInt()* cellHeight), opencv_imgproc.FONT_HERSHEY_COMPLEX_SMALL, 0.7, Scalar(0.0,g.toDouble(),0.0, 0.0))
            }
        }
    }

    addWeighted(img, 0.2, newImg, 0.8, 0.0, img)

    opencv_highgui.imshow("image", img)
    opencv_highgui.waitKey(0)

}

总结

本文通过使用opencv 的取色, 上色, 图片合并功能达到我们想要的效果。更多其他使用技巧,后续继续分享。

Java Geek Tech wechat
欢迎订阅 Java 技术指北,这里分享关于 Java 的一切。