|
7 | 7 | "# Azure Cognitive Search sample \n",
|
8 | 8 | "## Passing Images as Binary File References\n",
|
9 | 9 | "\n",
|
10 |
| - "Cognitive Search skillsets that need to pass images to custom skills use a binary file reference to serialize the images to pass them to and from skills. This sample demonstrates an example of how skills can be configured to accept an image as an input from the skillset and return images as outputs to the skillset. This example does nothing more than segment an image based on the layout from OCR. The sole purpose of this sample is to demonstrate how you pass images to skills and how skills can return images.\n", |
| 10 | + "Skillsets that pass images to custom skills use a binary file reference to serialize the images before passing them to other skills. This sample demonstrates how skills can be configured to accept image inputs and return image outputs. \n", |
| 11 | + "\n", |
| 12 | + "While the other steps in this skillset, such as OCR and redaction, have relevance, the key takeaway is configuring and passing binary file references. The custom skill does the heavy lifting. Each input record contains an image that is serialized as a `Base64` encoded string. The input also contains the layout text of image, as returned from the OCR skill. Upon receiving the input, the custom skill segments the image into smaller images based on the coordinates of the layout text. It then returns a list of images, each `Base64` encoded, back to the skillset. While this is not a particularly realistic exercise, it demonstrates techniques that could be leverage in more interesting ways, such as in a [Custom Vision](https://github.com/Azure-Samples/azure-search-power-skills/tree/master/Vision/CustomVision) skill that performs useful inferences on your images.\n", |
| 13 | + "\n", |
| 14 | + "For more information about the skills used in this example, see [OCR skill](https://docs.microsoft.com/azure/search/cognitive-search-skill-ocr), [PII skill](https://docs.microsoft.com/azure/search/cognitive-search-skill-pii-detection), and [custom skills](https://docs.microsoft.com/azure/search/cognitive-search-custom-skill-web-api).\n", |
11 | 15 | "\n"
|
12 | 16 | ]
|
13 | 17 | },
|
|
17 | 21 | "source": [
|
18 | 22 | "### Prerequisites \n",
|
19 | 23 | "\n",
|
20 |
| - "Provision the required services:\n", |
21 |
| - "1. [Azure Cognitive Search](https://docs.microsoft.com/azure/search/search-create-service-portal)\n", |
22 |
| - "2. [Azure Functions](https://docs.microsoft.com/azure/azure-functions/) used for hosting an API endpoint.\n", |
23 |
| - "3. [Storage Account](https://docs.microsoft.com/azure/storage/blobs/)\n" |
| 24 | + "+ [Azure subscription](https://Azure.Microsoft.com/subscription/free)\n", |
| 25 | + "+ [Azure Cognitive Search service](https://docs.microsoft.com/azure/search/search-create-service-portal) (get the full service endpoint and an admin API key)\n", |
| 26 | + "+ [Azure Blob storage service](https://docs.microsoft.com/azure/storage/common/storage-account-create) (get the connection string)\n", |
| 27 | + "+ [Python 3.6+](https://www.python.org/downloads/)\n", |
| 28 | + "+ [Jupyter Notebook](https://jupyter.org/install)\n", |
| 29 | + "+ [Visual Studio Code](https://code.visualstudio.com/download) with the [Azure Functions extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions) and the [Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python)\n", |
| 30 | + "\n", |
| 31 | + "If you adapt this exercise to include more image files, add [Azure Cognitive Services](https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account)." |
24 | 32 | ]
|
25 | 33 | },
|
26 | 34 | {
|
27 | 35 | "cell_type": "markdown",
|
28 | 36 | "metadata": {},
|
29 | 37 | "source": [
|
30 |
| - "### Deploy the Azure functions app \n", |
31 |
| - "The ```SplitImage``` folder contains an Azure function that will accept an input in the [custom skill format](https://docs.microsoft.com/azure/search/cognitive-search-custom-skill-web-api#skill-inputs). \n", |
32 |
| - "Each input record contains an image that is serialized as a ```Base64``` encoded string and the layout text returned from the OCR skill.\n", |
33 |
| - "The skill then segments the image into smaller images based on the coordinates of the layout text. It then returns a list of images, each ```Base64``` encoded back to the skillset. While this is not very useful, you could build a [Custom Vision](https://github.com/Azure-Samples/azure-search-power-skills/tree/master/Vision/CustomVision) skill to perform a useful inference on your images.\n", |
| 38 | + "### Configure inputs\n", |
34 | 39 | "\n",
|
35 |
| - "Follow the [Azure Functions tutorial](https://docs.microsoft.com/azure/developer/python/tutorial-vs-code-serverless-python-05) to deploy the function. Once the deployment completes, navigate to the function app in the portal, select the function (SplitImage) and click the Get Function Url button. Save the function url as we will use it in the next step." |
| 40 | + "Follow the instructions in the [readme](https://github.com/Azure-Samples/azure-search-python-samples/blob/master/Image-Processing/README.md) to set up the inputs used by the indexer, data source, and skillset.\n", |
| 41 | + "\n", |
| 42 | + "Besides connection information, you will need a blob container for the sample JPEG file, and a function app that provides the code used in the custom skill. All the necessary files are provided. The `SplitImage` folder contains an Azure function that will accept an input in the [custom skill format](https://docs.microsoft.com/azure/search/cognitive-search-custom-skill-web-api#skill-inputs). " |
36 | 43 | ]
|
37 | 44 | },
|
38 | 45 | {
|
39 | 46 | "cell_type": "markdown",
|
40 | 47 | "metadata": {},
|
41 | 48 | "source": [
|
42 | 49 | "### Create the enrichment pipeline\n",
|
43 |
| - "In the next few steps we will configure the Cognitive Search enrichment pipeline with the following steps:\n", |
44 |
| - "1. Create a blob storage data source. Ensure you have a blob storage container with at least one file containing images.\n", |
45 |
| - "2. Create a skillset to enrich the documents in the data source\n", |
46 |
| - "3. Create an index\n", |
47 |
| - "4. Create an indexer to move documents from the data source to the index while invoking the skillset\n" |
| 50 | + "In the next few steps, configure the Cognitive Search enrichment pipeline, creating these objects on your search service:\n", |
| 51 | + "1. Create an indexer data source. The data source references a blob storage container with at least one image file.\n", |
| 52 | + "2. Create a skillset that performs image analysis. The skillset references a Cognitive Services account, a custom function app, and a knowledge store.\n", |
| 53 | + "3. Create a search index.\n", |
| 54 | + "4. Create an indexer to move documents from the data source to the index while invoking the skillset.\n" |
48 | 55 | ]
|
49 | 56 | },
|
50 | 57 | {
|
|
59 | 66 | "import json\n",
|
60 | 67 | "import requests\n",
|
61 | 68 | "\n",
|
62 |
| - "# Configure all required variables for Cognitive Search. Replace each with the credentials from your accounts.\n", |
| 69 | + "# Configure all required variables for this exerences. Replace each with the credentials from your accounts.\n", |
63 | 70 | "\n",
|
64 |
| - "# Replace with Search Service name, API key, and endpoint from the Azure portal.\n", |
65 |
| - "search_service = \"\" # In the format \"https://searchservicename.search.windows.net\"\n", |
66 |
| - "api_key = 'your search service API key'\n", |
| 71 | + "# Replace with a full search service endpoint the format \"https://searchservicename.search.windows.net\"\n", |
| 72 | + "# Paste in an admin API key. Both values can be obtained from the Azure portal.\n", |
| 73 | + "search_service = \"https://<YOUR-SEARCH-SERVICE-NAME>.search.windows.net\"\n", |
| 74 | + "api_key = '<YOUR-SEARCH-ADMIN-API-KEY>'\n", |
67 | 75 | "\n",
|
68 | 76 | "# Leave the API version and content_type as they are listed here.\n",
|
69 | 77 | "api_version = '2020-06-30'\n",
|
70 | 78 | "content_type = 'application/json'\n",
|
71 | 79 | "\n",
|
72 |
| - "# Replace with a Cognitive Services all in one key.\n", |
73 |
| - "cog_svcs_key = '' #Required only if processing more than 20 documents\n", |
74 |
| - "cog_svcs_acct = 'your cog services account name'\n", |
| 80 | + "# Replace with a Cognitive Services account name and all-in-one key.\n", |
| 81 | + "# Required only if processing more than 20 documents\n", |
| 82 | + "cog_svcs_key = '' \n", |
| 83 | + "cog_svcs_acct = '' \n", |
75 | 84 | "\n",
|
76 |
| - "#Connection string to the storage account. This will be used for the datasource, knowledge store and cache\n", |
77 |
| - "STORAGECONNSTRING = \"DefaultEndpointsProtocol=https;AccountName=<Storage Acct>;AccountKey=<KEY>;EndpointSuffix=core.windows.net\"\n", |
78 |
| - "# The container with your files containing images\n", |
79 |
| - "datasource_container = 'bfrsample' # Replace with the container containging your files\n", |
80 |
| - "# This sample assumes you will use the same storage account for the datasource, knowledge store and indexer cache. The knowledge store will contain the projected images\n", |
81 |
| - "know_store_cache = STORAGECONNSTRING\n", |
82 |
| - "# Container where the sliced images will be projected to\n", |
| 85 | + "# Your Azure Storage account will be used for the datasource input and knowledge store output\n", |
| 86 | + "# Replace with a connection string to your Azure Storage account. \n", |
| 87 | + "STORAGECONNSTRING = \"DefaultEndpointsProtocol=https;AccountName=<YOUR-STORAGE-ACCOUNT>;AccountKey=<YOUR-ACCOUNT-KEY>;EndpointSuffix=core.windows.net\"\n", |
| 88 | + "# Replace with the blob container containing your image file\n", |
| 89 | + "datasource_container = 'bfr-sample' \n", |
| 90 | + "# Container where the sliced images will be projected to. Use the value provided below.\n", |
83 | 91 | "know_store_container = \"obfuscated\"\n",
|
84 |
| - "skill_uri = \"https://<skillname>.azurewebsites.net/api/SplitImage?code=CODE\"" |
| 92 | + "\n", |
| 93 | + "# Replace with the Function HTTP URL of the app deployed to Azure Function\n", |
| 94 | + "skill_uri = \"<YOUR-FUNCTION-APP-URL\"" |
85 | 95 | ]
|
86 | 96 | },
|
87 | 97 | {
|
88 | 98 | "cell_type": "markdown",
|
89 | 99 | "metadata": {},
|
90 | 100 | "source": [
|
91 |
| - "Create a helper function to invoke the Cognitive Search REST API" |
| 101 | + "Create a helper function to invoke the Cognitive Search REST APIs. " |
92 | 102 | ]
|
93 | 103 | },
|
94 | 104 | {
|
|
153 | 163 | "cell_type": "markdown",
|
154 | 164 | "metadata": {},
|
155 | 165 | "source": [
|
156 |
| - "#### Create the skillset" |
| 166 | + "#### Create the skillset\n", |
| 167 | + "\n", |
| 168 | + "Binary image references are passed as inputs and outputs, starting with \"/document/normalized_images/*\" in the OCR skill. OCR output is text and layout. Only the text component is passed to PIIDectection for analysis and redactive formatting. In the custom skill, the image is sliced into component parts (text and layout from OCR, and PII entity created in the PIIDetection step).\n", |
| 169 | + "\n", |
| 170 | + "Besides skills, a skillset also specifies the knowledge store projections that shape the final output in Blob storage." |
157 | 171 | ]
|
158 | 172 | },
|
159 | 173 | {
|
|
344 | 358 | "cell_type": "markdown",
|
345 | 359 | "metadata": {},
|
346 | 360 | "source": [
|
347 |
| - "#### Create the index" |
| 361 | + "#### Create the index\n", |
| 362 | + "\n", |
| 363 | + "A search index isn't used in this exercise, but because it's an indexer requirement, you'll create one anyway. You can use Search Explorer in the Azure portal to query the index on your own. It will contain text extracted from the image." |
348 | 364 | ]
|
349 | 365 | },
|
350 | 366 | {
|
|
515 | 531 | "cell_type": "markdown",
|
516 | 532 | "metadata": {},
|
517 | 533 | "source": [
|
518 |
| - "#### Create the indexer" |
| 534 | + "#### Create the indexer\n", |
| 535 | + "\n", |
| 536 | + "This step creates the index (you'll run it in a separate step). At run time, the indexer connects to the data source, invokes the skillset, and outputs results. This indexer is scheduled to run every two hours. " |
519 | 537 | ]
|
520 | 538 | },
|
521 | 539 | {
|
|
561 | 579 | " \"sourceFieldName\": \"/document/normalized_images/*/text\",\n",
|
562 | 580 | " \"targetFieldName\": \"image_text\"\n",
|
563 | 581 | " }\n",
|
564 |
| - " ],\n", |
565 |
| - " \"cache\": {\n", |
566 |
| - " \"enableReprocessing\": True,\n", |
567 |
| - " \"storageConnectionString\": f'{know_store_cache}'\n", |
568 |
| - " }\n", |
| 582 | + " ]\n", |
569 | 583 | "}\n",
|
570 | 584 | "r = requests.post(construct_Url(search_service, \"indexers\", None, None, api_version), data=json.dumps(indexer_def), headers=headers)\n",
|
571 | 585 | "print(r)\n",
|
|
577 | 591 | "cell_type": "markdown",
|
578 | 592 | "metadata": {},
|
579 | 593 | "source": [
|
580 |
| - "#### Run the indexer" |
| 594 | + "#### Run the indexer\n", |
| 595 | + "\n", |
| 596 | + "This step executes the indexer you just created. It will take several minutes to process." |
581 | 597 | ]
|
582 | 598 | },
|
583 | 599 | {
|
|
592 | 608 | "#print(json.dumps(res, indent=2))"
|
593 | 609 | ]
|
594 | 610 | },
|
| 611 | + { |
| 612 | + "cell_type": "markdown", |
| 613 | + "metadata": {}, |
| 614 | + "source": [ |
| 615 | + "#### Check status\n", |
| 616 | + "\n", |
| 617 | + "The final step in this exercise is to view results. Before doing so, make sure the lastResult status message indicates \"success\", which means that the indexer completed its work successfully, and the revised image now exists in blob storage." |
| 618 | + ] |
| 619 | + }, |
| 620 | + { |
| 621 | + "cell_type": "code", |
| 622 | + "execution_count": null, |
| 623 | + "metadata": {}, |
| 624 | + "outputs": [], |
| 625 | + "source": [ |
| 626 | + "r = requests.get(construct_Url(search_service, \"indexers\", indexername, \"status\", api_version), data=None, headers=headers)\n", |
| 627 | + "print(r)\n", |
| 628 | + "res = r.json()\n", |
| 629 | + "print(res[\"lastResult\"])" |
| 630 | + ] |
| 631 | + }, |
595 | 632 | {
|
596 | 633 | "cell_type": "markdown",
|
597 | 634 | "metadata": {},
|
598 | 635 | "source": [
|
599 | 636 | "### View Results\n",
|
600 |
| - "The following cell downloads the image so that you can verify skillset success." |
| 637 | + "The following cell downloads the output image so that you can verify skillset success. If you get an error, check the indexer status to make sure the indexer is finished and that there were no errors." |
601 | 638 | ]
|
602 | 639 | },
|
603 | 640 | {
|
|
622 | 659 | " if(count == 3):\n",
|
623 | 660 | " break\n",
|
624 | 661 | "\n",
|
625 |
| - "Image(filename='image2.jpg') " |
| 662 | + "Image(filename='image0.jpg') " |
626 | 663 | ]
|
627 | 664 | },
|
628 | 665 | {
|
629 | 666 | "cell_type": "markdown",
|
630 | 667 | "metadata": {},
|
631 | 668 | "source": [
|
632 | 669 | "### Next Steps\n",
|
633 |
| - "You now know how to pass images into skills and even return images to the skillset. As a next step, you can start from scratch and build a [custom AML Skill](https://docs.microsoft.com/en-us/azure/search/cognitive-search-aml-skill) to perform inferences on images or use the Custom Vision service to build a skill. The power skills github repository has a [sample custom vision skill](https://github.com/Azure-Samples/azure-search-power-skills/tree/master/Vision/CustomVision) to help you get started." |
| 670 | + "In this exercise, you learned how to pass images into skills and return the modified images to the skillset for further processing. \n", |
| 671 | + "\n", |
| 672 | + "As a next step, you can start from scratch and build a [custom AML Skill](https://docs.microsoft.com/azure/search/cognitive-search-aml-skill) to perform inferences on images, or use the Custom Vision service to build a skill. The power skills github repository has a [sample custom vision skill](https://github.com/Azure-Samples/azure-search-power-skills/tree/master/Vision/CustomVision) to help you get started." |
634 | 673 | ]
|
| 674 | + }, |
| 675 | + { |
| 676 | + "cell_type": "code", |
| 677 | + "execution_count": null, |
| 678 | + "metadata": {}, |
| 679 | + "outputs": [], |
| 680 | + "source": [] |
635 | 681 | }
|
636 | 682 | ],
|
637 | 683 | "metadata": {
|
|
650 | 696 | "name": "python",
|
651 | 697 | "nbconvert_exporter": "python",
|
652 | 698 | "pygments_lexer": "ipython3",
|
653 |
| - "version": "3.7.4" |
| 699 | + "version": "3.7.3" |
654 | 700 | }
|
655 | 701 | },
|
656 | 702 | "nbformat": 4,
|
|
0 commit comments