需求: 实现按键精灵或者触摸精灵里面的多点找色功能(指定第一个点的颜色值,指定后续颜色值和相对于第一个点的 x,y 偏移值,在图片中找到某个点,这个点的颜色和第一个点的颜色相等,同时这个点的坐标加上后续坐标的偏移,后续坐标的颜色全部相等)
入参示例:38CEE7,21|0|34D8EB,20|8|34D9EC,36|9|35D6EA,40|33|34D8EB,53|24|33D4E7,61|22|38D5E6,94|38|34D7E9,131|37|33CFE0,145|25|36D1E4 表示找颜色为 38CEE7 这个点的坐标,同时后续偏移位置的点颜色全部满足
目前实现: 将图片转成二维数组,遍历全部点,检查每个点是否和指定颜色相符
目前缺陷: 性能太差,1920*1080 的图片,输入 10 多个点,需要 14 至 20 秒才能找到点。请问大家有什么好的优化建议吗?
测试图片:
特征字符串:000000,-3|6|4D4D4D,-4|5|FFFFFF,0|12|08ADF9,-7|20|1010E8,16|27|000000,4|29|FFFFFF
返回:腾讯qq的图标位置 (652,508)
1
wingkou 2019-06-22 17:12:00 +08:00 via Android
把图片压平成一维的,你要的匹配模式也压成一维,按行展开就好了吧,然后用变种正则什么的(视颜色为字符)?这样或许可以。
|
2
nethard 2019-06-22 18:24:37 +08:00 via iPhone
首先,做一个 unordered_map,key 是 color value 是 unordered_set。
然后,初始化外层的 unordered_map,把待匹配的像素序列的颜色都加进去做 key,value 是新初始化的 unordered_set。同时,对于待匹配的像素序列里的每个节点,计算其累积 offset。 然后,遍历一趟这个图片,把像素的坐标,根据像素的颜色,减去对应的累计 offset,都加到对应的 unordered_set 里 最后,初始化一个坐标-累加器 map,遍历 unordered_map 里的每个 unordered_set,执行累加操作。最终,寻找 map 中值等于待匹配的像素序列的长度的 pair 的 key 即为输出。 更优化的方案可以在这个基础上考虑位运算和向量操作。 |
3
aguesuka 2019-06-22 18:57:07 +08:00 via Android
kmp 算法,理论只要过一次
|
4
nethard 2019-06-22 19:02:08 +08:00 via iPhone
@aguesuka 他这个怎么 kmp ?模式是非连续的子序列,并且考虑到 24 位的 rgb 值,模式里很难有重复的部分。
|
5
moodasmood OP @nethard 非常感谢您的建议。我之前是遍历图片,当某个点的颜色等于第一个点的时候就去判断后续是否满足,当全部满足的时候就 return 了,这样多数情况下并不会把图片遍历完。但是您这种得完全遍历图片,遍历数据更多了,反而速度更慢了。我目前消耗的主要时间是在遍历点上面,而不是比较,比较的话 10 多个点,做 10 多个==操作,速度并不慢
|
6
jiejiss 2019-06-22 20:42:52 +08:00
kmp 吧
应该没有更快的办法了 |
7
nethard 2019-06-22 20:47:06 +08:00 via iPhone
@moodasmood 你有没有考虑过你这样做存在遍历的非连续性,进而导致的 cache 命中率降低?尤其是一个图片,好几 MB 呢。
|
8
whileFalse 2019-06-22 21:16:16 +08:00 via iPhone
试试把你的需求转为正则表达式 /大笑
|
9
bilibilifi 2019-06-22 21:43:59 +08:00 via iPhone
写一个矩阵的 convolution 就可以了吧
|
10
bilibilifi 2019-06-22 21:44:39 +08:00 via iPhone
用 cuda 写的话感觉可以很快
|
11
moodasmood OP @bilibilifi 安卓,没有这些花里胡哨的东西
|
12
keith1126 2019-06-22 23:15:08 +08:00
|
13
mixplugs 2019-06-23 00:29:14 +08:00
估计得 GPU 加速,理论上复杂度下限应该就是 O(n)了。
|
14
tiluo 2019-06-23 01:14:57 +08:00
请问你确定性能问题是因为算法吗?理论上来说,你的算法需要做 m*n*len(s)次操作,大概是这样 1920*1080*10*6=124,416,000 . 如果是 in memory 的话,不需要 14-20s 吧?感觉上应该只需要 1s 不到
https://blog.csdn.net/yangchenju/article/details/84306840 |
15
moodasmood OP @tiluo 是的,遍历不需要那么久,但是对比颜色的时候还要计算颜色误差,还要排除某些特殊颜色(消除背景影响),所以导致速度很慢,另外,开发测试用的手机也有点差。我试了我 1 加 7pro,3120*1440 的图片才 4 秒左右,测试手机 1920*1080 要 20 秒左右
|
16
txy3000 2019-06-23 02:39:54 +08:00 via Android
Google ac 自动机
多字符串匹配 套路一样 |
17
binux 2019-06-23 03:03:08 +08:00 via iPhone
不能降低精度吗?
|
18
txy3000 2019-06-23 03:12:19 +08:00 via Android
只不过模式串个数是随间隔 指数级别增长
比如 红××红 就有 256×256 个符合的模式串 还要考虑二维坐标变换的边界条件 可以试一试构建特殊字典树减少模式串 反正要魔改 |
19
shidenggui 2019-06-23 08:29:02 +08:00
能不能给几个 testcases? 放几张图片和对应的匹配字符串以及结果?最近正在学 NFA,想尝试一下
|
20
moodasmood OP @binux 怎么降精度?压缩图片?
|
21
moodasmood OP @shidenggui 补充了
|
22
smallpython 2019-06-23 10:51:36 +08:00
请问是做手游脚本吗?
|
23
MengQuadra 2019-06-23 11:07:53 +08:00
分治+并行?
|
24
moodasmood OP @smallpython 是的,自己写着玩,按键精灵这种广告太恶心,而且老出问题,就自己造轮子了
|
25
guchengyehai1 2019-06-23 11:29:54 +08:00 via Android
编写 opengl shader 并行计算
|
26
JohnChiu 2019-06-23 11:53:46 +08:00
这个用目标检测多合适,SSD 训练一个模型就完事了
|
27
Nicapoet 2019-06-23 15:01:14 +08:00 via iPhone
压缩图片,拿到坐标以后再把缩放比乘回去。
(也可以考虑用图像处理相关算法,比如 surf 特征匹配配合颜色阈值化) |
28
bookit 2019-06-23 18:21:29 +08:00
用 opencv 的图片查找函数呢,这么小的图片,应该非常快就能找到。
我以前查的图片都是 2GB 的 |
29
DejaVud 2019-06-23 19:48:03 +08:00
直接用 opencv 或者类似的矩阵运算库,定义一个卷积核
我自己写 eve 脚本,1920*1080 分辨率,一次找图操作在电脑端不超过 1s |
30
Windsooon 2019-06-23 20:01:45 +08:00
1. 把要找的几个点的颜色以及相对位置理解成一个矩形。
2. 然后遍历这张图片找到**符合条件**的矩形(矩形的最左点到最右点,最上点到最低点必须包含需要的颜色),这样可以筛选掉大部分的矩形。 3. 接下来从结果的矩形中运用原本的算法来找点。 |
31
shidenggui 2019-06-23 20:40:37 +08:00
试了下你的例子,貌似 (652,508) 这个位置不太符合你给出的查找字符串。你能把图片转 hex 的代码也放下吗?
|
32
hmzt 2019-06-24 10:56:50 +08:00
你这不涉及计算,第一个点也必须要遍历查找,没什么优化空间,既然是用来做脚本,应该在脚本里限制找图的范围来减少时间.
|
33
aguesuka 2019-06-24 13:04:31 +08:00 via Android
模式里没有的点就是通配符。
|
34
aguesuka 2019-06-24 13:49:01 +08:00
如果我没理解错的话。需求就是我知道了 p 个点的颜色和关于第一个点的相对坐标,在二维数组里面做匹配?实际上二维数组可以转为一维数组,用 kmp 的话时间复杂度是 O(m+n),n 是图片大小。m 是第一个点到最后一个点的距离。按照原来的算法时间复杂度是 O(np)实际上 p 只有 10 多,而且通常情况下不会达到 np,所以就算改了算法也要 1 秒左右。我想知道如果只找一个点要多久?
|
35
moodasmood OP @aguesuka 1920*1080 的图片,遍历一次 16s 左右,主要想的优化方法就是减少第一个点的遍历次数
|