Autohotkey实现粘贴板图片用百度OCR识别

难点:

1、发送异步的http请求,包括获取获取token的get请求,和发送图片的post请求

2、post请求需要设置请求头,和请求体,请求体是图片,但是文档上说需要base64加密,还好windows上有一个工具叫certutil.exe 可以把文件加密为base64

3、certutil.exe原本不是用来加密图片的,看名字都知道是加密证书的,但是这里也能用,调用后会生成一个base64的txt文件,我们需要读取该文件把其中的'\',‘+’,'='转为%xx (xx是字符对应的16进制)

4、处理返回结果,结果是一个json字符串,我看网上大多数人是使用三方解析json,我这直接用正则给他匹配出来了。

5、双击窗口事件,读取窗口的文字,无视输入法的问题、

未解决的问题:

1、本来我是想直接从ahk读取粘贴板的图片,我研究了一下ahk的粘贴板没办法实现,需要使用dllcall调用系统的dll来实现数据的读取甚是麻烦(主要是不会,有会的同学可以在评论区告诉我),最后研究了半天还是没有从粘贴板读出图片,最后发现我电脑上之前装了工具叫做snipast,可以实时保存图片,我每次截图后他会保存在用户目录下的Pictures目录,我直接读取该图片就可以了、

2.、百度云那边成功读取数据后,ahk闪烁问题,想用ahk右下角图标闪烁的方式来提示用户请求已经完成,可以查看结果了,但是flash始终不生效。

实现效果:

1、先用snipast截图(必须是snipast)

Autohotkey实现粘贴板图片用百度OCR识别_第1张图片

2、然后ctrl+shift+空格呼出GUI,点击ESC可以隐藏窗口,ctrl+shift+空格可以继续显示出来, 双击可以把窗口文字显示在光标处,ctrl+shift+空格不会再显示了

Autohotkey实现粘贴板图片用百度OCR识别_第2张图片

实现代码

我们来一个功能一个功能的操作

1、请求百度云的OCR

 ①.这存在一个问题,就是snipast截图后有可能先于按下按键的时候生成,就是图片太小的时候,也可能后于按键生成,图片太大的时候,这个时候我们需要通过snipast保存的图片上自带的时间来判断

 ②.百度云基本信息需要自行到百度云上面去申请对应的ak和as

#Persistent
return

;检测粘贴板是否变化
OnClipboardChange:

    ;当包含粘贴板中包含图片的时候才执行ocr操作
    if(A_EventInfo!=2)
        return 

    global req
    global accessToken
    global count:=0
    global newPicName:=""
    global ocrText
    global ocrActualUrl:="https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic?access_token="
    global picDir:=userprofile "\Pictures" ;图片保存路径需要设置snipast

    ;允许捕捉ESC
    ESCFlag:=True
    GUIFlag:=false

    ;没有写入之前的png图片个数
    Loop, %picDir%\*.png
    count:=count+1
    if(count>=1)      
    {   
        
        picName:=getPngPath(picDir)
        timeNumber := RegExReplace(picName, "[^`\d]","")
        FormatTime, nowDateNumber,?%A_Now%,yyyyMMddHHmmss
        mSecond:=Abs(timeNumber-nowDateNumber)
        ;msgBox timeNumber=%timeNumber%
        ;msgBOx nowDateNumber=%nowDateNumber%
        ;msgBox mSecond=%mSecond%
        if(mSecond==0)
        {
            newPicName:=picName
            ;msgBox "save faster than reponse!"
        }
        else
        {
            if(mSecond&&mSecond<=1) ;时间先后在1s中的误差就ok
            {
                ;msgBox "save faster than reponse!"
                newPicName:=picName
            }
            else
            {
                newPicName:= 
            }

        }
        
    }

    
    req := ComObjCreate("Msxml2.XMLHTTP")
 
    ;百度云基本信息
    apiKey:="xxxxxxxxxxxxxxxxx"
    secretKey:="xxxxxxxxxxxxxxxxxxxx"
    ocrTokenUrl:="https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials"
    

    ;获取token的地址
    tokenUrl=%ocrTokenUrl%&client_id=%apiKey%&client_secret=%secretKey%
    ;发送请求获取accessToken
    if !accessToken
    {
        sendRequest(tokenUrl,"GET","getTokenSuccess","")
    }
    else
    {   
        dealPngPic()
    }
    return 

  
  

