http协议之libcurl库实现人脸识别, 车牌识别

libcurl原创: 冬冬他哥哥
链接: https://www.cnblogs.com/xietianjiao/p/13260021.html.

一、libcurl简介

libcurl是一个跨平台的网络协议库, 支持http, https, ftp, gopher, telnet, dict, file和dap协议。libcurl同样支持HTTPS证书授权, HTTP POST, HTTP PUT, FTP上传, HTTP基本表单上传, 代理, cookies和用户认证

库下载: https://github.com/curl/curl/releases/tag/curl-7_71_1

二、编译libcurl

首先去上面的链接下载libcurl库, 之后移动到ubuntu上面

  1. 解压资源包: tar xvf curl-7.71.1.tar.bz2
  2. ./configure --prefix=$PWD/_install 安装在当前路径下的_install文件夹
  3. make进行编译
  4. sudo make install进行安装, 安装完后, 当前所在的路径就会出现_install文件夹, 里面就有libcurl的头文件和libcurl的库文件

安装路径指令详解
–prefix: 指定安装路径
–host: 指定交叉编译
例如: ./configure --prefix=$PWD/_install --host=arm-linux(指定安装在当前路径下的_install文件夹, 指定交叉编译工具是arm-linux…)

三、libcurl的使用

调用curl_global_init()初始化libcurl
调用curl_easy_init()函数得到 easy interface型指针
调用curl_easy_setopt()设置传输选项
根据curl_easy_setopt()设置的传输选项,实现回调函数以完成用户特定任务
调用curl_easy_perform()函数完成传输任务
调用curl_easy_cleanup()释放内存

四、函数简介

1、CURLcode curl_global_init(long flags);函数只能用一次。(其实在调用curl_global_cleanup 函数后仍然可再用)

如果这个函数在curl_easy_init函数调用时还没调用,它将由libcurl库自动调用,所以多线程下最好主动调用该函数以防止在线程中curl_easy_init时多次调用。

注意:虽然libcurl是线程安全的,但curl_global_init是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init,应该将该函数的调用放在主线程中。
参数:flags

CURL_GLOBAL_ALL //初始化所有的可能的调用。
CURL_GLOBAL_SSL //初始化支持 安全套接字层。
CURL_GLOBAL_WIN32 //初始化win32套接字库。
CURL_GLOBAL_NOTHING //没有额外的初始化。

2、void curl_global_cleanup(void);
结束libcurl使用的时候,用来对curl_global_init做的工作清理。类似于close的函数。

注意:虽然libcurl是线程安全的,但curl_global_cleanup是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init,应该将该函数的调用放在主线程中。

3、char *curl_version( );
打印当前libcurl库的版本。

4、CURL curl_easy_init( );
curl_easy_init用来初始化一个CURL的指针(有些像返回FILE类型的指针一样). 相应的在调用结束时要用curl_easy_cleanup函数清理.
一般curl_easy_init意味着一个会话的开始. 它会返回一个easy_handle(CURL
对象), 一般都用在easy系列的函数中.

5、void curl_easy_cleanup(CURL *handle);
这个调用用来结束一个会话.与curl_easy_init配合着用.
参数:
CURL类型的指针.

6、CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
这个函数最重要了.几乎所有的curl 程序都要频繁的使用它.它告诉curl库.程序将有如何的行为. 比如要查看一个网页的html代码等.(这个函数有些像ioctl函数)参数:
1 CURL类型的指针
2 各种CURLoption类型的选项.(都在curl.h库里有定义,man 也可以查看到)
3 parameter 这个参数 既可以是个函数的指针,也可以是某个对象的指针,也可以是个long型的变量.它用什么这取决于第二个参数.
CURLoption 这个参数的取值很多.具体的可以查看man手册.

7、CURLcode curl_easy_perform(CURL *handle);

在初始化CURL类型的指针 以及curl_easy_setopt完成后调用. 就像字面的意思所说perform就像是个舞台.让我们设置的
option 运作起来.参数:
CURL类型的指针.

五、curl_easy_setopt函数部分选项介绍

本节主要介绍curl_easy_setopt中跟http相关的参数。该函数是curl中非常重要的函数,curl所有设置都是在该函数中完成的,该函数的设置选项众多,注意本节的阐述的只是部分常见选项。

1、CURLOPT_URL
设置访问URL

2、CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA
回调函数原型为:size_t function( void *ptr, size_t size, size_t nmemb, void *stream); 函数将在libcurl接收到数据后被调用,因此函数多做数据保存的功能,如处理下载文件。CURLOPT_WRITEDATA 用于表明CURLOPT_WRITEFUNCTION函数中的stream指针的来源。

如果你没有通过CURLOPT_WRITEFUNCTION属性给easy handle设置回调函数,libcurl会提供一个默认的回调函数,它只是简单的将接收到的数据打印到标准输出。你也可以通过 CURLOPT_WRITEDATA属性给默认回调函数传递一个已经打开的文件指针,用于将数据输出到文件里。

3、CURLOPT_HEADERFUNCTION,CURLOPT_HEADERDATA
回调函数原型为 size_t function( void *ptr, size_t size,size_t nmemb, void *stream); libcurl一旦接收到http 头部数据后将调用该函数。CURLOPT_WRITEDATA 传递指针给libcurl,该指针表明CURLOPT_HEADERFUNCTION 函数的stream指针的来源。

4、CURLOPT_READFUNCTION CURLOPT_READDATA
libCurl需要读取数据传递给远程主机时将调用CURLOPT_READFUNCTION指定的函数,函数原型是:size_t function(void *ptr, size_t size, size_t nmemb,void *stream). CURLOPT_READDATA 表明CURLOPT_READFUNCTION函数原型中的stream指针来源。

5、CURLOPT_NOPROGRESS,CURLOPT_PROGRESSFUNCTION,CURLOPT_PROGRESSDATA
跟数据传输进度相关的参数。CURLOPT_PROGRESSFUNCTION 指定的函数正常情况下每秒被libcurl调用一次,为了使CURLOPT_PROGRESSFUNCTION被调用,CURLOPT_NOPROGRESS必须被设置为false,CURLOPT_PROGRESSDATA指定的参数将作为CURLOPT_PROGRESSFUNCTION指定函数的第一个参数

6、CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT:
CURLOPT_TIMEOUT 由于设置传输时间,CURLOPT_CONNECTIONTIMEOUT 设置连接等待时间

7、CURLOPT_FOLLOWLOCATION
设置重定位URL

8、CURLOPT_RANGE: CURLOPT_RESUME_FROM:
断点续传相关设置。CURLOPT_RANGE 指定char *参数传递给libcurl,用于指明http域的RANGE头域,例如:
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
CURLOPT_RESUME_FROM 传递一个long参数给libcurl,指定你希望开始传递的 偏移量。

六、 curl_easy_perform 函数说明(error 状态码)

该函数是完成curl_easy_setopt指定的所有选项,本节重点介绍curl_easy_perform的返回值。返回0意味一切ok,非0代表错误发生。主要错误码说明:

1、CURLE_OK
任务完成一切都好

2、CURLE_UNSUPPORTED_PROTOCOL
不支持的协议,由URL的头部指定

3、CURLE_COULDNT_CONNECT
不能连接到remote 主机或者代理

4、CURLE_REMOTE_ACCESS_DENIED
访问被拒绝

5、CURLE_HTTP_RETURNED_ERROR
Http返回错误

6、CURLE_READ_ERROR
读本地文件错误

要获取详细的错误描述字符串,可以通过const char *curl_easy_strerror(CURLcode errornum ) 这个函数取得.

七、基础代码片

下面展示一些 内联代码片

#include <stdio.h>
#include <curl/curl.h>
bool getUrl(char *filename)
{
    CURL *curl;
    CURLcode res;
    FILE *fp;
    if ((fp = fopen(filename, "w")) == NULL)  // 返回结果用文件存储
        return false;
    struct curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Accept: Agent-007");
    curl = curl_easy_init();    // 初始化
    if (curl)
    {
        //curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");// 代理
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);// 改协议头
        curl_easy_setopt(curl, CURLOPT_URL,"http://www.baidu.com");
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); //将返回的http头输出到fp指向的文件
        curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp); //将返回的html主体数据输出到fp指向的文件
        res = curl_easy_perform(curl);   // 执行
        if (res != 0) {

            curl_slist_free_all(headers);
            curl_easy_cleanup(curl);
        }
        fclose(fp);
        return true;
    }
}
bool postUrl(char *filename)
{
    CURL *curl;
    CURLcode res;
    FILE *fp;
    if ((fp = fopen(filename, "w")) == NULL)
        return false;
    curl = curl_easy_init();
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "&logintype=uid&u=xieyan&psw=xxx86");    // 指定post内容
        //curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");
        curl_easy_setopt(curl, CURLOPT_URL, " http://mail.sina.com.cn/cgi-bin/login.cgi ");   // 指定url
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }
    fclose(fp);
    return true;
}
int main(void)
{
    getUrl("/tmp/get.html");
    postUrl("/tmp/post.html");
}

八、人脸识别

首先进入curl包里面删除旧的_install包, 因为之前编译的包没用, 不支持https的访问, docs文件夹里面的INSTALL.md文件明确告诉我们如果想要支持SSL, 就要用./configure --with-ssl指令, 要有这个–with-ssl参数选项, 如果加了运行指令后出现configure: error: --with-ssl was given but OpenSSL could not be detected的提示错误, 两种情况, 一种是没有安装openSSL, 因为要驱动openSSL, 你得先有openSSL, 另一种是编译环境没有加, 默认路径是在/usr/local/ssl底下

HTTPS 协议是由 SSL+HTTP 协议构建的

要让curl支持http加密, 它有依赖库, 系统得先有SSL, 才能去支持SSL

wget, 一个小窍门, 让linux的开源工具去linux的源码下面去找

安装OpenSSL: wget https://www.openssl.org/source/openssl-1.1.1a.tar.gz, 安装前提, 得先能上网, 不然会提示无法解析, 安装完后继续执行

相关博文: https://blog.csdn.net/weixin_38184741/article/details/86554438

避免在安装curl的时候要配置ssl的路径, 直接把ssl默认安装到系统路径底下去

解压, 进入openssl文件夹, 之后./config, make编译, sudo make install安装, 安装的时候记得要sudo, 默认是安装到/usr/local底下去, 因为在工作目录底下才有权限, 在/usr/local底下没有

上面操作放到树莓派上面都一样

回到curl文件夹, 重新编译libcurl, 系统现在已经有了ssl, 输入./configure --prefix=$PWD/_install --with-ssl, 之后make, 再之后make install

安装完之后, 重新编译文件, 这时候就能进行人脸识别了

下面展示一些 内联代码片

/*人脸识别代码, 人脸是识别有两种方式, 一种是base64流, 一种是file文档, 这里我用到的是base64流*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <curl/curl.h>

#define true 1
#define false 0

typedef unsigned int bool;

char buf[1024] = {'\0'};

size_t readData( void *ptr, size_t size, size_t nmemb, void *stream)
{
        strncpy(buf, ptr, 1024); //将运行完后的结果复制到buf中
        //printf("==============================getData=============================\n");
        //printf("%s\n",buf);
}

char *getPicBase64File(char *picFile)
{
        char cmd[32] = {'\0'};
        sprintf(cmd, "base64 %s > tmpFile", picFile); //base64指令, 运行完后将流放进tmpFile文件里
        system(cmd); //运行指令
        int fd = open("./tmpFile",O_RDWR); //打开流文件
        int fileLen = lseek(fd, 0, SEEK_END); //计算文件大小
        lseek(fd, 0, SEEK_SET); //计算完文件大小后, 光标的位置在最后, 我们接下来要做读操作, 所以把光标移到开头
        char *bufPic = (char *)malloc(fileLen); //定义一个跟流文件同样大小的字符串
        memset(bufPic, 0, fileLen);
        read(fd, bufPic, fileLen); //将文件里面的内容写入新创建的字符串里
        close(fd); //关闭文件

        system("rm ./tmpFile"); //删除tmpFile文件, 用完就删除   
        return bufPic;
}

bool postUrl()
{
      	CURL *curl;
    	CURLcode res;

        char *postString;

        /*base64流有六个参数, 详细可以见翔云人脸识别API文档*/
        char img1[32] = {'\0'}; //上传的文件(base64流, 用base64 ***.jpg生成)
        char img2[32] = {'\0'}; //上传的文件(base64流, 用base64 ***.jpg生成)
        char *key = ""; //ocrkey, 人脸识别需要用到的参数之一, 在翔云个人中心可以查到(我用的是翔云),  进行识别前记得先购买, 一毛一百次
        char *secret = ""; //ocrsecret, 人脸识别需要用到的参数之一, 在翔云个人中心可以查到, 进行识别前记得先购买, 一毛一百次
(我用的是翔云) 

        int typeId = 21; //对应的类型, 人脸识别对应的id是21
        char *format = "xml"; //返回格式有两种, 一种是xml, 一种是json, 我们用的是xml

        char *bufPic1 = getPicBase64File("./reba1.jpg"); //调用函数, 获取图片的base64流
        char *bufPic2 = getPicBase64File("./reba2.jpg"); //调用函数, 获取图片的base64流

        int len = strlen(key)+strlen(secret)+strlen(bufPic1)+strlen(bufPic2)+128; //算出六个参数所需要用到的空间大小
        postString = (char *)malloc(len);
        memset(postString, 0, len);
        sprintf(postString, "&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s",
                        bufPic1,bufPic2,key,secret,21,format); //封装post内容

        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"); // 指定url
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData); //将返回的html主体数据用函数读出       
                res = curl_easy_perform(curl);
                printf("OK:%d\n",res);

                /*找出返回数据的内容中是否带有是或否, 进行判断*/
                if(strstr(buf,"是") != NULL){
                        printf("the same person\n"); //是同一个人
                }else{
                        printf("diff person\n"); //不是同一个人
                }

                curl_easy_cleanup(curl);
        }
        return true;
}

