共用方式為


快速入門:使用自訂視覺用戶端程式庫建立物件偵測專案

開始使用適用於 .NET 的自訂視覺用戶端程式庫。 請遵循下列步驟來安裝套件,並試用建立物件偵測模型的程式碼範例。 您將建立專案、新增標記、在範例影像上將專案定型,並使用專案的預測端點 URL 以程式設計方式加以測試。 請使用此範例作為自行建置影像辨識應用程式的範本。

附註

如果您想要在「不用」撰寫程式碼的情況下,建立和訓練物件偵測模型,請改為參閱以瀏覽器為基礎的指引

參考文件 | 程式庫原始程式碼 (訓練)(預測) | 套件 (NuGet) (訓練)(預測) | 範例

先決條件

建立環境變數

在此範例中,您會在執行應用程式的本機電腦上將認證寫入環境變數。

移至 Azure 入口網站。 如果您在 [必要條件] 區段中建立的自訂視覺資源成功部署,請選取 [後續步驟] 底下的 [前往資源] 按鈕。 您可以在 [資源管理] 底下的 [金鑰和端點] 頁面中找到金鑰和端點。 您需要取得訓練資源和預測資源的金鑰以及 API 端點。

您可以在 Azure 入口網站中預測資源的 [屬性] 索引標籤上找到預測資源識別碼,該識別碼名為 [資源識別碼]

秘訣

您也可以使用 https://www.customvision.ai 來取得這些值。 登入之後,請選取右上方的 [設定] 圖示。 在 [設定] 頁面上,您可以檢視所有金鑰、資源識別碼和端點。

若要設定環境變數,請開啟主控台視窗,然後遵循作業系統和開發環境的指示進行。

  • 若要設定 VISION_TRAINING KEY 環境變數,請以您的訓練資源的其中一個金鑰取代 <your-training-key>
  • 若要設定 VISION_TRAINING_ENDPOINT 環境變數,請將 <your-training-endpoint> 取代為您的訓練資源的端點。
  • 若要設定 VISION_PREDICTION_KEY 環境變數,請以您的預測資源的其中一個金鑰取代 <your-prediction-key>
  • 若要設定 VISION_PREDICTION_ENDPOINT 環境變數,請將 <your-prediction-endpoint> 取代為您的預測資源的端點。
  • 若要設定 VISION_PREDICTION_RESOURCE_ID 環境變數,請將 <your-resource-id> 取代為您的預測資源的資源識別碼。

重要事項

我們建議使用適用於 Azure 資源的受控識別搭配 Microsoft Entra ID 驗證,以避免使用在雲端執行的應用程式儲存認證。

請謹慎使用 API 金鑰。 請勿在程式碼中直接包含 API 金鑰,且切勿公開張貼金鑰。 如果使用 API 金鑰,請將這些金鑰安全地儲存在 Azure Key Vault 中、定期輪替金鑰,並使用角色型存取控制和網路存取限制來限制對 Azure Key Vault 的存取。 如需在應用程式中安全地使用 API 金鑰的詳細資訊,請參閱透過 Azure Key Vault 使用 API 金鑰

如需 AI 服務安全性的詳細資訊,請參閱驗證對 Azure AI 服務的要求 (英文)。

setx VISION_TRAINING_KEY <your-training-key>
setx VISION_TRAINING_ENDPOINT <your-training-endpoint>
setx VISION_PREDICTION_KEY <your-prediction-key>
setx VISION_PREDICTION_ENDPOINT <your-prediction-endpoint>
setx VISION_PREDICTION_RESOURCE_ID <your-resource-id>

新增環境變數之後,您可能需要重新啟動任何會讀取環境變數的執行中程式,包括主控台視窗。

設定

建立新的 C# 應用程式

使用 Visual Studio,建立新的 .NET Core 應用程式。

安裝用戶端程式庫

建立新專案後,以滑鼠右鍵按一下 [方案總管] 中的專案解決方案,然後選取 [管理 NuGet 套件],以安裝用戶端程式庫。 在開啟的套件管理員中,選取 [瀏覽]、核取 [包含發行前版本],然後搜尋 Microsoft.Azure.CognitiveServices.Vision.CustomVision.TrainingMicrosoft.Azure.CognitiveServices.Vision.CustomVision.Prediction。 選取最新版本,然後選取 [安裝]

秘訣

想要立刻檢視整個快速入門程式碼檔案嗎? 您可以在 GitHub 上找到該檔案,其中包含本快速入門中的程式碼範例。

從專案目錄中中開啟 program.cs 檔案,並新增下列 using 指示詞:

using Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction;
using Microsoft.Azure.CognitiveServices.Vision.CustomVision.Training;
using Microsoft.Azure.CognitiveServices.Vision.CustomVision.Training.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;

在應用程式的 Main 方法中,建立變數,以從環境變數擷取資源的金鑰和端點。 您也會宣告一些要在稍後使用的基本物件。

    string trainingEndpoint = Environment.GetEnvironmentVariable("VISION_TRAINING_ENDPOINT");

    string trainingKey = Environment.GetEnvironmentVariable("VISION_TRAINING_KEY");
    string predictionEndpoint = Environment.GetEnvironmentVariable("VISION_PREDICTION_ENDPOINT");
    string predictionKey = Environment.GetEnvironmentVariable("VISION_PREDICTION_KEY");

    private static Iteration iteration;
    private static string publishedModelName = "CustomODModel";

在應用程式的 Main 方法中,針對本快速入門中使用的方法新增呼叫。 您稍後會實作這些呼叫。

CustomVisionTrainingClient trainingApi = AuthenticateTraining(trainingEndpoint, trainingKey);
CustomVisionPredictionClient predictionApi = AuthenticatePrediction(predictionEndpoint, predictionKey);

Project project = CreateProject(trainingApi);
AddTags(trainingApi, project);
UploadImages(trainingApi, project);
TrainProject(trainingApi, project);
PublishIteration(trainingApi, project);
TestIteration(predictionApi, project);

驗證用戶端

在新方法中,使用您的端點和金鑰來具現化定型和預測用戶端。

private CustomVisionTrainingClient AuthenticateTraining(string endpoint, string trainingKey, string predictionKey)
{
    // Create the Api, passing in the training key
    CustomVisionTrainingClient trainingApi = new CustomVisionTrainingClient(new Microsoft.Azure.CognitiveServices.Vision.CustomVision.Training.ApiKeyServiceClientCredentials(trainingKey))
    {
        Endpoint = endpoint
    };
    return trainingApi;
}
private CustomVisionPredictionClient AuthenticatePrediction(string endpoint, string predictionKey)
{
    // Create a prediction endpoint, passing in the obtained prediction key
    CustomVisionPredictionClient predictionApi = new CustomVisionPredictionClient(new Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction.ApiKeyServiceClientCredentials(predictionKey))
    {
        Endpoint = endpoint
    };
    return predictionApi;
}

建立新的自訂視覺專案

下一個方法會建立物件偵測專案。 所建立的專案會顯示在自訂視覺網站上。 當您建立專案時,請參閱 CreateProject 方法來指定其他選項 (如建置偵測器 Web 入口網站指南中所述)。

private Project CreateProject(CustomVisionTrainingClient trainingApi)
{
    // Find the object detection domain
    var domains = trainingApi.GetDomains();
    var objDetectionDomain = domains.FirstOrDefault(d => d.Type == "ObjectDetection");

    // Create a new project
    Console.WriteLine("Creating new project:");
    project = trainingApi.CreateProject("My New Project", null, objDetectionDomain.Id);

    return project;
}

將標記新增至專案

此方法會定義您將用來定型模型的標記。

private void AddTags(CustomVisionTrainingClient trainingApi, Project project)
{
    // Make two tags in the new project
    var forkTag = trainingApi.CreateTag(project.Id, "fork");
    var scissorsTag = trainingApi.CreateTag(project.Id, "scissors");
}

上傳和標記影像

首先,下載此專案的範例影像。 將範例影像資料夾的內容儲存到您的本機裝置。

為物件偵測專案中的影像加上標記時,您必須使用標準化座標來識別每個加上標記的物件所屬的區域。 下列程式碼會為每個範例影像及其已加上標記的區域建立關聯。

private void UploadImages(CustomVisionTrainingClient trainingApi, Project project)
{
    Dictionary<string, double[]> fileToRegionMap = new Dictionary<string, double[]>()
    {
        // FileName, Left, Top, Width, Height
        {"scissors_1", new double[] { 0.4007353, 0.194068655, 0.259803921, 0.6617647 } },
        {"scissors_2", new double[] { 0.426470578, 0.185898721, 0.172794119, 0.5539216 } },
        {"scissors_3", new double[] { 0.289215684, 0.259428144, 0.403186262, 0.421568632 } },
        {"scissors_4", new double[] { 0.343137264, 0.105833367, 0.332107842, 0.8055556 } },
        {"scissors_5", new double[] { 0.3125, 0.09766343, 0.435049027, 0.71405226 } },
        {"scissors_6", new double[] { 0.379901975, 0.24308826, 0.32107842, 0.5718954 } },
        {"scissors_7", new double[] { 0.341911763, 0.20714055, 0.3137255, 0.6356209 } },
        {"scissors_8", new double[] { 0.231617644, 0.08459154, 0.504901946, 0.8480392 } },
        {"scissors_9", new double[] { 0.170343131, 0.332957536, 0.767156839, 0.403594762 } },
        {"scissors_10", new double[] { 0.204656869, 0.120539248, 0.5245098, 0.743464053 } },
        {"scissors_11", new double[] { 0.05514706, 0.159754932, 0.799019635, 0.730392158 } },
        {"scissors_12", new double[] { 0.265931368, 0.169558853, 0.5061275, 0.606209159 } },
        {"scissors_13", new double[] { 0.241421565, 0.184264734, 0.448529422, 0.6830065 } },
        {"scissors_14", new double[] { 0.05759804, 0.05027781, 0.75, 0.882352948 } },
        {"scissors_15", new double[] { 0.191176474, 0.169558853, 0.6936275, 0.6748366 } },
        {"scissors_16", new double[] { 0.1004902, 0.279036, 0.6911765, 0.477124184 } },
        {"scissors_17", new double[] { 0.2720588, 0.131977156, 0.4987745, 0.6911765 } },
        {"scissors_18", new double[] { 0.180147052, 0.112369314, 0.6262255, 0.6666667 } },
        {"scissors_19", new double[] { 0.333333343, 0.0274019931, 0.443627447, 0.852941155 } },
        {"scissors_20", new double[] { 0.158088237, 0.04047389, 0.6691176, 0.843137264 } },
        {"fork_1", new double[] { 0.145833328, 0.3509314, 0.5894608, 0.238562092 } },
        {"fork_2", new double[] { 0.294117659, 0.216944471, 0.534313738, 0.5980392 } },
        {"fork_3", new double[] { 0.09191177, 0.0682516545, 0.757352948, 0.6143791 } },
        {"fork_4", new double[] { 0.254901975, 0.185898721, 0.5232843, 0.594771266 } },
        {"fork_5", new double[] { 0.2365196, 0.128709182, 0.5845588, 0.71405226 } },
        {"fork_6", new double[] { 0.115196079, 0.133611143, 0.676470637, 0.6993464 } },
        {"fork_7", new double[] { 0.164215669, 0.31008172, 0.767156839, 0.410130739 } },
        {"fork_8", new double[] { 0.118872553, 0.318251669, 0.817401946, 0.225490168 } },
        {"fork_9", new double[] { 0.18259804, 0.2136765, 0.6335784, 0.643790841 } },
        {"fork_10", new double[] { 0.05269608, 0.282303959, 0.8088235, 0.452614367 } },
        {"fork_11", new double[] { 0.05759804, 0.0894935, 0.9007353, 0.3251634 } },
        {"fork_12", new double[] { 0.3345588, 0.07315363, 0.375, 0.9150327 } },
        {"fork_13", new double[] { 0.269607842, 0.194068655, 0.4093137, 0.6732026 } },
        {"fork_14", new double[] { 0.143382356, 0.218578458, 0.7977941, 0.295751631 } },
        {"fork_15", new double[] { 0.19240196, 0.0633497, 0.5710784, 0.8398692 } },
        {"fork_16", new double[] { 0.140931368, 0.480016381, 0.6838235, 0.240196079 } },
        {"fork_17", new double[] { 0.305147052, 0.2512582, 0.4791667, 0.5408496 } },
        {"fork_18", new double[] { 0.234068632, 0.445702642, 0.6127451, 0.344771236 } },
        {"fork_19", new double[] { 0.219362751, 0.141781077, 0.5919118, 0.6683006 } },
        {"fork_20", new double[] { 0.180147052, 0.239820287, 0.6887255, 0.235294119 } }
    };

附註

在您自己的專案中,如果您沒有按住並拖曳公用程式可標示區域的座標,您可以使用自訂視覺網站上的 Web UI。 此範例已提供座標。

然後,此關聯對應會用來上傳每個範例影像及其區域座標。 您最多可以在單一批次中上傳 64 個影像。 您可能需要變更 imagePath 值,以指向正確的資料夾位置。

    // Add all images for fork
    var imagePath = Path.Combine("Images", "fork");
    var imageFileEntries = new List<ImageFileCreateEntry>();
    foreach (var fileName in Directory.EnumerateFiles(imagePath))
    {
        var region = fileToRegionMap[Path.GetFileNameWithoutExtension(fileName)];
        imageFileEntries.Add(new ImageFileCreateEntry(fileName, File.ReadAllBytes(fileName), null, new List<Region>(new Region[] { new Region(forkTag.Id, region[0], region[1], region[2], region[3]) })));
    }
    trainingApi.CreateImagesFromFiles(project.Id, new ImageFileCreateBatch(imageFileEntries));

    // Add all images for scissors
    imagePath = Path.Combine("Images", "scissors");
    imageFileEntries = new List<ImageFileCreateEntry>();
    foreach (var fileName in Directory.EnumerateFiles(imagePath))
    {
        var region = fileToRegionMap[Path.GetFileNameWithoutExtension(fileName)];
        imageFileEntries.Add(new ImageFileCreateEntry(fileName, File.ReadAllBytes(fileName), null, new List<Region>(new Region[] { new Region(scissorsTag.Id, region[0], region[1], region[2], region[3]) })));
    }
    trainingApi.CreateImagesFromFiles(project.Id, new ImageFileCreateBatch(imageFileEntries));
}

此時,您已上傳所有影像範例,並為每個影像 (叉子剪刀) 標記上相關聯的像素矩形。

為專案定型

此方法會建立專案中的第一個定型反覆運算。 其會查詢服務,直到定型完成為止。

private void TrainProject(CustomVisionTrainingClient trainingApi, Project project)
{

    // Now there are images with tags start training the project
    Console.WriteLine("\tTraining");
    iteration = trainingApi.TrainProject(project.Id);

    // The returned iteration will be in progress, and can be queried periodically to see when it has completed
    while (iteration.Status == "Training")
    {
        Thread.Sleep(1000);

        // Re-query the iteration to get its updated status
        iteration = trainingApi.GetIteration(project.Id, iteration.Id);
    }
}

秘訣

使用選取的標記進行訓練

您可以選擇只在已套用的標記子集上進行訓練。 如果您尚未套用足夠的特定標記,但已套用足夠的其他標記,則您可能需要執行此動作。 在 TrainProject 呼叫中,使用 trainingParameters 參數。 建構 TrainingParameters,並將其 SelectedTags 屬性設定為您要使用的標記識別碼清單。 此模型將訓練為僅辨識該清單上的標記。

發佈目前的反覆項目

此方法會讓模型的目前反覆運算可供查詢。 您可以使用模型名稱作為參考,以傳送預測要求。 您必須針對 predictionResourceId 輸入自己的值。 您可以在 Azure 入口網站中資源的 [屬性] 索引標籤上找到預測資源識別碼,該識別碼名為 [資源識別碼]

private void PublishIteration(CustomVisionTrainingClient trainingApi, Project project)
{

    // The iteration is now trained. Publish it to the prediction end point.
    var predictionResourceId = Environment.GetEnvironmentVariable("VISION_PREDICTION_RESOURCE_ID");
    trainingApi.PublishIteration(project.Id, iteration.Id, publishedModelName, predictionResourceId);
    Console.WriteLine("Done!\n");
}

測試預測端點

此方法會載入測試影像、查詢模型端點,並將預測資料輸出至主控台。

private void TestIteration(CustomVisionPredictionClient predictionApi, Project project)
{

    // Make a prediction against the new project
    Console.WriteLine("Making a prediction:");
    var imageFile = Path.Combine("Images", "test", "test_image.jpg");
    using (var stream = File.OpenRead(imageFile))
    {
        var result = predictionApi.DetectImage(project.Id, publishedModelName, stream);

        // Loop over each prediction and write out the results
        foreach (var c in result.Predictions)
        {
            Console.WriteLine($"\t{c.TagName}: {c.Probability:P1} [ {c.BoundingBox.Left}, {c.BoundingBox.Top}, {c.BoundingBox.Width}, {c.BoundingBox.Height} ]");
        }
    }
    Console.ReadKey();
}

執行應用程式

按一下 IDE 視窗頂端的 [偵錯] 按鈕,以執行應用程式。

應用程式在執行時,應會開啟主控台視窗並寫入下列輸出:

Creating new project:
        Training
Done!

Making a prediction:
        fork: 98.2% [ 0.111609578, 0.184719115, 0.6607002, 0.6637112 ]
        scissors: 1.2% [ 0.112389535, 0.119195729, 0.658031344, 0.7023591 ]

接著,您可以確認測試影像 (位於 Images/Test/ 中) 是否已正確加上標記,以及偵測的區域是否正確。 此時,您可以按任意鍵以結束應用程式。

清除資源

如果您想要實作您自己的物件偵測專案 (或改為嘗試影像分類專案),則您可能需要刪除此範例中的叉子/剪刀偵測專案。 免費訂用帳戶可使用兩個自訂視覺專案。

自訂視覺網站上,瀏覽至 [專案],然後選取 [我的新專案] 底下的資源回收筒。

螢幕擷取畫面,內有標示為 [我的新專案] 的面板,並含一個 [資源回收筒] 圖示。

後續步驟

現在,您已完成在程式碼中執行物件偵測程序的每個步驟。 此範例會執行單一的訓練反覆項目,但您通常必須對模型進行多次訓練和測試,以便提升其精確度。 下列指南會處理影像分類,但其原則類似於物件偵測。

本指南提供指示和範例程式碼,可協助您開始使用適用於 Go 的自訂視覺用戶端程式庫來建置物件偵測模型。 您將建立專案、新增標籤、將專案定型,並使用專案的預測端點 URL 以程式設計方式加以測試。 請使用此範例作為自行建置影像辨識應用程式的範本。

附註

如果您想要在「不用」撰寫程式碼的情況下,建立和訓練物件偵測模型,請改為參閱以瀏覽器為基礎的指引

參考文件 (訓練)(預測)

先決條件

  • Azure 訂用帳戶 - 建立免費帳戶
  • Go 1.8+
  • 擁有您的 Azure 訂用帳戶之後,請在 Azure 入口網站中建立自訂視覺資源,以建立訓練和預測資源。
    • 您可以使用免費定價層 (F0) 來試用服務,之後可升級至付費層以用於實際執行環境。

建立環境變數

在此範例中,您會在執行應用程式的本機電腦上將認證寫入環境變數。

移至 Azure 入口網站。 如果您在 [必要條件] 區段中建立的自訂視覺資源成功部署,請選取 [後續步驟] 底下的 [前往資源] 按鈕。 您可以在 [資源管理] 底下的 [金鑰和端點] 頁面中找到金鑰和端點。 您需要取得訓練資源和預測資源的金鑰以及 API 端點。

您可以在 Azure 入口網站中預測資源的 [屬性] 索引標籤上找到預測資源識別碼,該識別碼名為 [資源識別碼]

秘訣

您也可以使用 https://www.customvision.ai 來取得這些值。 登入之後,請選取右上方的 [設定] 圖示。 在 [設定] 頁面上,您可以檢視所有金鑰、資源識別碼和端點。

若要設定環境變數,請開啟主控台視窗,然後遵循作業系統和開發環境的指示進行。

  • 若要設定 VISION_TRAINING KEY 環境變數,請以您的訓練資源的其中一個金鑰取代 <your-training-key>
  • 若要設定 VISION_TRAINING_ENDPOINT 環境變數,請將 <your-training-endpoint> 取代為您的訓練資源的端點。
  • 若要設定 VISION_PREDICTION_KEY 環境變數,請以您的預測資源的其中一個金鑰取代 <your-prediction-key>
  • 若要設定 VISION_PREDICTION_ENDPOINT 環境變數,請將 <your-prediction-endpoint> 取代為您的預測資源的端點。
  • 若要設定 VISION_PREDICTION_RESOURCE_ID 環境變數,請將 <your-resource-id> 取代為您的預測資源的資源識別碼。

重要事項

我們建議使用適用於 Azure 資源的受控識別搭配 Microsoft Entra ID 驗證,以避免使用在雲端執行的應用程式儲存認證。

請謹慎使用 API 金鑰。 請勿在程式碼中直接包含 API 金鑰,且切勿公開張貼金鑰。 如果使用 API 金鑰,請將這些金鑰安全地儲存在 Azure Key Vault 中、定期輪替金鑰,並使用角色型存取控制和網路存取限制來限制對 Azure Key Vault 的存取。 如需在應用程式中安全地使用 API 金鑰的詳細資訊,請參閱透過 Azure Key Vault 使用 API 金鑰

如需 AI 服務安全性的詳細資訊,請參閱驗證對 Azure AI 服務的要求 (英文)。

setx VISION_TRAINING_KEY <your-training-key>
setx VISION_TRAINING_ENDPOINT <your-training-endpoint>
setx VISION_PREDICTION_KEY <your-prediction-key>
setx VISION_PREDICTION_ENDPOINT <your-prediction-endpoint>
setx VISION_PREDICTION_RESOURCE_ID <your-resource-id>

新增環境變數之後,您可能需要重新啟動任何會讀取環境變數的執行中程式,包括主控台視窗。

設定

安裝自訂視覺用戶端程式庫

若要使用適用於 Go 的自訂視覺來撰寫影像分析應用程式,您將需要自訂視覺服務用戶端程式庫。 在 PowerShell 中執行下列命令:

go get -u github.com/Azure/azure-sdk-for-go/...

或者,如果您使用 dep,請在存放庫中執行:

dep ensure -add github.com/Azure/azure-sdk-for-go

取得範例影像

這個範例使用了 GitHub 上 Foundry Tools Python SDK 範例 庫的圖片。 將此存放庫複製或下載到您的開發環境。 請記住其資料夾位置以便執行後續步驟。

建立自訂視覺專案

在您偏好的專案目錄中建立名為 sample.go 的新檔案,接著在您偏好的程式碼編輯器中開啟。

在指令碼中新增下列程式碼,以建立新的自訂視覺服務專案。

當您建立專案時,請參閱 CreateProject 方法來指定其他選項 (如建置偵測器 Web 入口網站指南中所述)。

import(
    "context"
    "bytes"
    "fmt"
    "io/ioutil"
    "path"
    "log"
    "time"
    "github.com/Azure/azure-sdk-for-go/services/cognitiveservices/v3.0/customvision/training"
    "github.com/Azure/azure-sdk-for-go/services/cognitiveservices/v3.0/customvision/prediction"
)

// retrieve environment variables:
var (
    training_key string = os.Getenv("VISION_TRAINING_KEY")
    prediction_key string = os.Getenv("VISION_PREDICTION_KEY")
    prediction_resource_id = os.Getenv("VISION_PREDICTION_RESOURCE_ID")
    endpoint string = os.Getenv("VISION_ENDPOINT")
   
    project_name string = "Go Sample OD Project"
    iteration_publish_name = "detectModel"
    sampleDataDirectory = "<path to sample images>"
)

func main() {
    fmt.Println("Creating project...")

    ctx = context.Background()

    trainer := training.New(training_key, endpoint)

    var objectDetectDomain training.Domain
    domains, _ := trainer.GetDomains(ctx)

    for _, domain := range *domains.Value {
        fmt.Println(domain, domain.Type)
        if domain.Type == "ObjectDetection" && *domain.Name == "General" {
            objectDetectDomain = domain
            break
        }
    }
    fmt.Println("Creating project...")
    project, _ := trainer.CreateProject(ctx, project_name, "", objectDetectDomain.ID, "")

在專案中建立標記

若要在專案中建立分類標記,請在 sample.go 結尾新增以下程式碼:

# Make two tags in the new project
forkTag, _ := trainer.CreateTag(ctx, *project.ID, "fork", "A fork", string(training.Regular))
scissorsTag, _ := trainer.CreateTag(ctx, *project.ID, "scissors", "Pair of scissors", string(training.Regular))

上傳和標記影像

為物件偵測專案中的影像加上標記時,您必須使用標準化座標來識別每個加上標記的物件所屬的區域。

附註

如果您沒有按住並拖曳公用程式可標示區域的座標,您可以使用 Customvision.ai 上的 Web UI。 此範例已提供座標。

若要將影像、標記和區域新增至專案,請在標記建立之後插入下列程式碼。 請注意,在本教學課程中,區域會以硬式編碼方式內嵌。 這些區域會在標準化座標中指定週框方塊,且座標會以下列順序指定:左、上、寬度、高度。

forkImageRegions := map[string][4]float64{
    "fork_1.jpg": [4]float64{ 0.145833328, 0.3509314, 0.5894608, 0.238562092 },
    "fork_2.jpg": [4]float64{ 0.294117659, 0.216944471, 0.534313738, 0.5980392 },
    "fork_3.jpg": [4]float64{ 0.09191177, 0.0682516545, 0.757352948, 0.6143791 },
    "fork_4.jpg": [4]float64{ 0.254901975, 0.185898721, 0.5232843, 0.594771266 },
    "fork_5.jpg": [4]float64{ 0.2365196, 0.128709182, 0.5845588, 0.71405226 },
    "fork_6.jpg": [4]float64{ 0.115196079, 0.133611143, 0.676470637, 0.6993464 },
    "fork_7.jpg": [4]float64{ 0.164215669, 0.31008172, 0.767156839, 0.410130739 },
    "fork_8.jpg": [4]float64{ 0.118872553, 0.318251669, 0.817401946, 0.225490168 },
    "fork_9.jpg": [4]float64{ 0.18259804, 0.2136765, 0.6335784, 0.643790841 },
    "fork_10.jpg": [4]float64{ 0.05269608, 0.282303959, 0.8088235, 0.452614367 },
    "fork_11.jpg": [4]float64{ 0.05759804, 0.0894935, 0.9007353, 0.3251634 },
    "fork_12.jpg": [4]float64{ 0.3345588, 0.07315363, 0.375, 0.9150327 },
    "fork_13.jpg": [4]float64{ 0.269607842, 0.194068655, 0.4093137, 0.6732026 },
    "fork_14.jpg": [4]float64{ 0.143382356, 0.218578458, 0.7977941, 0.295751631 },
    "fork_15.jpg": [4]float64{ 0.19240196, 0.0633497, 0.5710784, 0.8398692 },
    "fork_16.jpg": [4]float64{ 0.140931368, 0.480016381, 0.6838235, 0.240196079 },
    "fork_17.jpg": [4]float64{ 0.305147052, 0.2512582, 0.4791667, 0.5408496 },
    "fork_18.jpg": [4]float64{ 0.234068632, 0.445702642, 0.6127451, 0.344771236 },
    "fork_19.jpg": [4]float64{ 0.219362751, 0.141781077, 0.5919118, 0.6683006 },
    "fork_20.jpg": [4]float64{ 0.180147052, 0.239820287, 0.6887255, 0.235294119 },
}

scissorsImageRegions := map[string][4]float64{
    "scissors_1.jpg": [4]float64{ 0.4007353, 0.194068655, 0.259803921, 0.6617647 },
    "scissors_2.jpg": [4]float64{ 0.426470578, 0.185898721, 0.172794119, 0.5539216 },
    "scissors_3.jpg": [4]float64{ 0.289215684, 0.259428144, 0.403186262, 0.421568632 },
    "scissors_4.jpg": [4]float64{ 0.343137264, 0.105833367, 0.332107842, 0.8055556 },
    "scissors_5.jpg": [4]float64{ 0.3125, 0.09766343, 0.435049027, 0.71405226 },
    "scissors_6.jpg": [4]float64{ 0.379901975, 0.24308826, 0.32107842, 0.5718954 },
    "scissors_7.jpg": [4]float64{ 0.341911763, 0.20714055, 0.3137255, 0.6356209 },
    "scissors_8.jpg": [4]float64{ 0.231617644, 0.08459154, 0.504901946, 0.8480392 },
    "scissors_9.jpg": [4]float64{ 0.170343131, 0.332957536, 0.767156839, 0.403594762 },
    "scissors_10.jpg": [4]float64{ 0.204656869, 0.120539248, 0.5245098, 0.743464053 },
    "scissors_11.jpg": [4]float64{ 0.05514706, 0.159754932, 0.799019635, 0.730392158 },
    "scissors_12.jpg": [4]float64{ 0.265931368, 0.169558853, 0.5061275, 0.606209159 },
    "scissors_13.jpg": [4]float64{ 0.241421565, 0.184264734, 0.448529422, 0.6830065 },
    "scissors_14.jpg": [4]float64{ 0.05759804, 0.05027781, 0.75, 0.882352948 },
    "scissors_15.jpg": [4]float64{ 0.191176474, 0.169558853, 0.6936275, 0.6748366 },
    "scissors_16.jpg": [4]float64{ 0.1004902, 0.279036, 0.6911765, 0.477124184 },
    "scissors_17.jpg": [4]float64{ 0.2720588, 0.131977156, 0.4987745, 0.6911765 },
    "scissors_18.jpg": [4]float64{ 0.180147052, 0.112369314, 0.6262255, 0.6666667 },
    "scissors_19.jpg": [4]float64{ 0.333333343, 0.0274019931, 0.443627447, 0.852941155 },
    "scissors_20.jpg": [4]float64{ 0.158088237, 0.04047389, 0.6691176, 0.843137264 },
}

然後,使用此關聯對應,上傳每個範例影像及其區域座標 (您最多可以在單一批次中上傳 64 個影像)。 加入下列程式碼。

附註

你需要根據你之前下載的 Foundry Tools Go SDK 範例專案,改變圖片的路徑。

// Go through the data table above and create the images
fmt.Println("Adding images...")
var fork_images []training.ImageFileCreateEntry
for file, region := range forkImageRegions {
    imageFile, _ := ioutil.ReadFile(path.Join(sampleDataDirectory, "fork", file))

    regiontest := forkImageRegions[file]
    imageRegion := training.Region{
        TagID:  forkTag.ID,
        Left:   &regiontest[0],
        Top:    &regiontest[1],
        Width:  &regiontest[2],
        Height: &regiontest[3],
    }
    var fileName string = file

    fork_images = append(fork_images, training.ImageFileCreateEntry{
        Name:     &fileName,
        Contents: &imageFile,
        Regions:  &[]training.Region{imageRegion}
    })
}
    
fork_batch, _ := trainer.CreateImagesFromFiles(ctx, *project.ID, training.ImageFileCreateBatch{ 
    Images: &fork_images,
})

if (!*fork_batch.IsBatchSuccessful) {
    fmt.Println("Batch upload failed.")
}

var scissor_images []training.ImageFileCreateEntry
for file, region := range scissorsImageRegions {
    imageFile, _ := ioutil.ReadFile(path.Join(sampleDataDirectory, "scissors", file))

    imageRegion := training.Region { 
        TagID:scissorsTag.ID,
        Left:&region[0],
        Top:&region[1],
        Width:&region[2],
        Height:&region[3],
    }

    scissor_images = append(scissor_images, training.ImageFileCreateEntry {
        Name: &file,
        Contents: &imageFile,
        Regions: &[]training.Region{ imageRegion },
    })
}
    
scissor_batch, _ := trainer.CreateImagesFromFiles(ctx, *project.ID, training.ImageFileCreateBatch{ 
    Images: &scissor_images,
})
    
if (!*scissor_batch.IsBatchSuccessful) {
    fmt.Println("Batch upload failed.")
}     

定型和發佈專案

此程式碼會在預測模型中建立第一個反覆項目,然後將該反覆項目發佈至預測端點。 提供給已發佈反覆項目的名稱可用來傳送預測要求。 反覆項目要等到發佈後才可在預測端點中使用。

iteration, _ := trainer.TrainProject(ctx, *project.ID)
fmt.Println("Training status:", *iteration.Status)
for {
    if *iteration.Status != "Training" {
        break
    }
    time.Sleep(5 * time.Second)
    iteration, _ = trainer.GetIteration(ctx, *project.ID, *iteration.ID)
    fmt.Println("Training status:", *iteration.Status)
}

trainer.PublishIteration(ctx, *project.ID, *iteration.ID, iteration_publish_name, prediction_resource_id))

使用預測端點

若要將影像傳送到預測端點並擷取預測,在檔案結尾處新增以下程式碼:

    fmt.Println("Predicting...")
    predictor := prediction.New(prediction_key, endpoint)

    testImageData, _ := ioutil.ReadFile(path.Join(sampleDataDirectory, "Test", "test_od_image.jpg"))
    results, _ := predictor.DetectImage(ctx, *project.ID, iteration_publish_name, ioutil.NopCloser(bytes.NewReader(testImageData)), "")

    for _, prediction := range *results.Predictions    {
        boundingBox := *prediction.BoundingBox

        fmt.Printf("\t%s: %.2f%% (%.2f, %.2f, %.2f, %.2f)", 
            *prediction.TagName,
            *prediction.Probability * 100,
            *boundingBox.Left,
            *boundingBox.Top,
            *boundingBox.Width,
            *boundingBox.Height)
        fmt.Println("")
    }
}

執行應用程式

執行 sample.go

go run sample.go

應用程式的輸出應會顯示在主控台中。 接著,您可以確認測試影像 (位於 samples/vision/images/Test 中) 是否已正確加上標記,以及偵測的區域是否正確。

清除資源

如果您想要實作您自己的物件偵測專案 (或改為嘗試影像分類專案),則您可能需要刪除此範例中的叉子/剪刀偵測專案。 免費訂用帳戶可使用兩個自訂視覺專案。

自訂視覺網站上,瀏覽至 [專案],然後選取 [我的新專案] 底下的資源回收筒。

螢幕擷取畫面,內有標示為 [我的新專案] 的面板,並含一個 [資源回收筒] 圖示。

後續步驟

現在,您已完成在程式碼中執行物件偵測程序的每個步驟。 此範例會執行單一的訓練反覆項目,但您通常必須對模型進行多次訓練和測試,以便提升其精確度。 下列指南會處理影像分類,但其原則類似於物件偵測。

開始使用適用於 Java 的自訂視覺用戶端程式庫來建置物件偵測模型。 請遵循下列步驟來安裝套件,並試用基本工作的程式碼範例。 請使用此範例作為自行建置影像辨識應用程式的範本。

附註

如果您想要在「不用」撰寫程式碼的情況下,建立和訓練物件偵測模型,請改為參閱以瀏覽器為基礎的指引

參考文件 | 程式庫原始程式碼 (訓練)(預測)| 成品 (Maven) (訓練)(預測) | 範例

先決條件

建立環境變數

在此範例中,您會在執行應用程式的本機電腦上將認證寫入環境變數。

移至 Azure 入口網站。 如果您在 [必要條件] 區段中建立的自訂視覺資源成功部署,請選取 [後續步驟] 底下的 [前往資源] 按鈕。 您可以在 [資源管理] 底下的 [金鑰和端點] 頁面中找到金鑰和端點。 您需要取得訓練資源和預測資源的金鑰以及 API 端點。

您可以在 Azure 入口網站中預測資源的 [屬性] 索引標籤上找到預測資源識別碼,該識別碼名為 [資源識別碼]

秘訣

您也可以使用 https://www.customvision.ai 來取得這些值。 登入之後,請選取右上方的 [設定] 圖示。 在 [設定] 頁面上,您可以檢視所有金鑰、資源識別碼和端點。

若要設定環境變數,請開啟主控台視窗,然後遵循作業系統和開發環境的指示進行。

  • 若要設定 VISION_TRAINING KEY 環境變數,請以您的訓練資源的其中一個金鑰取代 <your-training-key>
  • 若要設定 VISION_TRAINING_ENDPOINT 環境變數,請將 <your-training-endpoint> 取代為您的訓練資源的端點。
  • 若要設定 VISION_PREDICTION_KEY 環境變數,請以您的預測資源的其中一個金鑰取代 <your-prediction-key>
  • 若要設定 VISION_PREDICTION_ENDPOINT 環境變數,請將 <your-prediction-endpoint> 取代為您的預測資源的端點。
  • 若要設定 VISION_PREDICTION_RESOURCE_ID 環境變數,請將 <your-resource-id> 取代為您的預測資源的資源識別碼。

重要事項

我們建議使用適用於 Azure 資源的受控識別搭配 Microsoft Entra ID 驗證,以避免使用在雲端執行的應用程式儲存認證。

請謹慎使用 API 金鑰。 請勿在程式碼中直接包含 API 金鑰,且切勿公開張貼金鑰。 如果使用 API 金鑰,請將這些金鑰安全地儲存在 Azure Key Vault 中、定期輪替金鑰,並使用角色型存取控制和網路存取限制來限制對 Azure Key Vault 的存取。 如需在應用程式中安全地使用 API 金鑰的詳細資訊,請參閱透過 Azure Key Vault 使用 API 金鑰

如需 AI 服務安全性的詳細資訊,請參閱驗證對 Azure AI 服務的要求 (英文)。

setx VISION_TRAINING_KEY <your-training-key>
setx VISION_TRAINING_ENDPOINT <your-training-endpoint>
setx VISION_PREDICTION_KEY <your-prediction-key>
setx VISION_PREDICTION_ENDPOINT <your-prediction-endpoint>
setx VISION_PREDICTION_RESOURCE_ID <your-resource-id>

新增環境變數之後,您可能需要重新啟動任何會讀取環境變數的執行中程式,包括主控台視窗。

設定

建立新的 Gradle 專案

在主控台視窗 (例如 cmd、PowerShell 或 Bash) 中,為您的應用程式建立新的目錄,並瀏覽至該目錄。

mkdir myapp && cd myapp

從您的工作目錄執行 gradle init 命令。 此命令會建立 Gradle 的基本組建檔案,包括 build.gradle.kts,此檔案將在執行階段用來建立及設定您的應用程式。

gradle init --type basic

出現選擇 DSL 的提示時,請選取 [Kotlin]

安裝用戶端程式庫

找出 build.gradle.kts,並使用您慣用的 IDE 或文字編輯器加以開啟。 然後,在其中複製下列組建組態。 此組態會將專案定義為進入點為 CustomVisionQuickstart 類別的 Java 應用程式。 這會匯入自訂視覺程式庫。

plugins {
    java
    application
}
application { 
    mainClassName = "CustomVisionQuickstart"
}
repositories {
    mavenCentral()
}
dependencies {
    compile(group = "com.azure", name = "azure-cognitiveservices-customvision-training", version = "1.1.0-preview.2")
    compile(group = "com.azure", name = "azure-cognitiveservices-customvision-prediction", version = "1.1.0-preview.2")
}

建立 Java 檔案

在您的工作目錄中執行下列命令,以建立專案來源資料夾:

mkdir -p src/main/java

瀏覽至新的資料夾,並建立名為 CustomVisionQuickstart.java 的檔案。 在您慣用的編輯器或 IDE 中開啟該檔案,並新增下列 import 陳述式:

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import com.google.common.io.ByteStreams;

import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.Classifier;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.Domain;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.DomainType;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.ImageFileCreateBatch;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.ImageFileCreateEntry;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.Iteration;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.Project;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.Region;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.TrainProjectOptionalParameter;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.CustomVisionTrainingClient;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.Trainings;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.CustomVisionTrainingManager;
import com.microsoft.azure.cognitiveservices.vision.customvision.prediction.models.ImagePrediction;
import com.microsoft.azure.cognitiveservices.vision.customvision.prediction.models.Prediction;
import com.microsoft.azure.cognitiveservices.vision.customvision.prediction.CustomVisionPredictionClient;
import com.microsoft.azure.cognitiveservices.vision.customvision.prediction.CustomVisionPredictionManager;
import com.microsoft.azure.cognitiveservices.vision.customvision.training.models.Tag;

秘訣

想要立刻檢視整個快速入門程式碼檔案嗎? 您可以在 GitHub 上找到該檔案,其中包含本快速入門中的程式碼範例。

在應用程式的 CustomVisionQuickstart 方法中,建立變數,以從環境變數擷取資源的金鑰和端點。

// retrieve environment variables
final static String trainingApiKey = System.getenv("VISION_TRAINING_KEY");
final static String trainingEndpoint = System.getenv("VISION_TRAINING_ENDPOINT");
final static String predictionApiKey = System.getenv("VISION_PREDICTION_KEY");
final static String predictionEndpoint = System.getenv("VISION_PREDICTION_ENDPOINT");
final static String predictionResourceId = System.getenv("VISION_PREDICTION_RESOURCE_ID");

在應用程式的 main 方法中,針對本快速入門中使用的方法新增呼叫。 稍後您會定義這些項目。

Project projectOD = createProjectOD(trainClient);
addTagsOD(trainClient, projectOD);
uploadImagesOD(trainClient, projectOD);
trainProjectOD(trainClient, projectOD);
publishIterationOD(trainClient, project);
testProjectOD(predictor, projectOD);

物件模型

下列類別和介面會處理自訂視覺 Java 用戶端程式庫的一些主要功能。

名稱 描述
CustomVisionTrainingClient 此類別會處理模型的建立、定型和發佈。
CustomVisionPredictionClient 此類別會處理您的模型查詢,以進行物件偵測預測。
ImagePrediction 此類別會定義單一影像上的單一物件預測。 其中包含物件識別碼和名稱的屬性、物件的周框方塊位置,以及信賴分數。

程式碼範例

這些程式碼片段說明如何使用適用於 Java 的自訂視覺用戶端程式庫來執行下列工作:

驗證用戶端

在您的 main 方法中,使用您的端點和金鑰來具現化定型和預測用戶端。

// Authenticate
CustomVisionTrainingClient trainClient = CustomVisionTrainingManager
        .authenticate(trainingEndpoint, trainingApiKey)
        .withEndpoint(trainingEndpoint);
CustomVisionPredictionClient predictor = CustomVisionPredictionManager
        .authenticate(predictionEndpoint, predictionApiKey)
        .withEndpoint(predictionEndpoint);

建立新的自訂視覺專案

下一個方法會建立物件偵測專案。 所建立的專案會顯示在您稍早瀏覽過的自訂視覺網站上。 當您建立專案時,請參閱 CreateProject 方法多載來指定其他選項 (如建立偵測器 Web 入口網站指南中所述)。

public static Project createProjectOD(CustomVisionTrainingClient trainClient) {
    Trainings trainer = trainClient.trainings();

    // find the object detection domain to set the project type
    Domain objectDetectionDomain = null;
    List<Domain> domains = trainer.getDomains();
    for (final Domain domain : domains) {
        if (domain.type() == DomainType.OBJECT_DETECTION) {
            objectDetectionDomain = domain;
            break;
        }
    }

    if (objectDetectionDomain == null) {
        System.out.println("Unexpected result; no objects were detected.");
    }

    System.out.println("Creating project...");
    // create an object detection project
    Project project = trainer.createProject().withName("Sample Java OD Project")
            .withDescription("Sample OD Project").withDomainId(objectDetectionDomain.id())
            .withClassificationType(Classifier.MULTILABEL.toString()).execute();

    return project;
}

將標記新增到您的專案

此方法會定義您將用來定型模型的標記。

public static void addTagsOD(CustomVisionTrainingClient trainClient, Project project) {
    Trainings trainer = trainClient.trainings();
    // create fork tag
    Tag forkTag = trainer.createTag().withProjectId(project.id()).withName("fork").execute();

    // create scissors tag
    Tag scissorsTag = trainer.createTag().withProjectId(project.id()).withName("scissor").execute();
}

上傳和標記影像

首先,下載此專案的範例影像。 將範例影像資料夾的內容儲存到您的本機裝置。

附註

您需要一組更廣泛的影像來完成訓練嗎? Trove 是 Microsoft Garage 專案,可讓您收集和購買影像集以供訓練使用。 收集影像之後,您可以下載影像,然後以一般方式將其匯入至自訂視覺專案。 如需深入了解,請造訪 Trove 頁面

為物件偵測專案中的影像加上標記時,您必須使用標準化座標來識別每個加上標記的物件所屬的區域。 下列程式碼會為每個範例影像及其已加上標記的區域建立關聯。

附註

如果您沒有按住並拖曳公用程式可標示區域的座標,您可以使用 Customvision.ai 上的 Web UI。 此範例已提供座標。

public static void uploadImagesOD(CustomVisionTrainingClient trainClient, Project project) {
    // Mapping of filenames to their respective regions in the image. The
    // coordinates are specified
    // as left, top, width, height in normalized coordinates. I.e. (left is left in
    // pixels / width in pixels)

    // This is a hardcoded mapping of the files we'll upload along with the bounding
    // box of the object in the
    // image. The boudning box is specified as left, top, width, height in
    // normalized coordinates.
    // Normalized Left = Left / Width (in Pixels)
    // Normalized Top = Top / Height (in Pixels)
    // Normalized Bounding Box Width = (Right - Left) / Width (in Pixels)
    // Normalized Bounding Box Height = (Bottom - Top) / Height (in Pixels)
    HashMap<String, double[]> regionMap = new HashMap<String, double[]>();
    regionMap.put("scissors_1.jpg", new double[] { 0.4007353, 0.194068655, 0.259803921, 0.6617647 });
    regionMap.put("scissors_2.jpg", new double[] { 0.426470578, 0.185898721, 0.172794119, 0.5539216 });
    regionMap.put("scissors_3.jpg", new double[] { 0.289215684, 0.259428144, 0.403186262, 0.421568632 });
    regionMap.put("scissors_4.jpg", new double[] { 0.343137264, 0.105833367, 0.332107842, 0.8055556 });
    regionMap.put("scissors_5.jpg", new double[] { 0.3125, 0.09766343, 0.435049027, 0.71405226 });
    regionMap.put("scissors_6.jpg", new double[] { 0.379901975, 0.24308826, 0.32107842, 0.5718954 });
    regionMap.put("scissors_7.jpg", new double[] { 0.341911763, 0.20714055, 0.3137255, 0.6356209 });
    regionMap.put("scissors_8.jpg", new double[] { 0.231617644, 0.08459154, 0.504901946, 0.8480392 });
    regionMap.put("scissors_9.jpg", new double[] { 0.170343131, 0.332957536, 0.767156839, 0.403594762 });
    regionMap.put("scissors_10.jpg", new double[] { 0.204656869, 0.120539248, 0.5245098, 0.743464053 });
    regionMap.put("scissors_11.jpg", new double[] { 0.05514706, 0.159754932, 0.799019635, 0.730392158 });
    regionMap.put("scissors_12.jpg", new double[] { 0.265931368, 0.169558853, 0.5061275, 0.606209159 });
    regionMap.put("scissors_13.jpg", new double[] { 0.241421565, 0.184264734, 0.448529422, 0.6830065 });
    regionMap.put("scissors_14.jpg", new double[] { 0.05759804, 0.05027781, 0.75, 0.882352948 });
    regionMap.put("scissors_15.jpg", new double[] { 0.191176474, 0.169558853, 0.6936275, 0.6748366 });
    regionMap.put("scissors_16.jpg", new double[] { 0.1004902, 0.279036, 0.6911765, 0.477124184 });
    regionMap.put("scissors_17.jpg", new double[] { 0.2720588, 0.131977156, 0.4987745, 0.6911765 });
    regionMap.put("scissors_18.jpg", new double[] { 0.180147052, 0.112369314, 0.6262255, 0.6666667 });
    regionMap.put("scissors_19.jpg", new double[] { 0.333333343, 0.0274019931, 0.443627447, 0.852941155 });
    regionMap.put("scissors_20.jpg", new double[] { 0.158088237, 0.04047389, 0.6691176, 0.843137264 });
    regionMap.put("fork_1.jpg", new double[] { 0.145833328, 0.3509314, 0.5894608, 0.238562092 });
    regionMap.put("fork_2.jpg", new double[] { 0.294117659, 0.216944471, 0.534313738, 0.5980392 });
    regionMap.put("fork_3.jpg", new double[] { 0.09191177, 0.0682516545, 0.757352948, 0.6143791 });
    regionMap.put("fork_4.jpg", new double[] { 0.254901975, 0.185898721, 0.5232843, 0.594771266 });
    regionMap.put("fork_5.jpg", new double[] { 0.2365196, 0.128709182, 0.5845588, 0.71405226 });
    regionMap.put("fork_6.jpg", new double[] { 0.115196079, 0.133611143, 0.676470637, 0.6993464 });
    regionMap.put("fork_7.jpg", new double[] { 0.164215669, 0.31008172, 0.767156839, 0.410130739 });
    regionMap.put("fork_8.jpg", new double[] { 0.118872553, 0.318251669, 0.817401946, 0.225490168 });
    regionMap.put("fork_9.jpg", new double[] { 0.18259804, 0.2136765, 0.6335784, 0.643790841 });
    regionMap.put("fork_10.jpg", new double[] { 0.05269608, 0.282303959, 0.8088235, 0.452614367 });
    regionMap.put("fork_11.jpg", new double[] { 0.05759804, 0.0894935, 0.9007353, 0.3251634 });
    regionMap.put("fork_12.jpg", new double[] { 0.3345588, 0.07315363, 0.375, 0.9150327 });
    regionMap.put("fork_13.jpg", new double[] { 0.269607842, 0.194068655, 0.4093137, 0.6732026 });
    regionMap.put("fork_14.jpg", new double[] { 0.143382356, 0.218578458, 0.7977941, 0.295751631 });
    regionMap.put("fork_15.jpg", new double[] { 0.19240196, 0.0633497, 0.5710784, 0.8398692 });
    regionMap.put("fork_16.jpg", new double[] { 0.140931368, 0.480016381, 0.6838235, 0.240196079 });
    regionMap.put("fork_17.jpg", new double[] { 0.305147052, 0.2512582, 0.4791667, 0.5408496 });
    regionMap.put("fork_18.jpg", new double[] { 0.234068632, 0.445702642, 0.6127451, 0.344771236 });
    regionMap.put("fork_19.jpg", new double[] { 0.219362751, 0.141781077, 0.5919118, 0.6683006 });
    regionMap.put("fork_20.jpg", new double[] { 0.180147052, 0.239820287, 0.6887255, 0.235294119 });

下一個程式碼區塊會將影像新增至專案。 您需要變更 GetImage 呼叫的引數以指向您先前下載的分支交叉資料夾。

    Trainings trainer = trainClient.trainings();

    System.out.println("Adding images...");
    for (int i = 1; i <= 20; i++) {
        String fileName = "fork_" + i + ".jpg";
        byte[] contents = GetImage("/fork", fileName);
        AddImageToProject(trainer, project, fileName, contents, forkTag.id(), regionMap.get(fileName));
    }

    for (int i = 1; i <= 20; i++) {
        String fileName = "scissors_" + i + ".jpg";
        byte[] contents = GetImage("/scissors", fileName);
        AddImageToProject(trainer, project, fileName, contents, scissorsTag.id(), regionMap.get(fileName));
    }
}

先前的程式碼片段利用兩個協助程式函式,將影像擷取為資源串流,然後上傳到服務 (您最多可以在單一批次中上傳 64 個影像)。 定義這些方法。

private static void AddImageToProject(Trainings trainer, Project project, String fileName, byte[] contents,
        UUID tag, double[] regionValues) {
    System.out.println("Adding image: " + fileName);
    ImageFileCreateEntry file = new ImageFileCreateEntry().withName(fileName).withContents(contents);

    ImageFileCreateBatch batch = new ImageFileCreateBatch().withImages(Collections.singletonList(file));

    // If Optional region is specified, tack it on and place the tag there,
    // otherwise
    // add it to the batch.
    if (regionValues != null) {
        Region region = new Region().withTagId(tag).withLeft(regionValues[0]).withTop(regionValues[1])
                .withWidth(regionValues[2]).withHeight(regionValues[3]);
        file = file.withRegions(Collections.singletonList(region));
    } else {
        batch = batch.withTagIds(Collections.singletonList(tag));
    }

    trainer.createImagesFromFiles(project.id(), batch);
}

private static byte[] GetImage(String folder, String fileName) {
    try {
        return ByteStreams.toByteArray(CustomVisionSamples.class.getResourceAsStream(folder + "/" + fileName));
    } catch (Exception e) {
        System.out.println(e.getMessage());
        e.printStackTrace();
    }
    return null;
}

為專案定型

此方法會建立專案中的第一個定型反覆運算。 其會查詢服務,直到定型完成為止。

public static String trainProjectOD(CustomVisionTrainingClient trainClient, Project project) {
    Trainings trainer = trainClient.trainings();
    System.out.println("Training...");
    Iteration iteration = trainer.trainProject(project.id(), new TrainProjectOptionalParameter());

    while (iteration.status().equals("Training")) {
        System.out.println("Training Status: " + iteration.status());
        Thread.sleep(5000);
        iteration = trainer.getIteration(project.id(), iteration.id());
    }
    System.out.println("Training Status: " + iteration.status());
}

發佈目前的反覆項目

此方法會讓模型的目前反覆運算可供查詢。 您可以使用模型名稱作為參考,以傳送預測要求。 您必須針對 predictionResourceId 輸入自己的值。 您可以在 Azure 入口網站中資源的 [屬性] 索引標籤上找到預測資源識別碼,該識別碼名為 [資源識別碼]

public static String publishIterationOD(CustomVisionTrainingClient trainClient, Project project) {
    Trainings trainer = trainClient.trainings();

    // The iteration is now trained. Publish it to the prediction endpoint.
    String publishedModelName = "myModel";
    String predictionID = "<your-prediction-resource-ID>";
    trainer.publishIteration(project.id(), iteration.id(), publishedModelName, predictionID);
    return publishedModelName;
}

測試預測端點

此方法會載入測試影像、查詢模型端點,並將預測資料輸出至主控台。

public static void testProjectOD(CustomVisionPredictionClient predictor, Project project) {

    // load test image
    byte[] testImage = GetImage("/ObjectTest", "test_image.jpg");

    // predict
    ImagePrediction results = predictor.predictions().detectImage().withProjectId(project.id())
            .withPublishedName(publishedModelName).withImageData(testImage).execute();

    for (Prediction prediction : results.predictions()) {
        System.out.println(String.format("\t%s: %.2f%% at: %.2f, %.2f, %.2f, %.2f", prediction.tagName(),
                prediction.probability() * 100.0f, prediction.boundingBox().left(), prediction.boundingBox().top(),
                prediction.boundingBox().width(), prediction.boundingBox().height()));
    }
}

執行應用程式

您可以使用下列命令來建置應用程式:

gradle build

使用 gradle run 命令執行應用程式:

gradle run

清除資源

如果您想要清除和移除 Azure AI 服務訂用帳戶,則可以刪除資源或資源群組。 刪除資源群組也會刪除其關聯的任何其他資源。

如果您想要實作您自己的物件偵測專案 (或改為嘗試影像分類專案),則您可能需要刪除此範例中的叉子/剪刀偵測專案。 免費訂用帳戶可使用兩個自訂視覺專案。

自訂視覺網站上,瀏覽至 [專案],然後選取 [我的新專案] 底下的資源回收筒。

螢幕擷取畫面,內有標示為 [我的新專案] 的面板,並含一個 [資源回收筒] 圖示。

後續步驟

現在,您已完成在程式碼中執行物件偵測程序的每個步驟。 此範例會執行單一的訓練反覆項目,但您通常必須對模型進行多次訓練和測試,以便提升其精確度。 下列指南會處理影像分類,但其原則類似於物件偵測。

本指南提供指示和範例程式碼,可協助您開始使用適用於 Node.js 的自訂視覺用戶端程式庫來建置物件偵測模型。 您會建立專案、新增標籤、訓練專案,並使用專案的預測端點 URL 以程式設計方式對其進行測試。 請使用此範例作為自行建置影像辨識應用程式的範本。

附註

如果您想要在「不用」撰寫程式碼的情況下,建立和訓練物件偵測模型,請改為參閱以瀏覽器為基礎的指引

參考文件 (訓練)(預測)| 套件 (npm) (訓練)(預測) | 範例

先決條件

  • Azure 訂用帳戶 - 建立免費帳戶
  • 最新版的 Node.js
  • 擁有您的 Azure 訂用帳戶之後,請在 Azure 入口網站中建立自訂視覺資源,以建立訓練和預測資源。
    • 您可以使用免費定價層 (F0) 來試用服務,之後可升級至付費層以用於實際執行環境。

建立環境變數

在此範例中,您會在執行應用程式的本機電腦上將認證寫入環境變數。

移至 Azure 入口網站。 如果您在 [必要條件] 區段中建立的自訂視覺資源成功部署,請選取 [後續步驟] 底下的 [前往資源] 按鈕。 您可以在 [資源管理] 底下的 [金鑰和端點] 頁面中找到金鑰和端點。 您需要取得訓練資源和預測資源的金鑰以及 API 端點。

您可以在 Azure 入口網站中預測資源的 [屬性] 索引標籤上找到預測資源識別碼,該識別碼名為 [資源識別碼]

秘訣

您也可以使用 https://www.customvision.ai 來取得這些值。 登入之後,請選取右上方的 [設定] 圖示。 在 [設定] 頁面上,您可以檢視所有金鑰、資源識別碼和端點。

若要設定環境變數,請開啟主控台視窗,然後遵循作業系統和開發環境的指示進行。

  • 若要設定 VISION_TRAINING KEY 環境變數,請以您的訓練資源的其中一個金鑰取代 <your-training-key>
  • 若要設定 VISION_TRAINING_ENDPOINT 環境變數,請將 <your-training-endpoint> 取代為您的訓練資源的端點。
  • 若要設定 VISION_PREDICTION_KEY 環境變數,請以您的預測資源的其中一個金鑰取代 <your-prediction-key>
  • 若要設定 VISION_PREDICTION_ENDPOINT 環境變數,請將 <your-prediction-endpoint> 取代為您的預測資源的端點。
  • 若要設定 VISION_PREDICTION_RESOURCE_ID 環境變數,請將 <your-resource-id> 取代為您的預測資源的資源識別碼。

重要事項

我們建議使用適用於 Azure 資源的受控識別搭配 Microsoft Entra ID 驗證,以避免使用在雲端執行的應用程式儲存認證。

請謹慎使用 API 金鑰。 請勿在程式碼中直接包含 API 金鑰,且切勿公開張貼金鑰。 如果使用 API 金鑰,請將這些金鑰安全地儲存在 Azure Key Vault 中、定期輪替金鑰,並使用角色型存取控制和網路存取限制來限制對 Azure Key Vault 的存取。 如需在應用程式中安全地使用 API 金鑰的詳細資訊,請參閱透過 Azure Key Vault 使用 API 金鑰

如需 AI 服務安全性的詳細資訊,請參閱驗證對 Azure AI 服務的要求 (英文)。

setx VISION_TRAINING_KEY <your-training-key>
setx VISION_TRAINING_ENDPOINT <your-training-endpoint>
setx VISION_PREDICTION_KEY <your-prediction-key>
setx VISION_PREDICTION_ENDPOINT <your-prediction-endpoint>
setx VISION_PREDICTION_RESOURCE_ID <your-resource-id>

新增環境變數之後,您可能需要重新啟動任何會讀取環境變數的執行中程式,包括主控台視窗。

設定

建立新的 Node.js 應用程式

在主控台視窗 (例如 cmd、PowerShell 或 Bash) 中,為您的應用程式建立新的目錄,並瀏覽至該目錄。

mkdir myapp && cd myapp

執行命令 npm init,以使用 package.json 檔案建立節點應用程式。

npm init

安裝用戶端程式庫

若要使用適用於 Node.js 的自訂視覺來撰寫影像分析應用程式,則您需要自訂視覺 npm 套件。 若要加以安裝,請在 PowerShell 中執行下列命令:

npm install @azure/cognitiveservices-customvision-training
npm install @azure/cognitiveservices-customvision-prediction

您應用程式的 package.json 檔案會隨著相依性而更新。

建立名為 index.js 的檔案,並匯入下列程式庫:

const util = require('util');
const fs = require('fs');
const TrainingApi = require("@azure/cognitiveservices-customvision-training");
const PredictionApi = require("@azure/cognitiveservices-customvision-prediction");
const msRest = require("@azure/ms-rest-js");

秘訣

想要立刻檢視整個快速入門程式碼檔案嗎? 您可以在 GitHub 上找到該檔案,其中包含本快速入門中的程式碼範例。

為資源的 Azure 端點和金鑰建立變數。

// retrieve environment variables
const trainingKey = process.env["VISION_TRAINING_KEY"];
const trainingEndpoint = process.env["VISION_TRAINING_ENDPOINT"];

const predictionKey = process.env["VISION_PREDICTION_KEY"];
const predictionResourceId = process.env["VISION_PREDICTION_RESOURCE_ID"];
const predictionEndpoint = process.env["VISION_PREDICTION_ENDPOINT"];

同時新增專案名稱的欄位,以及非同步呼叫的逾時參數。

const publishIterationName = "detectModel";
const setTimeoutPromise = util.promisify(setTimeout);

物件模型

名稱 描述
TrainingAPIClient 此類別會處理模型的建立、定型和發佈。
PredictionAPIClient 此類別會處理您的模型查詢,以進行物件偵測預測。
預測 此介面會定義單一影像上的單一預測。 其中包含物件識別碼和名稱的屬性,以及信賴分數。

程式碼範例

這些程式碼片段說明如何使用適用於 JavaScript 的自訂視覺用戶端程式庫來執行下列工作:

驗證用戶端

使用端點和金鑰將用戶端物件具現化。 使用金鑰建立 ApiKeyCredentials 物件,並使用該物件與您的端點建立 TrainingAPIClientPredictionAPIClient 物件。

const credentials = new msRest.ApiKeyCredentials({ inHeader: { "Training-key": trainingKey } });
const trainer = new TrainingApi.TrainingAPIClient(credentials, trainingEndpoint);
const predictor_credentials = new msRest.ApiKeyCredentials({ inHeader: { "Prediction-key": predictionKey } });
const predictor = new PredictionApi.PredictionAPIClient(predictor_credentials, predictionEndpoint);

新增協助程式函式

新增下列函數,以協助進行多個非同步呼叫。 您稍後將會用到此資訊。

const credentials = new msRest.ApiKeyCredentials({ inHeader: { "Training-key": trainingKey } });
const trainer = new TrainingApi.TrainingAPIClient(credentials, trainingEndpoint);
const predictor_credentials = new msRest.ApiKeyCredentials({ inHeader: { "Prediction-key": predictionKey } });
const predictor = new PredictionApi.PredictionAPIClient(predictor_credentials, predictionEndpoint);

建立新的自訂視覺專案

啟動新的函式,以包含所有的自訂視覺函式呼叫。 新增下列程式碼,以建立新的自訂視覺服務專案。

(async () => {
    console.log("Creating project...");
    const domains = await trainer.getDomains()
    const objDetectDomain = domains.find(domain => domain.type === "ObjectDetection");
    const sampleProject = await trainer.createProject("Sample Obj Detection Project", { domainId: objDetectDomain.id });

將標記新增至專案

若要在專案中建立分類標記,請在函式中新增以下程式碼:

const forkTag = await trainer.createTag(sampleProject.id, "Fork");
const scissorsTag = await trainer.createTag(sampleProject.id, "Scissors");

上傳和標記影像

首先,下載此專案的範例影像。 將範例影像資料夾的內容儲存到您的本機裝置。

若要將範例影像新增到專案,在標記建立之後插入下列程式碼。 此程式碼會上傳每個影像及其對應標記。 為物件偵測專案中的影像加上標記時,您必須使用標準化座標來識別每個加上標記的物件所屬的區域。 本教學課程中的區域是以硬式編碼內嵌於程式碼中。 這些區域會在標準化座標中指定週框方塊,且座標會以下列順序指定:左、上、寬度、高度。 您最多可以在單一批次中上傳 64 個影像。

const sampleDataRoot = "Images";

const forkImageRegions = {
    "fork_1.jpg": [0.145833328, 0.3509314, 0.5894608, 0.238562092],
    "fork_2.jpg": [0.294117659, 0.216944471, 0.534313738, 0.5980392],
    "fork_3.jpg": [0.09191177, 0.0682516545, 0.757352948, 0.6143791],
    "fork_4.jpg": [0.254901975, 0.185898721, 0.5232843, 0.594771266],
    "fork_5.jpg": [0.2365196, 0.128709182, 0.5845588, 0.71405226],
    "fork_6.jpg": [0.115196079, 0.133611143, 0.676470637, 0.6993464],
    "fork_7.jpg": [0.164215669, 0.31008172, 0.767156839, 0.410130739],
    "fork_8.jpg": [0.118872553, 0.318251669, 0.817401946, 0.225490168],
    "fork_9.jpg": [0.18259804, 0.2136765, 0.6335784, 0.643790841],
    "fork_10.jpg": [0.05269608, 0.282303959, 0.8088235, 0.452614367],
    "fork_11.jpg": [0.05759804, 0.0894935, 0.9007353, 0.3251634],
    "fork_12.jpg": [0.3345588, 0.07315363, 0.375, 0.9150327],
    "fork_13.jpg": [0.269607842, 0.194068655, 0.4093137, 0.6732026],
    "fork_14.jpg": [0.143382356, 0.218578458, 0.7977941, 0.295751631],
    "fork_15.jpg": [0.19240196, 0.0633497, 0.5710784, 0.8398692],
    "fork_16.jpg": [0.140931368, 0.480016381, 0.6838235, 0.240196079],
    "fork_17.jpg": [0.305147052, 0.2512582, 0.4791667, 0.5408496],
    "fork_18.jpg": [0.234068632, 0.445702642, 0.6127451, 0.344771236],
    "fork_19.jpg": [0.219362751, 0.141781077, 0.5919118, 0.6683006],
    "fork_20.jpg": [0.180147052, 0.239820287, 0.6887255, 0.235294119]
};

const scissorsImageRegions = {
    "scissors_1.jpg": [0.4007353, 0.194068655, 0.259803921, 0.6617647],
    "scissors_2.jpg": [0.426470578, 0.185898721, 0.172794119, 0.5539216],
    "scissors_3.jpg": [0.289215684, 0.259428144, 0.403186262, 0.421568632],
    "scissors_4.jpg": [0.343137264, 0.105833367, 0.332107842, 0.8055556],
    "scissors_5.jpg": [0.3125, 0.09766343, 0.435049027, 0.71405226],
    "scissors_6.jpg": [0.379901975, 0.24308826, 0.32107842, 0.5718954],
    "scissors_7.jpg": [0.341911763, 0.20714055, 0.3137255, 0.6356209],
    "scissors_8.jpg": [0.231617644, 0.08459154, 0.504901946, 0.8480392],
    "scissors_9.jpg": [0.170343131, 0.332957536, 0.767156839, 0.403594762],
    "scissors_10.jpg": [0.204656869, 0.120539248, 0.5245098, 0.743464053],
    "scissors_11.jpg": [0.05514706, 0.159754932, 0.799019635, 0.730392158],
    "scissors_12.jpg": [0.265931368, 0.169558853, 0.5061275, 0.606209159],
    "scissors_13.jpg": [0.241421565, 0.184264734, 0.448529422, 0.6830065],
    "scissors_14.jpg": [0.05759804, 0.05027781, 0.75, 0.882352948],
    "scissors_15.jpg": [0.191176474, 0.169558853, 0.6936275, 0.6748366],
    "scissors_16.jpg": [0.1004902, 0.279036, 0.6911765, 0.477124184],
    "scissors_17.jpg": [0.2720588, 0.131977156, 0.4987745, 0.6911765],
    "scissors_18.jpg": [0.180147052, 0.112369314, 0.6262255, 0.6666667],
    "scissors_19.jpg": [0.333333343, 0.0274019931, 0.443627447, 0.852941155],
    "scissors_20.jpg": [0.158088237, 0.04047389, 0.6691176, 0.843137264]
};

console.log("Adding images...");
let fileUploadPromises = [];

const forkDir = `${sampleDataRoot}/fork`;
const forkFiles = fs.readdirSync(forkDir);

await asyncForEach(forkFiles, async (file) => {
    const region = { tagId: forkTag.id, left: forkImageRegions[file][0], top: forkImageRegions[file][1], width: forkImageRegions[file][2], height: forkImageRegions[file][3] };
    const entry = { name: file, contents: fs.readFileSync(`${forkDir}/${file}`), regions: [region] };
    const batch = { images: [entry] };
    // Wait one second to accommodate rate limit.
    await setTimeoutPromise(1000, null);
    fileUploadPromises.push(trainer.createImagesFromFiles(sampleProject.id, batch));
});

const scissorsDir = `${sampleDataRoot}/scissors`;
const scissorsFiles = fs.readdirSync(scissorsDir);

await asyncForEach(scissorsFiles, async (file) => {
    const region = { tagId: scissorsTag.id, left: scissorsImageRegions[file][0], top: scissorsImageRegions[file][1], width: scissorsImageRegions[file][2], height: scissorsImageRegions[file][3] };
    const entry = { name: file, contents: fs.readFileSync(`${scissorsDir}/${file}`), regions: [region] };
    const batch = { images: [entry] };
    // Wait one second to accommodate rate limit.
    await setTimeoutPromise(1000, null);
    fileUploadPromises.push(trainer.createImagesFromFiles(sampleProject.id, batch));
});

await Promise.all(fileUploadPromises);

重要事項

您必須根據您下載 Foundry Tools Python SDK 範例存放庫的位置,變更影像 (sampleDataRoot) 的路徑。

附註

如果您沒有按住並拖曳公用程式可標示區域的座標,您可以使用 Customvision.ai 上的 Web UI。 此範例已提供座標。

為專案定型

此程式碼會建立預測模型的第一個反覆運算專案。

console.log("Training...");
let trainingIteration = await trainer.trainProject(sampleProject.id);

// Wait for training to complete
console.log("Training started...");
while (trainingIteration.status == "Training") {
    console.log("Training status: " + trainingIteration.status);
    // wait for ten seconds
    await setTimeoutPromise(10000, null);
    trainingIteration = await trainer.getIteration(sampleProject.id, trainingIteration.id)
}
console.log("Training status: " + trainingIteration.status);

發佈目前的反覆項目

此程式碼會將定型的反覆運算發佈至預測端點。 提供給已發佈反覆項目的名稱可用來傳送預測要求。 反覆項目要等到發佈後才可在預測端點中使用。

// Publish the iteration to the end point
await trainer.publishIteration(sampleProject.id, trainingIteration.id, publishIterationName, predictionResourceId);    

測試預測端點

若要將影像傳送到預測端點並擷取預測,請在函式新增以下程式碼。

const testFile = fs.readFileSync(`${sampleDataRoot}/test/test_image.jpg`);
const results = await predictor.detectImage(sampleProject.id, publishIterationName, testFile)

// Show results
console.log("Results:");
results.predictions.forEach(predictedResult => {
    console.log(`\t ${predictedResult.tagName}: ${(predictedResult.probability * 100.0).toFixed(2)}% ${predictedResult.boundingBox.left},${predictedResult.boundingBox.top},${predictedResult.boundingBox.width},${predictedResult.boundingBox.height}`);
});

然後,關閉您的自訂視覺函式並加以呼叫。

})()

執行應用程式

使用快速入門檔案上使用 node 命令執行應用程式。

node index.js

應用程式的輸出應會顯示在主控台中。 接著,您可以確認測試影像 (位於 <sampleDataRoot>/Test/) 是否已正確加上標記,以及偵測的區域是否正確。 您也可以返回自訂視覺網站,然後查看新建立專案的目前狀態。

清除資源

如果您想要實作您自己的物件偵測專案 (或改為嘗試影像分類專案),則您可能需要刪除此範例中的叉子/剪刀偵測專案。 免費訂用帳戶可使用兩個自訂視覺專案。

自訂視覺網站上,瀏覽至 [專案],然後選取 [我的新專案] 底下的資源回收筒。

螢幕擷取畫面,內有標示為 [我的新專案] 的面板,並含一個 [資源回收筒] 圖示。

後續步驟

現在,您已完成在程式碼中執行物件偵測程序的每個步驟。 此範例會執行單一的訓練反覆項目,但您通常必須對模型進行多次訓練和測試,以便提升其精確度。 下列指南會處理影像分類,但其原則類似於物件偵測。

開始使用適用於 Python 的自訂視覺用戶端程式庫。 請遵循下列步驟來安裝套件,並試用建立物件偵測模型的程式碼範例。 您會建立專案、新增標籤、訓練專案,並使用專案的預測端點 URL 以程式設計方式對其進行測試。 請使用此範例作為自行建置影像辨識應用程式的範本。

附註

如果您想要在「不用」撰寫程式碼的情況下,建立和訓練物件偵測模型,請改為參閱以瀏覽器為基礎的指引

參考文件 | 程式庫來源程式碼 | 套件 (PyPI) | 範例

先決條件

  • Azure 訂用帳戶 - 建立免費帳戶
  • Python 3.x
    • 您安裝的 Python 應包含 pip。 您可以在命令列上執行 pip --version 來檢查是否已安裝 pip。 安裝最新版本的 Python 以取得 pip。
  • 擁有您的 Azure 訂用帳戶之後,請在 Azure 入口網站中建立自訂視覺資源,以建立訓練和預測資源。
    • 您可以使用免費定價層 (F0) 來試用服務,之後可升級至付費層以用於實際執行環境。

建立環境變數

在此範例中,您會在執行應用程式的本機電腦上將認證寫入環境變數。

移至 Azure 入口網站。 如果您在 [必要條件] 區段中建立的自訂視覺資源成功部署,請選取 [後續步驟] 底下的 [前往資源] 按鈕。 您可以在 [資源管理] 底下的 [金鑰和端點] 頁面中找到金鑰和端點。 您需要取得訓練資源和預測資源的金鑰以及 API 端點。

您可以在 Azure 入口網站中預測資源的 [屬性] 索引標籤上找到預測資源識別碼,該識別碼名為 [資源識別碼]

秘訣

您也可以使用 https://www.customvision.ai 來取得這些值。 登入之後,請選取右上方的 [設定] 圖示。 在 [設定] 頁面上,您可以檢視所有金鑰、資源識別碼和端點。

若要設定環境變數,請開啟主控台視窗,然後遵循作業系統和開發環境的指示進行。

  • 若要設定 VISION_TRAINING KEY 環境變數,請以您的訓練資源的其中一個金鑰取代 <your-training-key>
  • 若要設定 VISION_TRAINING_ENDPOINT 環境變數,請將 <your-training-endpoint> 取代為您的訓練資源的端點。
  • 若要設定 VISION_PREDICTION_KEY 環境變數,請以您的預測資源的其中一個金鑰取代 <your-prediction-key>
  • 若要設定 VISION_PREDICTION_ENDPOINT 環境變數,請將 <your-prediction-endpoint> 取代為您的預測資源的端點。
  • 若要設定 VISION_PREDICTION_RESOURCE_ID 環境變數,請將 <your-resource-id> 取代為您的預測資源的資源識別碼。

重要事項

我們建議使用適用於 Azure 資源的受控識別搭配 Microsoft Entra ID 驗證,以避免使用在雲端執行的應用程式儲存認證。

請謹慎使用 API 金鑰。 請勿在程式碼中直接包含 API 金鑰,且切勿公開張貼金鑰。 如果使用 API 金鑰,請將這些金鑰安全地儲存在 Azure Key Vault 中、定期輪替金鑰,並使用角色型存取控制和網路存取限制來限制對 Azure Key Vault 的存取。 如需在應用程式中安全地使用 API 金鑰的詳細資訊,請參閱透過 Azure Key Vault 使用 API 金鑰

如需 AI 服務安全性的詳細資訊,請參閱驗證對 Azure AI 服務的要求 (英文)。

setx VISION_TRAINING_KEY <your-training-key>
setx VISION_TRAINING_ENDPOINT <your-training-endpoint>
setx VISION_PREDICTION_KEY <your-prediction-key>
setx VISION_PREDICTION_ENDPOINT <your-prediction-endpoint>
setx VISION_PREDICTION_RESOURCE_ID <your-resource-id>

新增環境變數之後,您可能需要重新啟動任何會讀取環境變數的執行中程式,包括主控台視窗。

設定

安裝用戶端程式庫

若要使用適用於 Python 的自訂視覺來撰寫影像分析應用程式,則您需要自訂視覺用戶端程式庫。 安裝 Python 之後,請在 PowerShell 或主控台視窗中執行下列命令:

pip install azure-cognitiveservices-vision-customvision

建立新的 Python 應用程式

建立新的 Python 檔案,並匯入下列程式庫。

from azure.cognitiveservices.vision.customvision.training import CustomVisionTrainingClient
from azure.cognitiveservices.vision.customvision.prediction import CustomVisionPredictionClient
from azure.cognitiveservices.vision.customvision.training.models import ImageFileCreateBatch, ImageFileCreateEntry, Region
from msrest.authentication import ApiKeyCredentials
import os, time, uuid

秘訣

想要立刻檢視整個快速入門程式碼檔案嗎? 您可以在 GitHub 上找到該檔案,其中包含本快速入門中的程式碼範例。

為資源的 Azure 端點和金鑰建立變數。

# Replace with valid values
ENDPOINT = os.environ["VISION_TRAINING_ENDPOINT"]
training_key = os.environ["VISION_TRAINING_KEY"]
prediction_key = os.environ["VISION_PREDICTION_KEY"]
prediction_resource_id = os.environ["VISION_PREDICTION_RESOURCE_ID"]

物件模型

名稱 描述
CustomVisionTrainingClient 此類別會處理模型的建立、定型和發佈。
CustomVisionPredictionClient 此類別會處理您的模型查詢,以進行物件偵測預測。
ImagePrediction 此類別會定義單一影像上的單一物件預測。 其中包含物件識別碼和名稱的屬性、物件的周框方塊位置,以及信賴分數。

程式碼範例

這些程式碼片段說明如何使用適用於 Python 的自訂視覺用戶端程式庫來執行下列工作:

驗證用戶端

使用您的端點和金鑰將訓練具現化並預測用戶端。 使用您的金鑰建立 ApiKeyServiceClientCredentials 物件,並與您的端點搭配使用,以建立 CustomVisionTrainingClientCustomVisionPredictionClient 物件。

credentials = ApiKeyCredentials(in_headers={"Training-key": training_key})
trainer = CustomVisionTrainingClient(ENDPOINT, credentials)
prediction_credentials = ApiKeyCredentials(in_headers={"Prediction-key": prediction_key})
predictor = CustomVisionPredictionClient(ENDPOINT, prediction_credentials)

建立新的自訂視覺專案

在指令碼中新增下列程式碼,以建立新的自訂視覺服務專案。

當您建立專案時,請參閱 create_project 方法來指定其他選項 (如建立偵測器 Web 入口網站指南中所述)。

publish_iteration_name = "detectModel"

# Find the object detection domain
obj_detection_domain = next(domain for domain in trainer.get_domains() if domain.type == "ObjectDetection" and domain.name == "General")

# Create a new project
print ("Creating project...")
# Use uuid to avoid project name collisions.
project = trainer.create_project(str(uuid.uuid4()), domain_id=obj_detection_domain.id)

將標記新增至專案

若要在專案中建立物件標記,請新增以下程式碼:

# Make two tags in the new project
fork_tag = trainer.create_tag(project.id, "fork")
scissors_tag = trainer.create_tag(project.id, "scissors")

上傳和標記影像

首先,下載此專案的範例影像。 將範例影像資料夾的內容儲存到您的本機裝置。

為物件偵測專案中的影像加上標記時,您必須使用標準化座標來識別每個加上標記的物件所屬的區域。 下列程式碼會為每個範例影像及其已加上標記的區域建立關聯。 這些區域會在標準化座標中指定週框方塊,且座標會以下列順序指定:左、上、寬度、高度。

fork_image_regions = {
    "fork_1": [ 0.145833328, 0.3509314, 0.5894608, 0.238562092 ],
    "fork_2": [ 0.294117659, 0.216944471, 0.534313738, 0.5980392 ],
    "fork_3": [ 0.09191177, 0.0682516545, 0.757352948, 0.6143791 ],
    "fork_4": [ 0.254901975, 0.185898721, 0.5232843, 0.594771266 ],
    "fork_5": [ 0.2365196, 0.128709182, 0.5845588, 0.71405226 ],
    "fork_6": [ 0.115196079, 0.133611143, 0.676470637, 0.6993464 ],
    "fork_7": [ 0.164215669, 0.31008172, 0.767156839, 0.410130739 ],
    "fork_8": [ 0.118872553, 0.318251669, 0.817401946, 0.225490168 ],
    "fork_9": [ 0.18259804, 0.2136765, 0.6335784, 0.643790841 ],
    "fork_10": [ 0.05269608, 0.282303959, 0.8088235, 0.452614367 ],
    "fork_11": [ 0.05759804, 0.0894935, 0.9007353, 0.3251634 ],
    "fork_12": [ 0.3345588, 0.07315363, 0.375, 0.9150327 ],
    "fork_13": [ 0.269607842, 0.194068655, 0.4093137, 0.6732026 ],
    "fork_14": [ 0.143382356, 0.218578458, 0.7977941, 0.295751631 ],
    "fork_15": [ 0.19240196, 0.0633497, 0.5710784, 0.8398692 ],
    "fork_16": [ 0.140931368, 0.480016381, 0.6838235, 0.240196079 ],
    "fork_17": [ 0.305147052, 0.2512582, 0.4791667, 0.5408496 ],
    "fork_18": [ 0.234068632, 0.445702642, 0.6127451, 0.344771236 ],
    "fork_19": [ 0.219362751, 0.141781077, 0.5919118, 0.6683006 ],
    "fork_20": [ 0.180147052, 0.239820287, 0.6887255, 0.235294119 ]
}

scissors_image_regions = {
    "scissors_1": [ 0.4007353, 0.194068655, 0.259803921, 0.6617647 ],
    "scissors_2": [ 0.426470578, 0.185898721, 0.172794119, 0.5539216 ],
    "scissors_3": [ 0.289215684, 0.259428144, 0.403186262, 0.421568632 ],
    "scissors_4": [ 0.343137264, 0.105833367, 0.332107842, 0.8055556 ],
    "scissors_5": [ 0.3125, 0.09766343, 0.435049027, 0.71405226 ],
    "scissors_6": [ 0.379901975, 0.24308826, 0.32107842, 0.5718954 ],
    "scissors_7": [ 0.341911763, 0.20714055, 0.3137255, 0.6356209 ],
    "scissors_8": [ 0.231617644, 0.08459154, 0.504901946, 0.8480392 ],
    "scissors_9": [ 0.170343131, 0.332957536, 0.767156839, 0.403594762 ],
    "scissors_10": [ 0.204656869, 0.120539248, 0.5245098, 0.743464053 ],
    "scissors_11": [ 0.05514706, 0.159754932, 0.799019635, 0.730392158 ],
    "scissors_12": [ 0.265931368, 0.169558853, 0.5061275, 0.606209159 ],
    "scissors_13": [ 0.241421565, 0.184264734, 0.448529422, 0.6830065 ],
    "scissors_14": [ 0.05759804, 0.05027781, 0.75, 0.882352948 ],
    "scissors_15": [ 0.191176474, 0.169558853, 0.6936275, 0.6748366 ],
    "scissors_16": [ 0.1004902, 0.279036, 0.6911765, 0.477124184 ],
    "scissors_17": [ 0.2720588, 0.131977156, 0.4987745, 0.6911765 ],
    "scissors_18": [ 0.180147052, 0.112369314, 0.6262255, 0.6666667 ],
    "scissors_19": [ 0.333333343, 0.0274019931, 0.443627447, 0.852941155 ],
    "scissors_20": [ 0.158088237, 0.04047389, 0.6691176, 0.843137264 ]
}

附註

如果您沒有按住並拖曳公用程式可標示區域的座標,您可以使用 Customvision.ai 上的 Web UI。 此範例已提供座標。

然後,使用此關聯對應,上傳每個範例影像及其區域座標 (您最多可以在單一批次中上傳 64 個影像)。 加入下列程式碼。

base_image_location = os.path.join (os.path.dirname(__file__), "Images")

# Go through the data table above and create the images
print ("Adding images...")
tagged_images_with_regions = []

for file_name in fork_image_regions.keys():
    x,y,w,h = fork_image_regions[file_name]
    regions = [ Region(tag_id=fork_tag.id, left=x,top=y,width=w,height=h) ]

    with open(os.path.join (base_image_location, "fork", file_name + ".jpg"), mode="rb") as image_contents:
        tagged_images_with_regions.append(ImageFileCreateEntry(name=file_name, contents=image_contents.read(), regions=regions))

for file_name in scissors_image_regions.keys():
    x,y,w,h = scissors_image_regions[file_name]
    regions = [ Region(tag_id=scissors_tag.id, left=x,top=y,width=w,height=h) ]

    with open(os.path.join (base_image_location, "scissors", file_name + ".jpg"), mode="rb") as image_contents:
        tagged_images_with_regions.append(ImageFileCreateEntry(name=file_name, contents=image_contents.read(), regions=regions))

upload_result = trainer.create_images_from_files(project.id, ImageFileCreateBatch(images=tagged_images_with_regions))
if not upload_result.is_batch_successful:
    print("Image batch upload failed.")
    for image in upload_result.images:
        print("Image status: ", image.status)
    exit(-1)

附註

你需要根據你之前下載 Foundry Tools Python SDK 範例庫的路徑來更改圖片路徑。

為專案定型

此程式碼會建立預測模型的第一個反覆運算專案。

print ("Training...")
iteration = trainer.train_project(project.id)
while (iteration.status != "Completed"):
    iteration = trainer.get_iteration(project.id, iteration.id)
    print ("Training status: " + iteration.status)
    time.sleep(1)

秘訣

使用選取的標記進行訓練

您可以選擇只在已套用的標記子集上進行訓練。 如果您尚未套用足夠的特定標記,但已套用足夠的其他標記,則您可能需要執行此動作。 在 train_project 呼叫中,將選擇性參數 selected_tags設定為您要使用的標記識別碼字串清單。 此模型會進行訓練以僅識別該清單上的標記。

發佈目前的反覆項目

反覆項目要等到發佈後才可在預測端點中使用。 下列程式碼會讓模型的目前反覆運算可供查詢。

# The iteration is now trained. Publish it to the project endpoint
trainer.publish_iteration(project.id, iteration.id, publish_iteration_name, prediction_resource_id)
print ("Done!")

測試預測端點

若要將影像傳送到預測端點並擷取預測,在檔案結尾處新增以下程式碼:

# Now there is a trained endpoint that can be used to make a prediction

# Open the sample image and get back the prediction results.
with open(os.path.join (base_image_location, "test", "test_image.jpg"), mode="rb") as test_data:
    results = predictor.detect_image(project.id, publish_iteration_name, test_data)

# Display the results.    
for prediction in results.predictions:
    print("\t" + prediction.tag_name + ": {0:.2f}% bbox.left = {1:.2f}, bbox.top = {2:.2f}, bbox.width = {3:.2f}, bbox.height = {4:.2f}".format(prediction.probability * 100, prediction.bounding_box.left, prediction.bounding_box.top, prediction.bounding_box.width, prediction.bounding_box.height))

執行應用程式

執行 CustomVisionQuickstart.py

python CustomVisionQuickstart.py

應用程式的輸出應會顯示在主控台中。 接著,您可以確認測試影像 (位於 <base_image_location>/images/Test 中) 是否已正確加上標記,以及偵測的區域是否正確。 您也可以返回自訂視覺網站,然後查看新建立專案的目前狀態。

清除資源

如果您想要實作您自己的物件偵測專案 (或改為嘗試影像分類專案),則您可能需要刪除此範例中的叉子/剪刀偵測專案。 免費訂用帳戶可使用兩個自訂視覺專案。

自訂視覺網站上,瀏覽至 [專案],然後選取 [我的新專案] 底下的資源回收筒。

螢幕擷取畫面,內有標示為 [我的新專案] 的面板,並含一個 [資源回收筒] 圖示。

後續步驟

現在,您已完成在程式碼中執行物件偵測程序的每個步驟。 此範例會執行單一的訓練反覆項目,但您通常必須對模型進行多次訓練和測試,以便提升其精確度。 下列指南會處理影像分類,但其原則類似於物件偵測。