调用百度OCR时遇到的问题(封装到类中的静态成员问题)

当我们调用百度的图像识别OCR时,把百度的官方代码粘过来一般是能直接用的

点击查看代码
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <curl/curl.h>
#include <json/json.h>
#include <fstream>
#include <memory>

std::string client_id = "nMEcgArz8ZzUh*********";
std::string client_secret = "a1fIdfXrGFlV********";

inline size_t onWriteData(void * buffer, size_t size, size_t nmemb, void * userp)
{
    std::string * str = dynamic_cast<std::string *>((std::string *)userp);
    str->append((char *)buffer, size * nmemb);
    return nmemb;
}


std::string getAccessToken()
{
    std::string result;
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    if(curl)
    {
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
        curl_easy_setopt(curl, CURLOPT_URL, "https://aip.baidubce.com/oauth/2.0/token");
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
        headers = curl_slist_append(headers, "Accept: application/json");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        // image 可以通过 getFileBase64Content('C:\fakepath\TestImage.png') 方法获取
		// 注意getFileBase64Content方法第二个参数,如果Content-Type是application/x-www-form-urlencoded时,urlencoded请设置为True
        std::string data = "grant_type=client_credentials&client_id="+client_id+"&client_secret="+client_secret;
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onWriteData);
        res = curl_easy_perform(curl);
    }
    curl_easy_cleanup(curl);
    Json::Value obj;
    std::string error;
    Json::CharReaderBuilder crbuilder;
    std::unique_ptr<Json::CharReader> reader(crbuilder.newCharReader());
    reader->parse(result.data(), result.data() + result.size(), &obj, &error);
    return obj["access_token"].asString();
}

int main(int argc, char *argv[])
{
    std::string result;
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
        curl_easy_setopt(curl, CURLOPT_URL, ("https://aip.baidubce.com/rest/2.0/ocr/v1/webimage?access_token=" + getAccessToken()).c_str());
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "undefined");
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
        headers = curl_slist_append(headers, "Accept: application/json");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        const char *data = "url=https%3A%2F%2Fbaidu-ai.bj.bcebos.com%2Focr%2Fwebimage.jpeg&detect_language=false&detect_direction=false";
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onWriteData);
        res = curl_easy_perform(curl);
        std::cout<<result;
    }
    curl_easy_cleanup(curl);
    return (int)res;
}

这是百度官方的代码,当然百度官方是直接把你上传的图片转化为2进制数据贴过来了,我因为太长了就没有传入,当然你可以根据百度官方的提示在main中这样写
点击查看代码
std::string ImaginePath="**********";//你的图片路径
std::string imageBase64 = getFileBase64Content(ImaginePath, true);
		std::string data = "image=" + imageBase64 + "&detect_language=false&detect_direction=false";
		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
然后一定要记住libcurl库是用C写的,你需要要用c_str(),而不能用str(),会出问题!!! 然后在单独的Test.cpp中可以运行,但是当我把代码粘到我的项目中,封装到一个类中时就出问题了,这是代码:
点击查看代码
#pragma once
//用于图片识别
#include <QWidget>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <curl/curl.h>
#include <json/json.h>
#include <fstream>
#include <memory>
#include<QFileDialog>
#include "ui_OCRImagine.h"

extern std::string client_id;
extern std::string client_secret;

class OCRImagine : public QWidget
{
	Q_OBJECT

public:
	OCRImagine(QWidget *parent = Q_NULLPTR);
	~OCRImagine();
	std::string get_ImagineData(const char*ImaginePath);//获取图片数据
private slots:
	void on_PushImagineBtn_clicked();//导入图片
private:
	//将服务器返回的 HTTP 响应数据(如 JSON 字符串)保存到 C++ 字符串中
	inline size_t onWriteData(void * buffer, size_t size, size_t nmemb, void * userp);
	//将传入的图片转化为Base64编码(API要求Base编码传入)
	std::string getFileBase64Content(const char * path, bool urlencoded = false);
	//获取调用 OCR API 所需的临时访问令牌(access_token),每30天重新授权一次
	std::string getAccessToken(); 
private:
	Ui::OCRImagine ui;
};
#include "OCRImagine.h"
std::string client_id = "nMEcgArz8********";
std::string client_secret = "a1fIdfXrGF*************";
OCRImagine::OCRImagine(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);
}
OCRImagine::~OCRImagine()
{
}
std::string OCRImagine::get_ImagineData(const char * ImaginePath)
{
	SetConsoleOutputCP(CP_UTF8);
	SetConsoleCP(CP_UTF8);
	std::string result;
	CURL *curl;
	CURLcode res;
	curl = curl_easy_init();
	if (curl) {
		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
		curl_easy_setopt(curl, CURLOPT_URL, ("https://aip.baidubce.com/rest/2.0/ocr/v1/webimage?access_token=" + getAccessToken()).c_str());
		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
		curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "undefined");
		struct curl_slist *headers = NULL;
		headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
		headers = curl_slist_append(headers, "Accept: application/json");
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
		// image 可以通过 getFileBase64Content('C:\fakepath\TestImage.png') 方法获取
		// 注意getFileBase64Content方法第二个参数,如果Content-Type是application/x-www-form-urlencoded时,urlencoded请设置为True
		std::string imageBase64 = getFileBase64Content(ImaginePath, true);
		std::string data = "image=" + imageBase64 + "&detect_language=false&detect_direction=false";
		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result);
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &OCRImagine::onWriteData);
		res = curl_easy_perform(curl);
		if (res != CURLE_OK) {
			std::cerr << "curl请求失败:" << curl_easy_strerror(res) << std::endl;
		}
	}
	//std::cout << result;
	ui.ImagineShow->setText(QString::fromUtf8(result.c_str(),result.length()));
	curl_easy_cleanup(curl);
	return result;
}
//将服务器返回的 HTTP 响应数据(如 JSON 字符串)保存到 C++ 字符串中
size_t OCRImagine::onWriteData(void * buffer, size_t size, size_t nmemb, void * userp)
{
	if (buffer == nullptr || userp == nullptr) return 0;
	std::string *response = static_cast<std::string*>(userp);
	size_t total = size * nmemb;
	response->append(static_cast<char*>(buffer), total);
	return total;
}
//将传入的图片转化为Base64编码(API要求Base编码传入)
std::string OCRImagine::getFileBase64Content(const char * path, bool urlencoded)
{
	const std::string base64_chars =
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		"abcdefghijklmnopqrstuvwxyz"
		"0123456789+/";
	std::string ret;
	int i = 0;
	int j = 0;
	unsigned char char_array_3[3];
	unsigned char char_array_4[4];
	const unsigned int bufferSize = 1024;
	unsigned char buffer[bufferSize];
	std::ifstream file_read;
	file_read.open(path, std::ios::binary);
	while (!file_read.eof())
	{
		file_read.read((char *)buffer, bufferSize * sizeof(char));
		int num = file_read.gcount();
		int m = 0;
		while (num--)
		{
			char_array_3[i++] = buffer[m++];
			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 += base64_chars[char_array_4[i]];
				}
				i = 0;
			}
		}
	}
	file_read.close();
	if (i)
	{
		for (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 (j = 0; (j < i + 1); j++)
		{
			ret += base64_chars[char_array_4[j]];
		}
		while ((i++ < 3))
		{
			ret += '=';
		}
	}
	if (urlencoded)
	{
		ret = curl_escape(ret.c_str(), ret.length());
	}
	return ret;
}
//获取调用 OCR API 所需的临时访问令牌(access_token),每30天重新授权一次
std::string OCRImagine::getAccessToken()
{
	std::string result;
	CURL *curl;
	CURLcode res;
	curl = curl_easy_init();
	if (curl == nullptr)
	{
		std::cerr << "致命错误:curl句柄创建失败!" << std::endl;
		return "";
	}
	else
	{
		try
		{

			curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
			curl_easy_setopt(curl, CURLOPT_URL, "https://aip.baidubce.com/oauth/2.0/token");
			curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
			curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
			//curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
			struct curl_slist *headers = NULL;
			headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
			headers = curl_slist_append(headers, "Accept: application/json");
			curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
			std::string data = "grant_type=client_credentials&client_id=" + client_id + "&client_secret=" + client_secret;
			curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
			curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result);
			curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &OCRImagine::onWriteData);
			res = curl_easy_perform(curl);//出现问题
			//*
			if (res != CURLE_OK) {
				std::cerr << "获取Token失败: " << curl_easy_strerror(res) << std::endl;
				curl_slist_free_all(headers);
				curl_easy_cleanup(curl);
				return "";
			}
			curl_slist_free_all(headers); // 释放headers
			
		}
		catch (const std::exception& e) {
			std::cerr << "getAccessToken异常: " << e.what() << std::endl;
			curl_easy_cleanup(curl);
			return "";
		}
	}
	Json::Value obj;
	std::string error;
	Json::CharReaderBuilder crbuilder;
	std::unique_ptr<Json::CharReader> reader(crbuilder.newCharReader());
	bool parseOk=reader->parse(result.data(), result.data() + result.size(), &obj, &error);
	if (!parseOk || obj["access_token"].isNull()) {
		std::cerr << "Token解析失败: " << error << " | 响应数据: " << result << std::endl;
		curl_easy_cleanup(curl);
		return "";
	}
	curl_easy_cleanup(curl);
	return obj["access_token"].asString();
}
//导入图片按钮
void OCRImagine::on_PushImagineBtn_clicked()
{
	QString File_path = QFileDialog::getOpenFileName(this,
		QString::fromLocal8Bit("请选择文件"),
		"/", QString::fromLocal8Bit("选择的文件(*.jpg;*.png);"));
	ui.ImaginePath->setText(File_path);
	QByteArray byteArr = File_path.toUtf8();
	this->get_ImagineData(byteArr.constData());
}
每次运行到getAccessToken函数的res = curl_easy_perform(curl);就出问题,而且每次都不会进入onWriteData函数!!!!!!!! 这是因为我在onWriteData中把代码粘过来了,前面有个inline修饰,而CURLOPT_WRITEFUNCTION要求全局变量或静态变量,显然这个内联函数不行,我们需要把onWriteData函数变为全局或静态函数,所以我在声明的时候前面加了static,然后代码就跑的通了,一直断点调试找问题花了我半天多时间……也是我基础没打好吧
posted @ 2026-01-14 09:22  dd_l  阅读(18)  评论(0)    收藏  举报