菜品识别能识别超过 9 千种菜品,支持客户创建属于自己的菜品图库,可准确识别图片中的菜品名称、位置,并获取百科信息,适用于多种客户识别菜品的业务场景中
应用场景
餐饮健康:根据拍摄照片,识别图片中菜品名称,获取菜品参考卡路里含量和百科信息,可结合识别结果进一步提供饮食推荐、健康管理方案等相关功能,增强用户体验,广泛应用于餐饮娱乐类和健康管理类APP中。
接口描述
该请求用于菜品识别。即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片的菜品名称、卡路里信息、置信度。
请求说明
HTTP 方法: POST
请求 URL: https://aip.baidubce.com/rest/2.0/image-classify/v2/dish
URL参数: access_token
Header 参数: Content-Type
= application/x-www-form-urlencoded
Body 参数:见下表
参数
是否必选
类型
默认值
说明
image
是
string
-
图像数据,base64
编码,要求 base64
编码后大小不超过 4M,最短边至少 15px
,最长边最大 4096px
, 支持 jpg/png/bmp
格式 。注意:图片需要 base64
编码、去掉编码头后再进行 urlencode
top_num
否
unit32
-
返回结果 top n,默认 5
filter_threshold
是
float
-
默认 0.95
,可以通过该参数调节识别效果,降低非菜识别率
baike_num
否
integer
0
返回百科信息的结果数,默认不返回
返回说明
返回参数如下表:
字段
是否必选
类型
说明
log_id
是
uint64
唯一的 log id,用于问题定位
result_num
否
unit32
返回结果数目,及result数组中的元素个数
result
否
array()
菜品识别结果数组
+name
否
string
菜名,示例:鱼香肉丝
+calorie
否
float
卡路里,每100g的卡路里含量
+probability
否
float
识别结果中每一行的置信度值,0-1
+baike_info
否
object
对应识别结果的百科词条名称
++baike_url
否
string
对应识别结果百度百科页面链接
++image_url
否
string
对应识别结果百科图片链接
++description
否
string
对应识别结果百科内容描述
返回示例如下:
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 { "log_id" : 7357081719365269362 , "result_num" : 5 , "result" : [ { "calorie" : "119" , "has_calorie" : true , "name" : "酸汤鱼" , "probability" : "0.396031" "baike_info" : { "baike_url" : "http://baike.baidu.com/item/%E9%85%B8%E6%B1%A4%E9%B1%BC/1754055" , "description" : "酸汤鱼,是黔桂湘交界地区的一道侗族名菜,与侗族相邻的苗、水、瑶等少数民族也有相似菜肴,但其中以贵州侗族酸汤鱼最为有名,据考证此菜肴最早源于黎平县雷洞镇牙双一带。制作原料主要有鱼肉、酸汤、山仓子等香料。成菜后,略带酸味、幽香沁人、鲜嫩爽口开胃,是贵州“黔系”菜肴的代表作之一。这道菜通常先自制酸汤,之后将活鱼去掉内脏,入酸汤煮制。" } }, { "calorie" : "38" , "has_calorie" : true , "name" : "原味黑鱼煲" , "probability" : "0.265432" , }, { "calorie" : "144" , "has_calorie" : true , "name" : "椒鱼片" , "probability" : "0.0998993" }, { "calorie" : "98" , "has_calorie" : true , "name" : "酸菜鱼" , "probability" : "0.0701917" }, { "calorie" : "257.65" , "has_calorie" : true , "name" : "柠檬鱼" , "probability" : "0.0471465" }] }
C++ 代码实现调用
这里假设已经将环境配置好了,环境配置的文章可以参考 Windows 下使用 Vcpkg 配置百度 AI 图像识别 C++开发环境(VS2017) 。
为了方便,首先根据返回参数定义了一个结构体,该结构体包括了返回参数中的参数,如下:
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 struct DishInfo { std ::string name; bool has_calorie; float calorie; float probability; std ::string baikeurl; std ::string imageurl; std ::string baikedesc; void print () { std ::cout << std ::setw(30 ) << std ::setfill('-' ) << '\n' ; std ::cout << "name: " << name << "\n" ; if (has_calorie) std ::cout << "calorie: " << calorie << "J" << "\n" ; else std ::cout << "has_calorie: false\n" ; std ::cout << "probability: " << probability << "\n" ; if (baikeurl != "null" ) std ::cout << "baikeurl: " << baikeurl << "\n" ; if (imageurl != "null" ) std ::cout << "imageurl: " << imageurl << "\n" ; if (baikedesc != "null" ) std ::cout << "baikedesc: " << baikedesc << "\n" ; } };
在 DishInfo
结构体中,定义了一个 print
方法以打印获取的结果。
然后定义了一个类来调用接口并获取结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Dish { public : Dish(); ~Dish(); Json::Value request (std ::string imgBase64, std ::map <std ::string , std ::string >& options) ; void getAllResult (std ::vector <DishInfo>& results) ; void getResult (DishInfo& result) ; private : Json::Value obj_; std ::string url_; std ::string filename_; };
类中的私有成员 obj_
表示返回结果对应的 json 对象。url_
表示请求的 url,filename_
表示用于存储 access token
的文件的文件名。
request
函数输入请求图像的 base64 编码以及请求参数,返回一个 json 对象,json 对象中包含请求的结果。
getAllResult
获取请求的结果,总共有 top_num 条结果。
getResult
获取 score 最高的一条结果。
完整代码如下
util.h
和 util.cpp
代码参见 (简单调用篇 01) 通用物体和场景识别高级版 - C++ 简单调用
Dish.h
代码如下:
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 #pragma once #include "util.h" struct DishInfo { std ::string name; bool has_calorie; float calorie; float probability; std ::string baikeurl; std ::string imageurl; std ::string baikedesc; void print () { std ::cout << std ::setw(30 ) << std ::setfill('-' ) << '\n' ; std ::cout << "name: " << name << "\n" ; if (has_calorie) std ::cout << "calorie: " << calorie << "J" << "\n" ; else std ::cout << "has_calorie: false\n" ; std ::cout << "probability: " << probability << "\n" ; if (baikeurl != "null" ) std ::cout << "baikeurl: " << baikeurl << "\n" ; if (imageurl != "null" ) std ::cout << "imageurl: " << imageurl << "\n" ; if (baikedesc != "null" ) std ::cout << "baikedesc: " << baikedesc << "\n" ; } }; class Dish { public : Dish(); ~Dish(); Json::Value request (std ::string imgBase64, std ::map <std ::string , std ::string >& options) ; void getAllResult (std ::vector <DishInfo>& results) ; void getResult (DishInfo& result) ; private : Json::Value obj_; std ::string url_; std ::string filename_; }; void dishTest () ;
Dish.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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 #include "Dish.h" Dish::Dish() { filename_ = "tokenKey" ; url_ = "https://aip.baidubce.com/rest/2.0/image-classify/v2/dish" ; } Dish::~Dish() { } Json::Value Dish::request (std ::string imgBase64, std ::map <std ::string , std ::string >& options) { std ::string response; Json::Value obj; std ::string token; std ::string body; mergeHttpPostBody(body, imgBase64, options); std ::string url = url_; getHttpPostUrl(url, filename_, token); int status_code = httpPostRequest(url, body, response); if (status_code != CURLcode::CURLE_OK) { obj["curl_error_code" ] = status_code; obj_ = obj; return obj; } generateJson(response, obj); if (obj["error_code" ].asInt() == 110 || obj["error_code" ].asInt() == 111 ) { token = getTokenKey(); writeFile(filename_, token); return request(imgBase64, options); } obj_ = obj; checkErrorWithExit(obj); return obj; } void Dish::getAllResult (std ::vector <DishInfo>& results) { int len = obj_["result" ].size(); results.reserve(len); DishInfo tmp; for (int i = 0 ; i < len; ++i) { tmp.name = UTF8ToGB(obj_["result" ][i]["name" ].asString().c_str()); tmp.has_calorie = obj_["result" ][i]["has_calorie" ].asBool(); tmp.calorie = stof(obj_["result" ][i]["calorie" ].asString()); tmp.probability = stof(obj_["result" ][i]["probability" ].asString()); tmp.baikeurl = obj_["result" ][i]["baike_info" ].get("baike_url" , "null" ).asString(); tmp.imageurl = obj_["result" ][i]["baike_info" ].get("image_url" , "null" ).asString(); tmp.baikedesc = UTF8ToGB(obj_["result" ][i]["baike_info" ].get("description" , "null" ).asString().c_str()); results.push_back(tmp); } } void Dish::getResult (DishInfo & result) { result.name = UTF8ToGB(obj_["result" ][0 ]["name" ].asString().c_str()); result.has_calorie = obj_["result" ][0 ]["has_calorie" ].asBool(); result.calorie = stof(obj_["result" ][0 ]["calorie" ].asString()); result.probability = stof(obj_["result" ][0 ]["probability" ].asString()); result.baikeurl = obj_["result" ][0 ]["baike_info" ].get("baike_url" , "null" ).asString(); result.imageurl = obj_["result" ][0 ]["baike_info" ].get("image_url" , "none" ).asString(); result.baikedesc = UTF8ToGB(obj_["result" ][0 ]["baike_info" ].get("description" , "none" ).asString().c_str()); } void dishTest () { std ::cout << "size: " << sizeof (DishInfo) << "\n" ; std ::string imgFile = "./images/dish_test.jpg" ; std ::string imgBase64; imgToBase64(imgFile, imgBase64); std ::map <std ::string , std ::string > options; options["baike_num" ] = "5" ; options["top_num" ] = "5" ; options["filter_threshold" ] = "0.95" ; Json::Value obj; Dish dishObj; obj = dishObj.request(imgBase64, options); DishInfo result; dishObj.getResult(result); result.print(); std ::vector <DishInfo> results; dishObj.getAllResult(results); for (auto & vec : results) { vec.print(); } }
main.cpp
代码如下:
1 2 3 4 5 6 7 8 9 10 #include "util.h" #include "Dish.h" #include <stdlib.h> int main () { dishTest(); system("pause" ); return EXIT_SUCCESS; }
运行结果
测试图像