写点什么

AI 提取图片里包含的文字信息 - 解决文字无法复制的痛点

作者:DS小龙哥
  • 2022 年 3 月 09 日
  • 本文字数:6343 字

    阅读完需:约 21 分钟

1. 前言

平时工作中编写开发技术文档,或者学生在编写论文时,经常会上网搜索一些参考文献、文档。


比如: 上网搜索相似的内容参考一下或者引用别人的一段文字,有时候看到一篇较好的内容想要保存等等。


这个过程中会发现,很多网站的提供的页面都是不能复制粘贴的,或者直接是图片形式提供,为了方便能获取这些文字,当前就利用华为云提供的 通用文字识别接口,识别图片里的文本内容,方便复制文字。这个功能 QQ 上也集成了,使用很方便,这里利用华为云的接口实现一个与 QQ 类似的功能,截图之后识别图片里包含的文本内容。


这个文字识别接口里不仅仅有通用文字识别功能,还支持很多其他功能:比如身份证、驾驶证、保险单、手写文本、火车票,行驶证.......等等功能。还支持用户自定义识别模板,指定需要识别的关键字段,实现用户特定格式图片的自动识别和结构化提取。


2. 文本识别接口使用介绍

2.1 开通服务

地址: https://console.huaweicloud.com/ocr/?region=cn-north-4#/ocr/overview



这个文字识别服务是按调用次数计费的,每个用户每月有 1000 次的免费调用次数,开通服务后就可以使用。

2.2 接口地址

官网帮助文档: https://support.huaweicloud.com/api-ocr/ocr_03_0042.html


POST https://{endpoint}/v2/{project_id}/ocr/general-text
示例:https://ocr.cn-north-4.myhuaweicloud.com/v2/0e5957be8a00f53c2fa7c0045e4d8fbf/ocr/general-text
请求头:{ "X-Auth-Token": "******", "Content-Type": "application/json;charset=UTF-8"}
请求体:{ "image": ----这是图片的bas64编码}
响应结果:{ "result": { "words_block_count": 13, "words_block_list": [ { "words": "撤,还是不撤?", "location": [ [ 43, 39 ], [ 161, 39 ], [ 161, 60 ], [ 43, 60 ] ] }, { "words": "让我更骄傲的是公司在大灾面前的表现。", "location": [ [ 72, 95 ], [ 332, 95 ], [ 332, 113 ], [ 72, 113 ] ] }, { "words": "2011年3月11日14时46分,日本东北部海域发生里氏9.0级", "location": [ [ 71, 122 ], [ 482, 122 ], [ 482, 142 ], [ 71, 142 ] ] }, { "words": "地震并引发海啸。那一刻,我们正在距离东京100公里的热海开会,", "location": [ [ 41, 149 ], [ 481, 149 ], [ 481, 171 ], [ 41, 171 ] ] }, { "words": "感觉“咚”", "location": [ [ 42, 180 ], [ 114, 180 ], [ 114, 199 ], [ 42, 199 ] ] }, { "words": "地被震了一下。面对地震,", "location": [ [ 115, 178 ], [ 296, 178 ], [ 296, 199 ], [ 115, 199 ] ] }, { "words": "大家都很镇定,", "location": [ [ 300, 179 ], [ 400, 179 ], [ 400, 197 ], [ 300, 197 ] ] }, { "words": "直到看到电", "location": [ [ 405, 179 ], [ 483, 179 ], [ 483, 196 ], [ 405, 196 ] ] }, { "words": "视上触目惊心的画面:15时 25 分,海啸到达陆前高田市海岸;15时", "location": [ [ 41, 206 ], [ 485, 206 ], [ 485, 228 ], [ 41, 228 ] ] }, { "words": "26分,海啸到达陆前高田市中心;15时43分,陆前高田市依稀只能", "location": [ [ 40, 234 ], [ 486, 234 ], [ 486, 258 ], [ 40, 258 ] ] }, { "words": "看到四层高的市府大楼的屋顶,一瞬间,城镇就变成了汪洋……对", "location": [ [ 40, 262 ], [ 487, 262 ], [ 487, 287 ], [ 40, 287 ] ] }, { "words": "我来说,地震跟家常便饭一样,可眼前的灾难比以往任何一次都要", "location": [ [ 40, 292 ], [ 487, 292 ], [ 487, 317 ], [ 40, 317 ] ] }, { "words": "惨烈,完全超出了我的预期。", "location": [ [ 41, 326 ], [ 231, 326 ], [ 231, 345 ], [ 41, 345 ] ] } ], "direction": -1 }}
复制代码


在请求参数里的 X-Auth-Token 参数比较重要,调用华为云的任何 API 接口都需要这个参数,获取方式可以看前面的文章。比如这篇文章: https://support.huaweicloud.com/api-ocr/ocr_03_0005.html

2.3 在线调试接口

地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=OCR&api=RecognizeGeneralText


使用调试接口想体验识别效果,图片的数据支持 base64 编码、http 网络图片地址传入,测试非常方便。


关于获取图片 base64 编码的方式,在文档里也有介绍,直接通过浏览器获取。



3. 实现代码

代码采用 QT 编写的,请求 API 接口实现调用。其他语言方法是一样的。

3.1 实现效果


3.2 核心代码

//解析反馈结果void Widget::replyFinished(QNetworkReply *reply){    QString displayInfo="";    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
//读取所有数据 QByteArray replyData = reply->readAll();
qDebug()<<"状态码:"<<statusCode; qDebug()<<"反馈的数据:"<<QString(replyData);
//更新token if(function_select==3) { displayInfo="token 更新失败."; //读取HTTP响应头的数据 QList<QNetworkReply::RawHeaderPair> RawHeader=reply->rawHeaderPairs(); qDebug()<<"HTTP响应头数量:"<<RawHeader.size(); for(int i=0;i<RawHeader.size();i++) { QString first=RawHeader.at(i).first; QString second=RawHeader.at(i).second; if(first=="X-Subject-Token") { Token=second.toUtf8(); displayInfo="token 更新成功.";
//保存到文件 SaveDataToFile(Token); break; } } QMessageBox::information(this,"提示",displayInfo,QMessageBox::Ok,QMessageBox::Ok); return; }
//判断状态码 if(200 != statusCode) { //解析数据 QJsonParseError json_error; QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error); if(json_error.error == QJsonParseError::NoError) { //判断是否是对象,然后开始解析数据 if(document.isObject()) { QString error_str=""; QJsonObject obj = document.object(); QString error_code; //解析错误代码 if(obj.contains("error_code")) { error_code=obj.take("error_code").toString(); error_str+="错误代码:"; error_str+=error_code; error_str+="\n"; } if(obj.contains("error_msg")) { error_str+="错误消息:"; error_str+=obj.take("error_msg").toString(); error_str+="\n"; }
//显示错误代码 QMessageBox::information(this,"提示",error_str,QMessageBox::Ok,QMessageBox::Ok); } } return; }
//结果返回 if(function_select==1) { //解析数据 QJsonParseError json_error; QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error); if(json_error.error == QJsonParseError::NoError) { //判断是否是对象,然后开始解析数据 if(document.isObject()) { QJsonObject obj = document.object(); QString error_code; //解析 if(obj.contains("result")) { QJsonObject obj1=obj.take("result").toObject();
QString bank_name; QString card_number; QString type;
QString text; if(obj1.contains("bank_name")) { bank_name=obj1.take("bank_name").toString(); } if(obj1.contains("card_number")) { card_number=obj1.take("card_number").toString(); } if(obj1.contains("type")) { type=obj1.take("type").toString(); }

text="发卡行:"+bank_name+"\n"; text+="卡号:"+card_number+"\n"; text+="卡类型:"+type+"\n";
ui->plainTextEdit->setPlainText(text); } } } }
//结果返回 if(function_select==2) { //解析数据 QJsonParseError json_error; QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error); if(json_error.error == QJsonParseError::NoError) { //判断是否是对象,然后开始解析数据 if(document.isObject()) { QJsonObject obj = document.object(); QString error_code; //解析 if(obj.contains("result")) { QJsonObject obj1=obj.take("result").toObject();
int words_block_count; QString text=""; if(obj1.contains("words_block_count")) { words_block_count=obj1.take("words_block_count").toInt();
// text=QString("识别到%1行文本.\n").arg(words_block_count); }
if(obj1.contains("words_block_list")) { QJsonArray array=obj1.take("words_block_list").toArray(); for(int i=0;i<array.size();i++) { QJsonObject obj2=array.at(i).toObject(); if(obj2.contains("words")) { text+=obj2.take("words").toString(); text+="\n"; } } }
ui->plainTextEdit->setPlainText(text); } } } }}
/*功能: 获取token*/void Widget::GetToken(){ //表示获取token function_select=3;
QString requestUrl; QNetworkRequest request;
//设置请求地址 QUrl url;
//获取token请求地址 requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens") .arg(SERVER_ID);
//自己创建的TCP服务器,测试用 //requestUrl="http://10.0.0.6:8080";
//设置数据提交格式 request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));
//构造请求 url.setUrl(requestUrl);
request.setUrl(url);
QString text =QString("{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":" "{\"user\":{\"domain\": {" "\"name\":\"%1\"},\"name\": \"%2\",\"password\": \"%3\"}}}," "\"scope\":{\"project\":{\"name\":\"%4\"}}}}") .arg(MAIN_USER) .arg(IAM_USER) .arg(IAM_PASSWORD) .arg(SERVER_ID);
//发送请求 manager->post(request, text.toUtf8());}
//粘贴图片void Widget::on_pushButton_copy_clicked(){ QClipboard *clipboard = QApplication::clipboard(); const QMimeData *mimeData = clipboard->mimeData(); if (mimeData->hasImage()) { //将图片数据转为QImage QImage img = qvariant_cast<QImage>(mimeData->imageData()); if(!img.isNull()) { ui->widget->SetImage(img); } }}
//获取图片里的文字信息void Widget::getTextInfo(QImage image){ function_select=2; QString requestUrl; QNetworkRequest request;
//存放图片BASE64编码 QString imgData;
//设置请求地址 QUrl url;
//人脸搜索请求地址 requestUrl = QString("https://ocr.%1.myhuaweicloud.com/v2/%2/ocr/general-text") .arg(SERVER_ID) .arg(PROJECT_ID);
//设置数据提交格式 request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));
//将图片进行Base64编码 imgData = QString(toBase64(image)); //编码后的图片大小不超过2M //设置token request.setRawHeader("X-Auth-Token",Token);
//构造请求 url.setUrl(requestUrl); request.setUrl(url);
QString post_param=QString ("{" "\"image\": \"%1\"" "}").arg(imgData);
//发送请求 manager->post(request, post_param.toUtf8());}
复制代码


发布于: 2022 年 03 月 09 日阅读数: 29
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
AI提取图片里包含的文字信息-解决文字无法复制的痛点_3月月更_DS小龙哥_InfoQ写作平台