【常见开源库的二次开发】libcurl实战项目——人脸识别(三)

开源 0

目录:

目录:

简介:

一、 人工智能OCR识别平台翔云等介绍

1.1 什么是OCR?

1.2 OCR技术的应用

1.3 人工智能开放平台——翔云

二、代码编写:

2.1 重要参数

2.2 代码编写

2.3 详细分析

2.4 配置opensll并且进行编译

三、加入图片base64编码

3.1 什么是base64编码?

3.2 实现原理:

3.3 Base64编码的步骤

3.4 图片格式转化 

3.5 读取图片文件 

3.6 实现思路: 

3.6.1. 基本思路:    

3.6.2. Base64编码文件 

3.6.3  发送POST请求 

3.6.4 程序入口 

四、可能遇到的问题


简介:

        libcurl是一个开源的、跨平台的网络传输库,它支持多种协议,包括HTTP、HTTPS、FTP、FTPS、TFTP、SCP、SFTP、SMB、SMBS、TELNET、DICT、LDAP、LDAPS、FILE、POP3、IMAP、SMTP、RTMP和RTMPS。libcurl库以其灵活性和易用性而闻名,它允许开发者轻松地在其应用程序中集成网络通信功能,本章我们先对libcurl库运用到实战项目中进行一个简单的教程与学习

一、 人工智能OCR识别平台翔云等介绍

1.1 什么是OCR?

        OCR(Optical Character Recognition,光学字符识别)是一种将不同类型的文档(如扫描的纸质文档、PDF文件或照片)中的文字转换成可编辑和搜索的文本的技术。OCR技术可以识别印刷的、手写的或打印的字符,并将其转换为机器可读的文本数据。

1.2 OCR技术的应用

  1. 文档数字化:将纸质文档扫描成电子文档并提取文字信息。
  2. 自动化数据输入:减少手动输入,提高效率和准确性。
  3. 文本搜索和编辑:在数字化文档中快速搜索和编辑文本内容。
  4. 身份验证和证件识别:识别身份证、护照、驾驶证等证件上的文字信息。
  5. 车牌识别:在智能交通系统中识别和记录车牌信息。

        OCR技术在文档处理、数据输入、信息检索等方面有着广泛的应用。知名的OCR平台如Google Cloud Vision、微软Azure OCR、Tesseract等各有其特点和优势,用户可以根据自己的需求选择合适的平台进行使用。虽然目前对“翔云”平台的具体情况不了解,但可以推测它可能提供类似的OCR功能,帮助用户实现高效的文字识别和数据处理。 

1.3 人工智能开放平台——翔云

        人工智能开放平台是一种通过云服务向开发者和企业提供人工智能能力的平台。这些平台通常集成了多种AI工具和API,涵盖了机器学习、自然语言处理、计算机视觉等领域。

  • 多类型API:包括NLP、计算机视觉、语音识别等API。
  • 开发者支持:提供SDK、文档、示例代码等支持,帮助开发者快速上手。
  • 自定义模型:支持用户上传数据进行自定义模型训练和部署。
  • 数据管理:提供安全可靠的数据存储和管理功能。
  • 行业应用:针对特定行业(如金融、医疗、零售等)提供定制化解决方案。

翔云平台传送门:翔云_人工智能_API_服务平台 (netocr.com) 

二、代码编写:

2.1 重要参数

2.2 代码编写

#include <stdio.h>#include <curl/curl.h>#include <stdbool.h>#include <stdlib.h>#include <string.h>// 使用 libcurl 发送 POST 请求并将结果存储到文件中bool postUrl(char *filename) {    CURL *curl;    CURLcode res;    FILE *fp = fopen(filename, "wb");  // 打开文件用于写入    if (!fp) {        perror("fopen");        return false;    }    char img1[100] = "";    char img2[100] = "";    char *key = "ETJBxUZ45LbKRbacTiAXig";    char *secret = "a484b1a76c2f4e248850611bb4208144";    int typeId = 21;    char *format = "xml";    char *postString = (char *)malloc(strlen(key) + strlen(secret) + 2048);    if (!postString) {        perror("malloc");        fclose(fp);        return false;    }    sprintf(postString, "&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", img1, img2, key, secret, typeId, format);    curl = curl_easy_init();    if (curl) {        // 设置 cookie 文件        curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt");        // 设置 POST 数据        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);        // 设置请求 URL        curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/verapi/verFaceImage.do");        // 设置将返回的 HTTP 头和主体数据输出到文件        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);    // 输出 HTML 主体数据        curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp);   // 输出 HTTP 头        // 执行请求        res = curl_easy_perform(curl);        if (res != CURLE_OK) {            fprintf(stderr, "curl_easy_perform() failed: %s/n", curl_easy_strerror(res));        } else {            printf("Request completed successfully./n");        }        // 清理 libcurl 资源        curl_easy_cleanup(curl);    } else {        fprintf(stderr, "curl_easy_init() failed/n");    }    free(postString);    // 关闭文件    fclose(fp);    return true;}int main(void) {    // 发送 POST 请求并将结果存储到 /tmp/post.html 文件    if (postUrl("/tmp/post.html")) {        printf("POST request sent successfully./n");    } else {        printf("Failed to send POST request./n");    }    return 0;}

2.3 详细分析

代码实现思路

1. 包含必要的头文件:引入用于文件操作、内存管理、字符串操作和 libcurl 库的头文件。

#include <stdio.h>#include <curl/curl.h>#include <stdbool.h>#include <stdlib.h>#include <string.h>

stdio.h:用于标准输入输出。
curl/curl.h:用于 libcurl 库中的各种函数和类型。
stdbool.h:用于布尔类型和值。
stdlib.h:用于动态内存分配、进程控制、转换以及其他实用函数。
string.h:用于字符串处理函数。


2. 函数声明与定义: 定义一个函数 `postUrl`,用于发送 HTTP POST 请求并将响应结果写入指定文件。

bool postUrl(char *filename);

定义 `postUrl` 函数,接收一个文件名作为参数,并返回布尔类型的值,表示操作是否成功。

3. 变量声明与初始化:声明并初始化一些字符串和变量,这些变量将用于构造 POST 请求的数据。

CURL *curl;CURLcode res;FILE *fp = fopen(filename, "wb");  // 打开文件用于写入if (!fp) {    perror("fopen");    return false;}char img1[100] = "";char img2[100] = "";char *key = "ETJBxUZ45LbKRbacTiAXig";char *secret = "a484b1a76c2f4e248850611bb4208144";int typeId = 21;char *format = "xml";

CURL *curl和 CURLcode res用于 libcurl 操作和存储返回的结果。
FILE *fp:用于文件操作,将响应结果写入文件。
fopen:打开文件用于写入,如果失败则输出错误信息并返回 false。
img1 和 img2:未使用的图像文件名,初始化为空字符串。
key,secret,typeId 和 format:用于构造 POST 请求的数据。

4. 动态分配内存:为 POST 数据字符串分配足够的内存并构造 POST 数据字符串。

char *postString = (char *)malloc(strlen(key) + strlen(secret) + 2048);if (!postString) {    perror("malloc");    fclose(fp);    return false;}sprintf(postString, "&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", img1, img2, key, secret, typeId, format);

动态分配足够的内存用于存储 POST 数据字符串。
检查 malloc是否成功,如果失败则输出错误信息、关闭文件并返回 false。

使用 `sprintf` 构造包含 POST 数据的字符串。

5. 初始化 CURL 会话:使用 `curl_easy_init` 初始化 CURL 会话。

curl = curl_easy_init();if (curl) {    // 设置 CURL 选项} else {    fprintf(stderr, "curl_easy_init() failed/n");    fclose(fp);    free(postString);    return false;}

初始化 CURL 会话,并检查是否成功。如果失败则输出错误信息、关闭文件并释放内存。 

6. 设置 CURL 选项:通过 curl_easy_setopt 设置各种选项,包括 URL、POST 数据和输出文件。

// 设置 cookie 文件curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt");// 设置 POST 数据curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);// 设置请求 URLcurl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/verapi/verFaceImage.do");// 设置将返回的 HTTP 头和主体数据输出到文件curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);    // 输出 HTML 主体数据curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp);   // 输出 HTTP 头

CURLOPT_COOKIEFILE:设置 cookie 文件。
CURLOPT_POSTFIELDS:设置 POST 数据。
CURLOPT_URL:设置请求 URL。
CURLOPT_WRITEDATA和 CURLOPT_HEADERDATA:设置将返回的 HTTP 头和主体数据输出到同一文件。

7. 执行 HTTP 请求:使用 `curl_easy_perform` 执行 HTTP 请求,并检查请求是否成功。

// 执行请求res = curl_easy_perform(curl);if (res != CURLE_OK) {    fprintf(stderr, "curl_easy_perform() failed: %s/n", curl_easy_strerror(res));} else {    printf("Request completed successfully./n");}

执行 HTTP 请求并检查是否成功,如果失败则输出错误信息。


8. 清理资源与关闭文件:清理 CURL 资源,关闭文件,并释放动态分配的内存。

// 清理 libcurl 资源curl_easy_cleanup(curl);free(postString);fclose(fp);return true;

清理 CURL 资源。

释放动态分配的内存。
关闭文件。
返回 true 表示操作成功。


9. 主函数:调用 postUrl 函数,并根据返回值输出相应的提示信息。

int main(void) {    // 发送 POST 请求并将结果存储到 /tmp/post.html 文件    if (postUrl("/tmp/post.html")) {        printf("POST request sent successfully./n");    } else {        printf("Failed to send POST request./n");    }    return 0;}

 调用 `postUrl` 函数,将结果写入 `/tmp/post.html` 文件。
 根据 `postUrl` 的返回值输出相应的提示信息。

        代码实现了向指定 URL 发送 POST 请求并将响应结果写入文件的功能。每一步都进行了必要的错误检查和资源管理,确保程序的健壮性和可维护性。

2.4 配置opensll并且进行编译

我们先要配置openssl 

 ./configure --prefix=$PWD/_install --with-ssl

book@100ask:~/httpHandler/curl-7.71.1$ make

book@100ask:~/httpHandler/curl-7.71.1$ make install

book@100ask:~/httpHandler$ gcc demo4.c -I ./curl-7.71.1/_install/include/ -L ./curl-7.71.1/_install/lib/ -lcurl

         执行程序后请求到的数据

三、加入图片base64编码

3.1 什么是base64编码?

        Base64编码是一种常见的用于在网络上传输二进制数据的编码方式。它将二进制数据转换为由64个可打印字符组成的字符串,这些字符包括大写字母、小写字母、数字以及两个符号(通常是+/)。Base64编码的主要优点是它使二进制数据能够安全地传输而不会被误解释为控制字符或其他不可见字符。

3.2 实现原理:

        Base64编码将每三个字节(24位)的二进制数据划分为四组,每组6位(24位 / 4 = 6位),然后将每组6位的数据映射为Base64字符集中的一个字符。由于Base64字符集是可打印的,它们可以在各种传输协议中安全地使用。Base64字符集如下:

ABCDEFGHIJKLMNOPQRSTUVWXYZ  (0-25)abcdefghijklmnopqrstuvwxyz  (26-51)0123456789                  (52-61)+ /                         (62, 63)

        如果输入的二进制数据不是3的倍数,编码过程会在末尾添加一个或两个填充字符(通常是=),以确保输出字符串的长度符合规范。

3.3 Base64编码的步骤

  1. 将输入字符串转换为二进制数据

            (1)输入字符串如 "hello" 转换为二进制表示:01101000 01100101 01101100 01101100 01101111
  2. 将二进制数据划分为6位一组

            (2)011010 000110 010101 101100 011011 000110 1111
  3. 将每个6位二进制数映射为Base64字符集中的字符

            (1)011010 -> a                                                                                                                        (2)000110 -> G                                                                                                                        (3)1111 -> P (因为它不足6位,所以填充两个零)                                                              (4)000110 -> G                                                                                                                        (5)011011 -> b                                                                                                                        (6)101100 -> s                                                                                                                        (7)010101 -> V
  4. 在必要时添加填充字符

           (1)因为最后一组不足6位,所以我们在末尾添加两个=,使整个字符串符合规范。

最终,"hello" 的 Base64 编码结果为 aGVsbG8=

3.4 图片格式转化 

book@100ask:~/httpHandler$ base64 face.png

修改代码进行图片解析: 

system("base64 face.png > tmpFile");

 编译代码查看是否解析成功:

3.5 读取图片文件 

        代码的目标是将图像文件 face.png 进行 Base64 编码,并将编码后的内容读取到内存中,然后打印出来。

system("base64 face.png > tmpFile");int fd = open("./tmpFile",O_RDWR);int filelen = lseek(fd, 0, SEEK_END);lseek(fd, 0, SEEK_SET);char *buf = (char *)malloc(filelen+2);memset(buf, 0, filelen+2);read(fd, buf, filelen);printf("file = %s/n",buf);close(fd);free(buf);

1. 执行系统命令进行 Base64 编码

 system("base64 face.png > tmpFile");

        功能:通过调用系统命令 `base64` 对文件 `face.png` 进行 Base64 编码,并将编码结果输出到临时文件 `tmpFile` 中。

        解释:`system` 函数用于执行系统命令,这里它执行的是 `base64 face.png > tmpFile`,其中 `base64 face.png` 将 `face.png` 文件进行 Base64 编码,`>` 将编码后的结果重定向到 `tmpFile`。

2. 打开临时文件

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

        功能:以读写模式打开临时文件 `tmpFile`。

        解释:`open` 函数用于打开文件,并返回文件描述符 `fd`。`O_RDWR` 表示以读写模式打开文件。如果文件打开失败,`fd` 会返回 -1。

3. 获取文件长度

int filelen = lseek(fd, 0, SEEK_END);

        功能:获取文件 `tmpFile` 的长度。

        解释:`lseek` 函数用于移动文件指针,这里它将文件指针移动到文件末尾 (`SEEK_END`),并返回文件长度 `filelen`。

4. 重置文件指针

 lseek(fd, 0, SEEK_SET);

        功能:将文件指针重置到文件开头。

        解释:`lseek` 函数将文件指针移动到文件开头 (`SEEK_SET`),这样后续操作可以从文件头开始读取数据。

5. 分配内存

char *buf = (char *)malloc(filelen + 2);

        功能:为读取文件内容分配足够的内存。

        解释:`malloc` 函数分配 `filelen + 2` 字节的内存空间并返回指向该空间的指针 `buf`。这里多分配了2个字节,通常是为了确保有足够的空间存储数据和末尾的空字符(用于字符串终止符)。

6. 初始化内存

memset(buf, 0, filelen + 2);

        功能:初始化分配的内存,将其全部置为0。

        解释:`memset` 函数将 `buf` 指向的内存区域的前 `filelen + 2` 个字节置为0,确保缓冲区干净。

7. 读取文件内容到内存中

 read(fd, buf, filelen);

        功能:从文件中读取内容到内存缓冲区 `buf` 中。

        解释:`read` 函数从文件描述符 `fd` 指定的文件读取 `filelen` 字节的数据到 `buf` 指向的内存中。

8. 打印文件内容

printf("file = %s/n", buf);

        功能:打印读取到的文件内容。

        解释:`printf` 函数将缓冲区 `buf` 中的内容作为字符串输出到标准输出。这将打印出 Base64 编码后的内容。

9. 详细思路:

(1). Base64 编码:使用系统命令将 `face.png` 文件进行 Base64 编码,并将结果保存到临时文件 `tmpFile` 中。
(2). 打开文件:以读写模式打开 `tmpFile`。
(3). 获取文件长度:使用 `lseek` 获取文件的长度。
(4). 重置文件指针:将文件指针重置到文件开头,以便后续读取。
(5). 分配内存:为读取文件内容分配内存,并初始化该内存。
(6). 读取文件内容:将文件内容读取到分配的内存缓冲区中。
(7). 打印文件内容:将读取到的 Base64 编码内容打印出来。

最终修改后的代码:

#include <stdio.h>#include <curl/curl.h>#include <stdbool.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>// 将文件进行 Base64 编码并读取到内存中char* base64_encode_file(const char *filename, size_t *out_len) {    char command[256];    // 构建系统命令,将文件进行 Base64 编码并输出到临时文件    snprintf(command, sizeof(command), "base64 %s > tmpFile", filename);    // 执行系统命令    if (system(command) != 0) {        perror("system");        return NULL;    }    // 打开临时文件进行读取    int fd = open("./tmpFile", O_RDWR);    if (fd == -1) {        perror("open");        return NULL;    }    // 获取文件长度    int filelen = lseek(fd, 0, SEEK_END);    if (filelen == -1) {        perror("lseek");        close(fd);        return NULL;    }    // 将文件指针移到文件开始    if (lseek(fd, 0, SEEK_SET) == -1) {        perror("lseek");        close(fd);        return NULL;    }    // 为读取内容分配内存    char *buffer = (char *)malloc(filelen + 1);    if (!buffer) {        perror("malloc");        close(fd);        return NULL;    }    // 初始化缓冲区并读取文件内容到缓冲区    memset(buffer, 0, filelen + 1);    if (read(fd, buffer, filelen) != filelen) {        perror("read");        free(buffer);        close(fd);        return NULL;    }    // 关闭文件描述符    close(fd);    // 设置输出长度    if (out_len) {        *out_len = filelen;    }    return buffer;}// 使用 libcurl 发送 POST 请求并将结果存储到文件中bool postUrl(const char *filename) {    CURL *curl;    CURLcode res;    // 打开文件用于写入    FILE *fp = fopen(filename, "wb");    if (!fp) {        perror("fopen");        return false;    }    // 定义请求参数    char *key = "ETJBxUZ45LbKRbacTiAX";  // 改为自己的key    char *secret = "a484b1a76c2f4e248850611bb42084";  // 改为自己的secret    int typeId = 21;    char *format = "xml";    // 对两个图像文件进行 Base64 编码    size_t bufpic1_len, bufpic2_len;    char *bufpic1 = base64_encode_file("face1.jpg", &bufpic1_len);    char *bufpic2 = base64_encode_file("face2.jpg", &bufpic2_len);    // 检查编码是否成功    if (!bufpic1 || !bufpic2) {        free(bufpic1);        free(bufpic2);        fclose(fp);        return false;    }    // 计算 POST 数据的长度并分配内存    int len = strlen(key) + strlen(secret) + bufpic1_len + bufpic2_len + 124;    char *postString = (char *)malloc(len);    if (!postString) {        perror("malloc");        free(bufpic1);        free(bufpic2);        fclose(fp);        return false;    }    // 构建 POST 数据字符串    snprintf(postString, len, "&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", bufpic1, bufpic2, key, secret, typeId, format);    free(bufpic1);    free(bufpic2);    // 初始化 CURL    curl = curl_easy_init();    if (curl) {        // 设置 CURL 选项        curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt");        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);        curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/faceliu.do");        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);  // 将响应数据写入文件        curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp);  // 将响应头写入文件        // 执行请求        res = curl_easy_perform(curl);        // 检查请求是否成功        if (res != CURLE_OK) {            fprintf(stderr, "curl_easy_perform() failed: %s/n", curl_easy_strerror(res));        } else {            printf("Request completed successfully./n");        }        // 清理 CURL 资源        curl_easy_cleanup(curl);    } else {        fprintf(stderr, "curl_easy_init() failed/n");    }    free(postString);    fclose(fp);    return (res == CURLE_OK);}int main(void) {    // 发送 POST 请求并将结果存储到 /tmp/post.html 文件    if (postUrl("/tmp/post.html")) {        printf("POST request sent successfully./n");    } else {        printf("Failed to send POST request./n");    }    return 0;}

3.6 实现思路: 

3.6.1. 基本思路:    

  • Base64编码:为了将二进制图像数据转换为文本格式(Base64编码),以便通过HTTP POST请求发送。
  • HTTP POST请求:使用libcurl库发送POST请求,包括Base64编码后的图像数据和其他参数。
  • 文件操作:读取图像文件,存储服务器响应。

3.6.2. Base64编码文件 

  • 功能:将输入的图像文件进行Base64编码,并将编码后的内容存储到字符串中返回。
  • 步骤
    1. 使用snprintf构建系统命令,将文件进行Base64编码并输出到临时文件tmpFile
    2. 调用system函数执行命令,如果命令失败,返回NULL
    3. 打开临时文件,如果打开失败,返回NULL
    4. 使用lseek获取文件长度,并将文件指针移到文件开始。
    5. 分配足够的内存来存储编码后的内容,并读取文件内容到内存中。
    6. 关闭文件描述符,返回编码后的字符串及其长度。

3.6.3  发送POST请求 

  • 功能:使用libcurl发送一个包含Base64编码图像数据的POST请求,并将服务器响应存储到文件中。
  • 步骤
    1. 打开一个文件用于写入服务器响应。
    2. 定义请求参数,包括用户的keysecret,以及要发送的图像文件名。
    3. 调用base64_encode_file函数,对两个图像文件进行Base64编码,并获取编码后的内容及其长度。
    4. 检查编码是否成功,如果失败,释放已分配的内存并关闭文件,返回false
    5. 计算POST数据的长度,并分配内存来存储POST数据字符串。
    6. 使用snprintf构建POST数据字符串,包含Base64编码后的图像数据、keysecrettypeIdformat
    7. 初始化libcurl,并设置CURL选项,包括POST字段、目标URL、以及用于写入响应数据的文件。
    8. 调用curl_easy_perform执行POST请求,并检查请求是否成功。
    9. 清理libcurl资源,释放分配的内存,关闭文件描述符,返回请求结果。

3.6.4 程序入口 

  • 功能:调用postUrl函数发送POST请求,并打印结果。
  • 步骤
    1. 调用postUrl函数,指定结果存储文件名为/tmp/post.html
    2. 根据postUrl的返回值,打印请求是否成功的信息。

四、可能遇到的问题

遇到以上问题解决方法:

注意:我们访问https,上传的是以base64编码的形式,而目前官网目前挂的接口是以文件file的形式,所以我们要使用正确的API接口,这就是导致我们之前访问https失败的原因。

如何找到正确接口API:

  1. 打开官网,选择人脸识别接口API

2.页面拉到最底下,点击查看详细API介绍。

3.下载所有产品的API接口

  1. WPS打开这API包,找到人脸识别的文件,如果是车牌识别,接打开车牌识别的文件。

接口地址,图片为 base64 流:https://netocr.com/api/faceliu.do

也许您对下面的内容还感兴趣: