使用AutoHotKey做了一个调用百度接口批量识别图片中文字的脚本

在大佬Thinkai的指导下,解决了关键的urlencode问题。。。与其说指导不如说直接帮我解决了= =

首先感谢Thinkai大佬,然后在本站也学到不少知识,感谢河许人(偷偷的说,我在B站给你投硬币了哟~)

原理是调用windows自带的certutil.exe将图片编码保存在txt文件中,然后从txt文件里读取再post给百度API获取识别结果

基于上述原理,系统要求WinXP以上。。。certutil.exe听说2003自带,XP可以尝试下载一个2003的版本放到脚本目录下供脚本调用(理论可以,没试过,本人测试环境为32位Win10 = =)

接下来是代码:

#Include, %A_WorkingDir%\lib\JSON.ahk
global Cfgfile := A_WorkingDir "\Cfg.dat"
global P_id := ""
global P_secret := ""
global P_token := ""
global C_n := 0
global C_finsh := 0
global C_txtC := 0
global C_txtNo := 0
global C_imgC := 0
global C_imgNo := 0
global C_basexe := "certutil.exe"
global C_basexeTmp := ""
Begin1(Cfgfile)
if StrLen(P_id) < 18 or StrLen(P_secret) < 24
{
    msgbox % "参数错误,请在Cfg.dat中填写正确的id与secret"
    ExitApp
}
global P_image := ""
P_Url := "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials"
P_Url := P_Url "&client_id=" P_id "&client_secret=" P_secret
P_Json := JSON.Load(URLDownloadToVar(P_Url))
P_access_token := P_Json.access_token
P_access_token := FixToken(P_Json.access_token)
MsgBox, 0, 批量图片识别 - By 0772Boy, 按Ctrl+F9开始\暂停识别`n按Ctrl+F10编辑配置文件(修改文件后请重启本程序)`n按Ctrl+F11查看帮助文档`n按Ctrl+F12直接结束程序。, 99
global Goon := 0
return
^F9::
if Goon = 0
    Goon := 1
else
    Goon := 0
Loop 999
{
    if Goon := 0
    {
        TrayTip, 程序已暂停, 按Ctrl+F9重新开始转换。,3
        Break
    }
    ;图片转换为Base64并保存为txt
    if Get_Base64(A_Index) = A_Index
    {
        C_txtNo := A_Index
    }
    Else
    {
        C_imgC := A_Index
        Writedat(Cfgfile)
        if C_imgC > 1
        {
            C_imgC := C_imgC - 1
            TrayTip, 停止, 已完成%C_imgC%张图片转换。,10
        }
        else
            TrayTip, 停止, 图片转换出现错误。,10
        Break
    }
    sleep, 500
    ;txt读取
    if Get_Txt(C_txtNo) != C_txtNo
    {
        C_txtC := A_Index
        Writedat(Cfgfile)
        if C_txtC > 1
        {
            C_txtC := C_txtC - 1
            TrayTip, 停止, 已完成%C_txtC%张图片转换。,10
        }
        else
            TrayTip, 停止, Base64读取出现错误。,10
        Break
    }
    Del_Txt(C_txtNo)
    ;调用百度Orc识别后再保存到text文件夹中
    Txtmp := Get_Url(P_image,P_access_token)
    FileTmp := A_WorkingDir "\text\" C_txtNo ".txt"
    FileAppend , %Txtmp%, %FileTmp%, UTF-8
    C_finsh := C_txtNo
    Writedat(Cfgfile)
    TrayTip, 批量图片识别 - By 0772Boy, 第%C_txtNo%张图片已经完成,请查看text\%C_txtNo%.txt中内容。,3
}
return

^F12::
MsgBox, 0, 批量图片识别 - By 0772Boy, 程序退出。, 9
ExitApp
Return

^F10::
runtmp := "notepad.exe " Get_space( A_WorkingDir "\Cfg.dat")
run, %runtmp%
Return

^F11::
Helptext := "注意事项:所有图片必须使用jpg格式,并且按1~999的顺序存放在img目录下,程序路径请勿包含中文,识别结果按1~999保存在程序目录下Text文件夹中,在识别过程中再按一次Ctrl+F9也需要等到当前这一张图片识别结束才会停止,暂停之后重新开始识别仍然是从1开始。"
Helptext := "按Ctrl+F9开始\暂停识别`n按Ctrl+F10编辑配置文件(修改文件后请重启本程序)`n按Ctrl+F11查看帮助文档`n按Ctrl+F12直接结束程序。`n"  Helptext
MsgBox, 0, 批量图片识别 - By 0772Boy, %Helptext%, 999
Return

Get_Url(imgBase64,access_token)
{
    P_Url := "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=" access_token
    postdata := "image=" urlencode(imgBase64)
    P_ReturnTxt := URLDownloadToVar(P_Url, "UTF-8", "POST", postdata, {"Content-Type":"application/x-www-form-urlencoded"})
    P_Json := JSON.Load(P_ReturnTxt)
    if P_Json.error_msg != ""
        return P_Json.error_msg
    wordsC := P_Json.words_result_num
    if wordsC > 1
    {
        Loop % wordsC
        {
            txtmp := txtmp P_Json.words_result[A_Index].words
            if C_n
            {
                txtmp := txtmp "`n"
            }
        }
        return txtmp
    }
    else
    {
        Return P_Json.words_result[1].words
    }
    Return 0
}

