在 i.MX8MP 上用 C++ 调用豆包 AI 大模型实现图像问答

server/2025/4/1 7:40:25/
本文介绍了如何在 i.MX8MP 嵌入式平台上使用 C++ 调用豆包 AI 大模型(Doubao-vision-pro-32k)进行图像问答。我们将详细讲解代码实现的各个步骤,包括文件读取、Base64 编码、构造 JSON 请求体、使用 libcurl 进行 HTTP POST 请求以及解析响应数据等。

一、背景介绍

在嵌入式平台上进行视觉理解任务时,通常需要将图像数据上传至云端的 AI 服务进行处理。豆包 AI 大模型提供了一套 API 接口,可以通过 HTTP 请求将图像信息发送给模型,返回图像内容的问答结果。本文通过 C++ 代码实现这一流程,并在 i.MX8MP 平台上进行了部署和测试。

二、代码实现说明

1. 文件读取与 Base64 编码

为了让 API 能够接收图像数据,我们首先需要将本地图片以二进制方式读取,然后将其转换为 Base64 编码的字符串。代码中实现了两个函数:
  • read_file:用于以二进制模式读取指定路径的文件,并返回存储在 std::vector 中的文件数据。
  • base64_encode:将读取到的二进制数据转换为 Base64 编码字符串。
这部分代码确保了图片数据能够以字符串形式嵌入 JSON 请求体中。

2. 构造 JSON 请求体

使用 nlohmann::json 库,我们构造了一个与豆包 AI 模型 API 要求一致的 JSON 请求体。请求体中包含以下内容:
  • 指定模型名称(例如 "doubao-vision-pro-32k-241028")。
  • 消息数组,其中包含用户输入的文本问题以及图片数据(通过 Base64 编码后的数据,嵌入在 image_url 字段中)。
这种结构与 Python 版示例保持一致,确保接口能正确解析传递的参数。

3. 使用 libcurl 发送 HTTP POST 请求

接下来,我们利用 libcurl 发送 POST 请求:
  • 设置 API URL(此处为 https://ark.cn-beijing.volces.com/api/v3/chat/completions,请根据实际情况调整)。
  • 配置 HTTP 头信息,包括 Content-Type: application/json 以及通过环境变量 ARK_API_KEY 获取的 API 密钥(通过 Authorization: Bearer ... 进行认证)。
  • 设置回调函数 WriteCallback 用于接收 HTTP 响应数据,并将其存入一个 std::string 中。

4. 解析响应并输出结果

请求成功后,返回的响应数据是 JSON 格式。代码使用 nlohmann::json 对响应进行解析,并提取出模型回复的内容(位于 JSON 的 "choices" 数组中)。若响应格式正确,则输出回复文本;否则打印异常信息。

三、代码运行步骤

  • 环境配置
    • 确保在 i.MX8MP 系统上安装了 libcurl、nlohmann/json 等依赖库。
    • 设置环境变量 ARK_API_KEY,保存你的 API 密钥:export ARK_API_KEY="your_api_key_here"
  • 编译代码
    • 使用 g++ 编译代码:${CXX} -std=c++11 main.cpp -o ark_example -lcurl
  • 运行程序
    • 运行生成的可执行文件,并传入图片路径(若不传,则默认使用代码中指定的路径):./ark_example test.png

四、源代码

qingzong@q-PowerEdge-R740xd:~/tmp$ cat main.cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cstdlib>
#include <string>
#include <curl/curl.h>
#include <nlohmann/json.hpp>// 使用 nlohmann::json 处理 JSON
using json = nlohmann::json;// Base64 编码函数:将二进制数据转换为 Base64 字符串
std::string base64_encode(const unsigned char* data, size_t len) {static const std::string base64_chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZ""abcdefghijklmnopqrstuvwxyz""0123456789+/";std::string ret;int i = 0;unsigned char char_array_3[3];unsigned char char_array_4[4];while (len--) {char_array_3[i++] = *(data++);if (i == 3) {char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);char_array_4[3] = char_array_3[2] & 0x3f;for (i = 0; i < 4; i++)ret.push_back(base64_chars[char_array_4[i]]);i = 0;}}if (i) {for (int j = i; j < 3; j++)char_array_3[j] = '\0';char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);char_array_4[3] = char_array_3[2] & 0x3f;for (int j = 0; j < i + 1; j++)ret.push_back(base64_chars[char_array_4[j]]);while ((i++ < 3))ret.push_back('=');}return ret;
}// 读取指定路径的文件,以二进制方式读取并返回数据
std::vector<unsigned char> read_file(const std::string &file_path) {std::ifstream file(file_path, std::ios::binary);if (!file) {std::cerr << "无法打开文件: " << file_path << std::endl;return {};}std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(file), {});return buffer;
}// libcurl 回调函数:将 HTTP 响应写入 std::string
size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {std::string* str = static_cast<std::string*>(userp);size_t totalSize = size * nmemb;str->append(static_cast<char*>(contents), totalSize);return totalSize;
}int main(int argc, char* argv[]) {// 从环境变量中获取 API 密钥const char* api_key = std::getenv("ARK_API_KEY");if (!api_key) {std::cerr << "请设置环境变量 ARK_API_KEY" << std::endl;return EXIT_FAILURE;}// 指定需要传给大模型的图片路径(根据实际情况修改)std::string image_path = "/home/qingzong/work/1.png";if (argc > 1) {image_path = argv[1];}std::cout << "Using image path: " << image_path << std::endl;// 读取图片并进行 Base64 编码std::vector<unsigned char> file_data = read_file(image_path);if (file_data.empty()) {std::cerr << "图片读取失败" << std::endl;return EXIT_FAILURE;}std::string base64_image = base64_encode(file_data.data(), file_data.size());// 构造 JSON 请求体,与 Python 代码中结构一致json payload;payload["model"] = "doubao-vision-pro-32k-241028";payload["messages"] = json::array({{{"role", "user"},{"content", json::array({{ {"type", "text"}, {"text", "图片里讲了什么?"} },{ {"type", "image_url"}, {"image_url", { {"url", "data:image/jpeg;base64," + base64_image} } } }})}}});std::string payload_str = payload.dump();// 设置 API URL,请根据实际 API 文档进行调整std::string api_url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions";// 初始化 CURLCURL* curl = curl_easy_init();if (!curl) {std::cerr << "无法初始化 CURL" << std::endl;return EXIT_FAILURE;}std::string response_string;// 设置 CURL 选项curl_easy_setopt(curl, CURLOPT_URL, api_url.c_str());curl_easy_setopt(curl, CURLOPT_POST, 1L);curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload_str.c_str());curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_string);curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);// 设置 HTTP 请求头:Content-Type 及 API 认证信息(头名称依据实际情况调整)struct curl_slist* headers = nullptr;headers = curl_slist_append(headers, "Content-Type: application/json");std::string header_api = "Authorization: Bearer " + std::string(api_key);headers = curl_slist_append(headers, header_api.c_str());curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);// 执行 HTTP POST 请求CURLcode res = curl_easy_perform(curl);if (res != CURLE_OK) {std::cerr << "CURL 请求失败: " << curl_easy_strerror(res) << std::endl;} else {try {// 解析返回的 JSON 数据json response_json = json::parse(response_string);if (response_json.contains("choices") && !response_json["choices"].empty() &&response_json["choices"][0].contains("message") &&response_json["choices"][0]["message"].contains("content")){std::string reply = response_json["choices"][0]["message"]["content"];std::cout << "回复: " << reply << std::endl;} else {std::cout << "响应格式异常: " << response_string << std::endl;}} catch (json::parse_error& e) {std::cerr << "JSON 解析错误: " << e.what() << std::endl;}}// 清理 CURL 相关资源curl_slist_free_all(headers);curl_easy_cleanup(curl);return EXIT_SUCCESS;
}

