Linux下基于百度智能云平台人脸检测分析
1.百度智能云接口及简介
https://cloud.baidu.com/product/face
接口技术文档:
网页功能演示:
2.人脸检测属性分析项目示例
硬件平台: ubuntu18.04、USB免驱摄像头
图像渲染: SDL库
开发语言: C语言
效果展示:
3.摄像头应用框架V4L2示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "video.h"
/*摄像头应用编程框架*/
int Video_Init(u8 *dev,int video_fd,struct video *video_info)
{
/*1.打开摄像设备文件*/
video_fd=open(dev,O_RDWR);
if(video_fd<0)return -1;
/*2.图像数据格式*/
struct v4l2_format video_format;
memset(&video_format,0,sizeof(struct v4l2_format));
video_format.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//捕获格式
video_format.fmt.pix.width=800;
video_format.fmt.pix.height=480;
video_format.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
if(ioctl(video_fd,VIDIOC_S_FMT,&video_format))return -2;
video_info->width=video_format.fmt.pix.width;
video_info->height=video_format.fmt.pix.height;
printf("图像尺寸:%d * %d\n",video_info->width,video_info->height);
/*3.申请空间*/
struct v4l2_requestbuffers video_requestbuffers;
memset(&video_requestbuffers,0,sizeof(struct v4l2_requestbuffers));
video_requestbuffers.count=4;//缓冲区个数
video_requestbuffers.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2捕获框架格式
video_requestbuffers.memory=V4L2_MEMORY_MMAP;//内存映射
if(ioctl(video_fd,VIDIOC_REQBUFS,&video_requestbuffers))return -3;
printf("缓冲区个数:%d\n",video_requestbuffers.count);
/*4.将缓冲映射到进程空间*/
int i=0;
struct v4l2_buffer video_buffer;
for(i=0;i<video_requestbuffers.count;i++)
{
memset(&video_buffer,0,sizeof(struct v4l2_buffer));
video_buffer.index=i;//
video_buffer.memory=V4L2_MEMORY_MMAP;//内存映射
video_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2捕获框架格式
if(ioctl(video_fd,VIDIOC_QUERYBUF,&video_buffer))return -4;
video_info->mmap_size=video_buffer.length;/*映射大小*/
video_info->mmapbuf[i]=mmap(NULL,video_buffer.length,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,video_buffer.m.offset);
}
/*5.将缓冲区添加到采集队列*/
for(i=0;i<video_requestbuffers.count;i++)
{
memset(&video_buffer,0,sizeof(struct v4l2_buffer));
video_buffer.index=i;//
video_buffer.memory=V4L2_MEMORY_MMAP;//内存映射
video_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2V4L2捕获框架格式
if(ioctl(video_fd,VIDIOC_QBUF,&video_buffer))return -5;
}
/*开启摄像头*/
int type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2V4L2捕获框架格式
if(ioctl(video_fd,VIDIOC_STREAMON,&type))
{
printf("摄像头开启失败\n");
return -6;
}
return video_fd;
}
4.调用百度人家属性分析接示例
// libcurl库下载链接:https://curl.haxx.se/download.html
// jsoncpp库下载链接:https://github.com/open-source-parsers/jsoncpp/
const static char * request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect";
char faceDetect_result[1024];
char faceDetect_result[1024];
/**
* 人脸检测与属性分析
* @return 调用成功返回0,发生错误返回其他错误码
*/
int faceDetect(char *json_result, const char *access_token,const char *image_base64)
{
//std::string url = request_url + "?access_token=" + access_token;
char url[1024];
snprintf(url,sizeof(url),"%s?access_token=%s",request_url,access_token);
CURL *curl = NULL;
CURLcode result_code;
int is_success=0;
char iamge[1024*1024];
snprintf(iamge,sizeof(iamge),"{\"image\":\"%s\",\"image_type\":\"BASE64\",\"face_field\":\"age,beauty,gender,glasses,eye_status,emotion,race,mask,facetype\"}",image_base64);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_POST, 1);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS,iamge);
result_code = curl_easy_perform(curl);
FILE *outfile;
outfile = fopen("test.cmd", "wb");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
result_code=curl_easy_perform(curl);
fclose(outfile);
if (result_code != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s",curl_easy_strerror(result_code));
is_success = 1;
return is_success;
}
strcpy(json_result,faceDetect_result);
curl_easy_cleanup(curl);
is_success = 0;
} else {
fprintf(stderr, "curl_easy_init() failed.");
is_success = 1;
}
return is_success;
}
5.base64图片编码示例
static const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char * base64_encode( const unsigned char * bindata, char * base64, int binlength )
{
int i, j;
unsigned char current;
for ( i = 0, j = 0 ; i < binlength ; i += 3 )
{
current = (bindata[i] >> 2) ;
current &= (unsigned char)0x3F;
base64[j++] = base64char[(int)current];
current = ( (unsigned char)(bindata[i] << 4 ) ) & ( (unsigned char)0x30 ) ;
if ( i + 1 >= binlength )
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
base64[j++] = '=';
break;
}
current |= ( (unsigned char)(bindata[i+1] >> 4) ) & ( (unsigned char) 0x0F );
base64[j++] = base64char[(int)current];
current = ( (unsigned char)(bindata[i+1] << 2) ) & ( (unsigned char)0x3C ) ;
if ( i + 2 >= binlength )
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
break;
}
current |= ( (unsigned char)(bindata[i+2] >> 6) ) & ( (unsigned char) 0x03 );
base64[j++] = base64char[(int)current];
current = ( (unsigned char)bindata[i+2] ) & ( (unsigned char)0x3F ) ;
base64[j++] = base64char[(int)current];
}
base64[j] = '\0';
return base64;
}
int base64_decode( const char * base64, unsigned char * bindata )
{
int i, j;
unsigned char k;
unsigned char temp[4];
for ( i = 0, j = 0; base64[i] != '\0' ; i += 4 )
{
memset( temp, 0xFF, sizeof(temp) );
for ( k = 0 ; k < 64 ; k ++ )
{
if ( base64char[k] == base64[i] )
temp[0]= k;
}
for ( k = 0 ; k < 64 ; k ++ )
{
if ( base64char[k] == base64[i+1] )
temp[1]= k;
}
for ( k = 0 ; k < 64 ; k ++ )
{
if ( base64char[k] == base64[i+2] )
temp[2]= k;
}
for ( k = 0 ; k < 64 ; k ++ )
{
if ( base64char[k] == base64[i+3] )
temp[3]= k;
}
bindata[j++] = ((unsigned char)(((unsigned char)(temp[0] << 2))&0xFC)) |
((unsigned char)((unsigned char)(temp[1]>>4)&0x03));
if ( base64[i+2] == '=' )
break;
bindata[j++] = ((unsigned char)(((unsigned char)(temp[1] << 4))&0xF0)) |
((unsigned char)((unsigned char)(temp[2]>>2)&0x0F));
if ( base64[i+3] == '=' )
break;
bindata[j++] = ((unsigned char)(((unsigned char)(temp[2] << 6))&0xF0)) |
((unsigned char)(temp[3]&0x3F));
}
return j;
}
6.JOSN数据格式解析示例
/*josn数据解析*/
int Josn_Get(const char *buff,struct Faceinfo *face_info)
{
/*创建cJSON对象*/
cJSON *root=cJSON_CreateObject();
char *p=strstr(buff,"{\"error_code\"");
root=cJSON_Parse(p);/*载入JSON数据*/
if(root==NULL)return -1;
/*2.解析字段*/
cJSON *item;
int i=0;
item=cJSON_GetObjectItem(root,"error_code");
if(item)
{
printf("error_code:%d\n",item->valueint);
if(item->valueint)return -2;
}
item=cJSON_GetObjectItem(root,"error_msg");
if(item)
{
printf("error_code:%s\n",item->valuestring);
}
item=cJSON_GetObjectItem(root,"result");
if(item)
{
cJSON *obj;
obj=cJSON_GetObjectItem(item, "face_num");
printf("face_num:%d\n",obj->valueint);
item=cJSON_GetObjectItem(item,"face_list");
if(item)
{
int arraysize=cJSON_GetArraySize(item);/*获取数组成员个数*/
for(i=0;i<arraysize;i++)
{
cJSON *array_item=cJSON_GetArrayItem(item,i);
if(array_item==NULL)continue;
cJSON *obj=cJSON_GetObjectItem(array_item,"face_token");
if(obj)printf("token=%s\n",obj->valuestring);
obj=cJSON_GetObjectItem(array_item,"location");
if(obj)
{
cJSON *data;
data=cJSON_GetObjectItem(obj, "left");
printf("left:%f\n",data->valuedouble);
face_info->x=data->valuedouble;
data=cJSON_GetObjectItem(obj, "top");
printf("top:%f\n",data->valuedouble);
face_info->y=data->valuedouble;
data=cJSON_GetObjectItem(obj, "width");
printf("width:%f\n",data->valuedouble);
face_info->w=data->valuedouble;
data=cJSON_GetObjectItem(obj, "height");
printf("height:%f\n",data->valuedouble);
face_info->h=data->valuedouble;
data=cJSON_GetObjectItem(obj, "face_probability");
}
obj=cJSON_GetObjectItem(array_item,"face_probability");
//printf("probability=%d\n",obj->valueint);
obj=cJSON_GetObjectItem(array_item,"age");
//printf("年龄=%d\n",obj->valueint);
face_info->age=obj->valueint;
obj=cJSON_GetObjectItem(array_item,"beauty");
if(obj!=NULL)
{
//printf("颜值=%f\n",obj->valuedouble);
face_info->beauty=obj->valuedouble;
}
obj=cJSON_GetObjectItem(array_item,"gender");//性别
if(obj)
{
cJSON *data;
data=cJSON_GetObjectItem(obj,"type");
if(strcmp(data->valuestring,"male")==0)
{
//printf("性别:男\n");
strcpy(face_info->gender,"男");
}
else if(strcmp(data->valuestring,"female")==0)
{
//printf("性别:女\n");
strcpy(face_info->gender,"女");
}
}
obj=cJSON_GetObjectItem(array_item,"glasses");//是否带眼镜
if(obj)
{
cJSON *data;
data=cJSON_GetObjectItem(obj,"type");
if(strcmp(data->valuestring,"none")==0)
{
//printf("眼镜:无\n");
strcpy(face_info->glasses,"无");
}
else if(strcmp(data->valuestring,"common")==0)
{
//printf("眼镜:普通眼镜\n");
strcpy(face_info->glasses,"普通眼镜");
}
else if(strcmp(data->valuestring,"sun")==0)
{
//printf("眼镜:墨镜\n");
strcpy(face_info->glasses,"墨镜");
}
}
obj=cJSON_GetObjectItem(array_item,"emotion");//表情
if(obj)
{
cJSON *data;
data=cJSON_GetObjectItem(obj,"type");
if(strcmp(data->valuestring,"angry")==0)
{
//printf("表情:愤怒\n");
strcpy(face_info->emotion,"愤怒");
}
else if(strcmp(data->valuestring,"disgust")==0)
{
//printf("表情:厌恶\n");
strcpy(face_info->emotion,"厌恶");
}
else if(strcmp(data->valuestring,"fear")==0)
{
//printf("表情:恐惧\n");
strcpy(face_info->emotion,"恐惧");
}
else if(strcmp(data->valuestring,"happy")==0)
{
//printf("表情:高兴\n");
strcpy(face_info->emotion,"高兴");
}
else if(strcmp(data->valuestring,"sad")==0)
{
//printf("表情:伤心\n");
strcpy(face_info->emotion,"伤心");
}
else if(strcmp(data->valuestring,"surprise")==0)
{
//printf("表情:惊讶\n");
strcpy(face_info->emotion,"惊讶");
}
else if(strcmp(data->valuestring,"neutral")==0)
{
//printf("表情:无表情\n");
strcpy(face_info->emotion,"无表情");
}
else if(strcmp(data->valuestring,"pouty")==0)
{
//printf("表情:噘嘴\n");
strcpy(face_info->emotion,"噘嘴");
}
else if(strcmp(data->valuestring,"grimace")==0)
{
//printf("表情:鬼脸\n");
strcpy(face_info->emotion,"鬼脸");
}
}
obj=cJSON_GetObjectItem(array_item,"mask");//是否带口罩
if(obj)
{
cJSON *data;
data=cJSON_GetObjectItem(obj,"type");
//printf("是否带口罩:%d\n",data->valueint);
if(data->valueint)strcpy(face_info->mask,"是");
else strcpy(face_info->mask,"否");
}
obj=cJSON_GetObjectItem(array_item,"face_type");
if(obj)
{
cJSON *data;
data=cJSON_GetObjectItem(obj,"type");
if(strcmp(data->valuestring,"human")==0)
{
printf("真实人脸\n");
}
else if(strcmp(data->valuestring," cartoon")==0)
{
printf("卡通人脸\n");
}
}
}
}
}
cJSON_Delete(root);//释放空间
return 0;
}
7.调用SDL库图像渲染
int main()
{
struct Faceinfo face_info={0};
int w,h;
/*初始化摄像头*/
video_fd=Video_Init(CAMERA_DEV,video_fd,&video_info);
if(video_fd<=0)
{
printf("摄像头初始化失败,res=%d\n",video_fd);
return 0;
}
/*TTF 初始化*/
TTF_Init();
/*打开字库*/
TTF_Font *ttffont=TTF_OpenFont("方正粗黑宋简体.ttf",30);
if(ttffont==NULL)
{
printf("字库打开失败\n");
return 0;
}
SDL_Color color={255,0,128,255};/*字体颜色 RGBA*/
/*创建窗口 */
SDL_Window *window=SDL_CreateWindow("人脸检测分析", SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,800,480,SDL_WINDOW_ALLOW_HIGHDPI|SDL_WINDOW_SHOWN);
SDL_GetWindowSize(window,&w,&h);
/*创建渲染器*/
SDL_Renderer *render=SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);
/*清空渲染器*/
SDL_RenderClear(render);
printf("图像尺寸:%d * %d\n",video_info.width,video_info.height);
/*创建纹理*/
SDL_Texture*sdltext=SDL_CreateTexture(render,SDL_PIXELFORMAT_RGB24,SDL_TEXTUREACCESS_STREAMING,video_info.width,video_info.height);
/*创建摄像头采集线程*/
u8 *rgb_data=malloc(video_info.height*video_info.width*3);
rgb_buff=malloc(video_info.height*video_info.width*3);//保存RGB颜色数据
u8 *jpg_data=malloc(video_info.height*video_info.width*3);//保存RGB颜色数据
u8 *imagebase64_data=malloc((video_info.height*video_info.width*3)*8/6);//保存RGB颜色数据
int jpg_size;
//printf("size=%d\n",video_info.mmap_size);
video_flag=1;/*摄像头采集标志*/
pthread_t pthid;
pthread_create(&pthid,NULL,Video_CollectImage, NULL);
bool quit=true;
SDL_Event event;
SDL_Rect rect;
int display_stat=1;
char buff[100];
while(quit)
{
while(SDL_PollEvent(&event))/*事件监测*/
{
if(event.type==SDL_QUIT)/*退出事件*/
{
quit=false;
video_flag=0;
pthread_cancel(pthid);/*杀死指定线程*/
continue;
}
else if(event.type==SDL_MOUSEBUTTONDOWN)/*点击事件*/
{
if(event.button.button==SDL_BUTTON_LEFT)/*左键*/
{
pthread_mutex_lock(&fastmutex);//互斥锁上锁
pthread_cond_wait(&cond,&fastmutex);
memcpy(rgb_data,rgb_buff,video_info.height*video_info.width*3);
pthread_mutex_unlock(&fastmutex);//互斥锁解锁
jpg_size=rgb_to_jpeg(video_info.width,video_info.height,video_info.height*video_info.width*3,rgb_data,jpg_data, 80);//RGB转JPG
base64_encode(jpg_data, imagebase64_data, jpg_size);
char json_result[1024];
int res=0;
res=faceDetect(json_result,access_token,imagebase64_data);
FILE *fp=fopen("test.cmd","rb");
if(fp==NULL)continue;
struct stat statbuf;
int size=0;
stat("test.cmd", &statbuf);
char *data=malloc(statbuf.st_size+1);
size=fread(data,1,statbuf.st_size,fp);
data[size]='\0';
fclose(fp);
//SDL_ttfDisplayFont(10,10,window,ttffont,45,color,"字库测试",render);
display_stat=0;
if(Josn_Get(data,&face_info))
{
display_stat=1;
continue;
}
printf("年龄:%d\n",face_info.age);
printf("颜值:%f\n",face_info.beauty);
printf("性别:%s\n",face_info.gender);
printf("表情:%s\n",face_info.emotion);
printf("是否戴眼镜:%s\n",face_info.glasses);
printf("是否戴口罩:%s\n",face_info.mask);
SDL_SetRenderDrawColor(render,255,0,0, 255);// 透明值
SDL_Rect rect;
rect.x=face_info.x;
rect.y=face_info.y;
rect.w=face_info.w;
rect.h=face_info.h;
SDL_UpdateTexture(sdltext,NULL,rgb_data, video_info.width*3);
SDL_RenderCopyEx(render, sdltext,NULL,NULL,0,NULL,SDL_FLIP_HORIZONTAL);
SDL_RenderDrawRect(render,&rect);
int y=80;
snprintf(buff,sizeof(buff),"年龄:%d",face_info.age);
size=SDL_ttfDisplayFont(w-300,y,window,ttffont,30,color,buff,render);
y+=size;
snprintf(buff,sizeof(buff),"颜值:%.2f",face_info.beauty);
size=SDL_ttfDisplayFont(w-300,y,window,ttffont,30,color,buff,render);
y+=size+10;
snprintf(buff,sizeof(buff),"性别:%s",face_info.gender);
size=SDL_ttfDisplayFont(w-300,y,window,ttffont,30,color,buff,render);
y+=size+10;
snprintf(buff,sizeof(buff),"表情:%s",face_info.emotion);
size=SDL_ttfDisplayFont(w-300,y,window,ttffont,30,color,buff,render);
y+=size+10;
snprintf(buff,sizeof(buff),"是否戴眼镜:%s",face_info.glasses);
size=SDL_ttfDisplayFont(w-300,y,window,ttffont,30,color,buff,render);
y+=size+10;
snprintf(buff,sizeof(buff),"是否戴口罩:%s",face_info.mask);
size=SDL_ttfDisplayFont(w-300,y,window,ttffont,30,color,buff,render);
SDL_RenderPresent(render); // 渲染
free(data);
}
else if(event.button.button==SDL_BUTTON_RIGHT)/*右键*/
{
printf("右键按下\n");
display_stat=1;
}
}
}
if(!video_flag)
{
quit=false;
continue;
}
pthread_mutex_lock(&fastmutex);//互斥锁上锁
pthread_cond_wait(&cond,&fastmutex);
memcpy(rgb_data,rgb_buff,video_info.height*video_info.width*3);
pthread_mutex_unlock(&fastmutex);//互斥锁解锁
SDL_UpdateTexture(sdltext,NULL,rgb_data, video_info.width*3);
//SDL_RenderCopy(render, sdltext, NULL,NULL); // 拷贝纹理到渲染器
SDL_RenderCopyEx(render, sdltext,NULL,NULL,0,NULL,SDL_FLIP_HORIZONTAL);
if(display_stat)SDL_RenderPresent(render); // 渲染
}
SDL_DestroyTexture(sdltext);/*销毁纹理*/
SDL_DestroyRenderer(render);/*销毁渲染器*/
SDL_DestroyWindow(window);/*销毁窗口 */
SDL_Quit();/*关闭SDL*/
pthread_mutex_destroy(&fastmutex);/*销毁互斥锁*/
pthread_cond_destroy(&cond);/*销毁条件变量*/
free(rgb_buff);
free(rgb_data);
}
Linux下SDL库安装教程:https://blog.csdn.net/weixin_44453694/article/details/121686023
工程示例源码:https://download.csdn.net/download/weixin_44453694/74088717