惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Project Zero
Project Zero
Security Archives - TechRepublic
Security Archives - TechRepublic
C
Cyber Attacks, Cyber Crime and Cyber Security
Security Latest
Security Latest
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Vulnerabilities – Threatpost
C
CERT Recently Published Vulnerability Notes
S
Schneier on Security
G
GRAHAM CLULEY
L
Lohrmann on Cybersecurity
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
Intezer
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
F
Full Disclosure
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
WordPress大学
WordPress大学
Microsoft Azure Blog
Microsoft Azure Blog
H
Help Net Security
大猫的无限游戏
大猫的无限游戏
MyScale Blog
MyScale Blog
Hacker News: Ask HN
Hacker News: Ask HN
G
Google Developers Blog
H
Heimdal Security Blog
O
OpenAI News
Hugging Face - Blog
Hugging Face - Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LangChain Blog
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
IT之家
IT之家
Cyberwarzone
Cyberwarzone
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Know Your Adversary
Know Your Adversary
博客园 - 聂微东
The Cloudflare Blog
C
Check Point Blog
K
Kaspersky official blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
月光博客
月光博客
T
Tor Project blog
T
Threat Research - Cisco Blogs
T
Tailwind CSS Blog
P
Proofpoint News Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
A
About on SuperTechFans
小众软件
小众软件
Cloudbric
Cloudbric
A
Arctic Wolf

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
用 AutoHotKey 给自己写一个翻译和文字识别工具 - 少数派
2025-02-20 · via 少数派

Matrix 首页推荐 

Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。 
文章代表作者个人观点,少数派仅对标题和排版略作修改。


我是一个 Mac 用户,因为工作原因,不得不在办公时使用一台性能堪忧的 Windows 笔记本电脑。

在此前的一篇文章里,我介绍了自己如何利用 PowerToys 和 AutoHotKey(下文简称 AHK) 等工具来改善我的 Windows 使用体验,提高办公效率。当时我提到自己在 Mac 上使用的翻译软件 Bob,主要功能是划词(句)翻译和截图识别文字,除了自带服务外,还可以接入各大服务商,借助各种模型完成翻译;而在 Windows 上,翻译功能我使用 PowerToys 自带的 PowerToys Run 加上有道翻译插件,配合 AHK 热键,实现复制选中内容并立即唤出 PowerToys Run 翻译。识别文字则直接使用的 PowerToys 自带的「文本提取器」工具。

很显然,上述替代方案不够好用,一是响应速度一般,二是翻译和识别的质量不好,特别是对于较长的文本和扫描件。

于是,我首先找到了 Windows 上各方面都和 Bob 挺相似的工具 STranslate,STranslate 说实话已经基本可以满足我的需求,但我还是产生了想要折腾一下的念头——当然这也不是全无原因的,一来可能是 STranslate 的用户相对较少,第三方插件不如 Bob 多,虽然 STranslate 已经提供了相当多的服务商选择,但我目前使用的 API 服务商还未在列表中;二来,要么是我的办公电脑性能太差、要么是办公网的限制,现有服务响应速度并不是特别快,这导致我往往得盯着弹出的窗口等待半天;三来,可能还是因为电脑不行,有时会出现热键失效,或者划词(句)翻译无法正确翻译被选中语句的情况,重启软件后才恢复正常。

本着能轻便就轻便,能少开一个进程就少开一个的原则,我打开了一直在后台默默工作的 AHK 脚本。我的目标是模仿 Bob 和 STranslate,利用 AHK 脚本和大模型 API,实现翻译选中文字,以及截图识别文字的功能。

在实现的过程中,我借助了 DeepSeek 和 Gemini 等 AI 来起草代码,效果相当不错,其中翻译功能的核心部分基本上可以拿 AI 给出的示例直接使用,识别文字的功能错误相对较多(经测试加上联网搜索效果会好一些)。在理解 AI 给出的代码后,经过各种小修小补,补上我自己的额外需求,最终得到了我想要的效果。

这是我写的第一个利用大模型 API 的小工具,写的过程还是十分有趣的。下面我将介绍 AHK 是如何实现这两个功能的,并在最后给出完整的代码示例。

准备工作

首先,需要 AHK 这个脚本工具(官方网址),AHK 有很多教程,其文档也有中文翻译版本,我在上一篇文章中也对此做了不少介绍,此处就不再赘述。

需要注意的是,如果你跟我一样使用 AHK 2.0 版本(推荐使用这个最新的版本),在询问 AI 时一定要强调使用 AHK 2.0 版本的语法,并且即使如此,AI 偶尔也会给出不能够在 2.0 版本中使用的代码,这时候针对错误要求 AI 修改即可;其次在向大模型服务商发送请求时要使用 JSON 格式,需要使用 JSON 库,这里我用的是这个,下载后放在同 AutoHotkey.exe 同目录的 Lib 文件夹内(如无就新建一个)就可以1

类似的,在截图文字识别中,要将剪贴板中的截图保存下来发送请求,需要使用到 GDIP 库。这里我走了很多弯路,一是 AI 总是给出不存在的语法,比如什么 Clipboard.GetData();二是调用 Windows API 函数 GetClipboardData,但在我这里尝试了许多次、换了都没有成功;三是网上的 GDIP 库很多是基于旧语法的,在 AHK 2.0 中不能使用。最终我找到的可用于 AHK 2.0 的 GDIP 库来自这个地址

在下载上述两个库并放在指定位置后,在脚本中加入这样两行代码,后续即可使用里面的函数:

#Include <JSON>
#Include <GDIP_ALL>

最后,是需要准备大模型 API,或者翻译 API,目前有大量选择,这里不做任何推荐,请注意保留好自己的 API KEY(可能有 ID 等)不要对外泄露。不同的 API 在请求服务时的代码有所不同,需要查阅各家提供的文档,当然更方便的方式是请 AI 查阅这些文档并给出相应的 AHK 2.0 代码。因为我自己使用的是 OpenRouter(文档地址),其他服务商在代码上与此可能有差异的地方,下面我会尽量做出提示。

设置热键、获取文字和截图

首先我们要针对翻译和文字识别功能,设置不同的热键(快捷键)

^+d:: translate()
^+1:: ocr()

这里我分别使用的是 Ctrl + Shift + D,以及 Ctrl + Shift + 1,没有什么特殊的原因,单纯是这个按键和我在 Mac 上使用的 Bob 设置相同。需要说明的是,我个人是交换了 Ctrl 和 Alt 的,所以这里其实使用的是键盘上的 Alt 按键。如果使用 Alt,只要将上面的 ^ 修改为 !。

然后我们需要获取文字和图片,这里我都使用的是剪贴板。并且对于返回的结果,我也会保存到剪贴板,如果你不想要让剪贴板受到「污染」,可以考虑加上以下代码来临时保存剪贴板内容再还原回去(参考文档),我自己未使用这个方式。

ClipSaved := ClipboardAll() ; 保存剪贴板内容
;(完成后续需要使用剪贴板的各种操作)
A_Clipboard := ClipSaved ; 恢复剪贴板
ClipSaved := ""

所谓划词(句)翻译,就是把选中的词句翻译为指定语言,因此这时候操作上一定是选中了要翻译的内容,这里发送 Ctrl + C 复制就可以,等待剪贴板存在文字内容后,继续后续操作。

translate(){
A_Clipboard:= ""
Send "^c"
ClipWait() ; 等待剪贴板存在文字
text:= A_Clipboard
;(完成后续的各种操作)
}

截图识别相对复杂,也是上面我提到走了不少弯路的地方,截图利用系统自带的 Windows + Shift + S 就可以,由于我自己在工作时常开着微信,微信截图在我这里稍快一点,且截图选框后还可以修改范围,所以我用的是微信截图快捷键 Ctrl + Alt + A。等待剪贴板存在内容后,这里需要临时将截图保存到某个文件中,文件的地址可以自己指定,事后会删除。

ocr(){
A_Clipboard:= ""
Send "^!a" ; 系统截图使用 #+s,微信截图使用 ^!a
ClipWait(, 1) ; ClipWait 第二个参数指定为 1,意思是任何类型的数据,否则默认仅限于文本或文件

file_tmp:= "tmp_clipboard_pic_" A_YYYY A_MM A_DD ".png" ; 选一个不会与已有文件重复的名字即可
path:= "C:\Users\用户名\Downloads\" file_tmp
; 下面几行是使用 GDIP 库的一系列操作,用途只有把图片临时保存并写入文件,然后再读取这个文件,存入变量 img
pToken:= Gdip_Startup()
pBitmap := Gdip_CreateBitmapFromClipboard()
Gdip_SaveBitmapToFile(pBitmap, path)
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(pToken)
img:= FileRead(path, "RAW")
;(完成后续的各种操作)
FileDelete(path)
}

