python多媒体应用:使用python和win32gui一步步教你按需截图和图片文字识别

前方高能预警,建议分多次阅读。

这里将会分成三大模块进行介绍。

1、如何使用百度AI识别的API接口进行图片文字识别;
2、如何通过使用python和win32gui进行截图;
3、如何通过使用python和win32gui进行截图,保存成图片,然后使用百度AI识别的API接口进行图片文字识别;

里面的信息量好大,建议收藏慢慢看。也感谢你的关注。
好的,下面一一进行介绍。

一、如何使用百度AI识别的API接口进行图片文字识别

(一)自行申请注册API账号

每天可以免费使用50000次/天免费,这个量足够我们日常使用和学习之用途了。
具体详情可以自行登录百度AI官网查看。

https://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu

(二)创建应用

自行按需创建好自己的应用。
在这里插入图片描述

(三)获取Access Token

# encoding:utf-8
import requests 

# client_id 为官网获取的AK, client_secret 为官网获取的SK
host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=【官网获取的AK】&client_secret=【官网获取的SK】'
response = requests.get(host)
if response:
    print(response.json())

(四)调用API接口

# encoding:utf-8

import requests
import base64

'''
通用文字识别
'''

request_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic"
# 二进制方式打开图片文件
f = open('[本地文件]', 'rb')
img = base64.b64encode(f.read())

params = {"image":img}
access_token = '[调用鉴权接口获取的token]'
request_url = request_url + "?access_token=" + access_token
headers = {'content-type': 'application/x-www-form-urlencoded'}
response = requests.post(request_url, data=params, headers=headers)
if response:
    print (response.json())

(五)一个简单的案例

1、原图

在这里插入图片描述

2、部分代码

import base64

request_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic"
# 二进制方式打开图片文件
f = open('222.png', 'rb')
img = base64.b64encode(f.read())

params = {"image":img}
request_url = request_url + "?access_token=" + access_token
headers = {'content-type': 'application/x-www-form-urlencoded'}
response = requests.post(request_url, data=params, headers=headers)
if response:
    print (response.json())

3、输出结果

E:\Python37\python.exe D:/main01.py
{'log_id': 6305098160455157211, 'words_result_num': 4, 'words_result': [{'words': 'arrow:指定绘制直线时两端是否有箭头。该选项支持NONE(两端无箭头)、 FIRST'}, {'words': '开始端有箭头)、LAST(结束端有箭头)、BOTH(两端都有箭头)选项值。艹'}, {'words': 'arrowshape:指定箭头形状。该选项是一个形如"202010°的字符串'}, {'words': '个整数依次指定填充长度、箭头长度、箭头竞度。艹'}]}

Process finished with exit code 0

大家可以自己实操一下,百度AI识别还是非常强大的。只有自己操作了才知道。

这里其实还是比较简单的,那么下面看是就比较复杂了。

二、如何通过使用python和win32gui进行截图

(一)核心代码

无论哪种截图,这里有一个精简版的核心代码,其实就是使用ImageGrab进行图像保存。

from PIL import ImageGrab
 
# x1:开始截图的x坐标
# x2:开始截图的y坐标;
# x3:结束截图的x坐标
# x4:结束截图的y坐标
box = (x1, y1, x2, y2)
 
image = ImageGrab.grab(box)
 
# 保存截图文件的路径
image.save('xx.png')

OK,我们现在可以演示一下。

from PIL import ImageGrab

x1=100
y1=100
x2=300
y2=300
box = (x1, y1, x2, y2)

image = ImageGrab.grab(box)

# 保存截图文件的路径
image.save('xx.png')

运行效果:
在这里插入图片描述
呵呵,这个图片完全只是为了教学之用,大家不要介意啊。

(二)实现桌面截图

使用tkinter和win32gui进行构建

1、核心代码为:

def CaptureScreen():
    HWND = win32gui.GetFocus() #获取当前窗口句柄
    rect=win32gui.GetWindowRect(HWND) #获取当前窗口坐标
    win32gui.SetFocus(HWND)
    print(rect)
    rect=(0,0,root.winfo_screenwidth(),root.winfo_screenheight())
    im=ImageGrab.grab(rect) #截取目标图像
    im.save("second.jpeg",'jpeg') #前面一个参数是保存路径,后面一个参数是保存格式

2、完整代码为:

from tkinter import *
import win32gui
from PIL import ImageGrab

#然后是获取Canvas的实现
def CaptureScreen():
    HWND = win32gui.GetFocus() #获取当前窗口句柄
    rect=win32gui.GetWindowRect(HWND) #获取当前窗口坐标
    win32gui.SetFocus(HWND)
    print(rect)
    rect=(0,0,root.winfo_screenwidth(),root.winfo_screenheight())
    im=ImageGrab.grab(rect) #截取目标图像
    im.save("second.jpeg",'jpeg') #前面一个参数是保存路径,后面一个参数是保存格式

root = Tk()
cv = Canvas(root, width=300, height=150)
cv.pack()
b = Button(root, text='截图', command=CaptureScreen)
b.pack()
mainloop()

运行效果就不放出来了,大家自行实验啊!

(二)实现部分截图

这里是非常复杂的一个功能。

1、整体逻辑

使用tkinter构建一个root主窗口,当用户点击截图时,创建一个toplevel窗口,然后将该窗口设置成透明模式,然后监听鼠标按键操作,同步实时生成矩形框,当鼠标再次松开的时候,就返回屏幕的位置信息,然后将该rect位置信息传递给ImageGrab进行保存。
代码如下:

im=ImageGrab.grab(rect) #截取目标图像
im.save("second.jpeg",'jpeg')#保存图像

2、相关包引入

import tkinter as tk
import win32gui
import win32api
import time
from PIL import ImageGrab

3、构建类mypartclik

其中核心部分代码如下:

    def run(self):
        while True:
            try:
                self.topwindow.update()
                time.sleep(0.01)
                if self.HWND_t != 0:
                    windowborder = win32gui.GetWindowRect(self.HWND_t)
                    cur_pos = win32api.GetCursorPos()
                    state_left_new = win32api.GetKeyState(0x01)
                    if state_left_new != self.state_left:
                        if windowborder[0] < cur_pos[0] and windowborder[2] > cur_pos[0] and windowborder[1] < cur_pos[1] and windowborder[3] > cur_pos[1]:
                            # print(windowborder)
                            # print(cur_pos)
                            if self.old_item != None:
                                self.canvas.delete(self.old_item)
                            self.drawline((cur_pos[0] - windowborder[0], cur_pos[1] - windowborder[1]))
                    else:
                        if (self.size != ()):
                            # print(self.size)
                            self.myquit()
                        self.old = ()
            except Exception as e:
                # print("error %r" % (e))
                self.myquit()
                break
         
        return self.size

4、调用go函数实现截图

def go():
    app = mypartclik(root)
    size = app.run()
    print(size)
    image = ImageGrab.grab(size)
    # 保存截图文件的路径
    image.save('xx.png')

5、构建测试主程序

if __name__ == '__main__':
    root = tk.Tk()
    root.title('测试截图')
    btn = tk.Button(root,text='截图',command=go)
    btn.pack()
    root.mainloop()

6、实现效果

在这里插入图片描述

备注:这个只是个测试程序,因此界面没有优化。

如果大家对这个截图的代码有兴趣,欢迎留言和点赞,我后续将会分享给大家。

三、如何通过使用python和win32gui进行截图,保存成图片,然后使用百度AI识别的API接口进行图片文字识别

(一)主界面

使用了之前的写字板代码:

from tkinter import *
# 导入ttk
from tkinter import ttk
from tkinter import messagebox as msgbox
from tkinter import filedialog
from collections import OrderedDict

def on_new():
    text.delete(1.0,END)

def on_open():
    localfile = filedialog.askopenfile(title='打开单个文件',
                filetypes=[("文本文件","*.jpg"),("文本文件","*.png"),],# 只处理的文件类型
                initialdir='D:/')
    if(localfile):
        text.delete(1.0,END)
        with open(localfile.name,'r',encoding='utf-8') as f:
            txt = f.readlines()
        text.insert(END,txt)
    else:
        msgbox.showinfo(message=('读取文件异常'))

def on_save():
    localfile = filedialog.asksaveasfile(title='保存文件',
                             filetypes=[("文本文件","*.jpg"),("文本文件","*.png"),],  # 只处理的文件类型
                             initialdir='D:/')
    if(localfile):
        txt = text.get(1.0,END)
        print(txt)
        with open(localfile.name,'w',encoding='utf-8') as f:
            f.write(txt)
    else:
        msgbox.showinfo(message=('读取文件异常'))

def on_exit():
    root.destroy()

root =Tk()
root.title("菜单测试")
root.geometry('400x200')

text =Text(root, height=12, width=60,
            foreground='darkgray',
            font=('微软雅黑',12),
            spacing2=8,# 设置行间距
            spacing3=12)# 设置段间距
text.pack(fill=BOTH,expand=Y)
st ='Tkinter 为菜单提供了 Menu 类,该类既可代表菜单条,也可代表菜单,还可代表上下文菜单(右键菜单)。简单来说,Menu 类就可以搞定所有菜单相关内容。\n'
text.insert(END, st)
scroll =Scrollbar(text, command=text.yview)
scroll.pack(side=RIGHT, fill=Y)
# 设置text2的纵向滚动影响scroll滚动条
text.configure(yscrollcommand=scroll.set)
text.configure(state=NORMAL)

menubar =Menu(root)
root['menu']= menubar
file_menu =Menu(menubar, tearoff=0)
menubar.add_cascade(label='文件', menu=file_menu)
lang_menu =Menu(menubar, tearoff=0)
menubar.add_cascade(label='格式', menu=lang_menu)
file_menu.add_command(label="新建", command=on_new)
file_menu.add_command(label="打开", command=on_open)
file_menu.add_command(label="保存", command=on_save)
file_menu.add_separator()
file_menu.add_command(label="退出", command=on_exit)
lang_menu.add_command(label="字体", command = None)
lang_menu.add_command(label="颜色", command = None)

def popup(event):
    # 在指定位置显示菜单
    popup_menu.post(event.x_root, event.y_root)

def choose(x):
    # 如果用户选择修改字体大小的子菜单项
    if x in my_items[0].keys():
        # 改变字体大小
        text['font'] = ('微软雅黑', my_items[0][x])
    # 如果用户选择修改颜色的子菜单项
    if x in my_items[1].keys():
        # 改变颜色
        text['foreground'] = my_items[1][x]

def handlerAdaptor(fun, **kwds):
    return lambda fun=fun, kwds=kwds: fun(**kwds)

text.bind('<Button-3>', popup)
popup_menu = Menu(text, tearoff=0)
my_items = (OrderedDict([('超大', 16), ('大', 14), ('中', 12),
                              ('小', 10), ('超小', 8)]),
                 OrderedDict([('红色', 'red'), ('绿色', 'green'), ('蓝色', 'blue')]))
i = 0
for k in ['字体大小', '颜色']:
    m = Menu(popup_menu, tearoff=0)
    popup_menu.add_cascade(label=k, menu=m)
    # 遍历OrderedDict的key(默认就是遍历key)
    for im in my_items[i]:
        m.add_command(label=im, command=handlerAdaptor(choose, x=im))
    i += 1

root.mainloop()

在这里插入图片描述

(二)增加截图菜单选项

file_menu.add_command(label="截图", command=on_clip)

(三)定义图片转文字解析并插入到text文本框中

