How to build an intelligent travel journal using Azure AI

Published Jan 25 2021 04:25 PM 3,206 Views
Microsoft

AI capabilities can enhance many types of applications, enabling you to improve your customer experience and solve complex problems. With Azure Cognitive Services, you can easily access and customize industry-leading AI models, using the tools and languages of your choice.

In this blog, we’ll walk through an exercise which you can complete in under an hour, to get started using Azure AI Services. Many of us are dreaming of traveling again, and building this intelligent travel journal app can help you capture memories from your next trip, whenever that may be. We’ll provide high level guidance and sample code to get you started, and we encourage you to play around with the code and get creative with your solution!

 

Features of the application:

  • Capture voice memos, voice tag photos, and transcribe speech to text.
  • Automatically tag your photos based on key phrase extraction and analysis of text in pictures.
  • Translate tags and text into desired language.
  • Organize your memos by key phrase and find similar travel experiences you enjoyed with AI-powered search.

travel blog app image.jpg

 

Prerequisites:

 

Key Azure technologies:

NOTE:  For more information refer to the “References.txt” file under respective folders within JournalHelper library project in the provided sample solution with this blog.

 

Solution Architecture

 

travel blog architecture image.png

 

App Architecture Description:

  • User records a voice memo; for example, to accompany an image they’ve captured. The recorded file is stored in a file repository (alternatively, you could use a database).
  • The recorded voice memo (e.g. .m4a) is converted into desired format (e.g. .wav), using Azure’s Speech Service batch transcription capability.
  • The folder containing voice memos is uploaded to a Blob container.
  • Images are uploaded into a separate container for analysis of any text within the photos, using Azure Computer Vision.
  • Use Translator to translate text to different languages, as needed. This may be useful to translate foreign street signs, menus, or other text in images.
  • Extract tags from the generated text files using Text Analytics, and send tags back to the corresponding image file. Tags can be travel related (#milan, #sunset, #Glacier National Park), or based on geotagging metadata, photo metadata (camera make, exposure, ISO), and more.
  • Create a search indexer with Azure Cognitive Search, and use the generated index to search your intelligent travel journal.

Implementation

Sample code

The entire solution code is available for download at this link. Download/clone and follow instructions in ReadMe.md solution item for further setup.

 

Implementation summary

The sample is implemented using various client libraries and samples available for Azure Cognitive Services. All these services are grouped together into a helper library project named “journalhelper”. In the library we introduce a helper class to help with scenarios that combine various Cognitive Services to achieve desired functionality.

We use “.Net Core console app” as the front end to test the scenarios. This sample also uses another open source library (FotoFly), which is ported to .Net Core here, to access and edit image metadata.

 

High level overview of steps, along with sample code snippets for illustration:

  1. Start by batch transcribing voice memos and extracting key tags from the text output. Group the input voice memos into a folder, upload them into an Azure Blob container or specify a list of their URls, and use batch transcription to get results back into the Azure Blob container, as well as a folder in your file system. The following code snippet illustrates how helper functions can be grouped together for a specific functionality. It combines local file system, Azure storage containers, and Cognitive Services speech batch transcription API.

 

 

Console.WriteLine("Uploading voice memos folder to blob container...");
Helper.UploadFolderToContainer(
HelperFunctions.GetSampleDataFullPath(customSettings.SampleDataFolders.VoiceMemosFolder),
customSettings.AzureBlobContainers.InputVoiceMemoFiles, deleteExistingContainer);
Console.WriteLine("Branch Transcribing voice memos using containers...");
//NOTE: Turn the pricing tier for Speech Service to standard for this below to work.

await Helper.BatchTranscribeVoiceMemosAsync(
customSettings.AzureBlobContainers.InputVoiceMemoFiles,
customSettings.AzureBlobContainers.BatchTranscribedJsonResults,
          customSettings.SpeechConfigSettings.Key,
          customSettings.SpeechConfigSettings.Region);

Console.WriteLine("Extract transcribed text files into another container and folder, delete the intermediate container with json files...");

await Helper.ExtractTranscribedTextfromJsonAsync(
customSettings.AzureBlobContainers.BatchTranscribedJsonResults,
customSettings.AzureBlobContainers.InputVoiceMemoFiles,
customSettings.AzureBlobContainers.ExtractedTranscribedTexts,
HelperFunctions.GetSampleDataFullPath(customSettings.SampleDataFolders.BatchTranscribedFolder), true);

 

 

  1. Next, create tags from the transcribed text. Sample helper function using the Text Analytics client library is listed below.

 

 

//text analytics
public static void CreateTagsForFolderItems(string key, string endpoint, string batchTranscribedFolder, string extractedTagsFolder)
{
    if (!Directory.Exists(batchTranscribedFolder))
    {
       Console.WriteLine("Input folder for transcribed files does not exist");
       return;
    }

    // ensure destination folder path exists
    Directory.CreateDirectory(extractedTagsFolder);
    TextAnalyticsClient textClient = TextAnalytics.GetClient(key, endpoint);

    var contentFiles = Directory.EnumerateFiles(batchTranscribedFolder);
    foreach(var contentFile in contentFiles
    {
var tags = TextAnalytics.GetTags(textClient, 
contentFile).ConfigureAwait(false).GetAwaiter().GetResult();

// generate output file with tags 
string outFileName = Path.GetFileNameWithoutExtension(contentFile);
                outFileName += @"_tags.txt";
string outFilePath = Path.Combine(extractedTagsFolder, outFileName);
File.WriteAllLinesAsync(outFilePath, tags).Wait() ;
    }
}

 

 

The actual client library or service calls are made as shown:

 

 

static public async Task<IEnumerable<string>> GetTags(TextAnalyticsClient 
client, string inputTextFilePath)
{
   string inputContent = await File.ReadAllTextAsync(inputTextFilePath);
   var entities = EntityRecognition(client, inputContent);
   var phrases = KeyPhraseExtraction(client, inputContent);
   var tags = new List<string>();
   tags.AddRange(entities);
   tags.AddRange(phrases);
   return tags;
}

 

 

  1. Update tags to the photo/image file, using the open source FotoFly library.  Alternatively, you can update the Blob metadata with these tags and include that in the search index, but the functionality will be limited to using Azure Blob storage.

 

 

string taggedPhotoFile = photoFile.Replace(inputPhotosFolder,    
      OutPhotosFolder);
File.Copy(photoFile, taggedPhotoFile, true);

if (tags.Count > 0)
{
    ImageProperties.SetPhotoTags(taggedPhotoFile, tags);
}

 

 

  1. Other useful functions to complete the scenario are:
    1. Helper.ProcessImageAsync, and
    2. Helper.TranslateFileContent

The first one can be used to extract text from images using OCR or regular text processing using Computer Vision. The second can detect the source language, translate using Azure’s Translator service into the desired output language, and then create more tags for an image file.

  1. Finally, use Azure Cognitive Search to create an index from the extracted text files saved in the Blob container, enabling you to search for documents and create journal text files. For example, you can search for images by cities or countries visited, date, or even cuisines. You can also search for images by camera-related metadata or geolocation.

In this sample we have demonstrated simple built-in skillsets for entity and language detection. The solution can be further enhanced by adding additional data sources to process tagged images and their metadata, and adding additional information to the searches.

NOTE:  The helper functions can be made more generic to take additional skillset input.

 

 

public static async Task CreateSearchIndexerAsync(
    string serviceAdminKey, string searchSvcUrl,
    string cognitiveServiceKey,
    string indexName, string jsonFieldsFilePath,
    string blobConnectionString, string blobContainerName
    )
{
    // Its a temporary arrangment.  This function is not complete
    IEnumerable<SearchField> fields = SearchHelper.LoadFieldsFromJSonFile(jsonFieldsFilePath);

    // create index
    var searchIndex = await 
Search.Search.CreateSearchIndexAsync(serviceAdminKey, 
searchSvcUrl, indexName, fields.ToList());

    // get indexer client
    var indexerClient = 
Search.Search.GetSearchIndexerClient(serviceAdminKey, searchSvcUrl);

    // create azure blob data source
    var dataSource = await 
Search.Search.CreateOrUpdateAzureBlobDataSourceAsync(indexerClient, 
blobConnectionString, indexName, blobContainerName);

    // create indexer

    // create skill set with minimal skills
    List<SearchIndexerSkill> skills = new List<SearchIndexerSkill>();
            skills.Add(Skills.CreateEntityRecognitionSkill());
            skills.Add(Skills.CreateLanguageDetectionSkill());
     var skillSet = await 
Search.Search.CreateOrUpdateSkillSetAsync(indexerClient,
             indexName + "-skillset", skills, cognitiveServiceKey);

     var indexer = await Search.Search.CreateIndexerAsync(indexerClient, 
dataSource, skillSet, searchIndex);

     // wait for some time to have indexer run and load documents
     Thread.Sleep(TimeSpan.FromSeconds(20));

     await Search.Search.CheckIndexerOverallStatusAsync(indexerClient, 
             indexer);
}

 

 

Finally, search documents and generate the corresponding journal files, utilizing the following functions:

  1. Helper.SearchDocuments
  2. Helper.CreateTravelJournal

Additional Ideas

In addition to the functionality described so far, there are many other ways you can  leverage Azure AI to further enhance your intelligent travel journal and learn more advanced scenarios. We encourage you to explore some the following ideas to enrich your app:

  • Add real time voice transcription and store transcriptions in an Azure managed database, to correlate voice transcription with images in context.
  • Include travel tickets and receipts as images for OCR-based image analysis (Form Recognizer) and include them as journal artifacts.
  • Use multiple data sources for a given search index. We have simplified and only included text files to index in this sample, but you can include the tagged photos from a different data source for the same search index.
  • Add custom skills and data extraction for search indexer. Extract metadata from images and include as search content.
  • Extract metadata from video and audio content using Video Indexer.
  • Experiment with Language Understanding and generate more elaborate and relevant search content based on top scoring intents and entities. Sample keywords and questions related to current sample data are included in Objectives.docx solution item.
  • Build a consumer front-end app that stitches all of this together and displays the journal in a UI.
%3CLINGO-SUB%20id%3D%22lingo-sub-2095168%22%20slang%3D%22en-US%22%3EHow%20to%20build%20an%20intelligent%20travel%20journal%20using%20Azure%20AI%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2095168%22%20slang%3D%22en-US%22%3E%3CP%3EAI%20capabilities%20can%20enhance%20many%20types%20of%20applications%2C%20enabling%20you%20to%20improve%20your%20customer%20experience%20and%20solve%20complex%20problems.%20With%20Azure%20Cognitive%20Services%2C%20you%20can%20easily%20access%20and%20customize%20industry-leading%20AI%20models%2C%20using%20the%20tools%20and%20languages%20of%20your%20choice.%3C%2FP%3E%0A%3CP%3EIn%20this%20blog%2C%20we%E2%80%99ll%20walk%20through%20an%20exercise%20which%20you%20can%20complete%20in%20under%20an%20hour%2C%20to%20get%20started%20using%20Azure%20AI%20Services.%20Many%20of%20us%20are%20dreaming%20of%20traveling%20again%2C%20and%20building%20this%20intelligent%20travel%20journal%20app%20can%20help%20you%20capture%20memories%20from%20your%20next%20trip%2C%20whenever%20that%20may%20be.%20We%E2%80%99ll%20provide%20high%20level%20guidance%20and%20sample%20code%20to%20get%20you%20started%2C%20and%20we%20encourage%20you%20to%20play%20around%20with%20the%20code%20and%20get%20creative%20with%20your%20solution!%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20class%3D%22lia-align-left%22%3E%3CSTRONG%3E%3CU%3EFeatures%20of%20the%20application%3C%2FU%3E%3C%2FSTRONG%3E%3CU%3E%3A%3C%2FU%3E%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%20class%3D%22lia-align-left%22%3ECapture%20voice%20memos%2C%20voice%20tag%20photos%2C%20and%20transcribe%20speech%20to%20text.%3C%2FLI%3E%0A%3CLI%20class%3D%22lia-align-left%22%3EAutomatically%20tag%20your%20photos%20based%20on%20key%20phrase%20extraction%20and%20analysis%20of%20text%20in%20pictures.%3C%2FLI%3E%0A%3CLI%20class%3D%22lia-align-left%22%3ETranslate%20tags%20and%20text%20into%20desired%20language.%3C%2FLI%3E%0A%3CLI%20class%3D%22lia-align-left%22%3EOrganize%20your%20memos%20by%20key%20phrase%20and%20find%20similar%20travel%20experiences%20you%20enjoyed%20with%20AI-powered%20search.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%3CSTRONG%3E%3CU%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-center%22%20image-alt%3D%22travel%20blog%20app%20image.jpg%22%20style%3D%22width%3A%20400px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F249197i5D7A090CB0DC4851%2Fimage-size%2Fmedium%3Fv%3D1.0%26amp%3Bpx%3D400%22%20role%3D%22button%22%20title%3D%22travel%20blog%20app%20image.jpg%22%20alt%3D%22travel%20blog%20app%20image.jpg%22%20%2F%3E%3C%2FSPAN%3E%3C%2FU%3E%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSTRONG%3E%3CU%3EPrerequisites%3A%3C%2FU%3E%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3EIf%20you%20don't%20have%20an%20Azure%20subscription%2C%20create%20a%20%3CA%20href%3D%22https%3A%2F%2Fazure.microsoft.com%2Ffree%2Fcognitive-services%2F%3FOCID%3DAID3024570%22%20target%3D%22_self%22%20rel%3D%22noopener%20noreferrer%22%3Efree%20account%3C%2FA%3E%20before%20you%20begin.%20If%20you%20have%20a%20subscription%2C%20log%20in%20to%20the%20%3CA%20href%3D%22https%3A%2F%2Fms.portal.azure.com%2F%3FOCID%3DAID3024570%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3EAzure%20Portal%3C%2FA%3E%3CSPAN%3E.%3C%2FSPAN%3E%3C%2FLI%3E%0A%3CLI%3ETo%20run%20the%20provided%20%3CA%20href%3D%22https%3A%2F%2Fgithub.com%2FAzure-Samples%2FAIDeveloperResources%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Esample%20code%3C%2FA%3E%2C%20you%20will%20need%20%3CA%20href%3D%22https%3A%2F%2Fnam06.safelinks.protection.outlook.com%2F%3Furl%3Dhttps%253A%252F%252Faka.ms%252Fvsdownload%26amp%3Bdata%3D04%257C01%257CMadison.Butzbach%2540microsoft.com%257Cf2e19207835247d0176308d8bde3a4d5%257C72f988bf86f141af91ab2d7cd011db47%257C1%257C0%257C637468134638687346%257CUnknown%257CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%253D%257C1000%26amp%3Bsdata%3DcKXR10KgmYmjZ8k5vFnzlNUcZGMl38oqoXHwsILIKj4%253D%26amp%3Breserved%3D0%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3EVisual%20Studio%202019%3C%2FA%3E%20and%20%3CA%20href%3D%22https%3A%2F%2Fnam06.safelinks.protection.outlook.com%2F%3Furl%3Dhttps%253A%252F%252Fdotnet.microsoft.com%252Flearn%252Fdotnet%252Fhello-world-tutorial%252Fintro%26amp%3Bdata%3D04%257C01%257CMadison.Butzbach%2540microsoft.com%257Cf2e19207835247d0176308d8bde3a4d5%257C72f988bf86f141af91ab2d7cd011db47%257C1%257C0%257C637468134638697298%257CUnknown%257CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%253D%257C1000%26amp%3Bsdata%3DIPinEHwdLDuphf1OMth%252BmbGGiD0Sgy5qk95jAzHLTTA%253D%26amp%3Breserved%3D0%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3E.NET%20Core%203.1%3C%2FA%3E%20or%20above%20(for%20FotoFly)%3C%2FLI%3E%0A%3CLI%3ERefer%20to%20this%20%3CA%20href%3D%22https%3A%2F%2Fnam06.safelinks.protection.outlook.com%2F%3Furl%3Dhttps%253A%252F%252Fdocs.microsoft.com%252Fen-us%252Fdotnet%252Fcore%252Ftutorials%252Fpublishing-with-visual-studio%26amp%3Bdata%3D04%257C01%257CMadison.Butzbach%2540microsoft.com%257Cf2e19207835247d0176308d8bde3a4d5%257C72f988bf86f141af91ab2d7cd011db47%257C1%257C0%257C637468134638697298%257CUnknown%257CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%253D%257C1000%26amp%3Bsdata%3D%252BG5LCd6x4TCjwpvzkzqn1szVyALEW94EaxVd1eFeqww%253D%26amp%3Breserved%3D0%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3Etutorial%3C%2FA%3E%20for%20detailed%20guidance%20on%20how%20to%20publish%20a%20console%20app.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSTRONG%3E%3CU%3EKey%20Azure%20technologies%3A%3C%2FU%3E%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3ESpeech%20Service%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fcognitive-services%2Fspeech-service%2Fbatch-transcription%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Ebatch%20transcription%3C%2FA%3E%20for%20speech%20to%20text%20transcription%3C%2FLI%3E%0A%3CLI%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fcognitive-services%2Ftext-analytics%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3EText%20Analytics%3C%2FA%3E%20for%20key%20phrase%2Fintent%20extraction%3C%2FLI%3E%0A%3CLI%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fcognitive-services%2Fcomputer-vision%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3EComputer%20Vision%3C%2FA%3E%20for%20analyzing%20text%20in%20images%3C%2FLI%3E%0A%3CLI%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fazure%2Fcognitive-services%2Ftranslator%2Freference%2Fv3-0-translate%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3ETranslator%3C%2FA%3E%20to%20normalize%20tags%2Ftext%20into%20desired%20language.%3C%2FLI%3E%0A%3CLI%3EOpen%20Source%20%3CA%20href%3D%22http%3A%2F%2Fwww.java2s.com%2FOpen-Source%2FCSharp_Free_Code%2FWindows_Presentation_Foundation_Library%2FDownload_Fotofly_Photo_Metadata_Library.htm%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3EFotoFly%3C%2FA%3E%20library%20for%20photo%20tagging.%20Alternatively%2C%20you%20can%20use%20blob%20metadata%20but%20functionality%20will%20be%20limited.%3C%2FLI%3E%0A%3CLI%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fsearch%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3EAzure%20Cognitive%20Search%3C%2FA%3E%20for%20AI-powered%20search.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3ENOTE%3A%26nbsp%3B%20%3CEM%3EFor%20more%20information%20refer%20to%20the%20%E2%80%9C%3CU%3EReferences.txt%3C%2FU%3E%E2%80%9D%20file%20under%20respective%20folders%20within%20JournalHelper%20library%20project%20in%20the%20provided%20sample%20solution%20with%20this%20blog.%3C%2FEM%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId--549130026%22%20id%3D%22toc-hId--549130032%22%3ESolution%20Architecture%3C%2FH2%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-center%22%20image-alt%3D%22travel%20blog%20architecture%20image.png%22%20style%3D%22width%3A%20699px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F249198i15CF06412667F92D%2Fimage-size%2Flarge%3Fv%3D1.0%26amp%3Bpx%3D999%22%20role%3D%22button%22%20title%3D%22travel%20blog%20architecture%20image.png%22%20alt%3D%22travel%20blog%20architecture%20image.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-141431448%22%20id%3D%22toc-hId-141431442%22%3E%3CU%3EApp%20Architecture%20Description%3A%3C%2FU%3E%3C%2FH3%3E%0A%3CUL%3E%0A%3CLI%3EUser%20records%20a%20voice%20memo%3B%20for%20example%2C%20to%20accompany%20an%20image%20they%E2%80%99ve%20captured.%20The%20recorded%20file%20is%20stored%20in%20a%20file%20repository%20(alternatively%2C%20you%20could%20use%20a%20%3CA%20href%3D%22https%3A%2F%2Fazure.microsoft.com%2Fsolutions%2Fdatabases%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Edatabase%3C%2FA%3E).%3C%2FLI%3E%0A%3CLI%3EThe%20recorded%20voice%20memo%20(e.g.%20.m4a)%20is%20converted%20into%20desired%20format%20(e.g.%20.wav)%2C%20using%20Azure%E2%80%99s%20Speech%20Service%20batch%20transcription%20capability.%3C%2FLI%3E%0A%3CLI%3EThe%20folder%20containing%20voice%20memos%20is%20uploaded%20to%20a%20Blob%20container.%3C%2FLI%3E%0A%3CLI%3EImages%20are%20uploaded%20into%20a%20separate%20container%20for%20analysis%20of%20any%20text%20within%20the%20photos%2C%20using%20Azure%20Computer%20Vision.%3C%2FLI%3E%0A%3CLI%3EUse%20Translator%20to%20translate%20text%20to%20different%20languages%2C%20as%20needed.%20This%20may%20be%20useful%20to%20translate%20foreign%20street%20signs%2C%20menus%2C%20or%20other%20text%20in%20images.%3C%2FLI%3E%0A%3CLI%3EExtract%20tags%20from%20the%20generated%20text%20files%20using%20Text%20Analytics%2C%20and%20send%20tags%20back%20to%20the%20corresponding%20image%20file.%20Tags%20can%20be%20travel%20related%20(%23milan%2C%20%23sunset%2C%20%23Glacier%20National%20Park)%2C%20or%20based%20on%20geotagging%20metadata%2C%20photo%20metadata%20(camera%20make%2C%20exposure%2C%20ISO)%2C%20and%20more.%3C%2FLI%3E%0A%3CLI%3ECreate%20a%20search%20indexer%20with%20Azure%20Cognitive%20Search%2C%20and%20use%20the%20generated%20index%20to%20search%20your%20intelligent%20travel%20journal.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CH2%20id%3D%22toc-hId-130928344%22%20id%3D%22toc-hId-130928338%22%3EImplementation%3C%2FH2%3E%0A%3CP%3E%3CSTRONG%3E%3CU%3ESample%20code%3C%2FU%3E%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3EThe%20entire%20solution%20code%20is%20available%20for%20download%20at%20this%20%3CA%20href%3D%22https%3A%2F%2Fgithub.com%2FAzure-Samples%2FAIDeveloperResources%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Elink.%3C%2FA%3E%20Download%2Fclone%20and%20follow%20instructions%20in%20ReadMe.md%20solution%20item%20for%20further%20setup.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSTRONG%3E%3CU%3EImplementation%20summary%3C%2FU%3E%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3EThe%20sample%20is%20implemented%20using%20various%20client%20libraries%20and%20samples%20available%20for%20Azure%20Cognitive%20Services.%20All%20these%20services%20are%20grouped%20together%20into%20a%20helper%20library%20project%20named%20%E2%80%9Cjournalhelper%E2%80%9D.%20In%20the%20library%20we%20introduce%20a%20helper%20class%20to%20help%20with%20scenarios%20that%20combine%20various%20Cognitive%20Services%20to%20achieve%20desired%20functionality.%3C%2FP%3E%0A%3CP%3EWe%20use%20%E2%80%9C.Net%20Core%20console%20app%E2%80%9D%20as%20the%20front%20end%20to%20test%20the%20scenarios.%20This%20sample%20also%20uses%20another%20open%20source%20library%20(FotoFly)%2C%20which%20is%20ported%20to%20.Net%20Core%20here%2C%20to%20access%20and%20edit%20image%20metadata.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSTRONG%3E%3CU%3EHigh%20level%20overview%20of%20steps%2C%20along%20with%20sample%20code%20snippets%20for%20illustration%3A%3C%2FU%3E%3C%2FSTRONG%3E%3C%2FP%3E%0A%3COL%3E%0A%3CLI%3EStart%20by%20batch%20transcribing%20voice%20memos%20and%20extracting%20key%20tags%20from%20the%20text%20output.%20Group%20the%20input%20voice%20memos%20into%20a%20folder%2C%20upload%20them%20into%20an%20Azure%20Blob%20container%20or%20specify%20a%20list%20of%20their%20URls%2C%20and%20use%20batch%20transcription%20to%20get%20results%20back%20into%20the%20Azure%20Blob%20container%2C%20as%20well%20as%20a%20folder%20in%20your%20file%20system.%20The%20following%20code%20snippet%20illustrates%20how%20helper%20functions%20can%20be%20grouped%20together%20for%20a%20specific%20functionality.%20It%20combines%20local%20file%20system%2C%20Azure%20storage%20containers%2C%20and%20Cognitive%20Services%20speech%20batch%20transcription%20API.%3C%2FLI%3E%0A%3C%2FOL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3EConsole.WriteLine(%22Uploading%20voice%20memos%20folder%20to%20blob%20container...%22)%3B%0AHelper.UploadFolderToContainer(%0AHelperFunctions.GetSampleDataFullPath(customSettings.SampleDataFolders.VoiceMemosFolder)%2C%0AcustomSettings.AzureBlobContainers.InputVoiceMemoFiles%2C%20deleteExistingContainer)%3B%0AConsole.WriteLine(%22Branch%20Transcribing%20voice%20memos%20using%20containers...%22)%3B%0A%2F%2FNOTE%3A%20Turn%20the%20pricing%20tier%20for%20Speech%20Service%20to%20standard%20for%20this%20below%20to%20work.%0A%0Aawait%20Helper.BatchTranscribeVoiceMemosAsync(%0AcustomSettings.AzureBlobContainers.InputVoiceMemoFiles%2C%0AcustomSettings.AzureBlobContainers.BatchTranscribedJsonResults%2C%0A%20%20%20%20%20%20%20%20%20%20customSettings.SpeechConfigSettings.Key%2C%0A%20%20%20%20%20%20%20%20%20%20customSettings.SpeechConfigSettings.Region)%3B%0A%0AConsole.WriteLine(%22Extract%20transcribed%20text%20files%20into%20another%20container%20and%20folder%2C%20delete%20the%20intermediate%20container%20with%20json%20files...%22)%3B%0A%0Aawait%20Helper.ExtractTranscribedTextfromJsonAsync(%0AcustomSettings.AzureBlobContainers.BatchTranscribedJsonResults%2C%0AcustomSettings.AzureBlobContainers.InputVoiceMemoFiles%2C%0AcustomSettings.AzureBlobContainers.ExtractedTranscribedTexts%2C%0AHelperFunctions.GetSampleDataFullPath(customSettings.SampleDataFolders.BatchTranscribedFolder)%2C%20true)%3B%0A%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3COL%20start%3D%222%22%3E%0A%3CLI%3ENext%2C%20create%20tags%20from%20the%20transcribed%20text.%20Sample%20helper%20function%20using%20the%20Text%20Analytics%20client%20library%20is%20listed%20below.%3C%2FLI%3E%0A%3C%2FOL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3E%2F%2Ftext%20analytics%0Apublic%20static%20void%20CreateTagsForFolderItems(string%20key%2C%20string%20endpoint%2C%20string%20batchTranscribedFolder%2C%20string%20extractedTagsFolder)%0A%7B%0A%20%20%20%20if%20(!Directory.Exists(batchTranscribedFolder))%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20Console.WriteLine(%22Input%20folder%20for%20transcribed%20files%20does%20not%20exist%22)%3B%0A%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F%2F%20ensure%20destination%20folder%20path%20exists%0A%20%20%20%20Directory.CreateDirectory(extractedTagsFolder)%3B%0A%20%20%20%20TextAnalyticsClient%20textClient%20%3D%20TextAnalytics.GetClient(key%2C%20endpoint)%3B%0A%0A%20%20%20%20var%20contentFiles%20%3D%20Directory.EnumerateFiles(batchTranscribedFolder)%3B%0A%20%20%20%20foreach(var%20contentFile%20in%20contentFiles%0A%20%20%20%20%7B%0Avar%20tags%20%3D%20TextAnalytics.GetTags(textClient%2C%20%0AcontentFile).ConfigureAwait(false).GetAwaiter().GetResult()%3B%0A%0A%2F%2F%20generate%20output%20file%20with%20tags%20%0Astring%20outFileName%20%3D%20Path.GetFileNameWithoutExtension(contentFile)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20outFileName%20%2B%3D%20%40%22_tags.txt%22%3B%0Astring%20outFilePath%20%3D%20Path.Combine(extractedTagsFolder%2C%20outFileName)%3B%0AFile.WriteAllLinesAsync(outFilePath%2C%20tags).Wait()%20%3B%0A%20%20%20%20%7D%0A%7D%0A%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EThe%20actual%20client%20library%20or%20service%20calls%20are%20made%20as%20shown%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Estatic%20public%20async%20Task%3CIENUMERABLE%3E%3CSTRING%3E%26gt%3B%20GetTags(TextAnalyticsClient%20%0Aclient%2C%20string%20inputTextFilePath)%0A%7B%0A%20%20%20string%20inputContent%20%3D%20await%20File.ReadAllTextAsync(inputTextFilePath)%3B%0A%20%20%20var%20entities%20%3D%20EntityRecognition(client%2C%20inputContent)%3B%0A%20%20%20var%20phrases%20%3D%20KeyPhraseExtraction(client%2C%20inputContent)%3B%0A%20%20%20var%20tags%20%3D%20new%20List%3CSTRING%3E()%3B%0A%20%20%20tags.AddRange(entities)%3B%0A%20%20%20tags.AddRange(phrases)%3B%0A%20%20%20return%20tags%3B%0A%7D%0A%3C%2FSTRING%3E%3C%2FSTRING%3E%3C%2FIENUMERABLE%3E%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3COL%20start%3D%223%22%3E%0A%3CLI%3EUpdate%20tags%20to%20the%20photo%2Fimage%20file%2C%20using%20the%20open%20source%20FotoFly%20library.%26nbsp%3B%20Alternatively%2C%20you%20can%20update%20the%20Blob%20metadata%20with%20these%20tags%20and%20include%20that%20in%20the%20search%20index%2C%20but%20the%20functionality%20will%20be%20limited%20to%20using%20Azure%20Blob%20storage.%3C%2FLI%3E%0A%3C%2FOL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Estring%20taggedPhotoFile%20%3D%20photoFile.Replace(inputPhotosFolder%2C%20%20%20%20%0A%20%20%20%20%20%20OutPhotosFolder)%3B%0AFile.Copy(photoFile%2C%20taggedPhotoFile%2C%20true)%3B%0A%0Aif%20(tags.Count%20%26gt%3B%200)%0A%7B%0A%20%20%20%20ImageProperties.SetPhotoTags(taggedPhotoFile%2C%20tags)%3B%0A%7D%0A%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3COL%20start%3D%224%22%3E%0A%3CLI%3EOther%20useful%20functions%20to%20complete%20the%20scenario%20are%3A%3COL%3E%0A%3CLI%3EHelper.ProcessImageAsync%2C%20and%3C%2FLI%3E%0A%3CLI%3EHelper.TranslateFileContent%3C%2FLI%3E%0A%3C%2FOL%3E%0A%3C%2FLI%3E%0A%3C%2FOL%3E%0A%3CP%3EThe%20first%20one%20can%20be%20used%20to%20extract%20text%20from%20images%20using%20OCR%20or%20regular%20text%20processing%20using%20Computer%20Vision.%20The%20second%20can%20detect%20the%20source%20language%2C%20translate%20using%20Azure%E2%80%99s%20Translator%20service%20into%20the%20desired%20output%20language%2C%20and%20then%20create%20more%20tags%20for%20an%20image%20file.%3C%2FP%3E%0A%3COL%20start%3D%225%22%3E%0A%3CLI%3EFinally%2C%20use%20Azure%20Cognitive%20Search%20to%20create%20an%20index%20from%20the%20extracted%20text%20files%20saved%20in%20the%20Blob%20container%2C%20enabling%20you%20to%20search%20for%20documents%20and%20create%20journal%20text%20files.%20For%20example%2C%20you%20can%20search%20for%20images%20by%20cities%20or%20countries%20visited%2C%20date%2C%20or%20even%20cuisines.%20You%20can%20also%20search%20for%20images%20by%20camera-related%20metadata%20or%20geolocation.%3C%2FLI%3E%0A%3C%2FOL%3E%0A%3CP%3EIn%20this%20sample%20we%20have%20demonstrated%20simple%20built-in%20skillsets%20for%20entity%20and%20language%20detection.%20The%20solution%20can%20be%20further%20enhanced%20by%20adding%20additional%20data%20sources%20to%20process%20tagged%20images%20and%20their%20metadata%2C%20and%20adding%20additional%20information%20to%20the%20searches.%3C%2FP%3E%0A%3CP%3ENOTE%3A%26nbsp%3B%20%3CEM%3EThe%20helper%20functions%20can%20be%20made%20more%20generic%20to%20take%20additional%20skillset%20input.%3C%2FEM%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Epublic%20static%20async%20Task%20CreateSearchIndexerAsync(%0A%20%20%20%20string%20serviceAdminKey%2C%20string%20searchSvcUrl%2C%0A%20%20%20%20string%20cognitiveServiceKey%2C%0A%20%20%20%20string%20indexName%2C%20string%20jsonFieldsFilePath%2C%0A%20%20%20%20string%20blobConnectionString%2C%20string%20blobContainerName%0A%20%20%20%20)%0A%7B%0A%20%20%20%20%2F%2F%20Its%20a%20temporary%20arrangment.%20%20This%20function%20is%20not%20complete%0A%20%20%20%20IEnumerable%3CSEARCHFIELD%3E%20fields%20%3D%20SearchHelper.LoadFieldsFromJSonFile(jsonFieldsFilePath)%3B%0A%0A%20%20%20%20%2F%2F%20create%20index%0A%20%20%20%20var%20searchIndex%20%3D%20await%20%0ASearch.Search.CreateSearchIndexAsync(serviceAdminKey%2C%20%0AsearchSvcUrl%2C%20indexName%2C%20fields.ToList())%3B%0A%0A%20%20%20%20%2F%2F%20get%20indexer%20client%0A%20%20%20%20var%20indexerClient%20%3D%20%0ASearch.Search.GetSearchIndexerClient(serviceAdminKey%2C%20searchSvcUrl)%3B%0A%0A%20%20%20%20%2F%2F%20create%20azure%20blob%20data%20source%0A%20%20%20%20var%20dataSource%20%3D%20await%20%0ASearch.Search.CreateOrUpdateAzureBlobDataSourceAsync(indexerClient%2C%20%0AblobConnectionString%2C%20indexName%2C%20blobContainerName)%3B%0A%0A%20%20%20%20%2F%2F%20create%20indexer%0A%0A%20%20%20%20%2F%2F%20create%20skill%20set%20with%20minimal%20skills%0A%20%20%20%20List%3CSEARCHINDEXERSKILL%3E%20skills%20%3D%20new%20List%3CSEARCHINDEXERSKILL%3E()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20skills.Add(Skills.CreateEntityRecognitionSkill())%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20skills.Add(Skills.CreateLanguageDetectionSkill())%3B%0A%20%20%20%20%20var%20skillSet%20%3D%20await%20%0ASearch.Search.CreateOrUpdateSkillSetAsync(indexerClient%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20indexName%20%2B%20%22-skillset%22%2C%20skills%2C%20cognitiveServiceKey)%3B%0A%0A%20%20%20%20%20var%20indexer%20%3D%20await%20Search.Search.CreateIndexerAsync(indexerClient%2C%20%0AdataSource%2C%20skillSet%2C%20searchIndex)%3B%0A%0A%20%20%20%20%20%2F%2F%20wait%20for%20some%20time%20to%20have%20indexer%20run%20and%20load%20documents%0A%20%20%20%20%20Thread.Sleep(TimeSpan.FromSeconds(20))%3B%0A%0A%20%20%20%20%20await%20Search.Search.CheckIndexerOverallStatusAsync(indexerClient%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20indexer)%3B%0A%7D%0A%3C%2FSEARCHINDEXERSKILL%3E%3C%2FSEARCHINDEXERSKILL%3E%3C%2FSEARCHFIELD%3E%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EFinally%2C%20search%20documents%20and%20generate%20the%20corresponding%20journal%20files%2C%20utilizing%20the%20following%20functions%3A%3C%2FP%3E%0A%3COL%3E%0A%3CLI%3EHelper.SearchDocuments%3C%2FLI%3E%0A%3CLI%3EHelper.CreateTravelJournal%3C%2FLI%3E%0A%3C%2FOL%3E%0A%3CH2%20id%3D%22toc-hId--1676526119%22%20id%3D%22toc-hId--1676526125%22%3EAdditional%20Ideas%3C%2FH2%3E%0A%3CP%3EIn%20addition%20to%20the%20functionality%20described%20so%20far%2C%20there%20are%20many%20other%20ways%20you%20can%20%26nbsp%3Bleverage%20Azure%20AI%20to%20further%20enhance%20your%20intelligent%20travel%20journal%20and%20learn%20more%20advanced%20scenarios.%20We%20encourage%20you%20to%20explore%20some%20the%20following%20ideas%20to%20enrich%20your%20app%3A%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3EAdd%20real%20time%20voice%20transcription%20and%20store%20transcriptions%20in%20an%20%3CA%20href%3D%22https%3A%2F%2Fazure.microsoft.com%2Fsolutions%2Fdatabases%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3EAzure%20managed%20database%3C%2FA%3E%2C%20to%20correlate%20voice%20transcription%20with%20images%20in%20context.%3C%2FLI%3E%0A%3CLI%3EInclude%20travel%20tickets%20and%20receipts%20as%20images%20for%20OCR-based%20image%20analysis%20(%3CA%20href%3D%22https%3A%2F%2Fazure.microsoft.com%2Fen-gb%2Fservices%2Fcognitive-services%2Fform-recognizer%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3EForm%20Recognizer%3C%2FA%3E)%20and%20include%20them%20as%20journal%20artifacts.%3C%2FLI%3E%0A%3CLI%3EUse%20multiple%20data%20sources%20for%20a%20given%20search%20index.%20We%20have%20simplified%20and%20only%20included%20text%20files%20to%20index%20in%20this%20sample%2C%20but%20you%20can%20include%20the%20tagged%20photos%20from%20a%20different%20data%20source%20for%20the%20same%20search%20index.%3C%2FLI%3E%0A%3CLI%3EAdd%20custom%20skills%20and%20data%20extraction%20for%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fsearch%2Fsearch-indexer-overview%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Esearch%20indexer%3C%2FA%3E.%20Extract%20metadata%20from%20images%20and%20include%20as%20search%20content.%3C%2FLI%3E%0A%3CLI%3EExtract%20metadata%20from%20video%20and%20audio%20content%20using%20%3CA%20href%3D%22https%3A%2F%2Fazure.microsoft.com%2Fservices%2Fmedia-services%2Fvideo-indexer%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3EVideo%20Indexer%3C%2FA%3E.%3C%2FLI%3E%0A%3CLI%3EExperiment%20with%20%3CA%20href%3D%22https%3A%2F%2Fwww.luis.ai%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3ELanguage%20Understanding%3C%2FA%3E%20and%20generate%20more%20elaborate%20and%20relevant%20search%20content%20based%20on%20top%20scoring%20intents%20and%20entities.%20Sample%20keywords%20and%20questions%20related%20to%20current%20sample%20data%20are%20included%20in%20Objectives.docx%20solution%20item.%3C%2FLI%3E%0A%3CLI%3EBuild%20a%20consumer%20front-end%20app%20that%20stitches%20all%20of%20this%20together%20and%20displays%20the%20journal%20in%20a%20UI.%3C%2FLI%3E%0A%3C%2FUL%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-2095168%22%20slang%3D%22en-US%22%3E%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-inline%22%20image-alt%3D%22travel%20journal%20image.png%22%20style%3D%22width%3A%20370px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F249220i02A5D6AF36F35149%2Fimage-size%2Flarge%3Fv%3D1.0%26amp%3Bpx%3D999%22%20role%3D%22button%22%20title%3D%22travel%20journal%20image.png%22%20alt%3D%22travel%20journal%20image.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EExplore%20Azure%20Cognitive%20Services%20by%20building%20a%20travel%20journal%20to%20help%20capture%20memories%20from%20your%20next%20trip.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-TEASER%3E%3CLINGO-LABS%20id%3D%22lingo-labs-2095168%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EAzure%20Cognitive%20Search%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3ECognitive%20Services%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EComputer%20Vision%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EKnowledge%20Mining%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EText%20Analytics%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E
Version history
Last update:
‎Jan 25 2021 04:26 PM
Updated by: