使用C++创建动态链接库:
dllmain.cpp
#include <windows.h>
#include <string>
#include <vector>
#include "opencv2/opencv.hpp"BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {return TRUE;
}extern "C" __declspec(dllexport) void ProcessImage(unsigned char* data, int width, int height, int channels, int* result);extern "C" __declspec(dllexport) void image_ocr_cropped_c(cv::Mat image,const char* rectangle_message,const char* model_path,const char* model_name,int buffer_size,char* out_buffer,const char* device_name = "cuda",int device_index = 0,float detect_thres = -1.0);extern "C" __declspec(dllexport) void image_ocr_cropped_c_adapter(unsigned char* data, int width, int height, int channels,const char* rectangle_message,const char* model_path,const char* model_name,int buffer_size,char* output_buffer,const char* device_name,int device_index,float detect_thres);
process_image.cpp
#define NOMINMAX
#include <windows.h>
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm> extern "C" __declspec(dllexport) void ProcessImage(unsigned char* data, int width, int height, int channels, int* result)
{try {// 构造 Mat 对象cv::Mat img(height, width, CV_8UC(channels), data);// 转灰度图cv::Mat gray;if (channels == 3) {cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);}else {gray = img.clone();}// 保存图像cv::imwrite("output_gray.jpg", gray);// 返回灰度图的尺寸result[0] = gray.cols; // widthresult[1] = gray.rows; // height}catch (...) {result[0] = -1;result[1] = -1;}
}// 导出函数接口(兼容 C 调用)
extern "C" __declspec(dllexport) void image_ocr_cropped_c(cv::Mat image, const char* rectangle_message, const char* model_path, const char* model_name, int buffer_size, char* out_buffer, const char* device_name = "cuda", int device_index = 0, float detect_thres = -1.0)
{std::vector<cv::Rect> rectangles;std::string input(rectangle_message);// 分割不同的矩形组size_t start = 0;while (true) {size_t rectStart = input.find("Rectangle", start);if (rectStart == std::string::npos) break;size_t braceStart = input.find('{', rectStart);size_t braceEnd = input.find('}', braceStart);if (braceStart == std::string::npos || braceEnd == std::string::npos) break;std::string rectStr = input.substr(braceStart + 1, braceEnd - braceStart - 1);// 分割矩形组内的各个多边形size_t polyStart = 0;while (true) {size_t colonPos = rectStr.find(':', polyStart);if (colonPos == std::string::npos) break;size_t semicolonPos = rectStr.find(';', colonPos);if (semicolonPos == std::string::npos) {semicolonPos = rectStr.length();}std::string polyStr = rectStr.substr(colonPos + 1, semicolonPos - colonPos - 1);//std::vector<int> coords = extractNumbers(polyStr);std::vector<int> coords;std::stringstream ss;for (char ch : polyStr) {if (isdigit(ch) || ch == '-') {ss << ch;}else if (ss.tellp() > 0) {int num;ss >> num;coords.push_back(num);ss.clear();ss.str("");}}// 处理最后一个数字if (ss.tellp() > 0) {int num;ss >> num;coords.push_back(num);}if (coords.size() >= 8) { // 至少4个点// 计算包围所有点的最小矩形int minX = coords[0], maxX = coords[0];int minY = coords[1], maxY = coords[1];for (size_t i = 2; i < coords.size(); i += 2) {int x = coords[i];int y = coords[i + 1];minX = std::min(minX, x);maxX = std::max(maxX, x);minY = std::min(minY, y);maxY = std::max(maxY, y);}cv::Rect rect(minX, minY, maxX - minX, maxY - minY);rectangles.push_back(rect);}polyStart = semicolonPos + 1;}start = braceEnd + 1;}//std::vector<cv::Rect> rectangles = parseRectangles_c(rectangle_message);// 打印所有矩形for (size_t i = 0; i < rectangles.size(); ++i) {cv::Rect roi = rectangles[i];std::cout << "Rectangle " << i + 1 << ": x=" << roi.x << ", y=" << roi.y<< ", w=" << roi.width << ", h=" << roi.height << std::endl;// 检查 ROI 是否在图像范围内if (roi.x >= 0 && roi.y >= 0 &&roi.x + roi.width <= image.cols &&roi.y + roi.height <= image.rows) {// 裁剪图像cv::Mat cropped = image(roi);// 显示或保存裁剪后的图像std::string windowName = "Cropped Image " + std::to_string(i + 1);cv::imshow(windowName, cropped);// 可选:保存裁剪后的图像到文件cv::imwrite("cropped_" + std::to_string(i + 1) + ".jpg", cropped);}}strncpy_s(out_buffer, buffer_size, rectangle_message, _TRUNCATE);return;
}extern "C" __declspec(dllexport) void image_ocr_cropped_c_adapter(unsigned char* data, int width, int height, int channels,const char* rectangle_message,const char* model_path,const char* model_name,int buffer_size,char* output_buffer,const char* device_name,int device_index,float detect_thres
)
{try {// 构造 Matcv::Mat image(height, width, CV_8UC(channels), data);// 调用原有函数image_ocr_cropped_c(image,rectangle_message,model_path,model_name,buffer_size,output_buffer,device_name,device_index,detect_thres);}catch (...) {strncpy_s(output_buffer, 1024, "Error during processing", buffer_size);}
}
生成对应的动态链接库ImageProcessorDll.dll
文件结构:
./
└── test_cv_dll.py
├── ImageProcessorDll.dll
├── opencv_world454.dll
└── test1.jpeg
Python调用:
import cv2
import ctypes
import os
# 加载 DLL
dll_path = r'.\ImageProcessorDll.dll'
# 确保路径存在
assert os.path.exists(dll_path), f"DLL 文件不存在:{dll_path}"# 加载 DLL
try:dll = ctypes.CDLL(dll_path)
except Exception as e:print("加载 DLL 出错:", e)exit(1)# 读取图像
img = cv2.imread('test1.jpeg') # BGR 格式# 获取图像信息
height, width, channels = img.shape
print(f"Original image size: {width}x{height}")# 图像数据转为指针
data = img.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte))# 输出缓冲区
buffer_size = 4096
output_buffer = ctypes.create_string_buffer(buffer_size)print("===========================ProcessImage()========================================")
# 定义函数参数类型
dll.ProcessImage.argtypes = [ctypes.POINTER(ctypes.c_ubyte),ctypes.c_int,ctypes.c_int,ctypes.c_int,ctypes.POINTER(ctypes.c_int)
]# 结果数组用于接收尺寸
result = (ctypes.c_int * 2)()
# 调用 DLL 函数
dll.ProcessImage(data, width, height, channels, result)
# 输出处理后的尺寸
print(f"Gray image size: {result[0]}x{result[1]}")print("=====================image_ocr_cropped_c_adapter()================================")
# 定义新接口的 argtypes
dll.image_ocr_cropped_c_adapter.argtypes = [ctypes.POINTER(ctypes.c_ubyte), # imageDatactypes.c_int, # widthctypes.c_int, # heightctypes.c_int, # channelsctypes.c_char_p, # rectangle_messagectypes.c_char_p, # model_pathctypes.c_char_p, # model_namectypes.c_size_t, # buffer_sizectypes.c_char_p, # output_bufferctypes.c_char_p, # device_namectypes.c_int, # device_indexctypes.c_float, # detect_thres
]
dll.image_ocr_cropped_c_adapter.restype = None# 调用适配器函数
dll.image_ocr_cropped_c_adapter(data,width,height,channels,b"Rectangle1:{50:(604,1420),(893,1420),(604,1511),(893,1511);50:(1758,1408),(2026,1408),(1758,1495),(2026,1495);20.0:(2319,865),(2589,865),(2319,956),(2589,956);20.0:(2313,1408),(2596,1408),(2313,1497),(2596,1497);}",b"K", # model_pathb"test", # model_namebuffer_size, # buffer_sizeoutput_buffer, # output_bufferb"cuda", # device_name0, # device_index-1.0 # detect_thres
)result = output_buffer.value.decode('utf-8')
print("OCR Result:\n", result)
Original image size: 3264x2448
=============ProcessImage()===============
Gray image size: 3264x2448
=====================image_ocr_cropped_c_adapter()================================
Rectangle 1: x=604, y=1420, w=289, h=91
Rectangle 2: x=1758, y=1408, w=268, h=87
Rectangle 3: x=2319, y=865, w=270, h=91
Rectangle 4: x=2313, y=1408, w=283, h=89
OCR Result:Rectangle1:{50:(604,1420),(893,1420),(604,1511),(893,1511);50:(1758,1408),(2026,1408),(1758,1495),(2026,1495);20.0:(2319,865),(2589,865),(2319,956),(2589,956);20.0:(2313,1408),(2596,1408),(2313,1497),(2596,1497);}