int main(void)
{
        postUrl(); //post请求
}

九、车牌识别

车牌识别的代码跟上面人脸识别的代码一样, 只需要更换成对应参数即可实现

下面展示一些 内联代码片

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <curl/curl.h>

#define true 1
#define false 0

typedef unsigned int bool;

char buf[1024] = {'\0'};

size_t readData( void *ptr, size_t size, size_t nmemb, void *stream)
{
        strncpy(buf, ptr, 1024); //将运行完后的结果复制到buf中
        printf("==============================getData=============================\n");
        printf("%s\n",buf);
}

char *getPicBase64File(char *picFile)
{
        char cmd[128] = {'\0'};
        sprintf(cmd, "base64 %s > tmpFile", picFile); //base64指令, 运行完后将流放进tmpFile文件里
        system(cmd); //运行指令
        int fd = open("./tmpFile",O_RDWR); //打开流文件
        int fileLen = lseek(fd, 0, SEEK_END); //计算文件大小
        lseek(fd, 0, SEEK_SET); //计算完文件大小后, 光标的位置在最后, 我们接下来要做读操作, 所以把光标移到开头
        char *bufPic = (char *)malloc(fileLen); //定义一个跟流文件同样大小的字符串
        memset(bufPic, 0, fileLen);
        read(fd, bufPic, fileLen); //将文件里面的内容写入新创建的字符串里
        close(fd); //关闭文件

        system("rm ./tmpFile"); //删除tmpFile文件       
        return bufPic;
}

bool postUrl()
{
        CURL *curl;
        CURLcode res;

        char *postString;

        /*车牌识别有五个参数, 详细可以见翔云人脸识别API文档*/
        char *key = ""; //ocrkey, 车牌识别需要用到的参数之一, 在翔云个人中心可以查到(我用的是翔云),  进行识别前记得先购买, 一毛一百次
        char *secret = ""; //ocrsecret, 车牌识别需要用到的参数之一, 在翔云个人中心可以查到, 进行识别前记得先购买, 一毛一百次
(我用的是翔云)
        int typeId = 19; //对应的类型, 车牌识别对应的id是19
        char *format = "xml"; //返回格式有两种, 一种是xml, 一种是json, 我们用的是xml

        char *bufPic = getPicBase64File("./car.jpg"); //调用函数, 获取图片的base64流

        int len = strlen(key)+strlen(secret)+strlen(bufPic)+128; //算出五个参数所需要用到的空间大小
        postString = (char *)malloc(len);
        memset(postString, 0, len);
        sprintf(postString, "&img=%s&key=%s&secret=%s&typeId=%d&format=%s",
                        bufPic,key,secret,typeId,format); //封装post内容

        curl = curl_easy_init();
        if (curl)
        {
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);    // 指定post内容
                curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/recogliu.do");   // 指定url
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData);        //将返回的html主体数据用函数读出        
                res = curl_easy_perform(curl);
                printf("OK:%d\n",res);

                /*找出返回数据的内容中是否带有是或否, 进行判断*/
                if(strstr(buf,"是") != NULL){
                        //printf("the same person\n");
                }else{
                        //printf("diff person\n");
                }

                curl_easy_cleanup(curl);
        }
        return true;
}

int main(void)
{
        postUrl(); //post请求
}

运行结果:
http协议之libcurl库实现人脸识别, 车牌识别

获取车牌号代码

下面展示一些 内联代码片

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main()
{
        int i = 0;

        char *str = (char *)malloc(12);
        memset(str, 0, 12);

        int fd = open("./carfile",O_RDWR);

        int len = lseek(fd, 0, SEEK_END);
        char *buf = (char *)malloc(len);
        memset(buf, 0, len);
        lseek(fd, 0, SEEK_SET);
        read(fd, buf, len);

        close(fd);

		/*返回的数据里面找到车牌号*/
        if(strstr(buf,"车牌号") != NULL){
                char *p = strstr(buf,"车牌号"); //strstr函数的作用是如果主字符串里包含子字符串, 它的返回值就是出现子字符串的位置
                p = p+strlen("车牌号"); //把光标移到号字后, 没移之前的光标在车前面
                p = p+11; //地址移位
                for(i=0; i<9; i++){
                        str[i] = *p++;
                }
                printf("get data:%s\n",str);
        }
        
        return 0;
}
        

运行结果:
http协议之libcurl库实现人脸识别, 车牌识别

上一篇:干货分享系列 - IPC摄像头在线状态ping检测与告警邮件发送项目总结


下一篇:用c#开发微信 (18) 多客服