牙叔教程 简单易懂
声明
本教程是教查找透明图的, 找透明图逻辑都是类似的, 不针对任何app.
效果
目标: 找图片中的双人头
本次测试一共使用了三种分辨率的图片
- 1600x720
- 2310x1080
- 2400x1080
每种分辨率各5张,
上图中左上角的头像, 可以看出是一个黑白图片, 是把一张2400x1080的大图, 灰度化, 二值化, 然后裁剪双人区域得到的, 所有分辨率图片找图, 都是用的同一张双人头小图.
有必要继续看下去吗
- 多分辨率找图你有思路吗
- 你会找透明图吗
- 你在多种分辨率下, 找透明图, 会不会?
- 你想不想看几十篇教程来学习sift
- 你会把java版本的sift改成autojs的吗
- 你想花掉两个星期的时间来研究一个东西吗
如果你对以上对你来说, 没有任何问题, 那就没必要看下去, 划走吧.
环境
手机: Mi 11 Pro
Android版本: 11
Autojs版本: 9.0.10
备注
autojs8自带的opencv版本是3, 不包含sift,
autojs9自带的opencv版本是4, 包含sift,
多分辨率找透明图思路
多分辨率找图用sift,
透明图先二值化, 然后再找图
二值化图片
我们需要选择一个合适的阈值来二值化, 因此需要可视化的调整阈值,
本教程挑选的阈值是246,
在这个阈值下, 二值化后的图片上, 只剩下双人头和右下角的星星,
可以最大限度的帮助我们排除图片中的噪音,
下面是阈值为246的时候, 对应的二值图
二值化代码
本代码非常优秀, 即使在ui界面16ms刷新的频率下, 也没有内存泄漏发生,
值得新手仔细研究
"ui"; engines.all().map((ScriptEngine) => { if (engines.myEngine().toString() !== ScriptEngine.toString()) { ScriptEngine.forceStop(); } }); importClass(org.opencv.imgproc.Imgproc); importClass(org.opencv.core.Mat); ui.layout( <vertical> <img id="原图"></img> <horizontal> <text>阈值: </text> <text id="threshold">0</text> <seekbar id="seekbar" margin="9" w="*"></seekbar> </horizontal> <img id="二值化"></img> </vertical> ); let imgPath = "./截图1.png"; imgPath = files.path(imgPath); let img = images.read(imgPath); let bitmap = img.getBitmap(); log(bitmap); ui.原图.setImageBitmap(bitmap); ui.seekbar.setMax(255); let thresholdBitmap; ui.seekbar.setOnSeekBarChangeListener({ onProgressChanged: function (seekBar, progress, fromUser) { if (fromUser) { let lastBitmap = thresholdBitmap; ui.threshold.setText(String(progress)); let threshold = parseInt(progress); log("二值化之前 img = " + img); let thresholdImg = 二值化(img, threshold); thresholdBitmap = thresholdImg.getBitmap(); ui.二值化.setImageBitmap(thresholdBitmap); ui.post(function () { thresholdImg.recycle(); }); if (lastBitmap) { log("lastBitmap = "); log(lastBitmap); lastBitmap.recycle(); } } }, }); events.on("exit", function () { bitmap.recycle(); img.recycle(); thresholdBitmap.recycle(); }); /* ---------------------------自定义函数----------------------------------------------- */ function 二值化(img, threshold) { log("threshold = " + threshold); let trainImage = img.mat; let trainImage_gray = new Mat(); Imgproc.cvtColor(trainImage, trainImage_gray, Imgproc.COLOR_BGR2GRAY); let binary = new Mat(); // Imgproc.threshold(trainImage_gray, binary, threshold, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); Imgproc.threshold(trainImage_gray, binary, threshold, 255, Imgproc.THRESH_BINARY); trainImage_gray.release(); var newImg = com.stardust.autojs.core.image.ImageWrapper.ofMat(binary); return newImg; }
裁剪图片
电脑上的话, 我用的是ps裁剪图片,
手机上的话, 可以使用有裁剪功能的app,
如果你找不到合适的app, 可以公众号回复 照片编辑器
该app有裁剪功能,
下面是我们从二值图中, 裁剪出的双人头
保存图片到sd卡
opencv中的图片格式都是Mat, 把Mat保存到sd卡, 代码如下
function viewMat(mat) { let mat2 = mat.clone(); Imgproc.cvtColor(mat, mat2, Imgproc.COLOR_BGRA2RGBA); let tempFilePath = files.join(files.getSdcardPath(), "脚本", "mat.png"); Imgcodecs.imwrite(tempFilePath, mat2); mat2.release(); app.viewFile(tempFilePath); }
中间有一行转换颜色的代码, 是因为mat默认使用bgr, 而不是rgb,
不转换颜色的话, 图片颜色看着就不正常.
读到这里你应该有这些东西了
- 原图
- 二值化的原图
- 裁剪出来的双人头小图
接下来就该sift上场了
sift找图效果
图片中连接双人头的彩色线两端的点, 就是sift在两种图片中找到的相似度最高的特征点,
在右下角的星星上, 也有一个特征点, 因此有一条紫色的线连过去了,
图片中的蓝色矩形是用来包裹相似特征点的,
再看看别的图
这张图效果就很好, 没有受到右下角星星的影响, 在看看别的图片
从图片中可以看到, 右下角的星星影响了我们的找图, 因此我们找图的时候,
可以先裁剪掉右侧对我们找图无用的图片, 比如把图片右侧三分之一的都裁减掉,
这样就避免了右下角星星对找图的影响.
所有没用的地方都可以预先裁减掉, 然后再去找图
因此, 我的自定义方法就有一个区域参数,
只保留我们有用的部分, 图片中的其他部分, 全部裁掉
/** * @description: * @param {img} bigImg * @param {img} smallImg * @param {Array} rect [left, top, right, bottom] * @return {Array} [left, top, right, bottom] */ function findImageSift(bigImg, smallImg, rect) {...}
总结
以上就是查找透明图的基本步骤,
这种方法理论是可行的, 实际效果, 我们要测试后才知道,
该方法是否符合任何场景呢?
当然是不可能的, 我们会遇到不同的场景, 需要对每个场景采取合适的方法,
找图这种, 我不认为有通用的方法, 尤其是手机分辨率那么多种,
但本方案是目前此场景中最合适的方案,
学会这个, 你的找图功力又前进一大步,
你的找图功力, 即将大圆满呢, 给自己点个赞吧 !
名人名言
思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问
--- 牙叔教程
声明
部分内容来自网络
本教程仅用于学习, 禁止用于其他用途