目标能力
- 為使用者建立智能雲相冊
- 支援人臉檢測,擷取包括人臉位置、表情、年齡等資訊
- 支援人臉分組,可以根據人物搜尋、展示照片
- 支援照片标簽檢測,可以根據标簽對照片進行分類
- 支援地理位置檢測,擷取照片拍攝的具體位置、時間,并且可以進行搜尋
開始之前
首先需要準備好以下内容:
- 準備阿裡雲賬号,申請好調用 API 使用的 AccessKeyId / AccessKeySecret 。
- 開通 OSS 服務,用于存儲使用者照片。在 Demo 中,由我們為您準備好照片,您可以在 這裡下載下傳壓縮包 ,并通過 工具 上傳到位于
的 Bucket 中。華東 2(上海)
在 Demo 中,我們統一上傳到 oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/
目錄下。
- 開通 IMM 服務,并建立一個項目。參考 這裡 。注意:
- 項目類型選擇 圖檔标準型
- QPS 可以選擇 1 QPS,目前為 免費 提供。若您需要線上上環境使用,可以根據需要增加 QPS 限額。
- 項目名稱可以自定義,後面的代碼以
為例。cloud-photo-album-demo
- 地域選擇
。華東 2(上海)
關于地域選擇,請確定 IMM 的項目地域和您照片存儲的 OSS 的地域保持一緻。在 Demo 中均以
華東 2(上海)
為例,您可以自由選擇其他已開放 IMM 服務到地域進行測試。
關于計費,請參考
計費說明。後續我們還會提供更多樣的計費模型,如按使用量計費等,敬請期待。
關于 Demo 的語言。我們使用 Python 3.7 作為 Demo 的語言。如果您使用其他語言,可以使用
API Explorer 生成您需要的語言的示例代碼。參考 Demo 進行簡單替換即可。我們支援 Java / Node.js / Go / PHP / Python / .Net / Ruby 。
建立相冊
相冊是一系列照片的集合,在 IMM 中對應
媒體集Set
這個概念。使用者可以在一個相冊中進行搜尋、人物分組等操作,但不允許跨相冊Set進行搜尋、人物分組等操作。我們推薦,對每個使用相冊對 C 端使用者,建立一個相冊,即
Set
#!/usr/bin/env python
#coding=utf-8
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkimm.request.v20170906.CreateSetRequest import CreateSetRequest
# 填入您的 AccessKeyId 和 AccessKeySecret
client = AcsClient('<accessKeyId>', '<accessSecret>', 'cn-shanghai')
# 後續代碼 Demo 省略以上内容
request = CreateSetRequest()
request.set_accept_format('json')
request.set_Project("cloud-photo-album-demo")
request.set_SetId("user-uid-0001")
response = client.do_action_with_exception(request)
# python2: print(response)
print(str(response, encoding='utf-8'))
運作後,傳回結果類似:
{
"RequestId": "4CF93317-6730-44AE-AEB2-30DF2D12A296",
"CreateTime": "2020-02-19T09:32:31.836Z",
"SetName": "",
"ModifyTime": "2020-02-19T09:32:31.836Z",
"SetId": "user-uid-0001"
}
至此我們為 0001 号使用者建立了相冊。
我們這裡在調用 API 時指定了非必選參數為
SetId
,這是為了能夠在實際應用場景時,能夠将相冊
user-uid-0001
和使用者 ID 進行名稱綁定。友善後續通過諸如 GetSet 接口擷取 / 修改這個相冊的資訊。
SetId
的建立數量沒有限制,但一個
Set
内的照片、人臉數量是有限制的。請參考 使用者限額
Set
添加照片
照片檔案實際存儲的位置是 OSS 上,我們需要将這些照片索引到 IMM 的
Set
中。在這個過程中,IMM 會對照片的内容進行讀取、檢測,将其中的中繼資料(如人臉、标簽等)提取出來進行索引。IMM 不會存儲、修改 OSS 上的照片檔案本身。
每個請求隻索引一張照片,是以我們通過一個循環将所有照片添加到相冊中。
image_list = [
# 請注意替換為您自己的 OSS Bucket 和其對應路徑。
# 以下三張為帶 EXIF 資訊(GPS、拍攝時間等)的照片,在武漢拍攝。
# 檔案名供開發者快速識别,和 AI 對中繼資料提取能力無關。
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-flower.jpg",
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-food.jpg",
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-river-sunset.jpg",
# 以下八張為人物照片,兩個人物各四張,用于展示人臉分組能力
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/mayun-01.jpg",
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/mayun-02.jpg",
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/mayun-03.jpg",
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/mayun-04.jpg",
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/zhangyong-01.jpg",
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/zhangyong-02.jpg",
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/zhangyong-03.jpg",
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/zhangyong-04.jpg"
]
for image_uri in image_list:
request = IndexImageRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
request.set_ImageUri(image_uri)
response = client.do_action_with_exception(request)
# python2: print(response)
print(str(response, encoding='utf-8'))
# 因為我們開通的 QPS 限制為 1,是以這裡休眠一段時間來避免觸發流量控制
time.sleep(1.5)
控制台輸出的結果應該類似
{"RemarksD":"","RemarksC":"","ExternalId":"","CreateTime":"2020-02-20T07:32:38.918Z","RequestId":"1D4CF498-2851-4490-BA65-456C48B101E6","ModifyTime":"2020-02-20T07:32:38.918Z","RemarksA":"","SetId":"user-uid-0001","RemarksB":"","ImageUri":"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-flower.jpg"}
...
此時照片已經被添加至相冊中。一般來說,每張照片在添加後 10 秒左右,即可完成其資訊提取。在提取完成後,我們即可進行後續分組、搜尋等操作。
如果您需要确切的知道一張照片完成索引的時間,可以參考 IndexImage API 文檔中的 MNS 通知相關字段,來訂閱索引結果。
擷取照片資訊
下面我們看看 IMM 能檢測哪些資訊。我們先用一張食物照片舉例。使用 GetImage 接口擷取其資訊。
request = GetImageRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
request.set_ImageUri(
"oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-flower.jpg")
response = client.do_action_with_exception(request)
# python2: print(response)
print(str(response, encoding='utf-8'))
結果如:
{
"Celebrity": [],
"FacesModifyTime": "2020-02-20T07:32:45.027Z",
"OCR": [],
"CelebrityFailReason": "",
"Faces": [],
"OCRStatus": "NotProcessed",
"Exif": "...此處略...",
"ImageUri": "oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-flower.jpg",
"AddressStatus": "Success",
"ImageWidth": 3024,
"RemarksD": "",
"ImageFormat": "jpg",
"RemarksC": "",
"AddressModifyTime": "2020-02-20T07:32:48.078Z",
"Orientation": "6",
"CelebrityModifyTime": "",
"ExternalId": "",
"SourceType": "image",
"CelebrityStatus": "NotProcessed",
"AddressFailReason": "",
"TagsModifyTime": "2020-02-20T07:32:48.042Z",
"Location": "30.546285,114.298899",
"ModifyTime": "2020-02-20T07:32:38.918Z",
"FileSize": 10940531,
"Tags": [
{
"TagConfidence": 0.9955320954322815,
"TagLevel": 1,
"TagName": "植物"
},
{
"TagConfidence": 0.9955320954322815,
"TagLevel": 2,
"ParentTagName": "植物",
"TagName": "花"
}
],
"ImageTime": "2019-07-13T01:39:32Z",
"SourceUri": "oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-flower.jpg",
"Address": {
"District": "武昌區",
"Township": "中華路街街道",
"AddressLine": "湖北省武漢市武昌區中華路街街道民主路92号",
"Province": "湖北省",
"Country": "中國",
"City": "武漢市"
},
"CreateTime": "2020-02-20T07:32:38.918Z",
"RequestId": "33B676B0-5299-4F85-B961-25F2E7DE642D",
"FacesStatus": "Success",
"TagsStatus": "Success",
"RemarksA": "",
"SetId": "user-uid-0001",
"ImageHeight": 4032,
"RemarksB": ""
}
我們能看到一些核心資訊:
- 照片資訊相關:圖檔大小、格式、尺寸。照片拍攝時間 `2019-07-13T01:39:32Z` 。
- 标簽相關:植物 -> 花。
- 地理位置相關:GPS 經緯度坐标 `30.546285,114.298899` ,位置在 `湖北省武漢市武昌區中華路街街道民主路92号` 。
我們再來看一個有人物的照片的檢測結果。僅需修改上面代碼的
ImageUri
中檔案名為
zhangyong-02.jpg
。看一下結果:
{
"Faces": [
{
"FaceQuality": 0.8648459911346436,
"Age": 38,
"GenderConfidence": 1,
"Attractive": 0.82,
"EmotionDetails": {
"SAD": 1.4997523622301001E-12,
"SCARED": 1.3939771646015453E-13,
"CALM": 3.39213018785145E-10,
"ANGRY": 3.840853281039322E-14,
"HAPPY": 1,
"DISGUSTED": 5.754174670384235E-14,
"SURPRISED": 9.682720120487986E-13
},
"Gender": "MALE",
"FaceConfidence": 0.9699456095695496,
"Emotion": "HAPPY",
"GroupId": "group-not-grouped",
"FaceId": "2c476fa26dd795a6bbe9f3a781c50a5a8393e7e3e9fec40fcdf0058a6c2cb158",
"FaceAttributes": {
"GlassesConfidence": 1,
"Glasses": "GLASSES",
"HeadPose": {
"Roll": 5.295282363891602,
"Yaw": 0.23102417588233948,
"Pitch": 10.299762725830078
},
"RaceConfidence": 1,
"Beard": "NONE",
"MaskConfidence": 1,
"Race": "YELLOW",
"BeardConfidence": 1,
"FaceBoundary": {
"Top": 50,
"Height": 69,
"Width": 67,
"Left": 206
},
"Mask": "NONE"
}
}
],
...其他略...
}
可以得到一些人臉相關核心資訊:
- 基礎資訊:年齡 38 歲、性别 男、人臉品質 `0.86`
- 心情:開心
- 人臉屬性:
- 戴眼鏡
- 頭部朝向:正臉
- 無胡須
- 黃種人
- 無口罩
- 人臉矩形框的位置
人臉分組
接下來我們對相冊中的照片按人物進行分組。我們
IndexImage
調用完成後,需要等待至少 15 秒,確定圖檔的索引、檢測均完成。接下來調用人臉聚類 API
CreateGroupFacesJob。這個接口會将
Set
内的照片的人臉按照人物進行分組,并将組的
GroupId
寫回到索引資訊中。
request = CreateGroupFacesJobRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
response = client.do_action_with_exception(request)
# python2: print(response)
print(str(response, encoding='utf-8'))
結果類似:
{
"JobType": "GroupImageFacesJob",
"RequestId": "075D76F7-4AD1-4129-BD95-DD6D53581D0B",
"JobId": "GroupImageFacesJob-113a8759-0483-4ad4-9a81-f7bd402c6b40",
"SetId": "user-uid-0001"
}
此時分組人物還在進行中,我們等待 30 秒左右任務即可完成。
聚類遇到的常見問題,可以參考 [人臉聚類相關 FAQ](
https://help.aliyun.com/knowledge_detail/153359.html)人臉搜尋
首先我們要列出所有的人臉分組,即看看這個
Set
内有幾個人物。
request = ListFaceGroupsRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
response = client.do_action_with_exception(request)
# python2: print(response)
print(str(response, encoding='utf-8'))
傳回結果類似:
{
"FaceGroups": [
{
"GroupName": "",
"CreateTime": "2020-02-20T08:12:29.949Z",
"ModifyTime": "2020-02-20T08:12:29.949Z",
"FaceCount": 4,
"GroupId": "Group-714ca168-5a86-4cc7-b4b1-c7f27ca1eb41",
"GroupCoverFace": {
"FaceBoundary": {
"Top": 60,
"Height": 105,
"Width": 127,
"Left": 207
},
"FaceId": "1d2ee16ee556bbce093be0b3e83c508d5c7da05bea32fa6306670befe85671de",
"ImageUri": "oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/mayun-03.jpg"
}
},
{
"GroupName": "",
"CreateTime": "2020-02-20T08:12:29.945Z",
"ModifyTime": "2020-02-20T08:12:29.945Z",
"FaceCount": 4,
"GroupId": "Group-c4474af0-c268-4753-b984-1496cd3bcf7a",
"GroupCoverFace": {
"FaceBoundary": {
"Top": 50,
"Height": 69,
"Width": 67,
"Left": 206
},
"FaceId": "2c476fa26dd795a6bbe9f3a781c50a5a8393e7e3e9fec40fcdf0058a6c2cb158",
"ImageUri": "oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/zhangyong-02.jpg"
}
}
],
"RequestId": "D6202A11-20AC-49D8-B0B0-C3743DA252D0",
"NextMarker":"
從這個 JSON 中我們可以看到,出現了兩個人物分組,和我們的預期一緻。其中幾個關鍵的字段:
-
指出這個分組内的人臉數量。FaceCount
-
是這個分組的唯一 ID,用于搜尋該人物。GroupId
-
是自動選取出來,用作該組封面圖的人臉。您可以通過其GroupCoverFace
和ImageUri
訓示的人臉框,快速截取出人臉部分的圖檔作為該分組頭像,用于給使用者展示。FaceBoundary
我們以
GroupId
Group-714ca168-5a86-4cc7-b4b1-c7f27ca1eb41
的人物為例,搜尋出該人物在
Set
内的其他照片。
request = FindImagesRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
request.set_GroupId("Group-714ca168-5a86-4cc7-b4b1-c7f27ca1eb41")
response = client.do_action_with_exception(request)
for image in json.loads(response)["Images"]:
print(image["ImageUri"])
輸出結果類似:
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/mayun-01.jpg
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/mayun-02.jpg
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/mayun-03.jpg
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/mayun-04.jpg
因為我們命名了圖檔,是以這裡可以很容易看到,所搜尋到的圖檔均為同一人物照片。
按内容分組照片
添加到
Set
内的照片,會預設進行 1600 類标簽的檢測。我們可以通過
ListSetTags接口看看這個相冊内都有哪些内容分組。
request = ListSetTagsRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
response = client.do_action_with_exception(request)
# python2: print(response)
print(str(response, encoding='utf-8'))
{
"Tags": [
{
"TagCount": 7,
"TagLevel": 2,
"TagName": "人物特寫"
},
{
"TagCount": 7,
"TagLevel": 1,
"TagName": "其他場景"
},
...
{
"TagCount": 1,
"TagLevel": 1,
"TagName": "飲食"
}
],
"RequestId": "F1E51BFA-34DB-4D64-9F2E-AB5E215C888E",
"SetId": "user-uid-0001"
}
可以看到這個相冊中有若幹标簽,傳回的結果按标簽出現的次數降序排列。這可以用于展示使用者的相冊中有哪些照片類别。并結合後面關于圖檔搜尋能力的介紹,搜尋對應标簽的照片。
使用各種方式搜尋照片
根據地理位置搜尋
request = FindImagesRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
request.set_AddressLineContentsMatch("武漢")
response = client.do_action_with_exception(request)
for image in json.loads(response)["Images"]:
print(image["ImageUri"])
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-food.jpg
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-river-sunset.jpg
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-flower.jpg
根據照片内容搜尋
request = FindImagesRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
request.set_TagNames('["食物"]')
response = client.do_action_with_exception(request)
for image in json.loads(response)["Images"]:
print(image["ImageUri"])
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-food.jpg
根據照片拍攝時間搜尋
request = FindImagesRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
request.set_ImageTimeRange(
'{"Start":"2019-07-13T00:00:00.0Z","End":"2019-07-14T00:00:00.0Z"}')
response = client.do_action_with_exception(request)
for image in json.loads(response)["Images"]:
print(image["ImageUri"])
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-food.jpg
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-river-sunset.jpg
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/wuhan-flower.jpg
組合搜尋
例如我們搜尋特定人物開心的照片
request = FindImagesRequest()
request.set_accept_format('json')
request.set_SetId("user-uid-0001")
request.set_Project("cloud-photo-album-demo")
request.set_GroupId("Group-c4474af0-c268-4753-b984-1496cd3bcf7a")
request.set_Emotion("HAPPY")
response = client.do_action_with_exception(request)
for image in json.loads(response)["Images"]:
print(image["ImageUri"])
可以看到,根據兩個條件同時過濾出了對應照片。
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/zhangyong-03.jpg
oss://imm-user-wmt-cn-shanghai/cloud-photo-album-demo/zhangyong-02.jpg
列出所有照片
當然,最常見的情況是按照一定順序将所有照片展示給使用者,我們使用
ListImages接口即可。這裡不做代碼示例。
其他功能
增删改查
IMM 對照片、相冊
Set
均提供了增删改查對應接口,您可以參考我們的
API 文檔進行使用。
外部 ID 綁定
我們可以幫助您将每一張照片和您自己的系統的 ID 進行綁定。也可以在照片中額外存儲一些資訊供您搜尋。如照片的權限,照片在您系統内的目錄結構等等。請參考
的 ExternalId 字段和
Remarks字段。
視訊索引
我們支援将視訊索引入相冊,并且對視訊進行截幀,檢測每一幀視訊的人臉、标簽資訊,以便使用者進行搜尋。請參考
IndexVideo接口。
結語
以上就是使用智能媒體管理服務 (IMM) 搭建一個智能雲相冊的方式。可以看出,IMM 可以提供大部分雲相冊所需要的能力,您可以很友善的在服務中內建該能力,而無需擔心資料存儲相關問題。
如果您有其他問題,請進入
釘釘使用者群實時交流。