「安卓按键」找到图片上文字的位置(另附切图+拼图方法)

大家好,我是公众号3分钟学堂的郭立员~

在群里看他们聊天,因为最近联众打码不能用了,遇到的过验证码问题,有没有其他解决办法。

说到这么一个验证码,如下图所示:

验证码分两部分,上部分是倾斜的数字,下面是4个选项,点击和上部分相同数字的选项即为验证成功。

原本通过这个验证可以整体截图对接打码平台,然后平台返回正确选项的位置坐标,脚本点击此坐标就可以完成验证了。

要是不用打码平台,本地识别可不可以解决呢?

这个验证的两部分识别难度是不一样的,上半部分难度高,下半部分难度低。

先从下半部分简单的开始识别,这里我先测试smartocr命令,看看识别的准确率。

通过测试发现这个部分的识别率基本接近100%准确,既然准确率非常高,那么就直接使用这个命令,不在测试其他的命令。

识别后的结果存入abcd这4个变量中,分别代表四个选项。

接下来要识别难度大的上半部分,还是测试:

①测试smartocr命令:

正确结果是64217,识别结果是66484,识别准确是20%

把5个文字分开又重新识别测试,识别准确率依然很低,所以smartocr命令可以pass掉了。

这部分之所以难识别,因为数字是双层的,并且每一个字都是倾斜的,关于这类数字的识别,我想到了这个软件。

这是电脑端的识别,如果安卓端使用,可以把图片用ftp传递电脑上识别,然后返回识别结果。

先看看识别效果:

5个数字只识别出4个数字,准确率也很一般,不过我尝试把数字放到一行,再次识别,发现准确提高了。

可以看到识别结果,5个数字识别正确了4个,准确率是80%,基本上达到可以用的程度。

那么怎么把这些数字放到一行,又是一个难题?

我想到的思路是把每一个数字截图出来,然后再合并到一个图片上~思路有了我们开始着手去做。

①把每一个图片的位置找出来

这里每个数字都不是粘连在一起,那么先横向把数字分块:

分块原理是纵向遍历每一列颜色点,如果在一列当中包含任意一个像素是数字的颜色值,就是有效区域,也就是上图中红框,如果一整列都没有一个符合的颜色点,那么就是非数字区域,也就是红框以外的。

这是一个二维遍历,先遍历一列的颜色点,再遍历所有列。

为了便于后续处理,做一个二值化的处理,具体处理方式是:以列为单位,如果一列中包含数字的颜色点,记作1,如果不包含记作0,如图所示:

要实现这个二值化的计算,代码如下所示:

TracePrintGetBinary(204,179,724,356,"2C4156")FunctionGetBinary(x1,y1,x2,y2,color)Dimbinary,line=""KeepCaptureForj = x1 To x2Fori = y1 To y2IfCmpColor(j, i, color, 0.9)=0 Then binary=1ExitForEndIfIfi = y2 Then binary=0EndIfNextline=line&binaryNextReleaseCaptureGetBinary=lineEndFunction

输入结果:

结果中有5段连续的数字1,说明验证图上有5个数字,那问题又来了,怎么提取每个数字对应连续1的位置?

可以使用查找命令,先看示意图:

示意图中,先查找“01”就可以找到连续数字1的左侧,再查找“10”就可以找到连续数字1的右侧,如果是只有一段连续数字1,那么一次查找即可,但是我们例子中有5段,所以需要循环查找,并且每次查找的起始位置都要在上次查找结果的基础上向后偏移至少1个位置。

代码这样写:先查找01的

Dimline="00000000000000000000001111111111111111111111111111110000000000000000000000000000000000000000000001111111111111111111111111110000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000"dimleftarr=GetPositions(line,"01")TracePrintjoin(leftarr,"|")FunctionGetPositions(line,str)Dimarr(),i=0DoIfInStr(arr(i - 1) + 1, line, str) = 0 Then ExitDoElsearr(i)=InStr(arr(i - 1) + 1, line, str) + 1i=i+1EndIfLoopGetPositions=arrEndFunction

运算结果:

当前脚本第3行:23|98|188|308|426

把“01”改成“10”在运算一遍:

当前脚本第3行:53|125|227|331|457

两组数值都是5个,分别代表5个数字左侧和右侧的位置。

易错点来了:上面所得到的位置坐标,都是相对坐标,相对的点是数字区域左上角的坐标:

还剩下每个数字的上下位置了,由于5个数字之间在纵向是没有间隙的。

需要单个数字去获取上下位置,方法还是刚刚那样,不同之处是要先遍历单行颜色点,再逐行遍历。

在提醒一遍,写代码的时候还是要注意易错点——相对坐标

TracePrintGetBinary2(23+204,179,53+204,356,"2C4156")FunctionGetBinary2(x1,y1,x2,y2,color)Dimbinary,line=""KeepCaptureForj = y1 To y2Fori = x1 To x2IfCmpColor(i, j, color, 0.9)=0 Then binary=1ExitForEndIfIfi = x2 Then binary=0EndIfNextline=line&binaryNextReleaseCaptureGetBinary2=lineEndFunction

输出结果:

当前脚本第1行:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111100000000000000000000000000000000000

获取位置还是使用查找命令,查找“01”和“10”,再次强调得到结果是相对坐标。

把所有的数字都按照这个方法获取上下坐标,到此处我们已经可以得到每个数字的范围坐标,接下来就是截图了。

考虑到后面数字要拼接到一起,所以截图时外延5像素。

扩展计算方式:

加上原始范围是x1,y1,x2,y2

扩展后的范围是:x1-5,y1-5,x2+5,y2+5

最终代码:

截图(204,179,724,356,"2C4156")Function截图(x1,y1,x2,y2,color)dimlines= Getbinary(x1,y1,x2,y2,color)TracePrintlinesdimleftarr= GetPositions(lines,"01")TracePrintjoin(leftarr,"|")dimrightarr= GetPositions(lines,"10")TracePrintjoin(rightarr,"|")Fori = 0 To UBOUND(leftarr)dimnewline= GetBinary2(leftarr(i)+x1, y1, rightarr(i)+x1, y2, color)TracePrintnewlineTracePrintleftarr(i)+x1TracePrintinstr(1,newline,"01")+y1TracePrintrightarr(i)+x1TracePrintinstr(1,newline,"10")+y1SnapShot("/sdcard/pictures/yzm/"&i&".png",leftarr(i)+x1-5,instr(1,newline,"01")+y1-5,rightarr(i)+x1+5,instr(1,newline,"10")+y1+5)TracePrint"--------------"NextEndFunctionFunctionGetBinary(x1,y1,x2,y2,color)Dimbinary,line=""KeepCaptureForj = x1 To x2Fori = y1 To y2IfCmpColor(j, i, color, 0.9)=0 Then binary=1ExitForEndIfIfi = y2 Then binary=0EndIfNextline=line&binaryNextReleaseCaptureGetBinary=lineEndFunctionFunctionGetBinary2(x1,y1,x2,y2,color)Dimbinary,line=""KeepCaptureForj = y1 To y2Fori = x1 To x2IfCmpColor(i, j, color, 0.9)=0 Then binary=1ExitForEndIfIfi = x2 Then binary=0EndIfNextline=line&binaryNextReleaseCaptureGetBinary2=lineEndFunctionFunctionGetPositions(line,str)Dimarr(),i=0DoIfInStr(arr(i - 1) + 1, line, str) = 0 Then ExitDoElsearr(i)=InStr(arr(i - 1) + 1, line, str) + 1i=i+1EndIfLoopGetPositions=arrEndFunction

在文件夹里面已经把所有数字单独截取出来了。

截图部分的内容已经完成,下面开始把所有图片拼接到一起。

第一步:获取所有图片的尺寸,并存入数组中

DimPicArr()Fori = 0 To 4DimPath = "/sdcard/pictures/yzm/"&i&".png"Dim返回值 = Image.Size(Path)PicArr(i)={返回值[1],返回值[2]}NextDimjson=encode.tabletojson(PicArr)TracePrintjson

运算结果:

当前脚本第9行:[[41,50],[38,46],[50,51],[34,49],[42,49]]

第二步:因为是横向拼接所有图片,所以最终合成图的宽度是所有图片宽度之和。

Dim PicArr={{41,50},{38,46},{50,51},{34,49},{42,49}}Dim xFor i = 0 To 4  x=x+PicArr[i+1][1]NextTracePrint x

第三步:合成图的高度,5张图中最高的高度就是合成图的高度。

