当前位置: 首页 > news >正文

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

本文介绍了如何在 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";

    // 初始化 CURL
    CURL* 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;
}

相关文章:

  • 东方通TongHttpServer报错403
  • RFID技术在机器人中的核心应用场景及技术实现
  • Reactive编程:什么是Reactive编程?Reactive编程思想
  • git中feature跟hotfix是什么意思
  • VSCode:Linux下安装使用
  • 区块链知识点知识点3
  • MybatisPlus(SpringBoot版)学习第六讲:插件(分页插件乐观锁)
  • dom0-kernel: /thermal-zones/soc_max/cooling-maps/map0: could not find phandle 2
  • (C语言)指针运算 习题练习1.2(压轴难题)
  • kali利用msf渗透Windows电脑测试
  • 《Keras 3 :AI 使用图神经网络和 LSTM 进行交通流量预测》
  • 数据结构之多项式相加的链表实现
  • PHP文件的导出和导入
  • 蓝桥杯省模拟赛 01串个数
  • 攻破tensorflow,勇创最佳agent(1)---学习率learning_rate问题
  • 【云服务器】在 Linux(Ubuntu / CentOS 7)上快速搭建我的世界 Minecraft 服务器,并实现远程联机,详细教程
  • 笔记:代码随想录算法训练营day62:108.冗余连接、109.冗余连接II
  • MybatisPlus(SpringBoot版)学习第四讲:常用注解
  • PHP MySQL 预处理语句
  • leetcode240.搜索二维矩阵||
  • 22国外长联合声明:要求以方立即允许全面恢复对加沙援助
  • 益阳通报“河水颜色异常有死鱼”:未发现排污,原因待鉴定
  • 倒票“黄牛”屡禁不绝怎么破?业内:强化文旅市场票务公开制度
  • 网警打谣:传播涉刘国梁不实信息,2人被处罚
  • 外企聊营商|上海仲裁:化解跨国企业纠纷的“上海路径”
  • 上海博物馆展览进校园,“小先生”传递文物知识