;发送异步请求
sendRequest(tokenUrl,Method,funcN,data)
{
    ;msgBox %data%
    req.onreadystatechange := Func(funcN)
    req.open(Method,tokenUrl, true)
    if data
        req.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
    req.send(data)
    while req.readyState != 4
        sleep 100 
}


;获取AccessToken 
getTokenSuccess() {
    if (req.readyState != 4)  ; Not done yet.
        return
    if (req.status == 200)              ; 执行成功
     {
         ;MsgBox % "Latest AutoHotkey version: " req.responseText

         FoundPos := RegExMatch(req.responseText, "expires_in.:.(.*?).,.*access_token.:.(.*?).,", SubPat)  ; 把结果保存SubPat1,和SubPat2
         ;MsgBox  %SubPat2%
         ;发送post请求
         accessToken :=SubPat2
         dealPngPic()
     }
    else
     {
        ;MsgBox 16,, % "Status " req.status
     }
    ;ExitApp ;退出App
}

;获取图片识别的文字 
getTextSuccess() {
    if (req.readyState != 4)  ; Not done yet.
        return
    if (req.status == 200)              ; 执行成功
     {
         ;MsgBox % "Latest AutoHotkey version: " req.responseText

         RegExMatch(req.responseText, "words_result_num..(.*?),..words_result.:..(.*?)\]\}", SubPat) 
        ;处理返回结果,有结果才处理
        if(SubPat1!=0)
        {
        	 ;msgBox %subPat2%
             words:=SubPat2
             StringReplace, words , words, `, `{"words`"`: `",`n, All   ;替换words为换行
             StringReplace, words , words,`{"words`"`: `",, All       ;替换第一个words
             StringReplace, words , words, `"},, All                      ;替换}为空
             ;msgBox %words% 
             ocrText:= words
           
        }
     }
    else
     {
        ;MsgBox 16,, % "Status " req.status
     }
    ;ExitApp ;退出App
}


;处理图片
dealPngPic()
{
    if(!newPicName)
    {
        ;msgBox no newPicName!
        flag:=ifHasNewPic(picDir)
        if(flag==1)                                                                                     
        {
         ;获取新图片的路径
         newPicName:=getPngPath(picDir)
         
        }
        ;msgBox flag=%flag%
    }
    if(!newPicName)
        return

    ;msgBox newPicName=%newPicName%
    ;msgBox %newPicName%

    ;生成base64的文件
    base64TxtPath:=Get_Base64(newPicName)
    if(base64TxtPath)
    {
        ;删除图片
        newPicPath=%picDir%\%newPicName%
        deleteObj(newPicPath)
        ;msgBox base64TxtPath=%base64TxtPath%
        ;发送post请求并获取txt
        sendPostAndGetStr(base64TxtPath)
    }
    return 
}


;判断图片是否写入到了pictures目录下
ifHasNewPic(picDir)
{   ;msgBox count:%count%
    cc:=0
    loop 8  ;等待3秒钟
    {
        sleep, 250
        count2:=0
        Loop, %picDir%\*.png
        count2:=count2+1
        ;msgBox count2: %count2%
        if(count2>count)
        {
            return 1
        }
        cc:=cc+1
    }

    return 0
}

;返回最新的一张图片
getPngPath(picDir){
    
    uPicPath:="there is no pic in dir picture!"
    Loop, %picDir%\*.png
        FileList = %FileList%%A_LoopFileTimeModified%`t%A_LoopFileName%`n
    Sort, FileList  ;根据日期排序.从小到大
    Loop, parse, FileList, `n
    {
        if A_LoopField =  ; 忽略列表末尾的最后一个换行符(空项).
            continue
        StringSplit, FileItem, A_LoopField, %A_Tab%  ;用 tab 作为分隔符切割.
            
        ;RunWait, %picDir%\%FileItem2%
        FileSetAttrib, -R, %picDir%\%FileItem2%
        ;uPicPath=%picDir%\%FileItem2%       
        uPicPath=%FileItem2%   
    }
    return uPicPath
}

;获取64位编码的文件 
Get_Base64(imgName)
{
    ;windows 的base64工具路径
    C_basexeTmp:= "C:\Windows\System32\certutil.exe"
    ;处理文件名
    StringReplace, imgNameNoSuffix,imgName, .png 

    base64Path := picDir "\" imgNameNoSuffix
    base64TxtPath:= base64Path ".txt"
    if FileExist(base64Path ".png")
    {
      ;如果路径中有空格就用双引号括起来
        base64Path := base64Path ".png"
        ;base64编码保存成txt文件放在图片同一个目录下
        TargetTmp := C_basexeTmp " -encode " base64Path " " base64TxtPath
        RunWait, %TargetTmp%
      return base64TxtPath
    }
    Else
    {
     msgBox % imgName ".png not exists!"
    }
}

;删除文件
deleteObj(objPath)
{
    ;msgBox %objPath%
    FileDelete, %objPath%

}

;发送高精文字识别请求
sendPostAndGetStr(base64TxtPath)
{

    urlWithToken = %ocrActualUrl%%accessToken% 
    ;msgBox urlWithToken=%urlWithToken%
    postData:="image=" getBase64Text(base64TxtPath)
    sendRequest(urlWithToken,"POST","getTextSuccess",postData)
    ;删除base64文本
    deleteObj(base64TxtPath)
}


;读取base64的文本文件
getBase64Text(base64Txt)
{
    
    if FileExist(base64Txt)
    {
        P_image := ""
        Loop, Read, %base64Txt%
        {
            if InStr(A_LoopReadLine, "-----") = 0 and StrLen(A_LoopReadLine) > 0
            {
             tempLine:=A_LoopReadLine 
             ;XtempLine:=RTrim(A_LoopReadLine, OmitChars = " `r`n") ;去掉换行
             StringReplace, tempLine , tempLine, +, `%2B, All     ;符号 +
             StringReplace, tempLine , tempLine, /, `%2F, All     ;替换 /
             StringReplace, tempLine , tempLine, =, `%3D, All     ;替换 =

             P_image= %P_image%%tempLine%
            }
        }
        ;msgBox %P_image%
        return P_image
    }
}

2、图形显示出ocr识别的结果

screenWith和Screenheight是屏幕的分辨率,需要你自己手动设置为你自己的分辨率,ahk我没找到可以获取分辨率的操作。

global ESCFlag 
global GUIFlag    ;是否界面上有图形
^+space::
  send #v
 ;sleep ,200 ;战术性休眠

 if(ocrText && !GUIFlag) ;有数据才显示,没数据不显示,界面上有图形不再重新显示
 {
 	 GUIFlag:=true
 	 ;获取数据行数
 	 lineCount:=getLineCount()
 	 ;msgBox lineCount=%lineCount%
 	 height:=14.5*lineCount
 	 windowHeight:=height+18

	 ScreenWith:=1600
	 ScreenHeight:=900
	 x:=ScreenWith-290-4
	 y:=ScreenHeight-470-height-20
	 if(y<30)
	 {
	 	y=30
	 	height:=ScreenHeight-470-y
	 	lineCount:=ceil((height-20)/14.5)
 	 	windowHeight:=height
	 }
	 MouseGetPos, OutputVarX, OutputVarY, OutputVarWin
	 Gui, MyGui:New
	 Gui +AlwaysOnTop
	 Gui, font,, Verdana  ; 首选字体.
	 Gui, MyGui:+Owner 
	 Gui, Add, edit, R%lineCount% w276 h%height% vMyEdit
	 GuiControl,, MyEdit ,%ocrText%
	 Gui, show,W290 H%windowHeight% X%x% Y%y%  NoActivate,百度云 OCR
	 ESCFlag :=true
	 ;监听复制数据
	 ;OnMessage(0x301, "WM_COPY")  ; 0x4a 为 复制事件
	 ;WM_NCLBUTTONDBLCLK = 0xA3 ;窗口的回车事件
	 ;WM_LBUTTONDBLCLK ;控件的回车事件
	 OnMessage(0xA3, "WM_NCLBUTTONDBLCLK")    ; 0x203为 左键双击事件
	 OnMessage(0x10, "WM_CLOSE")              ; 0x10 关闭窗口
 }
 return 
 
 

;获取返回值有多少行
getLineCount()
{
	count:=-1
	Loop, Parse, ocrText, `n, `r 
	{	
		count:=count+1
		if(strLen(A_LoopField)>22)
		{
			count:=count+round(strLen(A_LoopField)/22)
		}
	}
	count:=count==0?2:count+1
	return count
	
	
}
  
 return 

;退出ocr边框显示
#If ESCFlag
	 ESC::
	  Gui MyGui:Destroy 
	  GUIFlag:=false    ;图形可以重新显示
	  ESCFlag:=false
	  ;msgBox %ESCFlag%
	  send {ESC} 
	  return 

;GUI的双击事件
WM_NCLBUTTONDBLCLK(wParam, lParam)
{
	 GUIFlag:=false    ;图形可以重新显示
	 ;msgBox xxxx
	 Gui MyGui:Destroy 
	 Gui MyGui:Destroy
	 ocrText:=Trim(ocrText)
	 ocrText:=Trim(ocrText, OmitChars = " `r`n")
	 ocrText:=Trim(ocrText, OmitChars = " `t")
	 ocrText:=Trim(ocrText, OmitChars = " `n")
	 ;msgBox ocrText=%ocrText%
	 ;绕过输入法
	 ocrText:=ascInput(ocrText)
	 SendInput %ocrText%
	 return 
}

3、绕过输入法输入字符串

ascInput(string){
    u :=  A_IsUnicode ? 2 : 1 ;Unicode版ahk字符长度是2
    length:=StrPut(string,"CP0")
    if(A_IsUnicode)
        {
        VarSetCapacity(address,length),StrPut(string,&address,"CP0")
        }
    else
        address:=string
    VarSetCapacity(out,2*length*u)
    index =0
    Loop
    {
    index += 1
    if (index>length-1)
        Break
    asc := NumGet(address,index-1,"UChar")
    if asc > 126
        {
        index += 1
        asc2 := NumGet(address,index-1,"UChar")
        asc := asc*256+asc2
        }
    Send, % "{ASC " asc "}"
    }
    }
    ascaltinput(string){
    u :=  A_IsUnicode ? 2 : 1 ;Unicode版ahk字符长度是2
    length:=StrPut(string,"CP0")
    if(A_IsUnicode)
        {
        VarSetCapacity(address,length),StrPut(string,&address,"CP0")
        }
    else
        address:=string
    VarSetCapacity(out,2*length*u)
    index =0
    Loop
    {
    index += 1
    if (index>length-1)
        Break
    asc := NumGet(address,index-1,"UChar")
    if asc > 126
        {
        index += 1
        asc2 := NumGet(address,index-1,"UChar")
        asc := asc*256+asc2
        }
    StringSplit, var, asc,
    Loop % var0
    {
    str .= "{Numpad" var%A_index% "}"
    }
    send, {AltDown}%str%{Altup}
    str =
    }
}

总结:把上面三段代码拼起来就可以实现ocr了!

你可能感兴趣的:(autohotkey,ocr,百度)