Tutoriel : créer votre propre Windows Vision Skill (C#)
Remarque
L’espace de noms Microsoft.AI.Skills.SkillInterfacePreview est déconseillé, car il est hérité et n’est plus maintenu. Il a été remplacé par un package non surveillé offrant les mêmes fonctionnalités ainsi que des performances et une fiabilité accrues : Microsoft.AI.Skills.SkillInterface. Il est prévu que cette documentation soit mise à jour pour refléter les nouvelles fonctionnalités.
Si vous disposez déjà d’une solution de vision personnalisée, ce tutoriel montre comment wrapper la solution dans une compétence de vision Windows en étendant l’API de base Microsoft.AI.Skills.SkillInterfacePreview.
Nous allons créer une compétence d’analyse des sentiments du visage qui s’appuie sur :
- Les API Windows.Media.FaceAnalysis et Windows.AI.MachineLearning
- Un modèle Machine Learning au format ONNX qui déduit les sentiments à partir d’images de visage
Cette compétence prend :
- Une image d’entrée
Et elle génère :
- Un tenseur de valeurs de score de précision uniques dans la plage [0, 1] pour chaque sentiment qu’elle évalue
- Un tenseur de valeurs flottantes dans la plage [0, 1] définissant le cadre englobant d’un visage en coordonnées relatives : left (x,y), top (x,y), right (x,y) et bottom (x,y)
Le code source complet pour les versions C# et C++/WinRT de cet exemple est disponible dans le dépôt GitHub des exemples :
Ce tutoriel vous guide tout au long des étapes suivantes :
- Implémentation des interfaces principales requises pour une compétence de vision Windows
- Création d’un fichier .nuspec pour produire un package NuGet
- Obfuscation et désobfuscation des fichiers pour masquer leur contenu
Prérequis
- Visual Studio 2019 (ou Visual Studio 2017, version 15.7.4 ou ultérieure)
- Windows 10, version 1809 ou ultérieure
- SDK Windows 10, version 1809 ou ultérieure
- Package NuGet Microsoft.AI.Skills.SkillInterfacePreview
1. Créer et implémenter les principales classes de compétences
Tout d’abord, nous devons implémenter les principales classes de compétence (pour plus d’informations, consultez Concepts d’API importants) :
Ouvrez votre solution de vision personnalisée dans Visual Studio.
a. ISkillDescriptor
Créez et implémentez une classe de descripteur de compétence héritée de ISkillDescriptor qui fournit des informations sur la compétence, fournit une liste des appareils d’exécution pris en charge (processeur, GPU, etc.) et fait office d’objet de fabrique pour la compétence.
Importez l’espace de noms Microsoft.AI.Skills.SkillInterfacePreview et dérivez votre classe de l’interface [ISkillDescriptor][ISkillDescriptor].
... using Microsoft.AI.Skills.SkillInterfacePreview; ... public sealed class FaceSentimentAnalyzerDescriptor : ISkillDescriptor { ... }
Créez deux variables membres destinées à contenir les descriptions des entrées et des sorties requises par la compétence. Ensuite, dans le constructeur du descripteur, remplissez-les de façon appropriée avec les descripteurs de fonctionnalités d’entrée et de sortie. En outre, créez l’objet SkillInformation qui fournit toutes les propriétés de description nécessaires de votre compétence.
... // Member variables to hold input and output descriptions private List<ISkillFeatureDescriptor> m_inputSkillDesc; private List<ISkillFeatureDescriptor> m_outputSkillDesc; // Properties required by the interface public IReadOnlyList<ISkillFeatureDescriptor> InputFeatureDescriptors => m_inputSkillDesc; public IReadOnlyDictionary<string, string> Metadata => null; public IReadOnlyList<ISkillFeatureDescriptor> OutputFeatureDescriptors => m_outputSkillDesc; // Constructor public FaceSentimentAnalyzerDescriptor() { Information = SkillInformation.Create( "FaceSentimentAnalyzer", // Name "Finds a face in the image and infers its predominant sentiment from a set of 8 possible labels", // Description new Guid(0xf8d275ce, 0xc244, 0x4e71, 0x8a, 0x39, 0x57, 0x33, 0x5d, 0x29, 0x13, 0x88), // Id new Windows.ApplicationModel.PackageVersion() { Major = 0, Minor = 0, Build = 0, Revision = 8 }, // Version "Contoso Developer", // Author "Contoso Publishing" // Publisher ); // Describe input feature m_inputSkillDesc = new List<ISkillFeatureDescriptor>(); m_inputSkillDesc.Add( new SkillFeatureImageDescriptor( "InputImage", // skill input feature name "the input image onto which the sentiment analysis runs", true, // isRequired (since this is an input, it is required to be bound before the evaluation occurs) -1, // width -1, // height -1, // maxDimension BitmapPixelFormat.Nv12, BitmapAlphaMode.Ignore) ); // Describe first output feature m_outputSkillDesc = new List<ISkillFeatureDescriptor>(); m_outputSkillDesc.Add( new SkillFeatureTensorDescriptor( "FaceRectangle", // skill output feature name "a face bounding box in relative coordinates (left, top, right, bottom)", false, // isRequired (since this is an output, it automatically get populated after the evaluation occurs) new List<long>() { 4 }, // tensor shape SkillElementKind.Float) ); // Describe second output feature m_outputSkillDesc.Add( new SkillFeatureTensorDescriptor( FaceSentimentScores, // skill output feature name "the prediction score for each class", false, // isRequired (since this is an output, it automatically get populated after the evaluation occurs) new List<long>() { 1, 8 }, // tensor shape SkillElementKind.Float) ); }
Implémentez la méthode requise qui recherche les appareils d’exécution pris en charge sur lesquels il est possible d’exécuter la compétence et les retourne au consommateur. Dans notre cas, nous retournons le processeur et tous les périphériques DirectX prenant en charge D3D version 12 ou ultérieure.
public IAsyncOperation<IReadOnlyList<ISkillExecutionDevice>> GetSupportedExecutionDevicesAsync() { return AsyncInfo.Run(async (token) => { return await Task.Run(() => { var result = new List<ISkillExecutionDevice>(); // Add CPU as supported device result.Add(SkillExecutionDeviceCPU.Create()); // Retrieve a list of DirectX devices available on the system and filter them by keeping only the ones that support DX12+ feature level var devices = SkillExecutionDeviceDirectX.GetAvailableDirectXExecutionDevices(); var compatibleDevices = devices.Where((device) => (device as SkillExecutionDeviceDirectX).MaxSupportedFeatureLevel >= D3DFeatureLevelKind.D3D_FEATURE_LEVEL_12_0); result.AddRange(compatibleDevices); return result as IReadOnlyList<ISkillExecutionDevice>; }); });
Implémentez les méthodes requises pour l’instanciation de votre compétence.
L’une d’entre elles sélectionne le meilleur appareil disponible :
public IAsyncOperation<ISkill> CreateSkillAsync() { return AsyncInfo.Run(async (token) => { // Retrieve the available execution devices var supportedDevices = await GetSupportedExecutionDevicesAsync(); ISkillExecutionDevice deviceToUse = supportedDevices.First(); // Either use the first device returned (CPU) or the highest performing GPU int powerIndex = int.MaxValue; foreach (var device in supportedDevices) { if (device.ExecutionDeviceKind == SkillExecutionDeviceKind.Gpu) { var directXDevice = device as SkillExecutionDeviceDirectX; if (directXDevice.HighPerformanceIndex < powerIndex) { deviceToUse = device; powerIndex = directXDevice.HighPerformanceIndex; } } } return await CreateSkillAsync(deviceToUse); }); }
L’autre utilise l’appareil d’exécution spécifié :
public IAsyncOperation<ISkill> CreateSkillAsync(ISkillExecutionDevice executionDevice) { return AsyncInfo.Run(async (token) => { // Create a skill instance with the executionDevice supplied var skillInstance = await FaceSentimentAnalyzerSkill.CreateAsync(this, executionDevice); return skillInstance as ISkill; }); }
b. ISkillBinding
Créez et implémentez une classe de liaison de compétence héritée de l’interface [ISkillBinding][ISkillBinding] qui contient les variables d’entrée et de sortie consommées et produites par la compétence.
Importez l’espace de noms Microsoft.AI.Skills.SkillInterfacePreview et dérivez votre classe de l’interface [ISkillBinding][ISkillBinding] et son type de collection requis.
... using Microsoft.AI.Skills.SkillInterfacePreview; ... public sealed class FaceSentimentAnalyzerBinding : IReadOnlyDictionary<string, ISkillFeature>, ISkillBinding { ...
Commencez par créer deux variables membres :
- L’une est une classe d’assistance [VisionSkillBindingHelper][VisionSkillBindingHelper] fournie dans l’interface de base et destinée à contenir une fonctionnalité d’image d’entrée nommée « InputImage ».
private VisionSkillBindingHelper m_bindingHelper = null;
- L’autre contient le LearningModelBinding à passer à notre LearningModelSession spécifié en tant qu’argument pour notre constructeur par la suite dans la classe de compétence.
// WinML related member variables internal LearningModelBinding m_winmlBinding = null;
Déclarez les propriétés requises :
// ISkillBinding public ISkillExecutionDevice Device => m_bindingHelper.Device; // IReadOnlyDictionary public bool ContainsKey(string key) => m_bindingHelper.ContainsKey(key); public bool TryGetValue(string key, out ISkillFeature value) => m_bindingHelper.TryGetValue(key, out value); public ISkillFeature this[string key] => m_bindingHelper[key]; public IEnumerable<string> Keys => m_bindingHelper.Keys; public IEnumerable<ISkillFeature> Values => m_bindingHelper.Values; public int Count => m_bindingHelper.Count; public IEnumerator<KeyValuePair<string, ISkillFeature>> GetEnumerator() => m_bindingHelper.AsEnumerable().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => m_bindingHelper.AsEnumerable().GetEnumerator();
Et Implémentez le constructeur. Notez que le constructeur est interne ; dans notre paradigme, les instances ISkillBinding sont créées par la compétence et ne doivent donc pas exposer de constructeur autonome :
// Constructor internal FaceSentimentAnalyzerBinding( ISkillDescriptor descriptor, ISkillExecutionDevice device, LearningModelSession session) { m_bindingHelper = new VisionSkillBindingHelper(descriptor, device); // Create WinML binding m_winmlBinding = new LearningModelBinding(session); }
Créez une énumération qui facilite la lecture des types de sentiments générés par la compétence.
/// Defines the set of possible emotion label scored by this skill public enum SentimentType { neutral = 0, happiness, surprise, sadness, anger, disgust, fear, contempt };
Implémentez des méthodes supplémentaires facultatives qui facilitent l’obtention et la définition des opérations sur la liaison.
/// Returns whether or not a face is found given the bound outputs public bool IsFaceFound { get { ISkillFeature feature = null; if (m_bindingHelper.TryGetValue(FaceSentimentAnalyzerConst.SKILL_OUTPUTNAME_FACERECTANGLE, out feature)) { var faceRect = (feature.FeatureValue as SkillFeatureTensorFloatValue).GetAsVectorView(); return !(faceRect[0] == 0.0f && faceRect[1] == 0.0f && faceRect[2] == 0.0f && faceRect[3] == 0.0f); } else { return false; } } } /// Returns the sentiment with the highest score public SentimentType PredominantSentiment { get { SentimentType predominantSentiment = SentimentType.neutral; ISkillFeature feature = null; if (m_bindingHelper.TryGetValue(FaceSentimentAnalyzerConst.SKILL_OUTPUTNAME_FACESENTIMENTSCORES, out feature)) { var faceSentimentScores = (feature.FeatureValue as SkillFeatureTensorFloatValue).GetAsVectorView(); float maxScore = float.MinValue; for (int i = 0; i < faceSentimentScores.Count; i++) { if (faceSentimentScores[i] > maxScore) { predominantSentiment = (SentimentType)i; maxScore = faceSentimentScores[i]; } } } return predominantSentiment; } } /// Returns the face rectangle public IReadOnlyList<float> FaceRectangle { get { ISkillFeature feature = null; if (m_bindingHelper.TryGetValue(FaceSentimentAnalyzerConst.SKILL_OUTPUTNAME_FACERECTANGLE, out feature)) { return (feature.FeatureValue as SkillFeatureTensorFloatValue).GetAsVectorView(); } else { return null; } } }
c. ISkill
Créez et implémentez une classe de compétence héritée de l’interface [ISkill][ISkill] qui exécute la logique de compétence et produit une sortie en fonction d’un jeu d’entrées. Elle fait également office d’objet de fabrique pour la dérivée ISkillBinding.
Importez l’espace de noms Microsoft.AI.Skills.SkillInterfacePreview et dérivez votre classe de l’interface [ISkill][ISkill].
... using Microsoft.AI.Skills.SkillInterfacePreview; ... public sealed class FaceSentimentAnalyzerSkill : ISkill { ...
Commencez par créer deux variables membres :
- L’une destinée à contenir un FaceDetector pour trouver un visage sur l’image d’entrée.
private FaceDetector m_faceDetector = null;
- Une autre destinée à contenir le LearningModelSession utilisé pour évaluer le modèle d’analyse des sentiments :
private LearningModelSession m_winmlSession = null;
Déclarez les propriétés requises :
public ISkillDescriptor SkillDescriptor { get; private set; } public ISkillExecutionDevice Device { get; private set; }
Et implémentez le constructeur et la méthode de fabrique statique. Notez que le constructeur est privé et que la méthode de fabrique est interne ; dans notre paradigme, les instances ISkill sont créées par le descripteur de la compétence et ne doivent donc pas exposer de constructeur autonome :
// Constructor private FaceSentimentAnalyzerSkill( ISkillDescriptor description, ISkillExecutionDevice device) { SkillDescriptor = description; Device = device; } // ISkill Factory method internal static IAsyncOperation<FaceSentimentAnalyzerSkill> CreateAsync( ISkillDescriptor descriptor, ISkillExecutionDevice device) { return AsyncInfo.Run(async (token) => { // Create instance var skillInstance = new FaceSentimentAnalyzerSkill(descriptor, device); // Instantiate the FaceDetector skillInstance.m_faceDetector = await FaceDetector.CreateAsync(); // Load ONNX model and instantiate LearningModel var modelFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Contoso.FaceSentimentAnalyzer/emotion_ferplus.onnx")); var winmlModel = LearningModel.LoadFromFilePath(modelFile.Path); // Create LearningModelSession skillInstance.m_winmlSession = new LearningModelSession(winmlModel, GetWinMLDevice(device)); return skillInstance; }); }
Implémentez ensuite la méthode de fabrique ISkillBinding :
// ISkillBinding Factory method public IAsyncOperation<ISkillBinding> CreateSkillBindingAsync() { return AsyncInfo.Run((token) => { var completedTask = new TaskCompletionSource<ISkillBinding>(); completedTask.SetResult(new FaceSentimentAnalyzerBinding(SkillDescriptor, Device, m_winmlSession)); return completedTask.Task; }); }
Tout ce qui reste à implémenter maintenant est la logique principale de la compétence par le biais de la méthode EvaluateAsync() déclarée dans l’interface de base. Nous commençons par vérifier la validité et récupérons les fonctionnalités de sortie à remplir.
// Skill core logic public IAsyncAction EvaluateAsync(ISkillBinding binding) { FaceSentimentAnalyzerBinding bindingObj = binding as FaceSentimentAnalyzerBinding; if (bindingObj == null) { throw new ArgumentException("Invalid ISkillBinding parameter: This skill handles evaluation of FaceSentimentAnalyzerBinding instances only"); } return AsyncInfo.Run(async (token) => { // Retrieve input frame from the binding object VideoFrame inputFrame = (binding[FaceSentimentAnalyzerConst.SKILL_INPUTNAME_IMAGE].FeatureValue as SkillFeatureImageValue).VideoFrame; SoftwareBitmap softwareBitmapInput = inputFrame.SoftwareBitmap; // Retrieve a SoftwareBitmap to run face detection if (softwareBitmapInput == null) { if (inputFrame.Direct3DSurface == null) { throw (new ArgumentNullException("An invalid input frame has been bound")); } softwareBitmapInput = await SoftwareBitmap.CreateCopyFromSurfaceAsync(inputFrame.Direct3DSurface); } // Retrieve face rectangle output feature from the binding object var faceRectangleFeature = binding[FaceSentimentAnalyzerConst.SKILL_OUTPUTNAME_FACERECTANGLE]; // Retrieve face sentiment scores output feature from the binding object var faceSentimentScores = binding[FaceSentimentAnalyzerConst.SKILL_OUTPUTNAME_FACESENTIMENTSCORES]; ...
Ensuite, cette compétence particulière se poursuit en 2 étapes :
- Étape 1 : exécutez FaceDetector sur l’image et récupérez le rectangle englobant englobant du visage.
... // Run face detection and retrieve face detection result var faceDetectionResult = await m_faceDetector.DetectFacesAsync(softwareBitmapInput); ...
- Étape 2 : si un visage a été détecté, ajustez le rectangle englobant, normalisez ses coordonnées pour faciliter l’utilisation et poursuivez l’analyse des sentiments de cette partie de l’image à l’aide de Windows.AI.MachineLearning. Une fois l’inférence effectuée, mettez à jour le score de chaque sentiment possible retourné comme résultat.
... // If a face is found, update face rectangle feature if (faceDetectionResult.Count > 0) { // Retrieve the face bound and enlarge it by a factor of 1.5x while also ensuring clamping to frame dimensions BitmapBounds faceBound = faceDetectionResult[0].FaceBox; var additionalOffset = faceBound.Width / 2; faceBound.X = Math.Max(0, faceBound.X - additionalOffset); faceBound.Y = Math.Max(0, faceBound.Y - additionalOffset); faceBound.Width = (uint)Math.Min(faceBound.Width + 2 * additionalOffset, softwareBitmapInput.PixelWidth - faceBound.X); faceBound.Height = (uint)Math.Min(faceBound.Height + 2 * additionalOffset, softwareBitmapInput.PixelHeight - faceBound.Y); // Set the face rectangle SkillFeatureValue in the skill binding object // note that values are in normalized coordinates between [0, 1] for ease of use await faceRectangleFeature.SetFeatureValueAsync( new List<float>() { (float)faceBound.X / softwareBitmapInput.PixelWidth, // left (float)faceBound.Y / softwareBitmapInput.PixelHeight, // top (float)(faceBound.X + faceBound.Width) / softwareBitmapInput.PixelWidth, // right (float)(faceBound.Y + faceBound.Height) / softwareBitmapInput.PixelHeight // bottom }); // Bind the WinML input frame with the adequate face bounds specified as metadata bindingObj.m_winmlBinding.Bind( "Input3", // WinML input feature name defined in ONNX protobuf inputFrame, // VideoFrame new PropertySet() // VideoFrame bounds { { "BitmapBounds", PropertyValue.CreateUInt32Array(new uint[]{ faceBound.X, faceBound.Y, faceBound.Width, faceBound.Height }) } }); // Run WinML evaluation var winMLEvaluationResult = await m_winmlSession.EvaluateAsync(bindingObj.m_winmlBinding, ""); // Retrieve result using the WinML output feature name defined in ONNX protobuf var winMLModelResult = (winMLEvaluationResult.Outputs["Plus692_Output_0"] as TensorFloat).GetAsVectorView(); // Set the SkillFeatureValue in the skill binding object related to the face sentiment scores for each possible SentimentType // note that we SoftMax the output of WinML to give a score normalized between [0, 1] for ease of use var predictionScores = SoftMax(winMLModelResult); await faceSentimentScores.SetFeatureValueAsync(predictionScores); } else // if no face found, reset output SkillFeatureValues with 0s { await faceRectangleFeature.SetFeatureValueAsync(FaceSentimentAnalyzerConst.ZeroFaceRectangleCoordinates); await faceSentimentScores.SetFeatureValueAsync(FaceSentimentAnalyzerConst.ZeroFaceSentimentScores); } }); }
2. Empaqueter votre compétence dans NuGet
Il ne reste plus qu’à compiler votre compétence et à créer un package NuGet à partir de celle-ci afin qu’une application puisse l’ingérer.
(Découvrez-en plus sur les packages NuGet ici)
Pour créer un package NuGet, vous devez écrire un fichier .nuspec comme celui ci-dessous consultez le fichier d’origine dans le dépôt Git. Ce fichier est composé de deux sections principales :
métadonnées : cette partie contient le nom, la description, l’auteur, le propriétaire, la licence et les dépendances. Notez que dans notre cas, nous dépendons du package NuGet Microsoft.AI.Skills.SkillInterfacePreview. Ce package NuGet est également lié à une licence et déclenche une demande d’approbation avant ingestion.
fichiers : cette partie pointe vers vos bits et ressources compilés. Notez que l’emplacement cible pointe vers la version de framework uap10.0.17763. Cela permet de s’assurer que les applications ingérant votre package qui ciblent une version antérieure à 10.0.17763 (version minimale du système d’exploitation requise par cette compétence) recevront un message d’erreur.
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<!-- Required elements-->
<id>Contoso.FaceSentimentAnalyzer_CS</id>
<title>Contoso.FaceSentimentAnalyzer_CS</title>
<version>0.0.0.5</version>
<description>Face Sentiment Analyzer skill sample that extends the Microsoft.AI.Skills.SkillInterfacePreview APIs</description>
<authors>Contoso</authors>
<owners>Contoso</owners>
<copyright>Copyright (c) Microsoft Corporation. All rights reserved.</copyright>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<licenseUrl>https://github.com/Microsoft/WindowsVisionSkillsPreview/blob/master/license/doc/LICENSE_package.md</licenseUrl>
<projectUrl>https://github.com/Microsoft/WindowsVisionSkillsPreview</projectUrl>
<iconUrl>https://github.com/Microsoft/WindowsVisionSkillsPreview/blob/master/doc/Logo.png?raw=true</iconUrl>
<releaseNotes>v0.0.0.5 release https://github.com/Microsoft/WindowsVisionSkillsPreview/releases</releaseNotes>
<dependencies>
<dependency id="Microsoft.AI.Skills.SkillInterfacePreview" version="0.5.2.12" />
</dependencies>
<tags>ComputerVision AI VisionSkill</tags>
</metadata>
<files>
<!-- WinMD, Intellisense and resource files-->
<file src="..\common\emotion_ferplus.onnx" target="lib\uap10.0.17763\Contoso.FaceSentimentAnalyzer" />
<file src="..\cs\FaceSentimentAnalyzer\bin\Release\Contoso.FaceSentimentAnalyzer.winmd" target="lib\uap10.0.17763" />
<file src="..\cs\FaceSentimentAnalyzer\bin\Release\Contoso.FaceSentimentAnalyzer.pri" target="lib\uap10.0.17763" />
<file src="..\common\Contoso.FaceSentimentAnalyzer.xml" target="lib\uap10.0.17763" />
</files>
</package>
Vous devez ensuite empaqueter votre fichier .nuspec à l’aide de nuget.exe (télécharger à partir du site officiel) pour créer un fichier de package NuGet .nupkg. Ouvrez une ligne de commande et accédez à l’emplacement de nuget.exe, puis appelez :
> .\nuget.exe pack <path to your .nuspec>
Pour tester votre package localement, vous pouvez ensuite placer ce fichier .nupkg dans un dossier que vous définissez en tant que flux NuGet dans Visual Studio (consultez la procédure ici).
Bravo, vous avez créé votre première compétence de vision Windows ! Vous pouvez charger la compétence empaquetée sur NuGet.org.
3. Une dernière chose… Obfuscation et désobfuscation des fichiers de ressources pour masquer votre propriété intellectuelle
Pour dissuader le consommateur de falsifier vos ressources de compétence ou d’y accéder (fichiers de modèle, images, etc.), vous pouvez obfusquer les fichiers en guise d’étape de prégénération et les désobfusquer au moment de l’exécution. L’exemple dans notre dépôt GitHub d’exemples contient l’implémentation de classes d’assistance qui tirent parti de Windows.Security.Cryptography pour obfusquer les fichiers au moment de la compilation et les désobfusquer au moment de l’exécution. Notez que cette partie n’est montrée que dans la version C++/WinRT de l’exemple de compétence afin de simplifier la version C#.
L’obfuscation est un événement prébuild que vous pouvez définir dans votre projet afin qu’il l’exécute tout le temps ou qu’il l’exécute une seule fois et utilise la sortie comme ressource directement. Dans cet exemple, nous utilisons un outil compilé dédié (Obfuscator.exe). Vous devez veiller à compiler cet outil avant de l’appeler en tant qu’événement prébuild de la compilation de votre compétence. Notez qu’étant donné qu’il s’exécute sur votre ordinateur de développement au moment de la compilation, vous pouvez le compiler une seule fois à l’aide de n’importe quelle cible et plateforme prises en charge (en l’occurrence, Debug/Win32).
Vous pouvez définir cet événement prébuild dans Visual Studio en procédant comme suit :
Projet C++ : cliquez avec le bouton droit sur votre projet de compétence -> agrandissez Créer un événement -> sélectionnez Événement prébuild -> entrez la Ligne de commande
Projet C# : cliquez avec le bouton droit sur votre projet de compétence -> sélectionnez Créer un événement-> entrez la Ligne de commande de l’événement prébuild
Cette commande : 1 : Copie le fichier de ressource localement 2 : Chiffre le fichier au format .crypt (il peut s’agir de n’importe quel nom d’extension) à l’aide de la logique définie qui nécessite une clé GUID 3 : Supprime le fichier local
Remarque
Nous vous suggérons de modifier la logique de chiffrement proposée dans l’exemple afin de la rendre unique pour votre compétence.
copy $(ProjectDir)..\..\Common\emotion_ferplus.onnx $(ProjectDir) && ^$(ProjectDir)..\Obfuscator\Win32\Debug\Obfuscator.exe $(ProjectDir)emotion_ferplus.onnx $(ProjectDir) emotion_ferplus.crypt 678BD455-4190-45D3-B5DA-41543283C092 && ^del $(ProjectDir)emotion_ferplus.onnx
La désobfuscation est exposé par le biais d’un composant Windows Runtime d’application auxiliaire simple ingéré par la compétence. Sa logique de déchiffrement suit la logique de chiffrement définie à l’étape précédente.
// FaceSentimentAnalyzerSkill.cpp ... #include "winrt/DeobfuscationHelper.h" ... // ISkill Factory method Windows::Foundation::IAsyncOperation<winrt::Contoso::FaceSentimentAnalyzer::FaceSentimentAnalyzerSkill> FaceSentimentAnalyzerSkill::CreateAsync( ISkillDescriptor descriptor, ISkillExecutionDevice device) { ... // Load WinML model auto modelFile = Windows::Storage::StorageFile::GetFileFromApplicationUriAsync(Windows::Foundation::Uri(L"ms-appx:///Contoso.FaceSentimentAnalyzer/" + WINML_MODEL_FILENAME)).get(); // Deobfuscate model file and retrieve LearningModel instance LearningModel learningModel = winrt::DeobfuscationHelper::Deobfuscator::DeobfuscateModelAsync(modelFile, descriptor.Id()).get(); ...
Remarque
Utilisez les ressources suivantes pour obtenir de l’aide sur Windows Vision Skills :
- Pour signaler un bogue, veuillez signaler un problème dans notre plateforme GitHub.
- Pour demander une fonctionnalité, veuillez accéder à la page Windows Developer Feedback.
Commentaires
https://aka.ms/ContentUserFeedback.
Bientôt disponible : tout au long de 2024, nous allons éliminer progressivement GitHub Issues comme mécanisme de commentaires pour le contenu et le remplacer par un nouveau système de commentaires. Pour plus d'informations, consultez :Envoyer et afficher des commentaires pour