目录
- 说明
- 人工智能OCR识别平台介绍
- 人脸识别接口购买
- 编程打通OCR后台实现人脸识别(ubuntu上实现)
- 问题发现
- 编译libcurl库支持SSL
- 安装SSL依赖库openSSL(使用工具wget)
- curl库重新配置,编译,安装
- 开启SSL后重新编译人脸识别.C文件
- 传入图片的base64流
- 最终实现人脸识别程序
- 往期文章
说明
本章使用的平台是上位机Ubuntu,树莓派的要跟着这篇文章来:树莓派4B开发笔记(四)c语言https访问百度AI人脸识别接口之安装相关库
人工智能OCR识别平台介绍
OCR (Optical Character Recognition,光学字符识别)
百度搜索OCR识别,可以发现有很多大厂都在做这个项目,比如百度OCR,腾讯OCR,科大讯飞OCR等等。
国内中小型公司,甚至是大公司在做项目时希望能够快速占领市场,比如人脸识别,车牌识别需要非常稳定的算法,如果需要本公司研发部的研发人员去做,可能要包含两个硕士一个博士还有几个本科生一同做研发,生产周期以及稳定性都是面临了巨大的挑战。这时候,购买别人已经做好的方案,是非常划算的。
有个很好的例子,oppo手机vivo手机,未来要上5G,就得和华为合作,就是类似的原理。不是说他们搞不出来5G来,给他们时间肯定也搞得出来,但搞出来的时候已经晚了。
下面通过人工智能OCR识别平台翔云的使用,掌握调库调API开发的一般步骤,其他的平台也基本类似。
翔云注册登录
先看到【价格与购买】
在【产品】,看看人人脸识别的API文档
再看到【开发者中心】
点击图标下载看看能提供什么样子的案例
读一下这个C++示例代码
读取文档,捕捉类似这样的关键信息,一般注释都写得很清楚
strPostData.Format(_T("img=%s&key=%s&secret=%s&typeId=%d&format=xml"),strImageBase64,strKey,strSecret,nTypeID);
- 1
虽然是C++写的,到最后也可以用C语言做出来
来看看java的代码
java大多直接调库,非常简洁,这也是它开发效率高的原因之一。
人脸识别接口购买
初次体验购买,人脸识别100次只需支付0.01元。
购买完成可以在【个人中心】可以看到自己的OCRkey和密码,这两句在代码中肯定要用上来访问OCR平台。
编程打通OCR后台实现人脸识别(ubuntu上实现)
问题发现
第一版调试,最终程序是一步步改进得来的,想看最终程序的建议跳转。
程序大意:测试打通OCR后台,查看后台返回的数据
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>typedef unsigned int bool;//数据类型别名用typedef
#define true 1 //宏定义用define 就是宏替换
#define false 0//回调函数,读取从OCR后台返回的数据 前面打通百度的例子是将内容读取到本地文件fd里面
size_t readData(void *ptr, size_t size, size_t nmemb, void *stream)
{char buf[1024] = {'\0'};strncpy(buf,ptr,1024);printf("===========get data ===========\n");printf("%s\n",buf);
}bool postUrl() //根据文档,接口调用方法为post请求
{CURL *curl;CURLcode res;char* postString;//野指针,要开辟空间//根据翔云平台的接口要求 分开定义,然后字符串拼接char* img1 = NULL; //图片base64流 还未生成char* img2 = NULL;char* key = "xxx";char* secret = "xxx";int typeId = 21;char* format = "xml";//游标卡尺刻度 //2个image+typeId+formatpostString = (char* )malloc(strlen(key)+strlen(secret)+2048);//字符串拼接函数 仅测试,两张图片为空 sprintf(postString,"img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s","","",key,secret,typeId,format);curl = curl_easy_init();if(curl){curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie缓存文件 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);//指定post传输内容,get请求将URL和postString一次性发送curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/faceliu.do");// 指定urlcurl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,readData); //回调函数读取返回值res = curl_easy_perform(curl);printf("OK:%d\n",res);curl_easy_cleanup(curl);}return true;
}
int main(void)
{postUrl();
}
编译:
gcc OCR.c -I ./curl-7.71.1/_install/include/ -L ./curl-7.71.1/_install/lib/ -lcurl
运行结果:
ok:1
证明失败了,正常应该返回200,没有跟翔云的后台接通,回调函数没有打印后台返回来的数据。
编译libcurl库支持SSL
那是因为在编译libcurl库的时候,由于翔云接口是https:开头的,需要选择支持SSL。./configure --with -ssl
我们访问的接口是https开头的,要进行身份验证和数据加密的,体现在我们的key和secret。
HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议
所以要重新编译,让我们编译出来的libcurl支持SSL.
回忆之前libcurl 的 INSTALL.md
那我们就要回到/curl-7.71.1
路径下,删除rm _install -rf
上次编译出来的文件夹,重新编译支持SSL的库。由/docs/INSTALL.md
我们可以得知要想支持SSL,就得./configure --with-ssl
。
所以我们要这样配置(根据文档提示,如果你的openssl安装在/usr/local/ssl....如果安装在别的地方就要加一个环境变量,指定你openssl的安装位置)
./configure --prefix=$PWD/_install --with-ssl
编译错误,
cheking for SSL_connect in -lssl... (cached)no no
configure:error:openSSL libs and/or directories were not found where specified!
安装SSL依赖库openSSL(使用工具wget)
首先遇到问题,去百度搜索出现的提示内容。
查询到想要支持SSL必须要有依赖库,需要系统中已经安装好了SSL。
我们需要安装openSSL.tar
但是搜索的到的都是要钱的,如何免费下载呢?
利用linux的开源工具下载:wget是Linux中的一个下载文件的工具,wget是在Linux下开发的开放源代码的软件。
百度搜索技巧:wget openSSL.tar
找到类似于这样:
回到curl文件夹的上一级目录,执行命令(即安装在curl文件夹外面)
wget https://www.openssl.org/source/openssl-1.1.1a.tar.gz
注意:!!!大坑,这个a在Ubuntu可以用,但是在树莓派不行,总是报错表示他链接不到库,烦死了!!最好是在下面这个网站下载openssl-1.1.1j.tar.gz
https://www.openssl.org/source/
解压
tar xvf openssl-1.1.1a.tar.gz
进入文件夹,直接去看他的INSTALL。
为了避免到时候编译这个curl又要去配置SSL这个库,我们直接把SSL安装到默认的系统位置去(一般默认是在/usr/local底下),所以不指定安装路径了,直接
在/openssl-1.1.1a
路径下,配置
./config
编译(起码编译了8,9分钟)
make
安装:
sudo make install
要加 sudo因为肯定安装在了usr/local中,非工作目录无权限
curl库重新配置,编译,安装
回到/curl-7.71.1/
目录下重新进行配置:
./configure --prefix=$PWD/_install --with-ssl
make
make install
开启SSL后重新编译人脸识别.C文件
gcc OCR.c -I ./curl-7.71.1/_install/include/ -L ./curl-7.71.1/_install/lib/ -lcurl
在树莓派上编译的时候一定要多链接两个库
-lssl -lcrypto
否则会报错:
./curl-7.71.1/_install/lib//libcurl.so: undefined reference to `SSL_CTX_set_keylog_callback@OPENSSL_1_1_1'
./curl-7.71.1/_install/lib//libcurl.so: undefined reference to `SSL_CTX_set_post_handshake_auth@OPENSSL_1_1_1'
./curl-7.71.1/_install/lib//libcurl.so: undefined reference to `SSL_CTX_set_ciphersuites@OPENSSL_1_1_1'
collect2: error: ld returned 1 exit status
运行
可以看到已经收到了翔云后台的反馈:
===========get data ===========
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><data><message><status>-7</status><value>必传参数为空</value></message></data>
OK:23
必传参数为空的原因是:代码中没有传入图片给它识别
传入图片的base64流
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,可用于在HTTP环境下传递较长的标识信息。
在Linux下生成图片的base64编码
base64 photo.jpg
大致思路:(尚未进行封装)
system("base64 zhu1.jpg > tmpFile1");//把编码的结果保存到tmpFile1中int fdPic1 = open("./tmpFile1",O_RDWR);//创建文件保存编码
int fileLenPic1 = lseek(fdPic1,0,SEEK_END);//计算文件大小
lseek(fdPic1,0,SEEK_SET); //文件指针回头char* bufPic1 = (char* )malloc(fileLenPic1+8);//稍微多加一点空间
memset(bufPic1,'\0',fileLenPic1+8);read(fdPic1,bufPic1,fileLenPic1); //把文件内容度进取buf中
//printf("%s\n",buf); //做个简单的测试
close(fdPic1);
AAAAAAAAAAAAAAAA
如果平台返回的数据死活提示你传参错误:当你打开tmpFile1想看看编码的结果,如果后面出现很长很大一段的这个,成千上万的AAAAAAAAAAAAA,我想你应该跟我一样,直接手机拍自己一张图片就扔进去了。
查看图片的大小,往往是4~5个M,非常大!!也不符合平台的要求。转化成小一点的这些AAAAA就消失了。
怎样快速压缩图片大小??用QQ给图片截图,保存的时候转换为jpg格式,一般能够直接压缩到50多个K,非常好用。
最终实现人脸识别程序
程序大意:向OCR后台传入两张图片的base64流,后台经过判断返回数据,程序接收数据并判断是否含有“是”,若有,则输出人脸识别结果为“同一个人”。
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>typedef unsigned int bool;//数据类型别名用typedef
#define true 1 //宏定义用define
#define false 0char ocrRetBuf[1024] = {'\0'};//全局变量,用来接收从OCR后台返回的数据size_t readData(void *ptr, size_t size, size_t nmemb, void *stream)//回调函数,把从后台的数据拷贝给ocrRetBuf
{strncpy(ocrRetBuf,ptr,1024);
}char* getBase64FromFile(char* filePath)
{char* base64Buf = NULL;char cmd[256] = {'\0'};sprintf(cmd,"base64 %s > tmpFile",filePath);//图片的base64流导入到文件中system(cmd);int fd = open("./tmpFile",O_RDWR);int fileLen = lseek(fd,0,SEEK_END);lseek(fd,0,SEEK_SET);base64Buf = (char* )malloc(fileLen+8);memset(base64Buf,'\0',fileLen+8);read(fd,base64Buf,fileLen+8); //从文件中读取base64流到字符串 close(fd);system("rm -f tmpFile");return base64Buf;//指针变量随着子程序调用结束消失,但malloc开辟的空间地址还在,我拿到了这块地址,就能读
}bool postUrl()
{CURL *curl;CURLcode res;//分开定义,然后字符串拼接char* key = "xxx";char* secret = "xxx";int typeId = 21;char* format = "xml";char* base64BufPic1 = getBase64FromFile("./xiaoming1.jpg");char* base64BufPic2 = getBase64FromFile("./xiaoming2.jpg");int len = strlen(key)+strlen(secret)+strlen(base64BufPic1)+strlen(base64BufPic2)+128;//分配空间不够会>导致栈溢出char* postString = (char* )malloc(len);memset(postString,'\0',len);//因为postString是一个指针,不能用sizeof来计算其指向的大小sprintf(postString,"img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s",base64BufPic1,base64BufPic2,key,secret,typeId,format);//根据平台的传参格式编写//img1 img2两张图片的base64传给翔云OCR后台curl = curl_easy_init();if(curl){curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString); //指定post内容,传入参数 curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/faceliu.do");// 指定urlcurl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,readData); //回调函数readDate读取返回值res = curl_easy_perform(curl); //类似于状态码printf("OK:%d\n",res);if(strstr(ocrRetBuf,"否") != NULL){ //字符串检索 判断翔云后台返回的一大堆字符串中有没有“否”printf("不是同一个人\n");}else{printf("是同一个人\n");}curl_easy_cleanup(curl);}return true;
}int main(void)
{postUrl();return 0;
}
我这里编译没问题,运行提示找不到libcurl.so,4这个库,将它所在路径加入到环境变量就解决了。
运行结果:
stack smashing detecte 栈溢出
在调试过程中出现了这样的问题:
*** stack smashing detected ***: terminated
已放弃 (核心已转储)
当-fstack-protector启用时,当其检测到缓冲区溢出时,会立即终止正在执行的程序,并提示其检测到缓冲区存在的溢出的问题
一检查发现是计算开辟空间的大小不够(错写成了写了两个Pic1),修改即可。
int len = strlen(key)+strlen(secret)+strlen(base64BufPic1)+strlen(base64BufPic1)+128;//分配空间不够会导致栈溢出
char* postString = (char* )malloc(len);
引入下一篇:结合树莓派摄像头捕捉人脸
人脸识别API,调库会用了,那我们怎么样获取摄像头的数据,并且得到一张图片的base64流传给后台判断呢?
下一篇更新。
往期文章
智能家居 (1) ——智能家居整体功能框架
智能家居 (2) ——设计模式的引入
智能家居 (3) ——工厂模式继电器控制灯
智能家居 (4) ——工厂模式火焰报警
智能家居 (5) —— LD3320语音模块二次开发
智能家居 (6) ——语音识别线程控制
智能家居 (7) ——网络服务器线程控制
智能家居 (8) ——智能家居项目整合(网络控制线程、语音控制线程,火灾报警线程)
网络编程知识预备(1) ——了解OSI网络模型
网络编程知识预备(2) ——浅显易懂的三次握手与四次挥手
网络编程知识预备(3) ——SOCKET、TCP、HTTP之间的区别与联系
网络编程知识预备(4) ——了解HTTP协议与HTTPS协议
网络编程知识预备(5) ——libcurl库简介及其编程访问百度首页
智能家居 (9) ——人脸识别摄像头安装实现监控功能
智能家居 (10) ——人脸识别祥云平台编程使用
智能家居 (11) ——树莓派摄像头捕捉人脸并识别
智能家居 (12) ——人脸识别整合到智能家居系统
智能家居 (13) ——智能家居加入手机app端控制