同样是MATLAB for循环,运算速度咋差这么大

我只能说,MATLAB是真的出圈了,YYDS
除此之外,还收到不少朋友的留言私信,希望再深入聊一聊,图像定位这个问题
正好我这周花了不少时间在代码优化上,今天就简单分享一下,优化过程中踩过的那些坑,以及最终实现的效果
这里涉及的图像定位问题,指的是,在一张图片中计算出某个图像片段的具体位置
这个问题的拓展应用还是很广的
如果说MATLAB模拟鼠标移动到指定位置可以简化很多实际问题的话,那么在图像定位之后再实现进一步的鼠标键盘操作就会更智能
比如在网页上自动翻页下载论文,在电脑桌面上自动启动软件并登陆,在B站上自动搜索视频并下载,图像处理之后的模拟鼠标操作大大降低了爬虫和系统操作的门槛
在图片rgb2gray灰度处理之后,图片定位其实就是一个在二维矩阵中定位子矩阵的问题
平时大家经常碰到的一个情况是,在一个矩阵中查找定位某一个元素,这个问题很容易解决,find函数就能搞定
不过,如果要在矩阵中查找定位一个向量或是一个矩阵,实现起来相对比较繁琐,至少是一个find函数解决不了的问题
1
最开始我使用的方案是,通过两层for循环,在矩阵上逐点对比
比如,原矩阵是一个8x10的矩阵,想要查找的子矩阵是2x3的矩阵
构造两层for循环,在8x10的矩阵中逐行逐列按点构造一个2x3的矩阵和目标子矩阵做对比
如果两个矩阵完全一致,那么当前for循环所在的行列就是目标子矩阵的位置
这个方案在逻辑上非常顺,不过实际的效果很惨
因为我用的是4k屏,截屏图片像素是3840x2160,用这种两层for循环逐点对比的方法,一次图像定位就要做超过800万次的计算
即使我加入了“定位成功就break跳出循环”的功能,整体上定位一次平均要花46.04秒
2
显然这样的运算速度很让人崩溃,于是我就开始研究,怎么把两层for循环减少到一层
很快我在MATLAB官方社区上找到了答案,通过ismember函数搭配rows的方式,可以判断矩阵中有哪几行和子矩阵一致
不过这种方式需要保证每次判断的时候,两个矩阵的列数一样,因此需要for循环逐一对比
如果矩阵中有连续的几行和子矩阵一致,且行数等于子矩阵行数,那么循环所在的列以及ismember判定的行就是目标子矩阵的位置
在经过100次循环测试之后,用这种方式进行图像定位平均耗时2.46秒,运算速度要远远快于之前逐点判断的方式,速度提升了将近20倍
3
虽然运算速度有一个非常显著的提升,但平均2.46秒的耗时还是很难令人满意,这都已经无法用卡顿来形容了
然后我就在想,有没有可能在一层循环的基础上再进一步优化
琢磨了好久,我突然想到了一个方案
先取目标子矩阵的第一个元素,然后在整个矩阵中用find检索,然后for循环逐一判断检索到的点,如果该点所在的子矩阵和目标子矩阵完全一致,那么这个点就是目标子矩阵的位置
这次优化后的效果非常惊喜,在100次循环测试之后,图像定位平均耗时0.55秒,运算速度是最开始逐点判断法的80多倍
4
本以为这次的代码优化已经圆满结束了,结果我拿着这个函数去做谷歌浏览器翻页测试,半天没有反应
我还以为是MATLAB死机了,重启试了好几回,还是一样
然后我设断点调试了下脚本,突然意识到,这个方案其实有个逻辑bug
目标子矩阵的第一个元素对应的是白色,而整个矩阵出现最频繁的就是白色
如果以白色作为检索点,那无异于把整个矩阵所有点对比了一遍,效果和两层for循环没什么差别
于是我又开启了漫长的优化之路
中途还试了不少方案,最终效果最好的方案是,对整个矩阵进行元素频率统计,通过sortrows函数按频率由低到高排列,然后用ismember函数和子矩阵进行对比,找出矩阵中频率最低并且子矩阵中也包含的元素,最后对该元素进行for循环对比验证
在100次的循环测试之后,图像定位平均耗时0.48秒,运算速度是最开始逐点判断法的将近100倍
对于这次将近100倍的优化效果,完全出乎自己的意料
事实证明,即便完全没有办法回避for循环,但还是有非常大的优化提升空间
以上就是这一周MATLAB搞事情的分享,希望这篇文章对大家平时的工作学习有所帮助
最后祝大家,工作学习顺利!