圖片分類(WEB)
# FastWeb之圖片分類(WEB)
# 1. 說明
通過ml5js的影象識別分類功能,輔助實現影象識別的相關功能。ml5js (opens new window)是基於tensorflow.js的深度學習框架,它安裝簡便,API簡單易懂,可直接在瀏覽器裡面運行,可作為輕量化的智能識別工具。此示例需要線上呼叫數據模型,且會呼叫攝像頭的相關許可權,故此處建議使用https來訪問FastWeb中的此示例。
通過本範例學習,可以掌握ml5js的呼叫方式,並通過攝像頭拍照以及圖片上傳兩種方式實現影象分類的相關功能。
使用此示例前,請先確保訪問FastWeb的客戶端能夠訪問網際網路的資源,此處使用的是線上的數據模型資源。
# 2. 設計明細
開啟FastWeb設計器,分別加入下插圖之控制元件。或者點選左上角的[匯入]
選擇模板檔案來打開對應模板。
1:TUgLabel元件,控制元件名稱為UgLabel01
。
2:TUgURLFrame元件,控制元件名稱為UgHTMLFrame01
,此控制元件用於運行示例
3:TUgFSButton元件,控制元件名稱為UgFSButton01
。
4:TUgFSButton元件,控制元件名稱為UgFSButton02
。
5:TUgFSButton元件,控制元件名稱為UgFSButton03
。
TUgFileUploadButton元件,控制元件名稱為UgFileUploadButton01
。
6:TUgFSButton元件,控制元件名稱為UgFSButton04
。
7:TUgComboBox元件,控制元件名稱為UgComboBox01
。
8:TUgMemo元件,控制元件名稱為UgMemo01
。
9:TUgFSToast元件,控制元件名稱為UgFSToast01
。
10:TUgIconClsList元件,控制元件名稱為UgIconClsLit01
。
11:TUgTimer元件,控制元件名稱為UgTimer01
。
TUgImage元件,控制元件名稱為UgImage02
,此控制元件與UgWebCam01
重疊,用於顯示拍照的相片。
TUgWebCam元件,控制元件名稱為UgWebCam01
。此控制元件用於呼叫相機進行拍攝,但是控制元件不顯示。
TUgSynTaxEditor元件,控制元件名稱為UgSynTextEditor01
,此控制元件用於放置運行的HTML文字,但是控制元件不顯示。
UgWebRunFrame屬性設定
Height
:設定頁面高度=600
。Width
:設定頁面寬度=800
。
1:UgLabel01屬性設定
Align
:設定控制元件的對齊方式,設定為alTop
。Alignment
:設定控制元件中的文字的對齊方式,設定為taCenter
。AutoSize
:設定控制元件是否跟隨文字大小變化控制元件大小,設定為False
。Caption
:設定顯示的文字內容,設定為圖片分類
。Font
:設定字型,點選√
打開字型設定界面。
2:UgURLFrame01屬性設定
Anchors
:設定錨點資訊,設定akLeft
、akTop
、akRight
、akBottom
為True
。
UgWebCam01屬性設定
Align
:其位於UgURLFrame01控制元件中,設定錨點資訊,設定akLeft
、akTop
、akRight
、akBottom
為True
。
7:UgIconClsList10屬性設定
雙擊控制元件,打開編輯器,依次新增以下幾項的圖示,方括號中的內容為需要輸入的名稱。
3:UgFSButton01屬性設定
Anchors
:設定錨點資訊,設定akLeft
、akTop
為True
,其餘選項為False
。Caption
:設定顯示的文字內容,設定為開啟相機
。Images
:設定顯示圖示的控制元件,設定為UGIconClsList01
。ImageIndex
:設定顯示的圖示的序號,設定為0
。
4:UgFSButton02屬性設定
Anchors
:設定錨點資訊,設定akLeft
、akTop
為True
,其餘選項為False
。Caption
:設定顯示的文字內容,設定為拍照識別
。Enabled
:設定控制元件是否其餘,設定為False
。Images
:設定顯示圖示的控制元件,設定為UGIconClsList01
。ImageIndex
:設定顯示的圖示的序號,設定為1
。
5:UgFSButton03屬性設定
Anchors
:設定錨點資訊,設定akLeft
、akTop
為True
,其餘選項為False
。Caption
:設定顯示的文字內容,設定為選擇圖片識別
。Images
:設定顯示圖示的控制元件,設定為UGIconClsList01
。ImageIndex
:設定顯示的圖示的序號,設定為2
。
6:UgFSButton04屬性設定
Anchors
:設定錨點資訊,設定akLeft
、akTop
為True
,其餘選項為False
。Caption
:設定顯示的文字內容,設定為關閉相機
。Images
:設定顯示圖示的控制元件,設定為UGIconClsList01
。ImageIndex
:設定顯示的圖示的序號,設定為3
。
7:UgComboBox01屬性設定
FieldLabel
:設定標籤資訊,設定為攝像機列表
。FieldLabelWidth
:設定標籤顯示區域的寬度,設定為120
。
8:UgMemo01屬性設定
Anchors
:設定錨點資訊,設定akLeft
、akRight
、akTop
為True
,其餘選項為False
。
11UgTimer01屬性設定
Enabled
:設定是否啟用定時器,設定為False
。Interval
:設定觸發的時間間隔,設定為500
。
UgSyntaxEditor01屬性設定
Lines
:設定其中顯示的文字資訊。打開編輯器,填寫的內容如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script> function zoomImage(imageId, imageBoxWidth, imageBoxHeight) { var image = document.getElementById(imageId); //計算出圖片容器的寬高比 var imageBoxWidthHeightScale = imageBoxWidth / imageBoxHeight; //計算出實際圖片的寬高比 var imageWidthHeightScale = image.width / image.height; var widthScale = image.width / imageBoxWidth; var heightScale = image.height / imageBoxHeight; var maxScale = Math.max(widthScale, heightScale); if (maxScale < 1) { //圖片寬高都比盒子小時不對圖片操作 return null; } else { //圖片的寬或者高大於盒子大小時 if (imageBoxWidthHeightScale > imageWidthHeightScale) { //盒子相對圖片來說更"扁",此時控制圖片的高度 image.height *= heightScale > 1 ? 1 / heightScale : heightScale; } else { //圖片相對盒子來說更"扁",此時控制圖片的寬度 image.width *= widthScale > 1 ? 1 / widthScale : widthScale; } } } </script> <script src="library/js/ml5-library/p5.js/p5.min.js"></script> <script src="library/js/ml5-library/addons/p5.dom.min.js"></script> <script src="library/js/ml5-library/ml5.min.js"></script> <title>圖片分類器</title> <style type="text/css"> *{ margin: 0; padding: 0; } html,body{ height: 100%; width: 100%; margin scrollbar-width: none; /* firefox */ -ms-overflow-style: none; /* IE 10+ */ overflow-x: hidden; overflow-y: hidden; } #result{ width: 100%; height: 100%; display: table; /**key point**/ text-align: center; } .spaceSpan { height: 100%; display: table-cell; /**key point**/ vertical-align: middle; /**key point**/ } #img{ vertical-align: middle; } #button{ display: none; } </style > </head> <body onresize="zoomImage('img',document.body.clientWidth,document.body.clientHeight)"> <div id="result"> <span class="spaceSpan"> <img id="img" src="library/js/ml5-library/assets/bird.jpg" onload="zoomImage('img',document.body.clientWidth,document.body.clientHeight)"/> </span> </div> <input id="button" type="button" onclick="classify()" value="分類" /> <br /> </body> <script> var classifier = ml5.imageClassifier('MobileNet'); function classify() { classifier.classify(document.getElementById('img'), gotResult); function gotResult(error, results) { if (error) { console.error(error); } console.log(results); var URLFrame = top.Ext.getCmp("_URLFrame"); var params=[]; params = ["result=" + results[0].label, "probability=" + results[0].confidence.toFixed(4)]; top.ajaxRequest(URLFrame, 'OutputData', params); //createDiv(`Label: ${results[0].label}`); //createDiv(`Confidence: ${nf(results[0].confidence, 0, 2)}`); } } </script> </html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97Visible
:設定控制元件是否可見,設定為False
。
UgFileUploadButton01屬性設定
Anchors
:設定控制元件的錨點,設定akLeft
與akBottom
為True
,其餘設定為False
。ButtonVisible
:設定按鈕是否處於可見狀態,設定為False
。Filter
:設定過濾器,設定常用的圖片後綴格式,設定為*.jpg;*.jpeg;*.gif;*.tif
。
# 3. 程式設計
點選程式設計界面右下角的按鈕,切換至單元選擇界面,勾選需要使用的單元。該程式的程式不需要引用單元。
# 3.1. 程式初始設定
定義程式過程,用於設定兩種狀態下顯示的按鈕序列差異。
//JScript
function RefState(AStatus)
//重新整理按鈕的狀態
//1:表示為處於拍攝模式
//2:表示目前處於圖片處理模式
{
UgWebCam01.CameraName = UgComboBox01.Text;
if (AStatus == 0)
{
UgWebCam01.Visible = True;
UgWebCam01.StartCamera;
UgFSButton01.Enabled = False;
UgFSButton02.Enabled = True;
UgFileUpLoadButton01.Enabled = False;
UgFSButton04.Enabled = True;
}
if (AStatus == 1)
{
UgWebCam01.Visible = False;
UgWebCam01.StopCamera;
UgFSButton01.Enabled = True;
UgFSButton02.Enabled = False;
UgFileUpLoadButton01.Enabled = True;
UgFSButton04.Enabled = False;
}
}
{
Self.OnAfterRunScript = &UgWebRunFrameOnAfterRunScript;
UgWebCam01.OnCameraList = &UgWebCam01OnCameraList;
UgWebCam01.OnError = &UgWebCam01OnError;
UgWebCam01.OnSnap = &UgWebCam01OnSnap;
UgFSButton01.OnClick = &UgFSButton01OnClick;
UgFSButton04.OnClick = &UgFSButton04OnClick;
UgFSButton02.OnClick = &UgFSButton02OnClick;
UgURLFrame01.OnAjaxEvent = &UgURLFrame01OnAjaxEvent;
UgFileUploadButton01.OnCompleted = &UgFileUpLoadButton01OnCompleted;
UgTimer01.OnTimer = &UgTimer01OnTimer;
UgURLFrame01.ClientEvents.UniEvents.Values["beforeInit"] = "function beforeInit(sender, config){config.id = '_URLFrame';}";
RefState(1);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//PasScript
procedure RefState(AStatus: Integer);
//重新整理按鈕的狀態
//1:表示為處於拍攝模式
//2:表示目前處於圖片處理模式
Begin
UgWebCam01.CameraName := UgComboBox01.Text;
if AStatus = 0 Then
Begin
UgWebCam01.Visible := True;
UgWebCam01.StartCamera;
UgFSButton01.Enabled := False;
UgFSButton02.Enabled := True;
UgFileUpLoadButton01.Enabled := False;
UgFSButton04.Enabled := True;
End;
if AStatus = 1 Then
Begin
UgWebCam01.Visible := False;
UgWebCam01.StopCamera;
UgFSButton01.Enabled := True;
UgFSButton02.Enabled := False;
UgFileUpLoadButton01.Enabled := True;
UgFSButton04.Enabled := False;
End;
End;
//初始化界面,更新設定
Begin
UgURLFrame01.ClientEvents.UniEvents.Values['beforeInit'] := 'function beforeInit(sender, config){config.id = ''_URLFrame'';}';
RefState(1);
End.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Make sure to add code blocks to your code group
# 3.2. 事件設定
- UgWebRunFrame-OnAfterRunScript事件
初始化運行狀態,設定網頁顯示的內容。
//JScript
function UgWebRunFrameOnAfterRunScript(sender)
{
UGMM.LC(Self);
UgURLFrame01.HTML = UgSyntaxEditor01.Lines;
}
2
3
4
5
6
//PasScript
procedure UgWebRunFrameOnAfterRunScript(const sender: tobject);
begin
UGMM.LC(Self);
UgURLFrame01.HTML := UgSyntaxEditor01.Lines;
end;
2
3
4
5
6
// Make sure to add code blocks to your code group
- UgWebCam01-OnCameraList事件
此事件用於在啟動時獲取目前可用的攝像機的設備列表。
//JScript
function UgWebCam01onCameraList(sender)
//獲取攝像機列表
{
UgComboBox01.Items.Clear;
for (var i = 0; i <= UgWebCam01.CameraList.Count - 1; i++ )
UgComboBox01.Items.Add(UgWebCam01.CameraList.Names[i]);
if (UgComboBox01.Items.Count > 0)
UgComboBox01.ItemIndex= 0;
}
2
3
4
5
6
7
8
9
10
//PasScript
procedure UgWebCam01onCameraList(sender: tobject);
//獲取攝像機列表
var
i: Integer;
begin
UgComboBox01.Items.Clear;
for i := 0 to UgWebCam01.CameraList.Count-1 do
UgComboBox01.Items.Add(UgWebCam01.CameraList.Names[i]);
if UgComboBox01.Items.Count>0 then
UgComboBox01.ItemIndex:= 0;
end;
2
3
4
5
6
7
8
9
10
11
12
// Make sure to add code blocks to your code group
- UgWebCam01-OnError事件
當相機在運行的過程中出現錯誤時觸發事件,該事件中包含了錯誤的相關資訊。
//JScript
function UgWebCam01onError(sender,error)
//相機啟動出錯時的提示資訊
{
UgFSToast01.Info("",error);
}
2
3
4
5
6
//PasScript
procedure UgWebCam01onError(sender: tobject;const error: string);
//相機啟動出錯時的提示資訊
begin
UgFSToast01.Info('',error);
end;
2
3
4
5
6
// Make sure to add code blocks to your code group
- UgWebCam01-OnSnap事件
當照相機在執行照相操作后觸發此事件,將照相機拍攝獲得的圖片儲存后發送以進行識別。
//JScript
function UgWebCam01onSnap(sender,filename)
//拍照的結果
{
RefState(1);
var DestFolder=UGSM.StartPath + "UploadFolder\\";
var RemoteFileName = UGMM.CreateGuid + ".jpg";
var DestName=DestFolder+RemoteFileName;
//上傳檔案
CopyFile(filename, DestName, False);
var imgPath = "UploadFolder/"+RemoteFileName;
UniSession.AddJS(UgURLFrame01.JSName+".iframe.contentWindow.document.getElementById('img').setAttribute('src','"+imgPath+"')");
UgTimer01.Enabled = True;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
//PasScript
procedure UgWebCam01onSnap(sender: tobject;const filename: string);
//拍照的結果
var
DestName : string;
DestFolder : string;
RemoteFileName: String;
imgPath: String;
begin
RefState(1);
DestFolder:=UGSM.StartPath+'UploadFolder\';
RemoteFileName := UGMM.CreateGuid + '.jpg';
DestName:=DestFolder+RemoteFileName;
//上傳檔案
CopyFile(filename, DestName, False);
imgPath := 'UploadFolder/'+RemoteFileName;
UniSession.AddJS(UgURLFrame01.JSName+'.iframe.contentWindow.document.getElementById(''img'').setAttribute(''src'','''+imgPath+''')');
UgTimer01.Enabled := True;
end;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Make sure to add code blocks to your code group
- 3:UgFSButton01-OnClick事件
點選開啟相機按鈕,打開攝像機。
//JScript
function UgFSButton01OnClick(sender)
//開啟相機
{
RefState(0);
}
2
3
4
5
6
//PasScript
procedure UgFSButton01OnClick(sender: tobject);
//開啟相機
begin
RefState(0);
end;
2
3
4
5
6
// Make sure to add code blocks to your code group
- 4:UgFSButton02-OnClick事件
點選拍照按鈕,以執行拍照。
//JScript
function UgFSButton02OnClick(sender)
//拍攝照片
{
UgWebCam01.SnapPicture;
}
2
3
4
5
6
//PasScript
procedure UgFSButton02OnClick(sender: tobject);
//拍攝照片
begin
UgWebCam01.SnapPicture;
end;
2
3
4
5
6
// Make sure to add code blocks to your code group
- 6:UgFSButton04-OnClick事件
點選關閉相機按鈕,關閉攝像機。
//JScript
function UgFSButton04OnClick(sender)
//關閉相機
{
RefState(1);
}
2
3
4
5
6
//PasScript
procedure UgFSButton04OnClick(sender: tobject);
//關閉相機
begin
RefState(1);
end;
2
3
4
5
6
// Make sure to add code blocks to your code group
- 5:UgFileUploadButton01-OnCompleted事件
當上傳圖片后,進行判斷識別。
//JScript
function UgFileUploadButton01OnCompleted(sender,astream)
//上傳圖片
{
//遠端檔名
var RemoteFileName = ExtractFileName(varToStr(TUgFileUploadButton(sender).FileName));
//遠端檔案路徑
var DestFolder=UGSM.StartPath+"UploadFolder\\";
var DestName= DestFolder + RemoteFileName;
//上傳檔案
CopyFile(UGCM.GetFileStreamFileName(AStream), DestName, False);
var imgPath = "UploadFolder/"+RemoteFileName;
UniSession.AddJS(UgURLFrame01.JSName+".iframe.contentWindow.document.getElementById('img').setAttribute('src','"+imgPath+"')");
UgTimer01.Enabled = True;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//PasScript
procedure UgFileUploadButton01OnCompleted(sender: tobject;astream: tfilestream);
//上傳圖片
var
DestName : string;
DestFolder : string;
RemoteFileName:String;
imgPath:String;
begin
//遠端檔名
RemoteFileName := ExtractFileName(VarToStr(TUgFileUploadButton(sender).FileName));
//遠端檔案路徑
DestFolder:=UGSM.StartPath+'UploadFolder\';
DestName:=DestFolder+RemoteFileName;
//上傳檔案
CopyFile(UGCM.GetFileStreamFileName(AStream), DestName, False);
imgPath := 'UploadFolder/'+RemoteFileName;
UniSession.AddJS(UgURLFrame01.JSName+'.iframe.contentWindow.document.getElementById(''img'').setAttribute(''src'','''+imgPath+''')');
UgTimer01.Enabled := True;
end;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Make sure to add code blocks to your code group
- 2:UgURLFrame01-OnAjaxEvent事件
當識別到Ajax事件時觸發,執行相關的程式,輸出識別的結果資訊。
//JScript
function UgURLFrame01OnAjaxEvent(sender,eventname,params)
//識別結果輸出
{
if (eventname == "OutputData")
{
UgMemo01.Lines.Add(UGMM.LT("分類項:")+params.Values["result"]);
UgMemo01.Lines.Add(UGMM.LT("可信度:")+params.Values["probability"]);
}
}
2
3
4
5
6
7
8
9
10
//PasScript
procedure UgURLFrame01OnAjaxEvent(sender: tcomponent;eventname: string;params: tunistrings);
//識別結果輸出
begin
if eventname = 'OutputData' Then
Begin
UgMemo01.Lines.Add(UGMM.LT('分類項:')+params.Values['result']);
UgMemo01.Lines.Add(UGMM.LT('可信度:')+params.Values['probability']);
end;
end;
2
3
4
5
6
7
8
9
10
// Make sure to add code blocks to your code group
- 11:UgTimer01-OnTimer事件
定時器觸發事件,用於執行圖片分類的API。
//JScript
function UgTimer01OnTimer(sender)
//延遲觸發分類識別
{
UniSession.AddJS(UgURLFrame01.JSName+".iframe.contentWindow.classify()");
UgTimer01.Enabled = False;
}
2
3
4
5
6
7
//PasScript
procedure UgTimer01OnTimer(sender: tobject);
//延遲觸發分類識別
begin
UniSession.AddJS(UgURLFrame01.JSName+'.iframe.contentWindow.classify()');
UgTimer01.Enabled := False;
end;
2
3
4
5
6
7
// Make sure to add code blocks to your code group
# 4. 運行結果
使用滑鼠在FastWeb功能表,點選[儲存至資料庫]
按鈕,將其儲存至資料庫,點選[除錯運行]
確認能夠正常打開。
點選[選擇圖片識別]
按鈕,選擇圖片以打開,右側會顯示識別分類的結果。