soarli

记录一次开发Typecho检索图片中文字功能的实战
配置Typecho文本识别能力项目实战笔记,内容原创,转载请注明来源!前言早在去年参与苹果iOS 15内测中体验其...
扫描右侧二维码阅读全文
07
2022/04

记录一次开发Typecho检索图片中文字功能的实战

配置Typecho文本识别能力项目实战笔记,内容原创,转载请注明来源!

前言

早在去年参与苹果iOS 15内测中体验其在全局融入的文字识别能力以及相册场景文本搜索功能时,我就想给自己的博客也实现搜索图片中所含文本的功能。受限于当时的时间和技术条件,这个想法一直处于“挂起”状态。

恰好上个月微信在其新版本也发布了聊天内容中的图片文本搜索功能。刚好寒假期间对PaddlePaddle深度学习框架做了些许研究并在本地部署了OCR项目。择日不如撞日,昨天晚上一口气把这套“前端”逻辑以Python脚本的形式实现了出来。

程序代码已公开,可能存在不足之处与优化空间,希望各位大佬能在看过本文后不吝赐教。

配置

新增Typecho数据表字段:

ALTER TABLE `typecho_contents` ADD `ocr` TEXT NULL AFTER `views`;

修改Typecho搜索逻辑(1189行):

image-20220406045908036

安装用到的Python

pip3 install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

代码

其中xxxxx应换为自己的对应信息:

# -*- coding: utf-8 -*-
# @Author : soarli
# @Date : 2021/4/6

from datetime import datetime
import os
import time
import requests
import re
import pymysql

# <功能选项>
# 是否更新所有信息的OCR结果(False则仅对未OCR的文章执行OCR操作)
updateAll = False
# 处理每张图片请求后的冷却时间(秒,防止因频次限制导致漏掉部分图片)
cool = 5

# <数据库及网站配置相关信息>
# 数据库地址
host = 'xxxxx'
# 数据库用户名
user = 'xxxxx'
# 数据库密码
password = 'xxxxx'
# 数据库名
database = 'xxxxx'
# 博客域名
target = 'https://xxxxx.xxxxx.xxx/'

# <全局变量>
# OCR接口地址
url = "https://xxxxx.xxx/xxxxx"
# 全局请求头
headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36','referer':target}
upload_data = {"name": "1.jpg", "type": "image/jpeg"}

# 维护数据库连接状态
def keepAlive():
    cur = conn.cursor()
    sql = "SELECT ocr FROM `typecho_contents` WHERE cid=1"
    cur.execute(sql)
    
# 输入markdown文本,输出其中引用图片中的文字结果
def getPageOCRText(text):
    pic_list = re.findall(r"\!\[.*\]\((.+)\)",text)
    this_text = ''
    for i in pic_list:
        try:
            img = {'file':requests.get(i,headers=headers).content}
            img_text = requests.post(url, upload_data, files=img)
            this_text += img_text.text
            time.sleep(cool)
            # 维护数据库连接状态
            keepAlive()
            # print(this_text)
        except Exception as e:
            print('Error msg: ' + str(e))
    ocrtext = this_text.replace("\n","").replace('\'','\\\'').replace('\"','\\\"').replace('-','\-')
    if(ocrtext == ''):
        ocrtext = 'none!'
    return ocrtext

# 返回需要进行ocr操作的cid(直接传入下一个函数)
def getUnOCRedCid():
    cur = conn.cursor()
    if(updateAll):
        sql = "SELECT cid FROM `typecho_contents` WHERE 1"
    else:
        sql = "SELECT cid FROM `typecho_contents` WHERE ocr is NULL"
    try:
        cid_list = []
        cur.execute(sql)
        alldata = cur.fetchall()
        for rec in alldata:
            cid_list.append(rec[0])
    except Exception as e:
        print('Error msg: ' + str(e))
    return getText(cid_list)

# 获取cid列表中逐条cid对应的markdown内容并逐条调用ocr获取结果
def getText(cid_list):
    for i in cid_list:
        cur = conn.cursor()
        sql = "SELECT text FROM `typecho_contents` WHERE cid=%s" % (i)
        try:
            cur.execute(sql)
            text = cur.fetchall()[0]
        except Exception as e:
            conn.rollback()
            print('Error msg: ' + str(e))
        # print(type(text))
        print('='*50)
        print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()))
        print(i) # cid
        ocr_text = getPageOCRText(text[0]) # 去换行后的整篇图片OCR文本
        print(ocr_text)
        writeOCRText(ocr_text, i) # 入库

# 将识别到的结果写入数据表的ocr字段
def writeOCRText(text, cid):
    cur = conn.cursor()
    sql = "UPDATE `typecho_contents` SET `ocr` = '%s' WHERE `typecho_contents`.`cid` = %s" % (text,cid)
    try:
        cur.execute(sql)
        conn.commit()
    except Exception as e:
        conn.rollback()
        print('Error msg: ' + str(e))

if __name__ == '__main__':
    print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())+'开始运行')
    # 连接数据库
    conn = pymysql.connect(host=host, user=user, password=password, database=database)
    # 获取需要进行ocr操作的文章cid,其会调用getText,getText又会逐条调用getPageOCRText对markdown text引用的逐张图片进行OCR操作,返回的合成文本又会调用writeOCRText写回数据库的ocr字段
    getUnOCRedCid()
    # 断开数据库
    conn.close()
    print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())+'运行完毕')

执行

经过本地的反复测试与修正,本程序于4月6日凌晨3点左右在我的blog服务器正式运行,并于当天晚上9点左右运行完毕。期间OCR服务器(我的另一台腾讯云)的CPU一直处于满载状态:

image-20220407011711084

数据

看了看博客数据库中ocr字段总字符数:

SELECT sum(length(ocr)) FROM `typecho_contents`

我这三百多篇文章中的所有图片竟然识别出近两百万文字,真的有被吓到:

image-20220406231559986

效果

选了6个关键词,分别截图对比了OCR程序首次执行前后的搜索结果。

识别完成之前:

image-20220406050746873

image-20220406051213307

image-20220406051645427

image-20220406052204395

image-20220406052253788

image-20220406053715026

识别完成之后:

image-20220407012037847

image-20220407012121716

image-20220407012200876

image-20220407012235080

image-20220407012320003

image-20220407012406263

定时

如果数据库中没有文章更新,程序几乎在开始运行瞬间即可运行完毕。

考虑到OCR服务器的业务压力,程序的自动执行周期设置到了每天夜里。

image-20220407004742462

先执行一次,看看效果:

image-20220407004819828

完美搞定!

其他

不算不知道,一算吓一跳!

image-20220407020109415

image-20220407020136588

webp格式下的我的全站图片平均每张仅占40KB的大小!

Google yyds!

补充

04.07 4:25更新

从日志里看到,这篇文章的图像识别工作已经完成:

4月6号写的另一个程序,一并公开出来

用途:识别任意目录下的所有图片中的文字并在该目录下生成识别结果对应的txt文件

import os
import requests
# 填写soarli提供的OCR接口
url = "xxxx"
# 填写图片目录的绝对路径 如C:/Users/xxx/Desktop/imgs
filepath = 'xxxx'
# 常见的图片后缀
imageexts = {'xbm', 'tif', 'pjp', 'svgz', 'jpg', 'jpeg', 'ico', 'tiff', 'gif', 'svg', 'jfif', 'webp', 'png', 'bmp', 'pjpeg', 'avif'}
# 改变工作文件夹
os.chdir(filepath)
# 获取总图片数
pic_num = 0
for name in os.listdir(filepath):
    if(name.split('.')[-1] in imageexts):
        pic_num = pic_num + 1
# 当前个数计数器
i = 0
# 遍历改文件夹所有的文件,并for循环
for name in os.listdir(filepath):
    if(name.split('.')[-1] in imageexts):
        i = i + 1
        rate = round((i-1)/pic_num,4)
        print('='*50)
        print('正在智能识别:"' + name + '"(第'+str(i)+'张,共'+str(pic_num)+'张,已完成占比'+str(rate*100)+'%)')
        print('='*50)
        img = {'file':open(name, 'rb')}
        result = requests.post(url, files=img)
        print(result.text)
        file = open(name+".txt", 'w', encoding='utf-8')
        file.write(result.text)
        file.close()

参考资料:

https://blog.bossdong.cn/411.html

https://blog.csdn.net/qq_44754132/article/details/104418657

https://www.begtut.com/python/ref-requests-post.html

https://blog.csdn.net/weixin_40006779/article/details/109940125

https://blog.csdn.net/weixin_40394560/article/details/116269688

https://blog.csdn.net/itguangzhi/article/details/107501813

https://www.runoob.com/python/att-string-split.html

https://blog.csdn.net/qq_38276669/article/details/81329752

https://www.jianshu.com/p/801750d5a735

https://blog.csdn.net/m0_37696990/article/details/105925940

https://blog.csdn.net/menglinjie/article/details/78539022

https://blog.csdn.net/hhyygg2009/article/details/102844607

https://blog.csdn.net/weixin_50990952/article/details/111796512

https://www.php.cn/python-tutorials-462304.html

最后修改:2022 年 04 月 17 日 06 : 28 PM

发表评论