http://www.ppmy.cn/server/180190.html

相关文章

【含文档+源码】基于SpringBoot的过滤协同算法之网上服装商城设计与实现

项目介绍 本课程演示的是一款 基于SpringBoot的过滤协同算法之网上服装商城设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署…

【PCB工艺】软件是如何控制硬件的发展过程

软件与硬件的关系密不可分&#xff0c;软件的需求不断推动硬件的发展&#xff0c;而硬件的进步又为软件创新提供了基础。 时光回溯到1854年&#xff0c;亨利戈培尔发明了电灯泡&#xff08;1879年&#xff0c;托马斯阿尔瓦爱迪生找到了更合适的材料研制出白炽灯。&#xff09;…

Electron 项目开机自启动

app.setLoginItemSettings 与 auto-launch 对比分析 一、稳定性对比 1. app.setLoginItemSettings 优点&#xff1a;作为Electron官方API&#xff0c;有官方维护和支持缺点&#xff1a; 在某些Windows版本上存在已知问题部分Windows 10/11更新后可能失效在macOS权限更严格的…

JS—异步编程:3分钟掌握异步编程

个人博客&#xff1a;haichenyi.com。感谢关注 一. 目录 一–目录二–引言三–JavaScript 事件循环机制四–定时器的秘密&#xff1a;setTimeout 和 setInterval五–异步编程模型对比 二. 引言 在现代Web开发中&#xff0c;异步编程是提升性能的关键技术。无论是脚本加载&am…

Java实现pdf中动态插入图片

今天接到一个需求&#xff0c;需要在pdf中的签名处&#xff0c;插入签名照片&#xff0c;但签名位置不固定&#xff0c;话不多说上代码&#xff1a; 1、首先引入itextpdf依赖包&#xff1a; <dependency><groupId>com.itextpdf</groupId><artifactId>…

云原生CI/CD | Argo CD 详细介绍 (一)

什么是Argo CD? ArgoCD 是以 Kubernetes Controller 的形式来实现的,它会对运行在 Kubernetes 集群上的应用程序进行监听,并将实际运行状态和期望状态(在部署清单文件中指定,且存储在版本控制系统中)进行对比,当两者状态不一致的时候,则提示 OutOfSync,此时可以通过自…

智慧城市智慧调度系统的架构与关键技术研究

摘要 智慧城市建设是当今城市发展的重要趋势&#xff0c;智慧调度系统作为其核心组成部分&#xff0c;对于提升城市运行效率、优化资源配置起着关键作用。本文深入剖析智慧城市智慧调度系统的架构组成&#xff0c;详细阐述其所涉及的关键技术&#xff0c;旨在为智慧城市的高效…

《数字图像处理》第三章 3.8 基于模糊技术的图像强度变换与空间滤波学习笔记

请注意:笔记内容片面粗浅&#xff0c;请读者批判着阅读&#xff01; 在传统图像处理中&#xff0c;灰度变换和空间滤波通常采用确定性数学方法&#xff08;如直方图均衡化、均值滤波等&#xff09;。但当面对图像中的不确定性&#xff08;如光照不均、噪声模糊性、边缘过渡区&…