摘要
- 最近在完成2020年没有完成的一个DIY项目,去年年底整个人有点泄气,导致一直搁置的。现在重新把他做好
- 因为项目DIY项目中想引入图片识别,但是我的DIY作品不方便使用烧录等,所以我想用ART-PI来进行调试测试。
- 这个功能取决于强大的RT-THREAD软件包,只需要勾勾选选,就可以实现了。
图片识别实现
开发环境:
- 平台:rt-thread的ART-PI。- 软件包:webclient-v2.1.2,cjson-v1.0.2,mbedtls-v2.7.10。- 图片识别平台:百度云平台。
百度云平台
- 我的这个图片识别,是依赖百度云平台提供的图片识别API。
- 步骤:
- 需要注册一个百度云账号。
- 然后选择人工智能->图片识别。
- 百度云提供不同类别的API,而我这里选择了通用物体和场景识别
- 点击技术文档
- 技术文档中,百度云提供了通用物体和场景识别API的详细说明:①HTTP方法:post。②请求URL。③URL参数:token和header。④百度云提供了不同语言的开发实例。而我们使用的是基于RTT来开发,所以需要对其进行造轮子(可以根据bash的实例来了解)。
- bash的例子:
`curl -i -k 'https://aip.baidubce.com/rest...【调用鉴权接口获取的token】' --data 'image=【图片Base64编码,需UrlEncode】' -H 'Content-Type:application/x-www-form-urlencoded'
`
- 注意:需要注意的是图片需要进行Base64编码和UrlEncode
rt-thread代码实现:
- 增加软件:webclient-v2.1.2,cjson-v1.0.2,mbedtls-v2.7.10
- 通过HTTP方法:get获取token。
- token的获取方法可以看百度云的教程,如下:
- 需要AK(API Key)和SK(Secret Key)
`curl -i -k 'https://aip.baidubce.com/oaut...【百度云应用的AK】&client_secret=【百度云应用的SK】'
`
- AK和SK获取方式,需要通过创建一个应用,以个人的方式创建。
- rt-thread代码实现获取token
#define GET_TOKEN_URI "http://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=[填入自己的AK]&client_secret=[填入自己SK]"
void rp_get_token(void)
{
char *response = RT_NULL;
char *uri = RT_NULL;
uri = web_strdup(GET_TOKEN_URI);
if (webclient_request(uri, RT_NULL, RT_NULL, (unsigned char **)&response) < 0)
{
rt_kprintf("get image fail!\n");
return -RT_ERROR;
}
image_info->root = cJSON_Parse(response);
if(image_info->root != RT_NULL)
{
image_info->token = cJSON_GetObjectItem(image_info->root, "access_token");
rt_kprintf("token : %s\n", image_info->token->valuestring);
}
}
- Base64编码函数:
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *base64_encode(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 0;
}
- 图片UrlEncode:
static rt_bool_t is_non_symbol(char c)
{
if(c == '\0') return 1;
int c_int = (int)c;
return (c_int >= 48 && c_int <= 57) || (c_int >= 65 && c_int <= 90) || (c_int >= 97 && c_int <= 122);
}
char *url_encode(const char *input)
{
int end = strlen(input);
size_t final_size = (end * 3) + 1;
char *working = rt_malloc(final_size);
char *output = working;
if(working == NULL)
{
rt_kprintf("working malloc failed\n");
}
while(*input)
{
const char c = *input;
if(c < 0)
{
input++;
}
else if(is_non_symbol(c))
{
*working++ = *input++;
}
else
{
char encoded[4] = {0};
snprintf(encoded, 4, "%%%02X", c);
*working++ = encoded[0];
*working++ = encoded[1];
*working++ = encoded[2];
input++;
}
}
*working = 0;
//psram_free(working);
return output;
}
- rt-thread实现图片识别结果请求
void rp_get_image_result(void)
{
rt_uint8_t *image_tag = "image=";
// 获取图片编码后的数据
rt_uint8_t *image_base64 = pic_base64("./flash/fish.jpg");
rt_uint32_t image_tag_len = 0;
rt_uint32_t image_base64_len = 0;
rt_uint8_t *params = RT_NULL;
image_tag_len = strlen(image_tag);
image_base64_len = strlen(image_base64);
params = rt_malloc(image_tag_len + image_base64_len);
// 拼接HTTP请求格式中的(--data) 的数据
rt_memcpy(params, image_tag, image_tag_len);
rt_memcpy(params + image_tag_len, image_base64, image_base64_len);
char *image_uri = RT_NULL;
char *header = RT_NULL;
char *image_result = RT_NULL;
int index = 0;
image_uri = web_strdup(GET_IMAGE_RESULT_URI);
// HTTP header数据
webclient_request_header_add(&header, "Content-Length: %d\r\n", image_tag_len + image_base64_len);
webclient_request_header_add(&header, "Content-Type: application/x-www-form-urlencoded\r\n");
// HTTP POST
if (webclient_request(image_uri, (const char *)header, params, (unsigned char **)&image_result) < 0)
{
rt_kprintf("webclient send post request failed.");
web_free(header);
return -RT_ERROR;
}
// 答应返回结果
for (index = 0; index < rt_strlen(image_result); index++)
{
rt_kprintf("%c", image_result[index]);
}
rt_kprintf("\n");
}
- 测试图片:
- 测试结果:
关注微信公众号『Rice嵌入式开发技术分享』,后台回复“微信”添加作者微信,备注”入群“,便可邀请进入技术交流群。
推荐阅读
更多嵌入式技术干货请关注Rice 嵌入式开发技术分享