Del_Txt(txtNumber)
{
    txtPath := A_WorkingDir "\img\" txtNumber ".txt"
    if FileExist(txtPath)
    {
        FileDelete % txtPath
    }
}

Get_Txt(txtNumber)
{
    txtPath := A_WorkingDir "\img\" txtNumber ".txt"
    if FileExist(txtPath)
    {
        P_image := ""
        Loop, Read, %txtPath%
        {
            if InStr(A_LoopReadLine, "-----") = 0 and StrLen(A_LoopReadLine) > 0
            {
                P_image := P_image A_LoopReadLine
                ;MsgBox % Base64Text
            }
        }
        return txtNumber
    }
    Else
    {
        Return txtPath "`n文件不存在,请检查。"
    }
}

Get_Base64(imgNumber)
{
    imgPath := A_WorkingDir "\img\" imgNumber
    if FileExist(imgPath ".jpg")
    {
        ;如果路径中有空格就用双引号括起来
        imgPath := Get_space(imgPath ".jpg")
        ;base64编码保存成txt文件放在图片同一个目录下
        TargetTmp := Get_space(C_basexeTmp) " -encode " imgPath " " Get_space(A_WorkingDir "\img\" imgNumber ".txt")
        ;msgbox % TargetTmp
        RunWait, %TargetTmp%
        Return imgNumber
    }
    Else
    {
        Return imgPath ".jpg`n文件不存在,请检查。"
    }
}

FixToken(str)
{
    TokenTmp := str
    if InStr(str, ".") = 0
        TokenTmp := P_token
    return TokenTmp
}

Get_space(FilePath1)
{
    if InStr(FilePath1, " ")
    {
        return """" FilePath1 """"
    }
    return FilePath1
}

Begin1(Cfgpath)
{
    Begin11:
    if FileExist(Cfgpath)
    {
        IniRead, P_id, %Cfgpath%, Config, id
        IniRead, P_secret, %Cfgpath%, Config, secret
        IniRead, P_token, %Cfgpath%, Config, token
        IniRead, C_n, %Cfgpath%, Config, 保留换行
        IniRead, C_finsh, %Cfgpath%, Config, finsh
        IniRead, C_txtC, %Cfgpath%, Config, txtC
        IniRead, C_txtNo, %Cfgpath%, Config, txtNo
        IniRead, C_imgC, %Cfgpath%, Config, imgC
        IniRead, C_imgNo, %Cfgpath%, Config, imgNo
        IniRead, C_basexe, %Cfgpath%, Config, basexe
    }
    Else
    {
        MsgBox % Cfgpath "`n文件不存在,将使用默认配置创建该文件。"
        ; C_n := 0
        ; C_finsh := 
        ; C_txtC := 0
        ; C_txtNo := 0
        ; C_imgC := 0
        ; C_imgNo := 0
        ; C_basexe := "certutil.exe"
        Writedat(Cfgpath)
        Goto, Begin11
    }
    if FileExist("C:\Windows\System32\" C_basexe) or FileExist(A_WorkingDir "\" C_basexe)
    {
        if FileExist("C:\Windows\System32\" C_basexe)
            C_basexeTmp := "C:\Windows\System32\" C_basexe
        else
            C_basexeTmp := A_WorkingDir "\" C_basexe
    }
    Else
    {
        msgbox, 0, 系统文件缺失, 请自行下载对应系统版本的certutil.exe到程序目录中。,10
        ExitApp
    }
    return
}
Writedat(Cfgpath)
{
    IniWrite, %C_n%, %Cfgpath%, Config, 保留换行
    IniWrite, %C_finsh%, %Cfgpath%, Config, finsh
    IniWrite, %C_txtC%, %Cfgpath%, Config, txtC
    IniWrite, %C_txtNo%, %Cfgpath%, Config, txtNo
    IniWrite, %C_imgC%, %Cfgpath%, Config, imgC
    IniWrite, %C_imgNo%, %Cfgpath%, Config, imgNo
    IniWrite, %C_basexe%, %Cfgpath%, Config, basexe
}

URLDownloadToVar(url, Encoding = "",Method="GET",postData="",headers:=""){ ;网址,编码,请求方式,post数据
	hObject:=ComObjCreate("WinHttp.WinHttpRequest.5.1")
	hObject.SetTimeouts(30000,30000,1200000,1200000) ;设置超时
	try
		hObject.Open(Method,url,(Method="POST" ? True : False))  ;打开连接
	;设置header
	if IsObject(headers)
	{
		for k,v in headers
		{
			if v
				hObject.SetRequestHeader(k, v)
		}
	}
	if postData
	{
		try
			hObject.Send(postData)
		hObject.WaitForResponse(-1)
	}
	else
	{
		try
			hObject.Send()
	}

	if (Encoding && hObject.ResponseBody)
	{
		oADO := ComObjCreate("adodb.stream")
		oADO.Type := 1
		oADO.Mode := 3
		oADO.Open()
		oADO.Write(hObject.ResponseBody)
		oADO.Position := 0
		oADO.Type := 2
		oADO.Charset := Encoding
		return oADO.ReadText(), oADO.Close()
	}
	return hObject.ResponseText
}


urlencode(string){
	nstring := Ansi2UTF8(string)
	Loop % StrLen(nstring)
	{
		hex := format("{1:02x}", hex2 := NumGet(&nstring,A_index-1,"Uchar"))
		if (hex2==33 || (hex2>=39 && hex2 <=42) || hex2==45 || hex2 ==46 || (hex2>=48 && hex2<=57) || (hex2>=65 && hex2<=90) || hex2==95 || (hex2>=97 && hex2<=122) || hex2==126)
			content .= Chr(hex2)
		else
			content .= "`%" hex
	}
	return content
}


Ansi2UTF8(sString)
{
	Ansi2Unicode(sString, wString, 0)
	Unicode2Ansi(wString, zString, 65001)
	return zString
}

Ansi2Unicode(ByRef sString, ByRef wString, CP = 0)
{
	nSize := DllCall("MultiByteToWideChar"
		, "Uint", CP
		, "Uint", 0
		, "Uint", &sString
		, "int", -1
		, "Uint", 0
		, "int", 0)
	VarSetCapacity(wString, nSize * 2)
	DllCall("MultiByteToWideChar"
		, "Uint", CP
		, "Uint", 0
		, "Uint", &sString
		, "int", -1
		, "Uint", &wString
		, "int", nSize)
}

Unicode2Ansi(ByRef wString, ByRef sString, CP = 0)
{
	nSize := DllCall("WideCharToMultiByte"
		, "Uint", CP
		, "Uint", 0
		, "Uint", &wString
		, "int", -1
		, "Uint", 0
		, "int", 0
		, "Uint", 0
		, "Uint", 0)
	VarSetCapacity(sString, nSize)
	DllCall("WideCharToMultiByte"
		, "Uint", CP
		, "Uint", 0
		, "Uint", &wString
		, "int", -1
		, "str", sString
		, "int", nSize
		, "Uint", 0
		, "Uint", 0)
}

Cfg.dat 是用来保存配置的文件,看代码就知道它的格式,就不贴内容上来了

JSON部分的代码没贴上来,我是直接include它整个文件的,可以在这里找到:

https://github.com/cocobelgica/AutoHotkey-JSON
PS:本帖的代码务必用A32版本执行
再PS:代码中百度id和secret只作为示例,Token才是真实有效的,根据百度的规则token会在30天后过期,过期之后请自行注册百度帐号并申请id和secret来使用
生成的成品在此:
链接: https://pan.baidu.com/s/1iswRJKor4MaWAokcqx15pw
提取码: j83j

给TA捐赠
共{{data.count}}人
人已捐赠
其他教程

使用vim做ahk[autohotkey]的编辑器

2019-1-2 9:00:18

其他

随机播放本地电影 [脚本],[AutoHotkey],[按空格键随机播放本地电影视频文件],[ahk],[2019年1月18日5时33分],[作者 徐晓亮 aahk],[腾讯QQ号595076941]

2019-1-18 15:20:40

4 条回复 A文章作者 M管理员
  1. hexuren

    多谢您的长久支持

  2. 悠然生活

    我在52上看到了你的帖子,并且下载了exe文件试了一下,挺好的。点赞。

  3. -.-

    不用certutil.exe怎么进行base64编码,还有可以通过vbs脚本执行certutil.exe隐藏cmd界面

  4. icunchunz

    这个不错的

个人中心
购物车
优惠劵
有新私信 私信列表
搜索