def chag(image):
    client_id = 'xQ5aVGf0iRt20zP3hrV5o8ag'
    client_secret = 'M4qSPdMAE7UwR8V0oytqELQs5CzEXL8I'
    host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%d&client_secret=%d' %(client_id,client_secret)
    response = requests.get(host)

    access_token = '19624546'
    if response:
        json1 = response.json()
        access_token = json1['access_token']

    request_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic"
    # 二进制方式打开图片文件
    # f = open('222.png', 'rb')
    # print(f)
    # img = base64.b64encode(f.read())
    img = base64.b64encode(image)

    params = {"image": img}
    request_url = request_url + "?access_token=" + access_token
    headers = {'content-type': 'application/x-www-form-urlencoded'}
    response = requests.post(request_url, data=params, headers=headers)
    if response:
        text.delete(1.0,END)
        res = response.json()
        for word in res['words_result']:
            print(word['words'])
            text.insert(END,word['words']+'\n')

(四)截图功能

截图不保存成文件,只是存储在内存中,然后直接通过IMAGE转二进制进行文字解析。

def on_clip():
    root.iconify()  # 窗口最小化
    image = mypartclik.go1(root)
    imgByteArr = io.BytesIO()
    image.save(imgByteArr, format='JPEG')
    imgByteArr = imgByteArr.getvalue()
    print(imgByteArr)
    chag(imgByteArr)
    root.deiconify()  # 还原窗口

(五)打开图片功能

打开就是只打开图片文件,并调用上面的解析方法进行解析成为文字,然后输出到文本框中。

def on_open():
    localfile = filedialog.askopenfile(title='打开单个文件',
                filetypes=[("文本文件","*.jpg"),("文本文件","*.png"),],# 只处理的文件类型
                initialdir='D:/')
    if(localfile):
        chag(localfile.name,1)
    else:
        msgbox.showinfo(message=('读取文件异常'))

(六)实现主窗口隐藏和显示

root.iconify()  # 窗口最小化
root.deiconify()  # 还原窗口

(七)完整的主程序代码

from tkinter import *
from tkinter import messagebox as msgbox
from tkinter import filedialog
from collections import OrderedDict
import mypartclik
import requests
import base64
import io

def on_open():
    localfile = filedialog.askopenfile(title='打开单个文件',
                filetypes=[("文本文件","*.jpg"),("文本文件","*.png"),],# 只处理的文件类型
                initialdir='D:/')
    if(localfile):
        chag(localfile.name,1)
    else:
        msgbox.showinfo(message=('读取文件异常'))

def on_save():
    localfile = filedialog.asksaveasfile(title='保存文件',
                             filetypes=[("文本文件","*.txt")],  # 只处理的文件类型
                             initialdir='D:/')
    if(localfile):
        txt = text.get(1.0,END)
        print(txt)
        with open(localfile.name,'w',encoding='utf-8') as f:
            f.write(txt)
    else:
        msgbox.showinfo(message=('读取文件异常'))

def on_clip():
    root.iconify()  # 窗口最小化
    image = mypartclik.go1(root)
    imgByteArr = io.BytesIO()
    image.save(imgByteArr, format='JPEG')
    imgByteArr = imgByteArr.getvalue()
    print(imgByteArr)
    chag(imgByteArr,0)
    root.deiconify()  # 还原窗口


def chag(image,i):
    client_id = '你的keyid'
    client_secret = '你的client_secret_id'
    host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s' %(client_id,client_secret)
    response = requests.get(host)

    access_token = '19624546'
    if response:
        json1 = response.json()
        access_token = json1['access_token']

    request_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic"

    if i==1:
        f = open(image, 'rb')
        print(f)
        img = base64.b64encode(f.read())
    else:
        img = base64.b64encode(image)

    params = {"image": img}
    request_url = request_url + "?access_token=" + access_token
    headers = {'content-type': 'application/x-www-form-urlencoded'}
    response = requests.post(request_url, data=params, headers=headers)
    if response:
        text.delete(1.0,END)
        res = response.json()
        for word in res['words_result']:
            print(word['words'])
            text.insert(END,word['words']+'\n')

def on_exit():
    root.destroy()

root =Tk()
root.title("图像转文字软件V1.0")
root.geometry('600x400')

text =Text(root, height=12, width=60,
            foreground='darkgray',
            font=('微软雅黑',12),
            spacing2=8,# 设置行间距
            spacing3=12)# 设置段间距
text.pack(fill=BOTH,expand=Y)
st ='Tkinter 为菜单提供了 Menu 类,该类既可代表菜单条,也可代表菜单,还可代表上下文菜单(右键菜单)。简单来说,Menu 类就可以搞定所有菜单相关内容。\n'
text.insert(END, st)
scroll =Scrollbar(text, command=text.yview)
scroll.pack(side=RIGHT, fill=Y)
# 设置text2的纵向滚动影响scroll滚动条
text.configure(yscrollcommand=scroll.set)
text.configure(state=NORMAL)

menubar =Menu(root)
root['menu']= menubar
file_menu =Menu(menubar, tearoff=0)
menubar.add_cascade(label='文件', menu=file_menu)
lang_menu =Menu(menubar, tearoff=0)
menubar.add_cascade(label='格式', menu=lang_menu)
file_menu.add_command(label="打开", command=on_open)
file_menu.add_command(label="截图", command=on_clip)
file_menu.add_command(label="保存", command=on_save)
file_menu.add_separator()
file_menu.add_command(label="退出", command=on_exit)
lang_menu.add_command(label="字体", command = None)
lang_menu.add_command(label="颜色", command = None)

def popup(event):
    # 在指定位置显示菜单
    popup_menu.post(event.x_root, event.y_root)

def choose(x):
    # 如果用户选择修改字体大小的子菜单项
    if x in my_items[0].keys():
        # 改变字体大小
        text['font'] = ('微软雅黑', my_items[0][x])
    # 如果用户选择修改颜色的子菜单项
    if x in my_items[1].keys():
        # 改变颜色
        text['foreground'] = my_items[1][x]

def handlerAdaptor(fun, **kwds):
    return lambda fun=fun, kwds=kwds: fun(**kwds)

text.bind('<Button-3>', popup)
popup_menu = Menu(text, tearoff=0)
my_items = (OrderedDict([('超大', 16), ('大', 14), ('中', 12),
                              ('小', 10), ('超小', 8)]),
                 OrderedDict([('红色', 'red'), ('绿色', 'green'), ('蓝色', 'blue')]))
i = 0
for k in ['字体大小', '颜色']:
    m = Menu(popup_menu, tearoff=0)
    popup_menu.add_cascade(label=k, menu=m)
    # 遍历OrderedDict的key(默认就是遍历key)
    for im in my_items[i]:
        m.add_command(label=im, command=handlerAdaptor(choose, x=im))
    i += 1
root.mainloop()

(八)实现效果

1、打开

在这里插入图片描述

2、截图

在这里插入图片描述

四、回过头来说说核心截图类mypartclik

coding不易,且行且珍惜。

这块代码非常核心,而且也技术难度较大,因此,分享给大家,一方面希望能让大家少走弯路、希望大家能喜欢;另一方面,走过路过,点个赞、加个粉,也是彼此前进的动力!感谢!

(一)初始化需要的库

import tkinter as tk
import win32gui
import win32api
import time
from PIL import ImageGrab

(二)初始化相关参数

    def __init__(self, master):
        self.master=master
        self.LINEWIDTH = 3
        self.TRANSCOLOUR = 'gray'
        self.title = 'Virtual whiteboard'
        self.old = ()
        self.HWND_t = 0
        self.old_item = None
        self.size=()
        self.state_left = win32api.GetKeyState(0x01)  # Left button down = 0 or 1. Button up = -127 or -128
        self.initwidget()
        self.initbind()

(三)初始化控件信息

    def initwidget(self):
        self.topwindow = tk.Toplevel(self.master)
        self.topwindow.title(self.title)
        self.topwindow.geometry("+0+0")
        self.topwindow.lift()
        self.topwindow.wm_attributes("-topmost", True)
        self.topwindow.wm_attributes("-transparentcolor", self.TRANSCOLOUR)
        self.topwindow.overrideredirect(1)  # 去除窗口边框

        self.canvas = tk.Canvas(self.topwindow, width=self.master.winfo_screenwidth(), height=self.master.winfo_screenheight())
        self.canvas.pack(fill=tk.BOTH, expand=tk.Y)
        self.canvas.config(cursor='tcross')
        self.canvas.create_rectangle(0, 0, self.master.winfo_screenwidth(), self.master.winfo_screenheight(), fill=self.TRANSCOLOUR,
                                outline=self.TRANSCOLOUR)

(四)初始化事件绑定

    def initbind(self):
        self.topwindow.bind('<Visibility>', self.putOnTop)
        self.topwindow.bind("<Any-KeyPress>", self.myquit)
        self.topwindow.focus()
        win32gui.EnumWindows(self.enumHandler, None)

    def myquit(self,*args):
        self.topwindow.destroy()
        
    def putOnTop(self,event):
        event.widget.unbind('<Visibility>')
        event.widget.update()
        event.widget.lift()
        event.widget.bind('<Visibility>', self.putOnTop, event)

(五)实时画线框的方法

create_rectangle,可以设置边框的样式,自己可以看看我历史发过的博文。

    def drawline(self,data):
        if self.old !=():
            self.old_item = self.canvas.create_rectangle(self.old[0], self.old[1], data[0], data[1], width=self.LINEWIDTH,outline='blue')
            self.size = (self.old[0], self.old[1], data[0], data[1])
            # return self.item
        else:
            # print(old)
           self.old = (data[0], data[1])
           # print(self.old)

(六)主函数run的实现

    def run(self):
        while True:
            try:
                self.topwindow.update()
                time.sleep(0.01)
                if self.HWND_t != 0:
                    windowborder = win32gui.GetWindowRect(self.HWND_t)
                    cur_pos = win32api.GetCursorPos()
                    state_left_new = win32api.GetKeyState(0x01)
                    if state_left_new != self.state_left:
                        if windowborder[0] < cur_pos[0] and windowborder[2] > cur_pos[0] and windowborder[1] < cur_pos[1] and windowborder[3] > cur_pos[1]:
                            # print(windowborder)
                            # print(cur_pos)
                            if self.old_item != None:
                                self.canvas.delete(self.old_item)
                            self.drawline((cur_pos[0] - windowborder[0], cur_pos[1] - windowborder[1]))
                    else:
                        if (self.size != ()):
                            # print(self.size)
                            self.myquit()
                        self.old = ()
            except Exception as e:
                # print("error %r" % (e))
                self.myquit()
                break

        return self.size

五、尾声

终于把这么长的一篇博文写完了,其实里面的技术点非常多。下面一一分析给大家:

1、使用tkinter实现主窗口(用到了toplevel、canvas类),并用到了tkinter的原生的一些函数和方法,技术含量(两颗星),相对来说,还是蛮复杂的。
2、百度的图片转文字的API接口,虽然百度提供了相关操作文档,但是还是需要摸索,(当然,百度已经把最难的部分隐藏了,我们只需要用就好),这个我认为技术含量(1颗星),不难,但是需要摸索。
3、PIL的IMAGE操作函数(如从文件中读取图片、如从剪贴板中读取图片、图片转二进制流等等),相对来说,也是比较复杂,技术含量(两颗星)
4、实现桌面截图功能,核心模块使用了win32gui模块,这个相对来说非常难,我评估技术含量(三颗星)。

当然,上述只是我在实施过程中的个人看法,每个人观点不一样,但是上述的整理主要是为了让大家对整体有所了解和认知,并附上相关的代码,能给大家有所启发。
如果大家觉得这些还不够,我已经把代码上传到CSDN的资源中,码字不易,只求关注和分享。

努力到无能为力,拼搏到感动自己,致我们终将逝去的青春!

让我们一起共勉之!
我是俊哥,欢迎关注我的微信公众号“俊哥随笔”。

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值