发送请求以获取翻译结果、识别文字结果

从这一部分开始,不同 API 服务商操作会有所不同,以我使用的 OpenRouter 为例,首先我需要获取请求的网址,以及我申请的 API KEY,然后选择使用的模型。这里可以一次性填入多个模型,注意识别文字需要使用具有视觉功能的模型,下面以翻译功能为例,识别文字部分详见后面的完整代码。如果使用的是腾讯翻译,则需要的是 API ID 和 API KEY,且不需要选择模型。

url:= "https://openrouter.ai/api/v1/chat/completions"
api:= "" ; 填入申请的 API KEY
models:= ["",] ; 填入模型名称,可以填写多个,用引号包围,用逗号隔开(均为英文标点)
w_model:= 1
langs:= ["中文", "英文",] ; 如果有其他语言需要,可以继续填入
t_lang:= 1

可以看到,我这里设置了两个初始为 1 的标记变量,目的是用来切换使用的模型和目标语言,以目标语言为例,默认选项 t_lang = 1 时,数组 langs[t_lang] 为 “中文”(注意 AHK 数组是从 1 开始的)。为此,我当然也设置了切换使用模型和目标语言的热键,并且,在切换后会显示切换的结果,持续一小段时间。以下以切换目标语言为例:

^+2:: switch_target_lang()
switch_target_lang(){
global t_lang
t_lang:= Mod(t_lang, langs.Length) + 1 ; 循环切换数字,也就是对数组长度求余后加一
ToolTip Format("翻译为{}", langs[t_lang]) ; 显示当前设置为翻译成什么语言
SetTimer () => ToolTip(), -1200 ; 让显示窗口在短时间内消失
}

效果如下:

事实上,OpenRouter 也支持自动选择或自动切换使用的模型(参考文档),目前我采用自己切换选中模型的方式,主要是为了确定翻译结果来自于哪个模型,以便于比较各模型速度和效果。

接下来是获取翻译,将前面选中并复制的文字 text,以及选中的目标语言 langs[t_lang] 以及选择的模型 models[w_model] 分别传入以下函数。这部分内容因选择的服务商而异,在构造请求方面有不同,腾讯 API 似乎还有一个签名的过程,这部分可以请 AI 查阅对应服务商的文档来帮助我们起草:

get_translation(text, lang, model){
; 使用 COM 组件,其中 HTTP 请求头 Header 怎么构造在服务商的文档中一般有写
req:= ComObject("WinHttp.WinHttpRequest.5.1")
req.open("POST", url, true)
req.SetRequestHeader("Authorization", "Bearer " api)
req.SetRequestHeader("Content-Type", "application/json")
 
; 构造请求的内容,一般就是要翻译的内容,对于大模型而言需要加上提示词
prompt:= "你是一位精通多种语言的专业翻译,请将内容精准地翻译为" lang ",只返回译文不要额外内容。"
message:= [
Map("role", "system", "content", prompt),
Map("role", "user", "content", text)
]
 
; 发送请求
req.Send(JSON.Stringify(Map(
"model", model,
"messages", message,
"temperature", 0.5 ; 大模型的温度参数,越高越有创造力,越低越严谨
)))
req.WaitForResponse()
 
; 收到并返回结果
response:= JSON.Parse(req.ResponseText)
return response["choices"][1].Get("message").Get("content")
}

对于识别文字可能还有一些额外步骤,比如对于 OpenRouter,需要把图片先转换为 Base64 编码,以下是我测试后可行的转换方法,即调用 Windows API 函数 CryptBinaryToString,网上其实也有针对 AHK 2.0 的 Base64 库,如果以下方法不行,也可以尝试使用库(我的测试方法是,把转换后的 img_64 赋值给剪贴板 A_Clipboard,然后网上找一个 Base64 解码为图片的在线网站试一下能不能把它还原回去):

DllCall("crypt32\CryptBinaryToString", "ptr", img.Ptr, "uint", img.size, "uint", 0x1, "ptr", 0, "uint*", &size := 0)
buf := Buffer(size * 2)
DllCall("crypt32\CryptBinaryToString", "ptr", img.Ptr, "uint", img.size, "uint", 0x1, "ptr", buf.Ptr, "uint*", &size)
img_64:= StrGet(buf.Ptr)

识别文字使用的提示词和请求内容(毕竟要告知大模型这是 Base64 编码的图片)也有所不同:

prompt:= "提取图片中的全部文字(保持原文换行), 只需要回答提取的文字内容。"
message:= [
Map("role", "system", "content", prompt),
Map("role", "user", "content", [
Map("type", "text", "text", prompt),
Map("type", "image_url", "image_url", Map("url", "data:image/png;base64," img_64))
])
]

其余部分则没什么差异,与上面的翻译函数基本一致,这里就不展示了,详见后文完整代码。

显示、复制返回的结果

在得到上述翻译、识别文字结果后,我遇到了一个额外的困难,这部分我不确定是否是 OpenRouter 服务商独有的,那就是我得到的结果是一串乱码,从其颇有规律的排布来看应该是某种编码,经过一些测试,我发现它是 ISO-8859-1(Latin-1)编码。

因此额外地,我需要将结果再转为 UTF-8 编码:

str:= get_translation(text, langs[t_lang], models[w_model]) ; 获取翻译结果
; 以下三行的效果是将结果从 ISO-8859-1 编码(在 AHK 中为 cp28591)转为 UTF-8 编码
byte:= Buffer(StrPut(str, "cp28591"))
StrPut(str, byte, "cp28591")
result:= StrGet(byte, "utf-8")

显示结果有至少两种方法,一种是使用 ToolTip,效果是显示一个工具提示的小窗口,考虑到文字较小,且一次性翻译或识别文字的文字量较大,没有选择让它一段时间后自动消失,而是设计为任意位置点击左键消失,之后将内容复制到剪贴板,方便使用或再次查看。

ToolTip result
KeyWait("LButton", "D") ; 等待一次左键点击
ToolTip() ; 关闭工具提示窗口
A_Clipboard:= result ; 将结果复制到剪贴板

这种做法的优点是不会弹出额外窗口,显得很轻便,但缺点是文字较多的时候看起来较为不便。所以另一种选项是使用 msgbox,弹出一个对话框来显示结果,由于窗口的文字框的大小更合适,可以考虑把原文和译文都显示出来,如下所示:

msgbox Format("原文:{}`r`n`r`n`r`n译文:{}", text, result)
A_Clipboard:= result

我自己使用的仍然是 ToolTip 方法,因为我觉得这看起来更轻便,全屏任意位置点击左键关闭窗口也更不影响操作。

其他

考虑到请求服务可能失败,为了让自己知道请求已经失败了而不是网络太慢,可以使用 try 语句在失败时抛出错误。

try{
;(获取翻译的一些列操作)
} catch as e {
ToolTip Format("翻译失败:{}", e.Message) ; 显示翻译失败和报错内容
SetTimer () => ToolTip(), -1800 ; 短时间后自动关闭显示
}

自己写的脚本有一个好处,就是可以随时根据需要修改或添加功能,比如前面提到的切换模型,在每次只输出一个结果的使用场景下,切换模型比 Bob 等工具还要方便一点。同样的道理,也可以写一个切换不同服务商的开关并分配热键,或者像是 Bob 那样,同时显示多个不同的模型的输出结果,从上到下依次排布。

最后,给出我的完整代码供大家参考:

#Requires AutoHotkey v2.0
#Include <JSON>
#Include <GDIP_ALL>
 
url:= ""
api:= ""
models:= ["",]
w_model:= 1
langs:=["中文", "英文",]
t_lang:= 1
 
url_ocr:= ""
api_ocr:= ""
models_ocr:= ["",]
w_model_ocr:= 1
 
 
^+d:: translate()
^+1:: ocr()
^+2:: switch_target_lang()
^+3:: switch_model()
^+4:: switch_model_ocr()
 
switch_target_lang(){
global t_lang
t_lang:= Mod(t_lang, langs.Length) + 1
ToolTip Format("翻译为{}", langs[t_lang])
SetTimer () => ToolTip(), -1200
}
 
switch_model(){
global w_model
w_model:= Mod(w_model, models.Length) + 1
ToolTip Format("使用以下模型:{}", models[w_model])
SetTimer () => ToolTip(), -1800
}
 
switch_model_ocr(){
global w_model_ocr
w_model_ocr:= Mod(w_model_ocr, models_ocr.Length) + 1
ToolTip Format("使用以下模型:{}", models_ocr[w_model_ocr])
SetTimer () => ToolTip(), -1800
}
 
translate(){
A_Clipboard:= ""
Send "^c"
ClipWait()
text:= A_Clipboard
 
try{
str:= get_translation(text, langs[t_lang], models[w_model])
byte:= Buffer(StrPut(str, "cp28591"))
StrPut(str, byte, "cp28591")
result:= StrGet(byte, "utf-8")
 
ToolTip result
KeyWait("LButton", "D")
ToolTip()
A_Clipboard:= result
} catch as e {
ToolTip Format("翻译失败:{}", e.Message)
SetTimer () => ToolTip(), -1800
}
}
 
get_translation(text, lang, model){
req:= ComObject("WinHttp.WinHttpRequest.5.1")
req.open("POST", url, true)
req.SetRequestHeader("Authorization", "Bearer " api)
req.SetRequestHeader("Content-Type", "application/json")
 
prompt:= "你是一位精通多种语言的专业翻译,请将内容精准地翻译为" lang ",只返回译文不要额外内容。"
message:= [
Map("role", "system", "content", prompt),
Map("role", "user", "content", text)
]
 
req.Send(JSON.Stringify(Map(
"model", model,
"messages", message,
"temperature", 0.5
)))
req.WaitForResponse()
response:= JSON.Parse(req.ResponseText)
return response["choices"][1].Get("message").Get("content")
}
 
 
ocr(){
A_Clipboard:= ""
Send "#+s" ;^!a
ClipWait(, 1)
 
file_tmp:= "tmp_clipboard_pic_" A_YYYY A_MM A_DD ".png"
path:= "C:\Users\用户名\Downloads\" file_tmp
pToken:= Gdip_Startup()
pBitmap := Gdip_CreateBitmapFromClipboard()
Gdip_SaveBitmapToFile(pBitmap, path)
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(pToken)
img:= FileRead(path, "RAW")
 
try{
str:= get_ocr(img, models_ocr[w_model_ocr])
byte:= Buffer(StrPut(str, "cp28591"))
StrPut(str, byte, "cp28591")
result:= StrGet(byte, "utf-8")
 
ToolTip result
KeyWait("LButton", "D")
ToolTip()
A_Clipboard:= result
} catch as e {
ToolTip Format("识别失败:{}", e.Message)
SetTimer () => ToolTip(), -1800
}
 
FileDelete(path)
}
 
get_ocr(img, model){
DllCall("crypt32\CryptBinaryToString", "ptr", img.Ptr, "uint", img.size, "uint", 0x1, "ptr", 0, "uint*", &size := 0)
buf := Buffer(size * 2)
DllCall("crypt32\CryptBinaryToString", "ptr", img.Ptr, "uint", img.size, "uint", 0x1, "ptr", buf.Ptr, "uint*", &size)
img_64:= StrGet(buf.Ptr)
 
req:= ComObject("WinHttp.WinHttpRequest.5.1")
req.open("POST", url, true)
req.SetRequestHeader("Authorization", "Bearer " api)
req.SetRequestHeader("Content-Type", "application/json")
 
prompt:= "提取图片中的全部文字(保持原文换行), 只需要回答提取的文字内容。"
message:= [
Map("role", "system", "content", prompt),
Map("role", "user", "content", [
Map("type", "text", "text", prompt),
Map("type", "image_url", "image_url", Map("url", "data:image/png;base64," img_64))
])
]
 
req.Send(JSON.Stringify(Map(
"model", model,
"messages", message,
"temperature", 0.5
)))
req.WaitForResponse()
response:= JSON.Parse(req.ResponseText)
return response["choices"][1].Get("message").Get("content")
}

> 关注 少数派小红书,感受精彩数字生活 🍃

> 实用、好用的 正版软件,少数派为你呈现 🚀