道哥要的分析来啦, 第一次写分析和实现的过程, 我哪里写的不好还请大佬指导, 有看不懂的小伙伴也可以在评论区留言偶(毕竟我第一次写分析, 分析语言有点乱,感觉有为了解释而解释的感觉, 分析的比我写的时候想的还多)
开始:
我们看到的图像放大之后是一个个的像素点
本文通过比较有缺口的背景图和原图每列的像素点来实现
//获取3个画布, 属性选择器很好用, canvasBg是有缺口的背景图, canvasFull是原图, canvasSlice是滑块
let canvasBg = document.querySelector('canvas[class="geetest_canvas_bg geetest_absolute"]');
let canvasFull = document.querySelector('canvas[class="geetest_canvas_fullbg geetest_fade geetest_absolute"]');
let canvasSlice = document.querySelector('canvas[class="geetest_canvas_slice geetest_absolute"]');
//getContext获取画布对象, getImageData获取对象数据, data获取像素数据, 此数据是从画布左上角开始, 逐行扫描, 一行扫完从下一行开头扫, 每扫描一个像素点获取四个值, 分别是RGBA, 得到[RGBARGBARGBA...RGBA]这样的像素数据;
//getImageData(0, 0, 260, 160), 0,0表示从左上角获取图像数据, 260,160就是canvas元素的width和height
let originDataBg = canvasBg.getContext("2d").getImageData(0, 0, 260, 160).data;
let originDataFull = canvasFull.getContext("2d").getImageData(0, 0, 260, 160).data;
let originDataSlice = canvasSlice.getContext("2d").getImageData(0, 0, 260, 160).data;
//图像处理一般都会将其灰度化(还会加快下面的像素比较), 即图像颜色只有从白到黑的颜色, 灰度值=(R值+G值+B值)/3, A值在这里没用
//先定义三个空数组, 便于放入计算的灰度值, 每四个值计算一次, 所以i+=4, 由于除法会有小数, 所以用位运算^0快速取整
//(originDataBg[i] + originDataBg[i + 1] + originDataBg[i + 2])/3就是(r+g+b)/3,由于第i+4个数据是透明度, 所以不用管, 每计算一个值就放入到数组
let grayDataBg = [], grayDataFull = [], grayDataSlice = [];
for (let i = 0; i < originDataBg.length; i += 4) {
grayDataBg.push(((originDataBg[i] + originDataBg[i + 1] + originDataBg[i + 2]) / 3) ^ 0);
};
for (let i = 0; i < originDataFull.length; i += 4) {
grayDataFull.push(((originDataFull[i] + originDataFull[i + 1] + originDataFull[i + 2]) / 3) ^ 0);
};
for (let i = 0; i < originDataSlice.length; i += 4) {
grayDataSlice.push(((originDataSlice[i] + originDataSlice[i + 1] + originDataSlice[i + 2]) / 3) ^ 0);
};
//比较有缺口的背景图和原图, 只要每列像素出现较多的不同, 就代表此处是缺口位置, 所以将一行一行扫描的数据存储成一列一列的, 方便比较
//我们想得到[[g,g,g,g...g],[g,g,g,g..g],....,[g,g,g,g,g]]的数组, [g,g,g...g]是图像的列数据, g是灰度值
//由于是对一维数组二维处理, 所以需要两个循环, 外循环对行处理, 即得到[[g,g,g,g...g],[g,g,g,g..g],....,[g,g,g,g,g]], , g是灰度值内循环对列处理, 即得到[g,g,g...g]
//每列的数据相差若干行, 所以内循环里的下标递增260的整数倍, 由于一列是160个数据, 所以i<width, j<height,
//先定义三个空数组, 便于放入每列的数据, 外循环里也定义了三个空数组, 便于放入列数据的每个值, 当外循环循环时, 里面的三个数组就会被清空,
let columnBg = [], columnFull = [], columnSlice = [];
for (let i = 0; i < 260; i++) {
let columnDataBg = [], columnDataFull = [], columnDataSlice = [];
for (let j = 0; j < 160; j++) {
columnDataBg.push(grayDataBg[i + 260 * j]);
columnDataFull.push(grayDataFull[i + 260 * j]);
columnDataSlice.push(grayDataSlice[i + 260 * j]);
}
//内循环完毕,将得到的列数据放入空数组
columnBg.push(columnDataBg);
columnFull.push(columnDataFull);
columnSlice.push(columnDataSlice);
}
//然后就是比较有缺口的背景图和原图每列的数据, 只要这一列数据有一定数量的不同, 就代表缺口被找到
//不同指像素灰度值的差, 一定数量指有多少像素点不同; canvas扫描像素时, ^0取整时, 或则有缺口的背景图和原图本身就有一定的差都会导致灰度值有一定程度的差
//这导致第一列数据就不完全相等
//所以我们要把像素灰度值差到一定程度和超过一定数量结合起来判断
//我比较了背景图和原图不在缺口处的差值, 发现基本都在1到5之内,我这里取dif=5; 缺口处的像素大小是40*40, 即像素差大于5的数量应该比较接小于并且接近于40, 这里我取了credence=20
//照样是两个循环外循环处理行, 内循环处理列
//滑块也是一个260*160的画布, 只是看起来是个小块, 除此小块外的像素点灰度值都为0, 判断哪一列数据灰度值不为0的超过20, 就代表到了我们看到的那个小块的位置
function getX(credence = 20, dif = 5) {
//定义滑块距离左边的像素
let iSlice = null;
for (let i = 0; i < 260; i++) {
//定义超过差值的数量, 作为判断缺口的依据
let cre = null, creSlice = null;
for (let j = 0; j < 160; j++) {
//计算像素点的差值的绝对值
let dValue = Math.abs(columnBg[i][j] - columnFull[i][j]);
//如果大于5,则记录一次
if (dValue > dif) {
cre++
};
//如果滑块此像素点灰度值不为0, 则记录一次
if (!iSlice && columnSlice[i][j] != 0) {
creSlice++
}
};
//一列像素点循环完毕, 分析此列是否是滑块我们看到的那个小块处, 是否是缺口处
//由于小块所在的列数小于缺口所在的列数, 所以在找到小块前缺口是不会被找到的
//所以找到缺口直接return, 此时的i就是缺口最左边距离canvas最左边的像素(因为我们是一个像素一个像素比较的)
if (cre > credence) {
GM_log({ x: i - iSlice })
return i - iSlice
};
//判断是否是小块, 是则将i赋值给iSlice
if (creSlice > credence) {
iSlice = i
GM_log({ iSlice: iSlice })
}
}
}
getX();
完