有时候在读电子文档的过程中,往往会遇到图片形式的文本,想要复制下来,记个笔记甚是不便,需要对照着打字输入,活生生被逼成键盘侠啊…
被逼无奈,何不自己造个轮子,开发一款自己专属的文字识别工具呢,于是我们找到了Matlab App Designer。
玩过 Matlab 的朋友们都知道,构建图形用户界面,Matlab提供了两种工具,一是用
guide
构建,俗称
GUI
,在未来版本中会移除;二是用
App Designer
,俗称
App
,这是官方推荐的,也是以后主流的框架。
今天我们就通过一个简单案例来介绍如何利用
App
设计一个图片文字识别工具。
搭建的方式主要有两种:
-
App设计器:灵活、方便、简单,现代化方法;
-
基于
uifigure
的编程方式:灵活、重构方便,适合构建复杂、大型的图形用户界面,原始社会方法。
这里我们就以编程方式进行创建。
文字识别涉及到光学字符识别(Optical Character Recognition,OCR)技术,如果我们自己造这种底层的轮子,要有高精度的识别率,那估计累得够呛。
幸运的是市场上已经有成熟的工具了,如百度智能云、阿里云、科大讯飞等均提供了API接口,只需借过来用就完事。这里主要以
百度智能云
提供的文字识别API为例。
免费申请文字识别功能后,在控制台可以查看到
API Key
和
Secret Key
,由这两个参数可以获得
access_token
,它是调用
API
接口的必需参数(如下图红色方框所示)。
通过查看文字识别的技术文档,我们可以得到
通用文字识别(标准版)
的请求接口,如下:
HTTP 方法
:
POST
请求URL
:
https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic
URL参数
:属性名:access_token,值:通过
API Key
和
Secret Key
获取的
access_token
,参考“
Access Token获取
”
Header
:属性名:Content-Type,值:application/x-www-form-urlencoded
请求参数
:属性名:image,值:图像数据,base64编码后进行urlencode,要求base64编码和urlencode后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/jpeg/png/bmp格式
返回参数
:属性名:words_result,值:识别结果数组
关于具体的HTTP请求过程接下来会细聊。
Base64是网络上最常见的用于传输8Bit
字节码
的编码方式之一,它是包括小写字母
a-z
、大写字母
A-Z
、数字
0-9
、符号
+
、
/
共64个字符的字符集,等号
=
用来作为后缀用途。任何符号都可以转换成这个字符集中的字符,该转换过程就叫做Base64编码。Base64编码具有不可读性,需要解码后才能阅读。
许多编程语言都提供了现成的Base64编码库函数,Matlab也不例外,大家不妨 help
matlab.net.base64encode
查看细节。
下面提供三种Matlab中的实现方式:
-
Java类—org.apache.commons.codec.binary.Base64 和 matlab.net.base64encode
function base64string = img2base64(fileName)
%IMG2BASE64 Coding an image to base64 file
% INPUTS:
% fileName string, an image file name
% OUTPUTS:
% base64string string, the input image's base64 code
% USAGE:
% >>base64string = img2base64('1.jpg')
% >>base64string = 'xxx'
fid = fopen(fileName, 'rb');
bytes = fread(fid);
fclose(fid);
% -------------------------------------------
% First method
% -------------------------------------------
encoder = org.apache.commons.codec.binary.Base64;
base64string = char(encoder.encode(bytes))';
% -------------------------------------------
% Second method
% -------------------------------------------
% base64string = matlab.net.base64encode(bytes);
catch
disp('The file does not exist!');
base64string = '';
end % end try
end % end function
Matlab中可以直接使用Python,那Python中提供的模块base64
就可以直接使用了,源代码如下:
function base64string = img2base64_(fileName)
%IMG2BASE64 Coding an image to base64 file
% INPUTS:
% fileName string, an image file name
% OUTPUTS:
% base64string string, the input image's base64 code
% USAGE:
% >>base64string = img2base64('1.jpg')
% >>base64string = 'xxx'
f = py.open(fileName, 'rb');
bytes = f.read();
f.close();
temp = char(py.base64.b64encode(bytes));
temp = regexp(temp, '(?<=b'').+(?='')', 'match');
base64string = temp{1};
catch
disp('The file does not exist!');
base64string = '';
end % end try
end % end function
我们可以对如下所示的同一张图片(500 x 500)进行base64编码,比较一下编码速度:
结果:’/9j/4AAQSkZ…AAAAAAD/9k='
- Java类—org.apache.commons.codec.binary.Base64 ⏲ 0.000783 秒
- matlab.net.base64encode ⏲ 0.017589 秒
- Python
base64
模块 ⏲ 0.000709 秒
可以发现使用Java类和Python base64
模块的方法,速度相当,而使用matlab.net.base64encode
速度要慢20多倍,但编码一张大小为500 x 500的图像耗时0.02秒左右,其速度是非常之快了。
综合一下,我们推荐使用org.apache.commons.codec.binary.Base64
类进行base64编码。
识别扫描版pdf文档、视频教程等中的文字时,我们需要对待识别文字所在区域截个图,保存为图像再进行后续识别操作。要实现上述过程,首先需要对屏幕进行截图,Matlab通过借助java.awt.Robot
这个Java类来实现,截屏源代码如下所示:
function imgData = screenSnipping
%screenSnipping Capturel full-screen to an image
% Output:
% imgData, uint8, image data.
% Source code from: https://www.mathworks.com/support/search.html/answers/362358-how-do-i-take-a-screenshot-using-matlab.html?fq=asset_type_name:answer%20category:matlab/audio-and-video&page=1
% Modified: Qingpinwangzi
% Date: Apr 14, 2021.
% Take screen capture
robo = java.awt.Robot;
tk = java.awt.Toolkit.getDefaultToolkit();
rectSize = java.awt.Rectangle(tk.getScreenSize());
cap = robo.createScreenCapture(rectSize);
% Convert to an RGB image
rgb = typecast(cap.getRGB(0, 0, cap.getWidth, cap.getHeight, [], 0, cap.getWidth), 'uint8');
imgData = zeros(cap.getHeight, cap.getWidth, 3, 'uint8');
imgData(:, :, 1) = reshape(rgb(3:4:end), cap.getWidth, [])';
imgData(:, :, 2) = reshape(rgb(2:4:end), cap.getWidth, [])';
imgData(:, :, 3) = reshape(rgb(1:4:end), cap.getWidth, [])';
上述第1节中我们提到过,access_token
是调用API
接口的必需参数。通过阅读技术文档得知,需要API Key
和Secret Key
进行http请求就可以获得,核心代码如下:
url = ['https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=', apiKey, '&client_secret=', secretKey];
res = webread(url, options);
access_token = res.access_token;
有了access_token
我们就可以调用文字识别API进行文字识别了,这里再分享下识别文字的源代码:
function result = getWordsByBaiduOCR(fileName, apiKey, secretKey, accessToken, apiURL, outType)
%GETWORDSBYBAIDUOCR return recognition words
% INPUTS:
% fileName string, an image file name
% apiKey string, the API Key of the application
% secretKey string, The Secret Key of the application
% accessToken string, default is '', get the Access Token by API
% Key and Secret Key.
% apiURL string, such as:
% 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate'
% 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic'
% 'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic'
% outType, 'MultiLine|SingleLine'
% OUTPUTS:
% result []|struct
% USAGE:
% >>result = getWordsByBaiduOCR(fileName, apiKey, secretKey, accessToken, apiURL)
% Date: Mar 18, 2021.
% Author: 清贫王子
options = weboptions('RequestMethod', 'post');
if isempty(outType)
outType = 'MultiLine';
if isempty(accessToken)
url = ['https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=', apiKey, '&client_secret=', secretKey];
res = webread(url, options);
access_token = res.access_token;
access_token = accessToken;
end % end if
url = [apiURL, '?access_token=', access_token];
options.HeaderFields = { 'Content-Type', 'application/x-www-form-urlencoded'};
imgBase64String = img2base64(fileName);
if isempty(imgBase64String)
result = '';
return
end % end if
res = webwrite(url, 'image', imgBase64String, options);
wordsRsult = res.words_result;
data.ocrResultChar = '';
if strcmp(outType, 'SingleLine')
for ii = 1 : size(wordsRsult, 1)
data.ocrResultChar = [data.ocrResultChar, wordsRsult(ii,1).words];
end % end for
elseif strcmp(outType, 'MultiLine')
for ii = 1 : size(wordsRsult, 1)
data.ocrResultChar{ii} = wordsRsult(ii,1).words;
end % end for
result = data.ocrResultChar;
end % end function
简单测试下这个函数,输入下面所示的图片,我们进行图片(截图地址:https://ww2.mathworks.cn/products/matlab/app-designer.html)中的文字识别。
>> result =
1×7 cell 数组
列 1 至 4
{'App设计工具帮助您…'} {'开发专业背景。您只…'} {'面(GUI)设计布局,…'} {'编程。'}
列 5 至 7
{'要共享App,您可以使…'} {' MATLAB Compile…'} {'桌面App或 Web App'}
>> result{1}
ans =
'App设计工具帮助您创建专业的App,同时并不要求软件'
识别结果中共有7个cell
,代表识别了图片中的7行
文字,即1个cell
对应1行
识别的文字,如result{1}
的结果。
以基于uifigure
的编程方式创建APP,我们推荐面向对象(OOP)方法编程,简单起见,这里主要封装一个类来实现所需的功能。当然更标准的做法是利用MVC
等设计模式将界面和逻辑分离,能达到对扩展开放,对修改封闭
的软件设计原则。
我们的功能需求非常简单,主要有以下两个功能:
- 识别已经存在的图像中的文字
- 识别扫描版pdf文档、视频教程等中的文字
实现第1个功能,我们只需要加载图像,然后调用识别函数进行识别,将识别结果显示到文本区域就可以了;而实现第2个功能,首先需要屏幕截图,选取待识别文字所在的区域,存储为图像,后续处理和实现第1个功能的一样。
根据上述描述,我们需要的控件有:加载图像按钮,截图按钮,图像显示器,识别结果显示文本域。另外,需要一个清理按钮,用于清除显示的图像和识别结果;还需要一个设置按钮,用于配置API Key
和Secret Key
。
便于叙述,我们先展示下最终设计的结果,如下图所示:
在设置界面中,需要两个标签和两个文本框,两外需要两个按钮。据此,我们需要的控件都清楚了,接下来让我们一起来创建他们吧!
主要封装一个类来实现所需的功能,我们给这个类起个名:ReadWords
,这个类需要继承matlab.apps.AppBase
,它的属性就是界面中的所有控件,那么这个类看上去应该是这样的:
classdef ReadWords < matlab.apps.AppBase
properties
UIFig matlab.ui.Figure
ContainerForMain matlab.ui.container.GridLayout
ThisTB matlab.ui.container.Toolbar
SnippingToolBtn matlab.ui.container.toolbar.PushTool
ImgLoadToolBtn matlab.ui.container.toolbar.PushTool
SetupToolBtn matlab.ui.container.toolbar.PushTool
CleanToolBtn matlab.ui.container.toolbar.PushTool
ImgShow matlab.ui.control.Image
WordsShowTA matlab.ui.control.TextArea
ContainerForSetup matlab.ui.container.GridLayout
APIKeyText matlab.ui.control.EditField
SecrectKeyText matlab.ui.control.EditField
ResetBtn matlab.ui.control.Button
SaveBtn matlab.ui.control.Button
end % end properties
properties(Hidden, Dependent)
APIKeyVal
SecrectKeyVal
end % end properties
properties(Access = protected)
HasSetup = false
end % end properties
end % end classdef
下面说明下一些重要的属性
公有属性:
- UIFig 必须是
matlab.ui.Figure
类的属性,通过uifigure
构造,这是整个工具的主窗口 - ContainerForMain 必须是
matlab.ui.container.GridLayout
类的属性,通过uigridlayout
构造,这是主窗口的布局容器 - ThisTB 必须是
matlab.ui.container.Toolbar
类的属性,通过uitoolbar
构造,这是工具栏的容器,用于放置SnippingToolBtn
、ImgLoadToolBtn
、SetupToolBtn
、CleanToolBtn
这4个工具按钮 - ImgShow 必须是
matlab.ui.control.Image
类的属性,通过uiimage
构造,用于显示加载或者截图后的图像 - WordsShowTA 必须是
matlab.ui.control.TextArea
类的属性,通过uitextarea
构造,用于显示文字识别结果 - ContainerForSetup 设置界面中的网格容器
- APIKeyText和SecrectKeyText 主要用于输入
APIKey
和SecrectKey
- ResetBtn和SaveBtn两个按钮分别用来实现重置和保存
APIKey
和SecrectKey
从属、隐藏属性:
- APIKeyVal 用于接收APIKeyText中输入的
APIKey
的值 - SecrectKeyVal 用于接收SecrectKeyText中输入的
SecrectKey
的值
受保护属性:
- HasSetup 用于标识是否配置了
APIKey
和SecrectKey
,默认为false
至此,我们设置好了所有的属性,然后进行构造方法、析构方法以及类方法的编写。
加上构造方法、析构方法以及从属属性APIKeyVal
和SecrectKeyVal
的get
方法的代码后看上去是这样的:
classdef ReadWords < matlab.apps.AppBase
properties
UIFig matlab.ui.Figure
ContainerForMain matlab.ui.container.GridLayout
ThisTB matlab.ui.container.Toolbar
SnippingToolBtn matlab.ui.container.toolbar.PushTool
ImgLoadToolBtn matlab.ui.container.toolbar.PushTool
SetupToolBtn matlab.ui.container.toolbar.PushTool
CleanToolBtn matlab.ui.container.toolbar.PushTool
ImgShow matlab.ui.control.Image
WordsShowTA matlab.ui.control.TextArea
ContainerForSetup matlab.ui.container.GridLayout
APIKeyText matlab.ui.control.EditField
SecrectKeyText matlab.ui.control.EditField
ResetBtn matlab.ui.control.Button
SaveBtn matlab.ui.control.Button
end % end properties
properties(Hidden, Dependent)
APIKeyVal
SecrectKeyVal
end % end properties
properties(Access = protected)
HasSetup = false
end % end properties
methods
% --------------------------------------
% % Constructor
% --------------------------------------
function app = ReadWords
% Create UIFigure and components
app.buildApp();
% Register the app with App Designer
registerApp(app, app.UIFig)
if nargout == 0
clear app
end % end Constructor
% --------------------------------------
% % Destructor
% --------------------------------------
% Code that executes before app deletion
function delete(app)
% Delete UIFigure when app is deleted
delete(app.UIFig)
end % end Constructor
% --------------------------------------
% % Get/Set methods
% --------------------------------------
% get.APIKeyVal
function apiKeyVal = get.APIKeyVal(app)
apiKeyVal = app.APIKeyText.Value;
% get.SecrectKeyVal
function secrectKeyVal = get.SecrectKeyVal(app)
secrectKeyVal = app.SecrectKeyText.Value;
end % end methods
end % end classdef
析构方法(Destructor)的写法是固定的,构造方法中的registerApp(app, app.UIFig)
也是固定的,另外的buildApp()
方法就用来创建界面、注册各个控件。
我们将后续的方法都创建为私有方法,添加了buildApp()
方法后的整个ReadWords
类是下面这样的:
classdef ReadWords < matlab.apps.AppBase %% properties UIFig matlab.ui.Figure ContainerForMain matlab.ui.container.GridLayout ThisTB matlab.ui.container.Toolbar SnippingToolBtn matlab.ui.container.toolbar.PushTool ImgLoadToolBtn matlab.ui.container.toolbar.PushTool SetupToolBtn matlab.ui.container.toolbar.PushTool CleanToolBtn matlab.ui.container.toolbar.PushTool ImgShow matlab.ui.control.Image WordsShowTA matlab.ui.control.TextArea ContainerForSetup matlab.ui.container.GridLayout APIKeyText matlab.ui.control.EditField SecrectKeyText matlab.ui.control.EditField ResetBtn matlab.ui.control.Button SaveBtn matlab.ui.control.Button end % end properties %% properties(Hidden, Dependent) APIKeyVal SecrectKeyVal end % end properties %% properties(Access = protected) HasSetup = false end % end properties %% methods % -------------------------------------- % % Constructor % -------------------------------------- function app = ReadWords % Create UIFigure and components app.buildApp(); % Register the app with App Designer registerApp(app, app.UIFig) if nargout == 0 clear app end end % end Constructor % -------------------------------------- % % Destructor % -------------------------------------- % Code that executes before app deletion function delete(app) % Delete UIFigure when app is deleted delete(app.UIFig) end % end Constructor % -------------------------------------- % % Get/Set methods % -------------------------------------- % get.APIKeyVal function apiKeyVal = get.APIKeyVal(app) apiKeyVal = app.APIKeyText.Value; end % get.SecrectKeyVal function secrectKeyVal = get.SecrectKeyVal(app) secrectKeyVal = app.SecrectKeyText.Value; end end % end methods %% methods(Access = private) % buildApp function buildApp(app) % % -------------------------------------- % % Main Figure % -------------------------------------- app.UIFig = uifigure(); app.UIFig.Icon = 'icons/img2text.png'; app.UIFig.Name = 'ReadWords'; app.UIFig.Visible = 'off'; app.UIFig.Position = [app.UIFig.Position(1), app.UIFig.Position(2), 745, 420]; app.UIFig.AutoResizeChildren = 'on'; app.UIFig.Units = 'Normalized'; app.setAutoResize(app.UIFig, true); % -------------------------------------- % % Toolbar % -------------------------------------- app.ThisTB = uitoolbar(app.UIFig); % SetupToolBtn app.SetupToolBtn = uipushtool(app.ThisTB); app.SetupToolBtn.Icon = 'icons/setup.png'; app.SetupToolBtn.Tooltip = 'Setup'; % SnippingToolBtn app.SnippingToolBtn = uipushtool(app.ThisTB); app.SnippingToolBtn.Icon = 'icons/snip.png'; app.SnippingToolBtn.Tooltip = 'Screenshot'; % ImgLoadToolBtn app.ImgLoadToolBtn = uipushtool(app.ThisTB); app.ImgLoadToolBtn.Icon = 'icons/load.png'; app.ImgLoadToolBtn.Tooltip = 'Load image'; % CleanToolBtn app.CleanToolBtn = uipushtool(app.ThisTB); app.CleanToolBtn.Icon = 'icons/clean.png'; app.CleanToolBtn.Tooltip = 'Clean'; % -------------------------------------- % % ContainerForMain % -------------------------------------- app.ContainerForMain = uigridlayout(app.UIFig, [1, 2]); % ContainerForMain imgShowPanel = uipanel(app.ContainerForMain, 'Title', 'Original'); resultShowPanel = uipanel(app.ContainerForMain, 'Title', 'Result'); % ImgShow imgShowPanelLay = uigridlayout(imgShowPanel, [1, 1]); imgShowPanelLay.RowSpacing = 0; imgShowPanelLay.ColumnSpacing = 0; app.ImgShow = uiimage(imgShowPanelLay); % WordsShowTA resultShowPanelLay = uigridlayout(resultShowPanel, [1, 1]); resultShowPanelLay.RowSpacing = 0; resultShowPanelLay.ColumnSpacing = 0; app.WordsShowTA = uitextarea(resultShowPanelLay); app.WordsShowTA.FontSize = 22; % -------------------------------------- % % ContainerForSetup % -------------------------------------- app.ContainerForSetup = uigridlayout(app.UIFig, [4, 3]); app.ContainerForSetup.RowHeight = {22, 22, 22, '1x'}; app.ContainerForSetup.ColumnWidth = {'1x', '1x', '2.5x'}; app.ContainerForSetup.Visible = 'off'; apiKeyLabel = uilabel(app.ContainerForSetup, 'Text', 'API Key'); apiKeyLabel.HorizontalAlignment = 'right'; apiKeyLabel.Layout.Row = 1; apiKeyLabel.Layout.Column = 1; % APIKeyText app.APIKeyText = uieditfield(app.ContainerForSetup); app.APIKeyText.Layout.Row = 1; app.APIKeyText.Layout.Column = 2; secrectKeyLabel = uilabel(app.ContainerForSetup, 'Text', 'Secrect Key'); secrectKeyLabel.HorizontalAlignment = 'right'; secrectKeyLabel.Layout.Row = 2; secrectKeyLabel.Layout.Column = 1; % SecrectKeyText app.SecrectKeyText = uieditfield(app.ContainerForSetup); app.SecrectKeyText.Layout.Row = 2; app.SecrectKeyText.Layout.Column = 2; % ResetBtn app.ResetBtn = uibutton(app.ContainerForSetup, 'Text', 'Reset'); app.ResetBtn.Layout.Row = 3; app.ResetBtn.Layout.Column = 1; % SaveBtn app.SaveBtn = uibutton(app.ContainerForSetup, 'Text', 'Save'); app.SaveBtn.Layout.Row = 3; app.SaveBtn.Layout.Column = 2; % Set visibility for UIFig movegui(app.UIFig, 'center'); app.UIFig.Visible = 'on'; % -------------------------------------- % % RunstartupFcn % -------------------------------------- app.runStartupFcn(@startupFcn); end % end buildApp end % methodsend % end classdef
需要注意的是,工具栏按钮和窗口的图标来源于:https://www.easyicon.cc/。一些常见的图标素材都可以从中免费下载。我们已经将图标下载完毕,需要的朋友可以点击下方链接来下载:
链接:https://pan.baidu.com/s/11kIvt4SX-MhQ2ltEeC18ZA
提取码:5i3k
另外,app.runStartupFcn(@startupFcn);
语句调用的是父类matlab.apps.AppBase
的方法,我们将各个控件的注册任务放在startupFcn
这个方法中完成。这里不妨先注释掉这个语句,直接运行ReadWords.m
便可以显示出我们刚才在buildApp
方法中构造的界面了,动图演示如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oDSZ5OlZ-1632731967843)(https://i.loli.net/2021/09/26/hae3bNmIAcowu8Z.gif)]
可以看到,我们在点击工具栏各个按钮时,没有反应,这是因为到目前为止我们还没有给各个控件注册回调方法,那接下来将会在startupFcn
这个方法中完成各个控件的注册任务,代码如下:
classdef ReadWords < matlab.apps.AppBase %% properties UIFig matlab.ui.Figure ContainerForMain matlab.ui.container.GridLayout ThisTB matlab.ui.container.Toolbar SnippingToolBtn matlab.ui.container.toolbar.PushTool ImgLoadToolBtn matlab.ui.container.toolbar.PushTool SetupToolBtn matlab.ui.container.toolbar.PushTool CleanToolBtn matlab.ui.container.toolbar.PushTool ImgShow matlab.ui.control.Image WordsShowTA matlab.ui.control.TextArea ContainerForSetup matlab.ui.container.GridLayout APIKeyText matlab.ui.control.EditField SecrectKeyText matlab.ui.control.EditField ResetBtn matlab.ui.control.Button SaveBtn matlab.ui.control.Button end % end properties %% properties(Hidden, Dependent) APIKeyVal SecrectKeyVal end % end properties %% properties(Access = protected) HasSetup = false end % end properties %% methods % -------------------------------------- % % Constructor % -------------------------------------- function app = ReadWords % Create UIFigure and components app.buildApp(); % Register the app with App Designer registerApp(app, app.UIFig) if nargout == 0 clear app end end % end Constructor % -------------------------------------- % % Destructor % -------------------------------------- % Code that executes before app deletion function delete(app) % Delete UIFigure when app is deleted delete(app.UIFig) end % end Constructor % -------------------------------------- % % Get/Set methods % -------------------------------------- % get.APIKeyVal function apiKeyVal = get.APIKeyVal(app) apiKeyVal = app.APIKeyText.Value; end % get.SecrectKeyVal function secrectKeyVal = get.SecrectKeyVal(app) secrectKeyVal = app.SecrectKeyText.Value; end end % end methods %% methods(Access = private) % buildApp function buildApp(app) % % -------------------------------------- % % Main Figure % -------------------------------------- app.UIFig = uifigure(); app.UIFig.Icon = 'icons/img2text.png'; app.UIFig.Name = 'ReadWords'; app.UIFig.Visible = 'off'; app.UIFig.Position = [app.UIFig.Position(1), app.UIFig.Position(2), 745, 420]; app.UIFig.AutoResizeChildren = 'on'; app.UIFig.Units = 'Normalized'; app.setAutoResize(app.UIFig, true); % -------------------------------------- % % Toolbar % -------------------------------------- app.ThisTB = uitoolbar(app.UIFig); % SetupToolBtn app.SetupToolBtn = uipushtool(app.ThisTB); app.SetupToolBtn.Icon = 'icons/setup.png'; app.SetupToolBtn.Tooltip = 'Setup'; % SnippingToolBtn app.SnippingToolBtn = uipushtool(app.ThisTB); app.SnippingToolBtn.Icon = 'icons/snip.png'; app.SnippingToolBtn.Tooltip = 'Screenshot'; % ImgLoadToolBtn app.ImgLoadToolBtn = uipushtool(app.ThisTB); app.ImgLoadToolBtn.Icon = 'icons/load.png'; app.ImgLoadToolBtn.Tooltip = 'Load image'; % CleanToolBtn app.CleanToolBtn = uipushtool(app.ThisTB); app.CleanToolBtn.Icon = 'icons/clean.png'; app.CleanToolBtn.Tooltip = 'Clean'; % -------------------------------------- % % ContainerForMain % -------------------------------------- app.ContainerForMain = uigridlayout(app.UIFig, [1, 2]); % ContainerForMain imgShowPanel = uipanel(app.ContainerForMain, 'Title', 'Original'); resultShowPanel = uipanel(app.ContainerForMain, 'Title', 'Result'); % ImgShow imgShowPanelLay = uigridlayout(imgShowPanel, [1, 1]); imgShowPanelLay.RowSpacing = 0; imgShowPanelLay.ColumnSpacing = 0; app.ImgShow = uiimage(imgShowPanelLay); % WordsShowTA resultShowPanelLay = uigridlayout(resultShowPanel, [1, 1]); resultShowPanelLay.RowSpacing = 0; resultShowPanelLay.ColumnSpacing = 0; app.WordsShowTA = uitextarea(resultShowPanelLay); app.WordsShowTA.FontSize = 22; % -------------------------------------- % % ContainerForSetup % -------------------------------------- app.ContainerForSetup = uigridlayout(app.UIFig, [4, 3]); app.ContainerForSetup.RowHeight = {22, 22, 22, '1x'}; app.ContainerForSetup.ColumnWidth = {'1x', '1x', '2.5x'}; app.ContainerForSetup.Visible = 'off'; apiKeyLabel = uilabel(app.ContainerForSetup, 'Text', 'API Key'); apiKeyLabel.HorizontalAlignment = 'right'; apiKeyLabel.Layout.Row = 1; apiKeyLabel.Layout.Column = 1; % APIKeyText app.APIKeyText = uieditfield(app.ContainerForSetup); app.APIKeyText.Layout.Row = 1; app.APIKeyText.Layout.Column = 2; secrectKeyLabel = uilabel(app.ContainerForSetup, 'Text', 'Secrect Key'); secrectKeyLabel.HorizontalAlignment = 'right'; secrectKeyLabel.Layout.Row = 2; secrectKeyLabel.Layout.Column = 1; % SecrectKeyText app.SecrectKeyText = uieditfield(app.ContainerForSetup); app.SecrectKeyText.Layout.Row = 2; app.SecrectKeyText.Layout.Column = 2; % ResetBtn app.ResetBtn = uibutton(app.ContainerForSetup, 'Text', 'Reset'); app.ResetBtn.Layout.Row = 3; app.ResetBtn.Layout.Column = 1; % SaveBtn app.SaveBtn = uibutton(app.ContainerForSetup, 'Text', 'Save'); app.SaveBtn.Layout.Row = 3; app.SaveBtn.Layout.Column = 2; % Set visibility for UIFig movegui(app.UIFig, 'center'); app.UIFig.Visible = 'on'; % -------------------------------------- % % RunstartupFcn % -------------------------------------- app.runStartupFcn(@startupFcn); end % end buildApp % startupFcn function startupFcn(app, ~, ~) % Setup APIKeyText and SecrectKeyText if exist('apikey.mat', 'file') temp = load('apikey.mat'); app.APIKeyText.Value = temp.key.apiKeyVal; app.APIKeyText.Editable = 'off'; app.SecrectKeyText.Value = temp.key.secrectKeyVal; app.SecrectKeyText.Editable = 'off'; end % Register callback app.SnippingToolBtn.ClickedCallback = @app.clickedSnippingToolBtn; app.ImgLoadToolBtn.ClickedCallback = @app.clickedImgLoadToolBtn; app.SetupToolBtn.ClickedCallback = @app.clickedSetupToolBtn; app.CleanToolBtn.ClickedCallback = @app.clickedCleanToolBtn; app.ResetBtn.ButtonPushedFcn = @app.callbackResetBtn; app.SaveBtn.ButtonPushedFcn = @app.callbackSaveBtn; end % end function end % methodsend % end classdef
由此,我们总共为6个按钮注册了6个回调方法,需要都进行实现,不然触发按钮时,该按钮不会做出响应。简单起见,这里我们以实现设置界面中的SaveBtn
的回调方法callbackSaveBtn
为例子来说明。
在没有设置APIKey
或SecrectKey
前,触发SnippingToolBtn
或者ImgLoadToolBtn
会有先进行设置的提示:
callbackSaveBtn
方法实现的逻辑:首先由HasSetup
属性判断是否进行了APIKey
和SecrectKey
的设置(初始默认是false
没有设置),如果没有设置,会提示没有APIKey
或SecrectKey
,则需要输入APIKey
和SecrectKey
的值,然后点击保存按钮,那么后台会将获取到的值存储下来(.mat文件),更新HasSetup
的值为true
,后续我们就不必要再次输入了,要想更换值的话,点击重置按钮重新配置即可;如果进行了设置(HasSetup
属性为true
),直接保存即可。
具体的代码如下:
% --------------------------------------% % Callback functions% --------------------------------------% callbackSaveBtnfunction callbackSaveBtn(app, ~, ~) if ~isempty(app.SecrectKeyText.Value) && ~isempty(app.APIKeyText.Value) key.apiKeyVal = app.APIKeyText.Value; key.secrectKeyVal = app.SecrectKeyText.Value; if exist('apikey.mat', 'file') delete('apikey.mat'); end save('apikey.mat', 'key'); !attrib +s +h apikey.mat uialert(app.UIFig, 'Save successfully!', 'Confirm', 'Icon', 'success'); app.APIKeyText.Editable = 'off'; app.SecrectKeyText.Editable = 'off'; else uialert(app.UIFig, 'API Key or Secrect Key is empty!', 'Confirm', 'Icon', 'warning'); end % end ifend % callbackSaveBtn
实现了保存按钮的功能后,就可以得到如下动图所示的效果了。
其他的回调函数源代码:
% clickedSnippingToolBtnfunction clickedSnippingToolBtn(app, ~, ~) if ~isempty(app.SecrectKeyText.Value) && ~isempty(app.APIKeyText.Value) app.UIFig.Visible = 'off'; pause(0.1); outFileName = 'temp.png'; cropImg(outFileName); !attrib +s +h temp.png % app.ImgShow.ImageSource = imread(outFileName); app.UIFig.Visible = 'on'; % apiURL = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic'; words = getWordsByBaiduOCR(outFileName, app.APIKeyVal, app.SecrectKeyVal, '', apiURL, 'MultiLine'); app.WordsShowTA.Value = words; else msg = {'API Key or Secrect Key is empty!'; 'Please set it up first!'}; uialert(app.UIFig, msg, 'Confirm', 'Icon', 'warning'); endend % end clickedSnippingToolBtn% clickedImgLoadToolBtnfunction clickedImgLoadToolBtn(app, ~, ~) if ~isempty(app.SecrectKeyText.Value) && ~isempty(app.APIKeyText.Value) [fName, fPath] = uigetfile({'*.png'; '*.jpg'; '*.bmp'; '*.tif'}, 'Open image'); if ~isequal(any([fName, fPath]), 0) img = imread(strcat(fPath, fName)); outFileName = 'temp.png'; if exist(outFileName, 'file') delete(outFileName) end imwrite(img, outFileName); !attrib +s +h temp.png % app.ImgShow.ImageSource = imread(outFileName); app.UIFig.Visible = 'on'; % apiURL = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic'; words = getWordsByBaiduOCR(outFileName, app.APIKeyVal, app.SecrectKeyVal, '', apiURL, 'MultiLine'); app.WordsShowTA.Value = words; else return end % end if else % end if msg = {'API Key or Secrect Key is empty!'; 'Please set it up first!'}; uialert(app.UIFig, msg, 'Confirm', 'Icon', 'warning'); endend % end clickedImgLoadToolBtn% clickedSetupToolBtnfunction clickedSetupToolBtn(app, ~, ~) if ~app.HasSetup app.ContainerForMain.Visible = 'off'; app.ContainerForSetup.Visible = 'on'; app.HasSetup = true; else app.ContainerForMain.Visible = 'on'; app.ContainerForSetup.Visible = 'off'; app.HasSetup = false; endend % end clickedSetupToolBtn% clickedCleanToolBtnfunction clickedCleanToolBtn(app, ~, ~) app.WordsShowTA.Value = ''; app.ImgShow.ImageSource = '';end % end clickedCleanToolBtn% callbackResetBtnfunction callbackResetBtn(app, ~, ~) app.APIKeyText.Value = ''; app.APIKeyText.Editable = 'on'; app.SecrectKeyText.Value = ''; app.SecrectKeyText.Editable = 'on';end % callbackResetBtn
现在让我们来测试一下搭建的图像识别工具吧,比如,某麻子同学是一名研究生,在阅读那种扫描版的pdf文献时,想把其中的一段语句复制下来用于记录笔记或者做PPT用,这时我们的工具就派上用场了:
刹那间,某麻子同学得到了想要的结果,露出了久违的幸福的一笑!
至此,我们完成了一个比较完整的文字识别工具!希望您喜欢,并且可以从中获得有用的东西。
本文完整代码,请在gh内回复“文字识别工具”进行下载。
【往期推荐】
文章目录一、前言二、预备1. API接口2. 图像的Base64编码3. 屏幕截图4. 调用百度API识别文字三、工具搭建1. 功能需求2. 实现细节四、使用演示五、结语一、前言有时候在读电子文档的过程中,往往会遇到图片形式的文本,想要复制下来,记个笔记甚是不便,需要对照着打字输入,活生生被逼成键盘侠啊…被逼无奈,何不自己造个轮子,开发一款自己专属的文字识别工具呢,于是我们找到了Matlab App Designer。玩过 Matlab 的朋友们都知道,构建图形用户界面,Matlab提供了两种工具
现在微信小程序越来越火,用户不需下载安装app即可体验产品,产品推广和运营也比较方便,这些优点使得越来越多的企业选择开发微信小程序,所以会写小程序也成为前端比较重要的技能,今天就来教大家怎么入门微信小程序。点击根据指引填写信息和提交相应的资料,就可以拥有自己的小程序帐号。登录,我们可以在菜单“设置”-“开发设置”看到小程序的AppID了。前往,根据自己的操作系统下载对应的安装包进行安装,有关开发者工具更详细的介绍可以查看。先创建一个空文件夹然后打开开发者工具,新建项目,按需选择启动模板,这里选择的是建立普通快速启动模板点击确定后,你的小程序就创建好啦。默认的项目结构关于默认项目结构里的介绍,我
最近也是在研究MATLAB App Designer,开这样的一个系列主要是为了记录我在研究App Designer时遇到的一部分问题的解决,和一部分有趣的想法,每一篇文章会展示如何将一个项目mlapp化。
mlapp化的大部分都是以前做的小项目,因此各个组件的属性便可以直接复制,这一部分不详细解说,以下是将像素化程序使用App Designer编写过程中可能会遇到的问题。
入门实战第一弹——像素画绘制App
目录1.如何在App启动时,让坐标区显示图像2.如何在App启动时,为变量赋值,如何设置全局变量。
二、 APP Designer
最左侧,有 最近使用的App 和 打开 这两个内容,方便我们直接打开之前建立的APP文件或者本地文件中的APP。
中间部分,有 新建(空白、两栏和三栏式)、示例(快速入门和编程任务) 两大部分。
示例学习1
1.响应数值输入
在TextArea中刷新日志,有以下几个作用:
1)可以打印过程数据,方便观看
2)很重要的一点,就是可以将你的控件操作日志输出(打印)在文本区域中,这样方便操作者观察是否进行或者执行了“哪些操作”
2.手把手教你怎么搞。(代码+举例说明)
搭建好架子后,比如架子如下:
然后3步走即可。
1)设属性
2)写“刷日志的函数”
今天要做的是一个屏幕颜色提取器,为了友好的对待没使用过MATLAB APP设计工具的人,这里做一个简化版的颜色提取器的教程,希望零基础的人能够根据教程一步一步的将功能实现!
既然是零基础。。。。我们首先就要从怎么打开APP designer开始:
我们如果是新创建app程序(mlapp后缀文件),可以通过 新建→App来创建: