avatar

目录
(简单调用篇 08) 红酒识别 - C++ 简单调用

红酒识别能识别图像中的红酒标签,返回红酒名称、国家、产区、酒庄、类型、糖分、葡萄品种、酒品描述等信息,可识别数十万中外红酒

应用场景

  • 红酒介绍与推荐:识别用户拍摄图片中的红酒,提供详细的红酒介绍,同时可结合识别结果进一步为用户提供商品推荐、营养搭配等服务,适用于酒类电商平台、红酒展销、拍照识图软件、美食健康APP等

接口描述

该服务用于识别红酒标签,即对于输入的一张图片(可正常解码,长宽比适宜,且酒标清晰可见),输出图片中的红酒名称、国家、产区、酒庄、类型、糖分、葡萄品种、酒品描述等信息。可识别数十万中外常见红酒。

请求说明

  • HTTP 方法: POST
  • 请求 URL: https://aip.baidubce.com/rest/2.0/image-classify/v1/redwine
  • URL参数: access_token
  • Header 参数: Content-Type = application/x-www-form-urlencoded
  • Body 参数:见下表
参数 是否必选 类型 默认值 说明
image string - 图像数据,base64 编码,要求 base64 编码后大小不超过 4M,最短边至少 15px,最长边最大 4096px, 支持 jpg/png/bmp 格式 。注意:图片需要 base64 编码、去掉编码头后再进行 urlencode

返回说明

返回参数如下表:

字段 是否必选 类型 说明
log_id uint64 请求标识码,随机数,唯一
result dict 识别结果
+hasdetail unit 判断是否返回详细信息(除红酒中文名之外的其他字段),含有返回 1,不含有返回 0
+wineNameCn string 红酒中文名,无法识别返回空,示例:波斯塔瓦经典赤霞珠品丽珠半甜红葡萄酒
+wineNameEn string 红酒英文名,hasdetail = 0 时,表示无法识别,该字段不返回,示例:Bostavan Classic Cabernet
+countryCn string 国家中文名,hasdetail = 0 时,表示无法识别,该字段不返回,示例:摩尔多瓦
+countryEn string 国家英文名,hasdetail = 0 时,表示无法识别,该字段不返回,示例:Moldova
+regionCn string 产区中文名,hasdetail = 0 时,表示无法识别,该字段不返回,示例:波尔多
+regionEn string 产区英文名,hasdetail = 0 时,表示无法识别,该字段不返回,示例:Bordeaux
+subRegionCn string 子产区中文名,hasdetail = 0 时,表示无法识别,该字段不返回,示例:梅多克
+subRegionEn string 子产区英文名,hasdetail = 0 时,表示无法识别,该字段不返回,示例:Medoc
+wineryCn string 酒庄中文名,hasdetail = 0 时,表示无法识别,该字段不返回,示例:波斯塔瓦酒庄
+wineryEn string 酒庄英文名,hasdetail = 0 时,表示无法识别,该字段不返回,示例:Vinaria Bostavan
+classifyByColor string 酒类型,hasdetail = 0 时,表示无法识别,该字段不返回,示例:红葡萄酒
+classifyBySugar string 糖分类型,hasdetail = 0 时,表示无法识别,该字段不返回,示例:半甜型
+color string 色泽,hasdetail = 0 时,表示无法识别,该字段不返回,示例:宝石红色
+grapeCn string 葡萄品种,可能有多种葡萄,hasdetail = 0 时,表示无法识别,该字段不返回,示例:品丽珠;赤霞珠
+grapeEn string 葡萄品种英文名,可能有多种葡萄,hasdetail = 0 时,表示无法识别,该字段不返回,示例:Cabernet Franc;Cabernet Sauvignon
+tasteTemperature string 品尝温度,hasdetail = 0 时,表示无法识别,该字段不返回,示例:6-11℃
+description string 酒品描述,hasdetail = 0 时,表示无法识别,该字段不返回,示例:葡萄酒呈深宝石红色,具有香料、香草和新鲜水果的果香,酒体分明,口感畅顺,果香横溢,单宁软化程度高,让你回味无穷

返回示例如下:

仅识别出红酒名称,hasdetail = 0

json
1
2
3
4
5
6
7
8
{
"log_id": 3450013152046070669,
"result":
{
"wineNameCn": "银色高地阙歌干红",
"hasdetail":0
}
}

识别出详细信息,hasdetail = 1

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"log_id": 2495538539661269738,
"result": {
"classifyByColor": "红葡萄酒/Red Wine",
"subRegionCn": "梅多克",
"wineNameCn": "拉图嘉利庄园红葡萄酒(正牌)",
"subRegionEn": "Medoc",
"regionEn": "Bordeaux",
"color": "深紫红色/Dark Violet",
"wineNameEn": "Chateau La Tour Carnet",
"hasdetail": 1,
"wineryCn": "拉图嘉利庄园",
"classifyBySugar": "干型/Dry",
"tasteTemperature": "16-18℃",
"regionCn": "波尔多",
"wineryEn": "Chateau La Tour-Carnet",
"grapeCn": "",
"grapeEn": "",
"countryCn": "法国",
"countryEn": "France",
"description": "此酒充满红果和黑果味道,并带有矿物质和花香(紫罗兰,玫瑰),混合些许香草气息,单宁柔软,余香悠长。在口中留下清新的味道,香料和香草味道萦绕口中。"
}
}

C++ 代码实现调用

这里假设已经将环境配置好了,环境配置的文章可以参考 Windows 下使用 Vcpkg 配置百度 AI 图像识别 C++开发环境(VS2017)

为了方便,首先根据返回参数定义了一个结构体,该结构体包括了返回参数中的参数,如下:

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
struct RedwineInfo {
bool hasdetail;
std::string wineNameCn; // 红酒中文名
std::string wineNameEn;
std::string countryCn;
std::string countryEn;
std::string regionCn;
std::string regionEn;
std::string subRegionCn;
std::string subRegionEn;
std::string wineryCn; // 酒庄
std::string wineryEn;
std::string classifyByColor; // 类型
std::string classifyBySugar; // 糖分类型
std::string color; // 色泽
std::string grapeCn; // 品种
std::string grapeEn;
std::string tasteTemperature; // 品尝温度
std::string description;

void print() {
std::cout << std::setw(30) << std::setfill('-') << '\n';
std::cout << "name: " << wineNameCn << " - " << wineNameEn << '\n';

if (hasdetail) {
std::cout << "country: " << countryCn << " - " << countryEn << '\n';
std::cout << "region: " << countryCn << " - " << regionEn << '\n';
std::cout << "subregion: " << subRegionCn << " - " << subRegionEn << '\n';
std::cout << "winery: " << wineryCn << " - " << wineryEn << '\n';
std::cout << "classify by color: " << classifyByColor << '\n';
std::cout << "classify by sugar: " << classifyBySugar << '\n';
std::cout << "color: " << color << '\n';
std::cout << "grape: " << grapeCn << " - " << grapeEn << '\n';
std::cout << "taste temperature: " << tasteTemperature << '\n';
std::cout << "description: " << description << '\n';
}

}
};

RedwineInfo 结构体中,定义了一个 print 方法以打印获取的结果。

然后定义了一个类来调用接口并获取结果

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Redwine
{
public:
Redwine();
~Redwine();

Json::Value request(std::string imgBase64, std::map<std::string, std::string>& options);

// only get first result
void getResult(RedwineInfo& result);


private:
Json::Value obj_;
std::string url_;
// file to save token key
std::string filename_;
};

类中的私有成员 obj_ 表示返回结果对应的 json 对象。url_ 表示请求的 url,filename_ 表示用于存储 access token 的文件的文件名。

request 函数输入请求图像的 base64 编码以及请求参数,返回一个 json 对象,json 对象中包含请求的结果。

getResult 获取红酒识别结果。


完整代码如下

util.hutil.cpp 代码参见 (简单调用篇 01) 通用物体和场景识别高级版 - C++ 简单调用

Redwine.h 代码如下:

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#pragma once
#include "util.h"

struct RedwineInfo {
bool hasdetail;
std::string wineNameCn; // 红酒中文名
std::string wineNameEn;
std::string countryCn;
std::string countryEn;
std::string regionCn;
std::string regionEn;
std::string subRegionCn;
std::string subRegionEn;
std::string wineryCn; // 酒庄
std::string wineryEn;
std::string classifyByColor; // 类型
std::string classifyBySugar; // 糖分类型
std::string color; // 色泽
std::string grapeCn; // 品种
std::string grapeEn;
std::string tasteTemperature; // 品尝温度
std::string description;

void print() {
std::cout << std::setw(30) << std::setfill('-') << '\n';
std::cout << "name: " << wineNameCn << " - " << wineNameEn << '\n';

if (hasdetail) {
std::cout << "country: " << countryCn << " - " << countryEn << '\n';
std::cout << "region: " << countryCn << " - " << regionEn << '\n';
std::cout << "subregion: " << subRegionCn << " - " << subRegionEn << '\n';
std::cout << "winery: " << wineryCn << " - " << wineryEn << '\n';
std::cout << "classify by color: " << classifyByColor << '\n';
std::cout << "classify by sugar: " << classifyBySugar << '\n';
std::cout << "color: " << color << '\n';
std::cout << "grape: " << grapeCn << " - " << grapeEn << '\n';
std::cout << "taste temperature: " << tasteTemperature << '\n';
std::cout << "description: " << description << '\n';
}

}
};

class Redwine
{
public:
Redwine();
~Redwine();

Json::Value request(std::string imgBase64, std::map<std::string, std::string>& options);

// only get first result
void getResult(RedwineInfo& result);


private:
Json::Value obj_;
std::string url_;
// file to save token key
std::string filename_;
};

void redwineTest();

Redwine.cpp 代码如下:

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include "Redwine.h"



Redwine::Redwine()
{
filename_ = "tokenKey";
url_ = "https://aip.baidubce.com/rest/2.0/image-classify/v1/redwine";
}


Redwine::~Redwine()
{
}

Json::Value Redwine::request(std::string imgBase64, std::map<std::string, std::string>& options)
{
std::string response;
Json::Value obj;
std::string token;

// 1. get HTTP post body
std::string body;
mergeHttpPostBody(body, imgBase64, options);

// 2. get HTTP url with access token
std::string url = url_;
getHttpPostUrl(url, filename_, token);

// 3. post request, response store the result
int status_code = httpPostRequest(url, body, response);
if (status_code != CURLcode::CURLE_OK) {
obj["curl_error_code"] = status_code;
obj_ = obj;
return obj; // TODO: maybe should exit
}

// 4. make string to json object
generateJson(response, obj);

// if access token is invalid or expired, we will get a new one
if (obj["error_code"].asInt() == 110 || obj["error_code"].asInt() == 111) {
token = getTokenKey();
writeFile(filename_, token);
return request(imgBase64, options);
}

obj_ = obj;
// check for other error code
checkErrorWithExit(obj);

return obj;
}

void Redwine::getResult(RedwineInfo & result)
{
result.hasdetail = obj_["result"]["hasdetail"].asBool();
result.wineNameCn = UTF8ToGB(obj_["result"]["wineNameCn"].asString().c_str());
result.wineNameEn = obj_["result"]["wineNameEn"].asString();
result.countryCn = UTF8ToGB(obj_["result"]["countryCn"].asString().c_str());
result.countryEn = obj_["result"]["countryEn"].asString();
result.regionCn = UTF8ToGB(obj_["result"]["regionCn"].asString().c_str());
result.regionEn = obj_["result"]["regionEn"].asString();
result.subRegionCn = UTF8ToGB(obj_["result"]["subRegionCn"].asString().c_str());
result.subRegionEn = obj_["result"]["subRegionEn"].asString();
result.wineryCn = UTF8ToGB(obj_["result"]["wineryCn"].asString().c_str());
result.wineryEn = obj_["result"]["wineryEn"].asString();
result.classifyByColor = UTF8ToGB(obj_["result"]["classifyByColor"].asString().c_str());
result.classifyBySugar = UTF8ToGB(obj_["result"]["classifyBySugar"].asString().c_str());
result.color = UTF8ToGB(obj_["result"]["color"].asString().c_str());
result.grapeCn = UTF8ToGB(obj_["result"]["grapeCn"].asString().c_str());
result.grapeEn = UTF8ToGB(obj_["result"]["grapeEn"].asString().c_str());
result.tasteTemperature = UTF8ToGB(obj_["result"]["tasteTemperature"].asString().c_str());
result.description = UTF8ToGB(obj_["result"]["description"].asString().c_str());
}

void redwineTest()
{
std::cout << "size: " << sizeof(RedwineInfo) << "\n";

// read image and encode to base64
std::string imgFile = "./images/wine.png";
std::string imgBase64;
imgToBase64(imgFile, imgBase64);

// set options
std::map<std::string, std::string> options;

Json::Value obj;
Redwine redwineObj;
obj = redwineObj.request(imgBase64, options);

RedwineInfo result;
redwineObj.getResult(result);
result.print();

}

main.cpp 代码如下:

cpp
1
2
3
4
5
6
7
8
9
10
#include "util.h"
#include "Redwine.h"
#include <stdlib.h>

int main() {
redwineTest();

system("pause");
return EXIT_SUCCESS;
}

运行结果

测试图像

文章作者: Busyboxs
文章链接: http://yangshun.win/blogs/a558ea6c/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Busyboxs

评论