

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 演示模板：使用 `crowd-bounding-box` 的映像注释
<a name="sms-custom-templates-step2-demo1"></a>

当您在 Amazon G SageMaker round Truth 控制台中选择使用自定义模板作为任务类型时，您将进入**自定义标签任务面板**。在该面板中，您可以从多个基本模板中进行选择。模板表示一些最常见的任务，并在您创建自定义标注任务的模板时提供样本。如果您没有使用控制台，或者作为其他补救措施，请参阅 [Amazon SageMaker AI Ground Truth 示例任务 UIs ](https://github.com/aws-samples/amazon-sagemaker-ground-truth-task-uis)，了解各种标签任务类型的演示模板存储库。

此演示适用于**BoundingBox**模板。该演示还适用于任务前后处理数据所需的 Amazon Lambda 函数。在上面的 Github 存储库中，要查找与 Amazon Lambda 函数配合使用的模板，请在模板`{{ task.input.{{<property name>}} }}`中查找。

**Topics**
+ [入门边界框自定义模板](#sms-custom-templates-step2-demo1-base-template)
+ [您自己的边界框自定义模板](#sms-custom-templates-step2-demo1-your-own-template)
+ [您的清单文件](#sms-custom-templates-step2-demo1-manifest)
+ [注释前 Lambda 函数](#sms-custom-templates-step2-demo1-pre-annotation)
+ [注释后 Lambda 函数](#sms-custom-templates-step2-demo1-post-annotation)
+ [标注作业的输出](#sms-custom-templates-step2-demo1-job-output)

## 入门边界框自定义模板
<a name="sms-custom-templates-step2-demo1-base-template"></a>

这是所提供的入门边界框模板。

此自定义模板使用 [Liquid 模板语言](https://shopify.github.io/liquid/)，双大括号之间的每个项都是一个变量。预注解 Amazon Lambda 函数应提供一个名为的对象，`taskInput`并且可以像在模板`{{ task.input.<property name> }}`中一样访问该对象的属性。

## 您自己的边界框自定义模板
<a name="sms-custom-templates-step2-demo1-your-own-template"></a>

举例来说，假设您有大量的动物照片，您可以通过之前的图像分类作业知道图像中的动物种类。现在，您希望在其周围绘制一个边界框。

在入门示例中，有三个变量：`taskObject`、`header` 和 `labels`。

其中每个变量都表示在边界框的不同部分中。
+ `taskObject` 是要注释的照片的 HTTP(S) URL 或 S3 URI。添加的 `| grant_read_access` 是一种筛选器，可将 S3 URI 转换为具有对该资源的短暂访问权限的 HTTPS URL。如果您使用的是 HTTP(S) URL，则不需要该筛选器。
+ `header` 是照片上方要标注的文本，如“在照片中的鸟周围画一个框”。
+ `labels` 是一个数组，表示为 `['item1', 'item2', ...]`。这些标签可以由工作人员分配给他们绘制的不同边界框。您可以有一个或多个标签。

每个变量的名称都来自于注释前 Lambda 响应中的 JSON 对象，上述名称只是建议性的，请使用任何对您有意义的变量名，这样可以提高团队的代码可读性。

**仅在必要时才使用变量**  
如果某个字段不会改变，可以从模板中删除该变量并用该文本替换，否则就必须在清单中的每个对象中重复该文本作为值，或者将其编码到注释前 Lambda 函数中。

**Example ：最终的自定义边界框模板**  
为简单起见，此模板将具有一个变量、一个标签和非常基本的说明。假设您的清单中每个数据对象都有一个“animal”属性，那么这个值就可以在模板的两个部分中重复使用。  
注意在整个模板中重复使用 `{{ task.input.animal }}`。如果您的清单中所有动物名称都以大写字母开头，您可以使用 `{{ task.input.animal | downcase }}`，在需要小写的句子中加入 Liquid 的内置筛选器之一。

## 您的清单文件
<a name="sms-custom-templates-step2-demo1-manifest"></a>

您的清单文件应提供您在模板中使用的变量值。您可以在注释前 Lambda 中对清单数据进行一些转换，但如果您不需要这样做，就可以保持较低的出错风险，而且 Lambda 运行速度也会更快。下面是模板的清单文件示例。

```
{"source-ref": "<S3 image URI>", "animal": "horse"}
{"source-ref": "<S3 image URI>", "animal" : "bird"}
{"source-ref": "<S3 image URI>", "animal" : "dog"}
{"source-ref": "<S3 image URI>", "animal" : "cat"}
```

## 注释前 Lambda 函数
<a name="sms-custom-templates-step2-demo1-pre-annotation"></a>

作为任务设置的一部分，请提供一个 Amazon Lambda 函数的 ARN，该函数可用于处理清单条目并将其传递给模板引擎。

**命名 Lambda 函数**  
命名函数的最佳实践是使用以下四个字符串之一作为函数名称的一部分：`SageMaker`、`Sagemaker`、`sagemaker` 或 `LabelingFunction`。这同时适用于注释前函数和注释后函数。

使用控制台时，如果您的账户拥有的 Amazon Lambda 函数，则系统将提供符合命名要求的函数下拉列表供您选择。

在这个非常基本的示例中，您只是传递来自清单的信息，而不对其进行任何额外的处理。这个注释前函数示例是为 Python 3.7 编写的。

```
import json

def lambda_handler(event, context):
    return {
        "taskInput": event['dataObject']
    }
```

来自清单的 JSON 对象将作为 `event` 对象的子对象提供。`taskInput` 对象内部的属性将作为变量提供给模板，因此只需将 `taskInput` 的值设置为 `event['dataObject']` 即可将清单对象中的所有值传递给模板，而无需单独复制它们。如果您希望向模板发送更多的值，可以将这些值添加到 `taskInput` 对象中。

## 注释后 Lambda 函数
<a name="sms-custom-templates-step2-demo1-post-annotation"></a>

作为作业设置的一部分，提供一个 Amazon Lambda 函数的 ARN，当工作人员完成任务时，可以调用该函数来处理表单数据。这可以很简单，也可以很复杂。如果你想在答案出现时进行整合和评分，你可以应用你选择的评分 and/or 合并算法。如果您想要存储原始数据以进行脱机处理，则这是一个选项。

**提供对注释后 Lambda 的权限**  
注释数据将位于一个文件中，该文件由 `payload` 对象中的 `s3Uri` 字符串指定。要处理传入的注释，即使是简单的传递函数，也需要为 Lambda 分配 `S3ReadOnly` 访问权限，以使其能够读取注释文件。  
在创建 Lambda 的控制台页面中，滚动到**执行角色**面板。选择**从一个或多个模板中创建新角色**。指定角色的名称。从**策略模板**下拉列表中，选择 **Amazon S3 对象只读权限**。保存 Lambda，将保存并选择该角色。

下面是 Python 2.7 中的示例。

```
import json
import boto3
from urlparse import urlparse

def lambda_handler(event, context):
    consolidated_labels = []

    parsed_url = urlparse(event['payload']['s3Uri']);
    s3 = boto3.client('s3')
    textFile = s3.get_object(Bucket = parsed_url.netloc, Key = parsed_url.path[1:])
    filecont = textFile['Body'].read()
    annotations = json.loads(filecont);
    
    for dataset in annotations:
        for annotation in dataset['annotations']:
            new_annotation = json.loads(annotation['annotationData']['content'])
            label = {
                'datasetObjectId': dataset['datasetObjectId'],
                'consolidatedAnnotation' : {
                'content': {
                    event['labelAttributeName']: {
                        'workerId': annotation['workerId'],
                        'boxesInfo': new_annotation,
                        'imageSource': dataset['dataObject']
                        }
                    }
                }
            }
            consolidated_labels.append(label)
    
    return consolidated_labels
```

注释后 Lambda 通常会在事件对象中接收成批的任务结果。该批次将是 Lambda 应该遍历的 `payload` 对象。您发回的将是一个符合 [API 合约](sms-custom-templates-step3.md)的对象。

## 标注作业的输出
<a name="sms-custom-templates-step2-demo1-job-output"></a>

您将在指定的目标 S3 存储桶中以标注作业命名的文件夹中找到作业的输出。它将位于名为 `manifests` 的子文件夹中。

对于边界框任务，您在输出清单找到的输出将有点类似于下面的演示。该示例已清理以供打印。实际输出将是每条记录一行。

**Example ：输出清单中的 JSON**  

```
{
  "source-ref":"<URL>",
  "<{{label attribute name}}>":
    {
       "workerId":"<URL>",
       "imageSource":"<image URL>",
       "boxesInfo":"{\"boundingBox\":{\"boundingBoxes\":[{\"height\":878, \"label\":\"bird\", \"left\":208, \"top\":6, \"width\":809}], \"inputImageProperties\":{\"height\":924, \"width\":1280}}}"},
  "<{{label attribute name}}>-metadata":
    {
      "type":"groundTruth/custom",
      "job_name":"<Labeling job name>",
      "human-annotated":"yes"
    },
  "animal" : "bird"
}
```
请注意原始清单中的 `animal` 附加属性是如何与 `source-ref` 和标注数据同级传递到输出清单的。输入清单中的任何属性，无论是否在模板中使用，都将传递到输出清单中。