一组数字比较大小,可以用冒泡法,即相邻两个数字比较,前面数字大于后面数字,两个数字调换位置,如果前面数字小于后面数字,两个数字位置不变,这样所有大的数字都被放到后面,那么最后一个数字就是最大的数字。

Dim PicArr={{41,50},{38,46},{50,51},{34,49},{42,49}}Dim yFor i = 1 To 4  If PicArr[i][2] > PicArr[i+1][2] Then    PicArr[i+1][2]=PicArr[i][2]End IfNexty = PicArr[4][2]TracePrint y

第四步:做一个以验证图背景色颜色值的图片

Dimx=205,y=51DimPixelData = Image.GetScreenData(1,1,x,y)Dimr,g,bDimbackground="C7D1DB"ColorToRGB(background,r,g,b)TracePrintr,g,b Forj = 1 To xFori = 1 To yPixelData[j][i][3]=rPixelData[j][i][2]=gPixelData[j][i][1]=bNextNextImage.SavePixelDataPixelData, "/sdcard/pictures/yzm/aa.png"

第五步:把每张数字图片的颜色数据,都赋值给上面的图片。

Dimx=205,y=51DimPicArr={{41,50},{38,46},{50,51},{34,49},{42,49}}DimPixelData =Image.GetPicData("/sdcard/pictures/yzm/aa.png")Forn = 0 To 4Ifn = 0 Then dimm = 0Elsem=m+PicArr[n][1]EndIfdimPixelDataword=Image.GetPicData("/sdcard/pictures/yzm/"&n&".png")Forj = 1 To PicArr[n+1][1]Fori = 1 To PicArr[n + 1][2]Fork = 1 To 3PixelData[m+j][i][k]=PixelDataword[j][i][k]NextNextNextNextImage.SavePixelDataPixelData, "/sdcard/pictures/yzm/ab.png"

完整代码:

拼图(4)Function拼图(num)DimPicArr()Fori = 0 To numDimPath = "/sdcard/pictures/yzm/"&i&".png"Dim返回值 = Image.Size(Path)PicArr(i)={返回值[1],返回值[2]}NextDimjson=encode.tabletojson(PicArr)TracePrintjsonDimxFori = 0 To numx=x+PicArr[i+1][1]NextTracePrintxDimyFori = 1 To numIfPicArr[i][2] > PicArr[i+1][2] Then PicArr[i+1][2]=PicArr[i][2]EndIfNexty=PicArr[num][2]TracePrintyDimPixelData = Image.GetScreenData(1,1,x,y)Dimr,g,bDimbackground="C7D1DB"ColorToRGB(background,r,g,b)TracePrintr,g,b Forj = 1 To xFori = 1 To yPixelData[j][i][3]=rPixelData[j][i][2]=gPixelData[j][i][1]=bNextNextTracePrintx,yPicArr=Encode.JsonToTable(json)Forn = 0 To numIfn = 0 Then dimm = 0Elsem=m+PicArr[n][1]EndIfdimPixelDataword=Image.GetPicData("/sdcard/pictures/yzm/"&n&".png")Forj = 1 To PicArr[n+1][1]Fori = 1 To PicArr[n + 1][2]Fork = 1 To 3PixelData[m+j][i][k]=PixelDataword[j][i][k]NextNextNextNextImage.SavePixelDataPixelData, "/sdcard/pictures/yzm/ab.png"EndFunction

这期文章实现的功能很简单,但是思考的逻辑过程还是比较复杂的,另外在群里问怎么把两张图合并在一起的同学可以来领教程了。

写到这里有点累了,找到正确答案的识别和比对,不想写了,说个大概思路:

(1)比对数字个数,上部分是5个数字,只有选项B/C满足,排除2个错误答案

(2)因为选项的识别准确率非常高,我假定它是完全准确的。上部分的数字和选项比对这么几个维度,并且用打分形式记录

①单个数字对比,一个相同数字(+5分)

②同位数字比对,比如第一位都是6,每对一位(+10分)

③连续位数相同,这个情况比较多,先判断所有位相同的情况,如果这个满足,直接认定为正确答案,后续是1位不同,2位不同,分值依次降低。

就我们这个例子来说,它非常简单,几个选项相差很大,所以这个比对就简单很多。

=正文完=

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容