You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从C主函数调用OpenCV C++ API并解决编译报错问题

问题与解决方案

问题背景

现有C语言编写的应用程序,需调用OpenCV的C++ API实现图像处理,但OpenCV的C API已被弃用。尝试从C代码调用C++ API时,因头文件包含C专属的opencv2/opencv.hpp,导致C编译器编译主程序失败,且无法用g编译主程序(依赖大量仅支持C编译的库)。

核心问题

  1. C编译器无法解析C头文件及cv::Mat这类C专属类型
  2. 原头文件暴露的ConnectedComponentsResults结构体包含C++类型,C代码无法处理

解决方案:API隔离与不透明指针

通过**不透明指针(Opaque Pointer)**隐藏C实现细节,让C代码仅与基础C类型交互,C类型完全封装在C++编译单元中。

步骤1:创建纯C兼容的头文件

新建opencv_grouping_c.h,仅保留C语言可识别的内容,用不透明指针替代C++类型:

#ifndef CONNECTED_COMPONENTS_C_H
#define CONNECTED_COMPONENTS_C_H

#include <stdint.h>

// 不透明指针,C代码无需知道内部结构
typedef struct ConnectedComponentsResultsImpl* ConnectedComponentsResults;

// C兼容的函数声明,确保C链接
#ifdef __cplusplus
extern "C" {
#endif

// 创建结果对象(C++侧分配内存)
ConnectedComponentsResults connected_components_create();

// 执行阈值分割与连通组件分析
void threshold_then_group(uint8_t **image_array, uint32_t SizeY, uint32_t SizeX, double threshold_value, ConnectedComponentsResults results);

// 获取连通组件数量
int connected_components_get_num(ConnectedComponentsResults results);

// 销毁结果对象(释放C++侧内存)
void connected_components_destroy(ConnectedComponentsResults results);

#ifdef __cplusplus
}
#endif

#endif

步骤2:修改C++实现文件

更新opencv_grouping.cpp,实现纯C头文件中声明的函数,封装C++逻辑:

#include <stdint.h>
#include <cstring>
#include <opencv2/opencv.hpp>
#include "opencv_grouping_c.h"

// 仅C++可见的实际结果结构体
struct ConnectedComponentsResultsImpl {
    int num_components;
    cv::Mat labels;
    cv::Mat stats;
    cv::Mat centroids;
};

extern "C" {

ConnectedComponentsResults connected_components_create() {
    return new ConnectedComponentsResultsImpl();
}

void threshold_then_group(uint8_t **image_array, uint32_t SizeY, uint32_t SizeX, double threshold_value, ConnectedComponentsResults results) {
    uint8_t *image_data = new uint8_t[SizeY * SizeX];
    double max_value = 255.0;

    // 转换2D数组为连续内存
    for (int i = 0; i < SizeY; i++) {
        memcpy(image_data + i * SizeX, image_array[i], SizeX * sizeof(uint8_t));
    }

    cv::Mat image(SizeY, SizeX, CV_8U, image_data);
    cv::Mat binary;
    cv::threshold(image, binary, threshold_value, max_value, cv::THRESH_BINARY);

    int num_components = cv::connectedComponentsWithStats(
        binary, results->labels, results->stats, results->centroids, 8, CV_32S
    );
    results->num_components = results->stats.rows - 1;

    delete[] image_data;
}

int connected_components_get_num(ConnectedComponentsResults results) {
    return results->num_components;
}

void connected_components_destroy(ConnectedComponentsResults results) {
    delete results;
}

} // extern "C"

步骤3:修改C主函数

更新grouping_test_main.c,使用新的C兼容接口:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <malloc.h>

#include "opencv_grouping_c.h"

uint8_t **allocate_2D_array(uint32_t SizeY, uint32_t SizeX);
int free_2D_array(uint8_t **image_array, uint32_t SizeY);

int main() {
    uint32_t SizeY = 19;
    uint32_t SizeX = 20;
    uint8_t **image_array;
    FILE* fimage_array = fopen("./test_array.txt", "r");
    double threshold_value = 128;
    ConnectedComponentsResults results;
    int i, j;

    image_array = allocate_2D_array(SizeY, SizeX);

    // 读取图像数据
    for (i = 0; i < SizeY; i++) {
        for (j = 0; j < SizeX; j++) {
            fscanf(fimage_array, "%hhd", &image_array[i][j]);
        }
    }
    fclose(fimage_array);

    // 创建结果对象
    results = connected_components_create();
    // 调用处理函数
    threshold_then_group(image_array, SizeY, SizeX, threshold_value, results);
    // 获取并打印结果
    printf("Number of connected components: %d\n", connected_components_get_num(results));
    // 销毁结果对象
    connected_components_destroy(results);

    free_2D_array(image_array, SizeY);
    return 0;
}

uint8_t **allocate_2D_array(uint32_t SizeY, uint32_t SizeX) {
    int i;
    uint8_t **image_array = malloc(SizeY * sizeof(uint8_t *));
    for (i = 0; i < SizeY; i++) {
        image_array[i] = malloc(SizeX * sizeof(uint8_t));
        if (image_array[i] == NULL) {
            fprintf(stderr, "Memory allocation for image_array[%d] failed.\n", i);
            exit(EXIT_FAILURE);
        }
    }
    return image_array;
}

int free_2D_array(uint8_t **image_array, uint32_t SizeY) {
    int i;
    for (i = 0; i < SizeY; i++) {
        free(image_array[i]);
    }
    free(image_array); // 补充释放顶层指针
    return 1;
}

编译命令

# 编译C++实现
g++ `pkg-config --cflags opencv4` -c opencv_grouping.cpp -o opencv_grouping.o
# 编译C主程序(无需OpenCV头文件)
gcc -c grouping_test_main.c -o grouping_test_main.o
# 链接所有目标文件
gcc grouping_test_main.o opencv_grouping.o -lstdc++ `pkg-config --libs opencv4` -o opencv_test2

关键说明

  • 不透明指针ConnectedComponentsResults隐藏了C结构体细节,C代码无需处理cv::Mat等C类型
  • C侧统一负责内存分配与释放,避免C代码操作C对象内存
  • C主程序编译时不再依赖OpenCV的C++头文件,完全由C编译器处理

内容的提问来源于stack exchange,提问作者Brassard1984

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 03:16:14