1.開(kāi)發(fā)環(huán)境及運(yùn)行效果
硬件平臺(tái):Ubuntu18.04、USB免驅(qū)攝像頭
UI界面:GTK+_2.0
開(kāi)發(fā)語(yǔ)言:C/C++
2.GTK簡(jiǎn)介
GTK(GIMP Toolkit)是一套源碼以LGPL許可協(xié)議分發(fā)、跨平臺(tái)的圖形工具包。最初是為GIMP寫(xiě)的,已成為一個(gè)功能強(qiáng)大、設(shè)計(jì)靈活的一個(gè)通用圖形庫(kù),是GNU/Linux下開(kāi)發(fā)圖形界面的應(yīng)用程序的主流開(kāi)發(fā)工具之一。當(dāng)然,GTK也是支持跨平臺(tái)的,支持Unix類的系統(tǒng)、Windows,甚至手機(jī)平臺(tái)。
GTK是使用C語(yǔ)言寫(xiě)的,所以其原生API都是面向C的,同時(shí)GTK的一大特點(diǎn)是,在C語(yǔ)言層面實(shí)現(xiàn)了面向?qū)ο蟮奶匦?。GTK是完全免費(fèi)的,而且基于LGPL協(xié)議,這可以保證私有軟件通過(guò)鏈接使用GTK可以不把軟件源代碼開(kāi)放,對(duì)商業(yè)應(yīng)用較友好,這跟GPL協(xié)議是不一樣的。也正是LGPL協(xié)議,使得早些年Gnome(基于GTK編寫(xiě))風(fēng)頭勝過(guò)KDE(基于QT編寫(xiě))。
3.開(kāi)發(fā)流程
3.1 GTK初始化和攝像頭初始化
??初始化GTK,創(chuàng)建窗口,初始化攝像頭設(shè)備,檢測(cè)攝像頭設(shè)備是否正常。
int main(int argc,char **argv) { GtkWidget *dialog; /*gtk初始化*/ gtk_init(&argc,&argv); /*創(chuàng)建窗口*/ window=gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window),"人臉識(shí)別"); /*固定窗口大小*/ gtk_window_set_resizable (GTK_WINDOW(window),FALSE); /*設(shè)置窗口大小*/ gtk_widget_set_size_request(window,800,480); gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER_ALWAYS);//居中顯示 /*連接信號(hào)*/ g_signal_connect(G_OBJECT(window),"destroy",G_CALLBACK(on_delete_event),NULL); /*創(chuàng)建標(biāo)簽*/ GdkColor color; color.red=0xffff; color.green=0; color.blue=0; label=gtk_label_new(""); /*設(shè)置字體大小和顏色*/ gtk_label_set_markup(GTK_LABEL(label),"正在啟動(dòng)攝像頭,請(qǐng)稍等..."); g_timeout_add(500,timer_func, label);//開(kāi)啟定時(shí)器1s時(shí)間 gtk_label_set_justify (GTK_LABEL(label),GTK_JUSTIFY_CENTER); gtk_container_add(GTK_CONTAINER(window), label); gtk_widget_show(label); change_background(window, 800, 480, "1.bmp"); gtk_widget_show(window); gtk_main(); return 0; }
??在定時(shí)器中等待攝像頭啟動(dòng)成功,完成人臉識(shí)別UI界面配置。
等待攝像頭啟動(dòng)界面
攝像頭啟動(dòng)成功界面
/*定時(shí)器處理函數(shù)*/ GtkWidget *window; gboolean timer_func(gpointer data) { GtkWidget *widget=(GtkWidget *)data; int ret; ret=Camera_Init(&video_info); if(ret==0) { g_print("攝像頭啟動(dòng)成功n"); gtk_widget_destroy(label);//關(guān)閉對(duì)話框 GtkWidget *hbox; GtkWidget *vbox,*box; GtkWidget *button; GtkTooltips *tiptool; /*圖像寬高*/ width=video_info.image_w; height=video_info.image_h; rgbbuf=malloc(width*height*3); jpg_data=malloc(width*height*3);//保存RGB顏色數(shù)據(jù) imagebase64_data=malloc((height*width*3)*8/6);/*base64編碼后數(shù)據(jù)*/ /*創(chuàng)建橫向盒*/ hbox=gtk_hbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(window),hbox); gtk_widget_show(hbox); /*初始化gdk的rgb*/ gdk_rgb_init(); /*將gdk視件和顏色表放入gtk構(gòu)件中*/ gtk_widget_push_visual(gdk_rgb_get_visual()); gtk_widget_push_colormap(gdk_rgb_get_cmap()); /*創(chuàng)建繪圖區(qū)域*/ drawarea=gtk_drawing_area_new(); gtk_widget_pop_visual(); gtk_widget_pop_colormap(); gtk_box_pack_start(GTK_BOX(hbox),drawarea,FALSE,FALSE,0); g_signal_connect(G_OBJECT(drawarea),"expose_event",G_CALLBACK(draw_image),NULL); guint id=gtk_idle_add((GtkFunction)read_data,NULL); /*設(shè)置窗口大小*/ gtk_widget_set_size_request(GTK_WIDGET(drawarea),width,height); /*創(chuàng)建縱向盒*/ vbox=gtk_vbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(hbox),vbox,TRUE,FALSE,0); gtk_widget_show(vbox); /*人臉注冊(cè)時(shí)輸入姓名*/ box=gtk_hbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(vbox),box,FALSE,TRUE,30); gtk_widget_show(box); label=gtk_label_new("姓名:"); gtk_box_pack_start(GTK_BOX(box),label,FALSE,TRUE,0); gtk_widget_show(label); /*創(chuàng)建輸入文本框*/ entry1=gtk_entry_new(); /*設(shè)置輸入文本框尺寸*/ gtk_widget_set_size_request(entry1,120,-1); gtk_entry_set_max_length(GTK_ENTRY(entry1),20); gtk_box_pack_start(GTK_BOX(box),entry1,FALSE,FALSE,0); g_signal_connect(G_OBJECT(entry1),"key-press-event", G_CALLBACK(entry_callback),"1"); gtk_widget_show(entry1); /*創(chuàng)建提示對(duì)象工具*/ tiptool=gtk_tooltips_new(); /*添加提示信息到按鈕*/ gtk_tooltips_set_tip(tiptool,entry1,"填寫(xiě)姓名",NULL); /*人臉注冊(cè)時(shí)輸入手機(jī)號(hào)*/ box=gtk_hbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(vbox),box,FALSE,TRUE,15); gtk_widget_show(box); label=gtk_label_new("電話:"); gtk_box_pack_start(GTK_BOX(box),label,FALSE,TRUE,0); gtk_widget_show(label); /*創(chuàng)建輸入文本框*/ entry2=gtk_entry_new(); /*設(shè)置輸入文本框尺寸*/ gtk_widget_set_size_request(entry2,120,-1); gtk_entry_set_max_length(GTK_ENTRY(entry2),20); gtk_box_pack_start(GTK_BOX(box),entry2,FALSE,FALSE,0); g_signal_connect(G_OBJECT(entry2),"key-press-event", G_CALLBACK(entry_callback),"2"); gtk_widget_show(entry2); /*創(chuàng)建提示對(duì)象工具*/ tiptool=gtk_tooltips_new(); /*添加提示信息到按鈕*/ gtk_tooltips_set_tip(tiptool,entry2,"填寫(xiě)手機(jī)號(hào)",NULL); /*設(shè)置標(biāo)簽,用于顯示狀態(tài)信息*/ box=gtk_hbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(vbox),box,FALSE,FALSE,80); gtk_widget_show(box); label=gtk_label_new(""); gtk_label_set_justify (GTK_LABEL(label),GTK_JUSTIFY_CENTER); gtk_box_pack_start(GTK_BOX(box),label,TRUE,FALSE,20); gtk_widget_show(label); box=gtk_hbox_new(FALSE,0); gtk_box_pack_end(GTK_BOX(vbox),box,FALSE,FALSE,20); gtk_widget_show(box); button=gtk_button_new_with_label("人臉識(shí)別"); gtk_box_pack_start(GTK_BOX(box),button,TRUE,TRUE,0); g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(button_callback),"1"); gtk_widget_show(button); box=gtk_hbox_new(FALSE,0); gtk_box_pack_end(GTK_BOX(vbox),box,FALSE,FALSE,20); gtk_widget_show(box); button=gtk_button_new_with_label("人臉注冊(cè)"); gtk_box_pack_start(GTK_BOX(box),button,TRUE,TRUE,0); g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(button_callback),"2"); gtk_widget_show(button); gtk_widget_show_all(window); return FALSE;//返回FALSE結(jié)束定時(shí)器 } g_print("攝像頭打開(kāi)失敗ret=%dn",ret); gtk_label_set_markup(GTK_LABEL(widget),"攝像頭啟動(dòng)失敗,請(qǐng)檢測(cè)設(shè)備是否正常!"); return TRUE;//返回TURE繼續(xù)觸發(fā)定時(shí)器 }
6.2 人臉識(shí)別和人臉注冊(cè)
??將攝像頭采集的圖像進(jìn)行Base64編碼,然后調(diào)用百度AI接口進(jìn)行人臉注冊(cè)和人臉比對(duì)。
/*按鈕處理函數(shù)*/ void button_callback(GtkWidget *widget,gpointer data) { gint res=0; gchar format_data[512]; gchar *p=(gchar *)data; guint size=(height*width*3)*8/6; gchar *imagebase64=malloc(size);/*base64編碼后數(shù)據(jù)*/ if(strcmp(p,"1"))//人臉注冊(cè) { g_print("人臉注冊(cè)n"); /*保證線程安全,上鎖*/ gdk_threads_enter(); memcpy(imagebase64,imagebase64_data,size); /*保證線程安全,釋放*/ gdk_threads_leave(); if(user_info.name[0]=='' || user_info.phone[0]=='' || strlen(user_info.phone)!=11) { //g_print("請(qǐng)正常填寫(xiě)名字手機(jī)號(hào)碼n"); gtk_label_set_markup(GTK_LABEL(label),"請(qǐng)正常填寫(xiě)名字n手機(jī)號(hào)碼"); //開(kāi)啟定時(shí)器1s時(shí)間 time_flag=g_timeout_add(1000,timer_func2,label); free(imagebase64); return ; } /*人臉注冊(cè)*/ g_print("phone:%s,name:%sn",user_info.phone,user_info.name); snprintf(format_data,512,""group_id":"wbyq","user_id":"%s","user_info":"%s","quality_control":"LOW","liveness_control":"NORMAL"}",user_info.phone,user_info.name); g_print("format=%sn",format_data); faceDetect(request_url,access_token,imagebase64,format_data); if(res)/*訪問(wèn)服務(wù)器失敗*/ { gtk_label_set_markup(GTK_LABEL(label),"注冊(cè)失敗"); } /* {"error_code":0,"error_msg":"SUCCESS","log_id":9465001899925,"timestamp":1641801995,"cached":0,"result":{"face_token":"66d2d1512ec57835ec4ef25ed95bec77", "location":{"left":265.38,"top":251.49,"width":201,"height":192,"rotation":0}}} */ else if(JsonData_analysis(platform_data,""error_msg"",format_data))/*查找“error_msg”標(biāo)志失敗*/ { gtk_label_set_markup(GTK_LABEL(label),"注冊(cè)失敗"); } else if(strcmp(format_data,"SUCCESS"))/*返回狀態(tài)標(biāo)志失敗*/ { gtk_label_set_markup(GTK_LABEL(label),"注冊(cè)失敗"); } else gtk_label_set_markup(GTK_LABEL(label),"注冊(cè)成功"); } else if(strcmp(p,"2"))//人臉識(shí)別 { if(time_flag)g_source_remove(time_flag);//關(guān)閉定時(shí)器 /*保證線程安全,上鎖*/ gdk_threads_enter(); memcpy(imagebase64,imagebase64_data,size); /*保證線程安全,釋放*/ gdk_threads_leave(); snprintf(format_data,sizeof(format_data),""group_id_list":"wbyq","quality_control":"LOW","liveness_control":"NORMAL"}"); res=faceDetect(face_search,access_token,imagebase64,format_data);//人臉比對(duì) if(res==0) { struct USER user_data; if(Josn_faceSearch(platform_data,&user_data)) { //g_print("比對(duì)失敗n"); gtk_label_set_markup(GTK_LABEL(label),"請(qǐng)重試"); gtk_entry_set_text(GTK_ENTRY(entry1),""); gtk_entry_set_text(GTK_ENTRY(entry2),""); } else { //g_print("比對(duì)成功:%sn",format_data); gtk_label_set_markup(GTK_LABEL(label),"識(shí)別成功"); gtk_entry_set_text(GTK_ENTRY(entry1),user_data.name); gtk_entry_set_text(GTK_ENTRY(entry2),user_data.phone); } } } free(imagebase64); }
3.3 base64格式編碼
Base64是網(wǎng)絡(luò)上最常見(jiàn)的用于傳輸8Bit字節(jié)碼的編碼方式之一,Base64就是一種基于64個(gè)可打印字符來(lái)表示二進(jìn)制數(shù)據(jù)的方法??刹榭?a href="http://ttokpm.com/tongxin/rf/" target="_blank">RFC2045~RFC2049,上面有MIME的詳細(xì)規(guī)范。
Base64編碼是從二進(jìn)制到字符的過(guò)程,可用于在HTTP環(huán)境下傳遞較長(zhǎng)的標(biāo)識(shí)信息。采用Base64編碼具有不可讀性,需要解碼后才能閱讀。
Base64由于以上優(yōu)點(diǎn)被廣泛應(yīng)用于計(jì)算機(jī)的各個(gè)領(lǐng)域,然而由于輸出內(nèi)容中包括兩個(gè)以上“符號(hào)類”字符(+, /, =),不同的應(yīng)用場(chǎng)景又分別研制了Base64的各種“變種”。為統(tǒng)一和規(guī)范化Base64的輸出,Base62x被視為無(wú)符號(hào)化的改進(jìn)版本。
Base64要求把每三個(gè)8Bit的字節(jié)轉(zhuǎn)換為四個(gè)6Bit的字節(jié)(38 = 46 = 24),然后把6Bit再添兩位高位0,組成四個(gè)8Bit的字節(jié),也就是說(shuō),轉(zhuǎn)換后的字符串理論上將要比原來(lái)的長(zhǎng)1/3。
3.3.1 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] = ''; return base64; }
3.3.2 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] != '' ; 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; }
3.4 CJSON格式數(shù)據(jù)解析
JSON(JavaScript Object Notation, JS對(duì)象簡(jiǎn)譜)是一種輕量級(jí)的數(shù)據(jù)交換格式。它基于 ECMAScript(European Computer Manufacturers Association, 歐洲計(jì)算機(jī)協(xié)會(huì)制定的js規(guī)范)的一個(gè)子集,采用完全獨(dú)立于編程語(yǔ)言的文本格式來(lái)存儲(chǔ)和表示數(shù)據(jù)。簡(jiǎn)潔和清晰的層次結(jié)構(gòu)使得 JSON 成為理想的數(shù)據(jù)交換語(yǔ)言。 易于人閱讀和編寫(xiě),同時(shí)也易于機(jī)器解析和生成,并有效地提升網(wǎng)絡(luò)傳輸效率
JSON是一個(gè)標(biāo)記符的序列。這套標(biāo)記符包含六個(gè)構(gòu)造字符、字符串、數(shù)字和三個(gè)字面名。
? JSON是一個(gè)序列化的對(duì)象或數(shù)組。
JSON格式數(shù)據(jù)解析示例
/******************json數(shù)據(jù)解析**************** 形參:u8* buff原始數(shù)據(jù) u8 *flag數(shù)據(jù)標(biāo)志 u8 *data解析獲取到的數(shù)據(jù) 返回值:0---成功,其他值---失敗 ************************************************/ guchar JsonData_analysis(guchar* buff,guchar *flag,guchar *data) { guchar *p=NULL; guint i=0; p=strstr((char *)buff,(char *)flag); if(p) { p+=strlen((char *)flag)+2; i=0; while(*p!='"' && *p!='') { data[i++]=*p++; } data[i]=''; return 0; } else return -1; } /* josn數(shù)據(jù)解析 buff --要解析的內(nèi) user_info--解析成功返回用戶名和id 返回值:成功返回0,失敗返回其它值 */ int Josn_faceSearch(const char *buff,struct USER *user_info) { /* data={"error_code":0,"error_msg":"SUCCESS","log_id":2014555550012,"timestamp":1641792320,"cached":0, "result":{"face_token":"6473abd658b76d6843de9a0820aeb2d2","user_list":[{"group_id":"wbyq","user_id":"18172864683","user_info":"u738bu65b0u6c34","score":99.232490539551}]}} */ /*創(chuàng)建cJSON對(duì)象*/ cJSON *root=cJSON_CreateObject(); char *p=strstr(buff,"{"error_code""); root=cJSON_Parse(p);/*載入JSON數(shù)據(jù)*/ if(root==NULL)return -1; /*2.解析字段*/ cJSON *item; item=cJSON_GetObjectItem(root,"error_msg"); if(item) { printf("error_code:%sn",item->valuestring); if(strcmp(item->valuestring,"SUCCESS"))return -2; } item=cJSON_GetObjectItem(root,"result"); if(item) { item=cJSON_GetObjectItem(item,"user_list"); cJSON *array_item=cJSON_GetArrayItem(item,0); if(array_item==NULL)return -1; cJSON *obj=cJSON_GetObjectItem(array_item,"user_info"); if(obj)printf("info=%sn",obj->valuestring); strcpy(user_info->name,obj->valuestring); obj=cJSON_GetObjectItem(array_item,"user_id"); strcpy(user_info->phone,obj->valuestring); } cJSON_Delete(root);//釋放空間 return 0; }
4.人臉識(shí)別和人臉注冊(cè)運(yùn)行效果
-
Linux
+關(guān)注
關(guān)注
87文章
11212瀏覽量
208721 -
人臉識(shí)別
+關(guān)注
關(guān)注
76文章
4002瀏覽量
81678 -
界面設(shè)計(jì)
+關(guān)注
關(guān)注
0文章
22瀏覽量
10468
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論