概述:
最近做了人脸皮肤疾病检测项目。功能包括色素分解、皮肤敏感、毛孔检测、斑点检测、肌底抗衰、粉刺等皮肤疾病检测。包括“局部皮肤检测”和“全脸皮肤检测”。在做全脸皮肤疾病检测的时候做了提取人脸皮肤的功能。试过基于RGB颜色空间、HSV颜色空间、YCrCb颜色空间的Cr分量+OSTU等多种方法,效果不是那么完美。
后来也试了调阿里云的人脸检测接口。要先获取调用API的url及所需要的ak_id和ak_secret。这里使用的是libcurl和cJSON,通过https请求来获取人脸关键点。libcurl 需要自己编译,我有编译好的已上传给大家,需要的朋友可以到我的下载资源里去下载。cJSON也比较方便。下载后可以直接将cJSON.h和cJSON.c拷贝到项目中去,添加头文件即可。
这里将调用阿里云的功能封装成一个函数。相关参数如下:
参数1:要调用的阿里云url:https://dtplus-cn-shanghai.data.aliyuncs.com/face/detect
参数2:传入的图片
参数3:图片长度
参数4,5:申请的ak_id和ak_secret
参数6:返回的关键点信息,放入vector中
// 调用阿里云,返回图片关键点
int HttpPostAliYun(const string& url, const unsigned char* image, int imageLen, const string&ak_id, const string& ak_secret, vector<double> &landmardk_info)
{
string strStl;
// 返回信息
RespData respData = { 0,0 };
int size = 102400; // 这里需要确认 长度要固定吗?
if (respData.buff == 0)
respData.buff = new char[size];
respData.len = 0;
memset(respData.buff, 0, size);
// 创建一个Object结构体
cJSON * root = cJSON_CreateObject();
// 将图片加密,得到base64字符串
string base64str = CUtils::Base64Encode(image, imageLen);
cout << "base64Str:" << base64str << endl;
string body("{\"type\":1,\"content\":");
body += "\"" + base64str + "\"}";
string method = "POST";
string accept = "application/json";
string content_type = "application/json";
string path = "/face/attribute";
string date = CUtils::GetGMTDatetime();
string bodyMd5str = CUtils::Md5Base64(body);
string stringToSign = method + "\n" + accept + "\n" + bodyMd5str + "\n" + content_type + "\n" + date + "\n" + path;
// 签名
string signature = CUtils::HMACSha1Base64(stringToSign, ak_secret);
string authHeader = "Dataplus " + ak_id + ":" + signature;
cout << "body:" << body.c_str() << endl;
cout << "url :" << url.c_str() << endl;
cout << "date :" << date.c_str() << endl;
cout << "bodyMd5str:" << bodyMd5str.c_str() << endl;
cout << "signature :" << signature.c_str() << endl;
CURL* curl = NULL;
CURLcode res = CURLE_OK;
struct curl_slist* headers = NULL;
char tmp_str[256] = { 0 };
snprintf(tmp_str, sizeof(tmp_str), "Content-Type: %s", "application/json");
headers = curl_slist_append(headers, tmp_str);
snprintf(tmp_str, sizeof(tmp_str), "Accept: %s", "application/json");
headers = curl_slist_append(headers, tmp_str);
snprintf(tmp_str, sizeof(tmp_str), "Date: %s", date.c_str());
headers = curl_slist_append(headers, tmp_str);
snprintf(tmp_str, sizeof(tmp_str), "Authorization: %s", authHeader.c_str());
headers = curl_slist_append(headers, tmp_str);
// 1.curl初始化
curl = curl_easy_init();
if (curl)
{
//CUtils::writeLog("curl初始化成功");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &respData);
// 2.以阻塞方式执行文件请求
CURLcode retcode = curl_easy_perform(curl);
// 请求返回成功
if (CURLE_OK == retcode)
{
// 3.执行文件成功,解析返回的json
cout << " respData:" << respData.buff << endl;
cJSON *strStl = cJSON_Parse(respData.buff);
CUtils::writeLog("respData:" + (std::string)respData.buff);
if (!strStl)
{
cout << "parse json failed." << endl;
CUtils::writeLog("parse json failed.");
return -1;
}
// 解析json成功(这里使用的是cJSON)
// 创建一个对象 获取Json里面的错误代码(errno)
cJSON *error = cJSON_GetObjectItem(strStl, "errno"); // 错误代码
cJSON *facenum = cJSON_GetObjectItem(strStl, "face_num"); // 人脸个数
cJSON *landmark_num = cJSON_GetObjectItem(strStl, "landmark_num"); // 特征点个数
// 打印错误代码
char* strErr = cJSON_Print(error);
//std::string strErr = error->valuestring;
string str_landmark_num = cJSON_Print(landmark_num);
cout << "特征点个数:" << str_landmark_num << endl;
//CUtils::writeLog("特征点个数:" + (std::string)str_landmark_num);
// 判断返回的错误代码error
// 错误代码是0,表示成功
if (error->valueint == 0)
{
// 判断人脸数目
if (facenum->valueint == 0)
{
// 没有找到人脸
return -1;
}
else {
// 找到了人脸
// 如果人脸个数是1
if (facenum->valueint == 1)
{
// 获取返回数据中的“landmark”字段
cJSON *landmarks = cJSON_GetObjectItem(strStl, "landmark");
// 将特征点写入文本文件
char* strLandmarks = cJSON_Print(landmarks);
//CUtils::writeLog("人脸特征点:" + (std::string)strLandmarks);
// 获取特征点的个数 就是cJSON对象landmarks的长度
int len = cJSON_GetArraySize(landmarks);
cout << "特征点个数的长度:" << len << endl;
//CUtils::writeLog("特征点个数的长度:" + CUtils::int_to_string(len));
// 长度
for (int i = 0; i < len; i++)
{
// 将返回的一个个数据放入vector容器中
cJSON *subItem = cJSON_GetArrayItem(landmarks, i); // 获取第i个数据
double value_landmark_item = subItem->valuedouble; // 转换成double数据
landmardk_info.push_back(value_landmark_item);
}
}
else {
// 多个人脸
return -1;
}
}
}
//解析json失败
else {
//CUtils::writeLog("parse json failed. the errno is:" + (std::string)strErr);
return -1;
}
}
else
{
// 执行文件请求返回失败
// 获取错误代码
string error = curl_easy_strerror(retcode);
// 可以更新错误代码
curl_easy_cleanup(curl);
return -1;
}
}
else {
CUtils::writeLog("curl初始化失败...");
return -1;
}
return 0;
}
效果图: