qq_41747698
2021-10-08 15:15
采纳率: 76.5%
浏览 217

opencv dnn::Net变量初始化引起的内存越界

opencv 初始化样例代码为 dnn::Net = readNetFromonnx(string path),然后自动释放,但是这在dll中无法正常使用

dllexport class ocr(){
    private:
    Net net
    public:
    void set_net(string_path)
    {net = readnetFromonnx(path)}
}

这会导致所有内存分配错误,所有参数为乱码,所以我用简单方法添加了如下两段,创建该类对象时在构造函数中读取一次默认模型分配内存,然后set_net指定路径读取用户自己的模型,即可解决分配内存错误问题

ocr()
{net = net = readnetFromonnx(path)}
~ocr()
{nothing}

但是这样又出现了如下错误
stack overrun:
Unhandled exception at 0x00E61A6C in testdll_1.exe: Stack cookie instrumentation code detected a stack-based buffer overrun.
可以运行但无法正常退出,初步推断为参数导致的内存越界,应该如何正确初始化该变量?

即便使用一个函数其中只有imshow图像也会存在该内存越界的问题

更多代码如下

头文件如下


class _declspec(dllexport) ocr
{
private:
    //string _model_path;
    int crop_mode = 1;
    int thresh_mode = 1;
    int bitwise_mode2 = 0;
    double blur1 = 3;
    double blur2 = 11;

    int morph_xx, morph_yy;
    int morph_x = 1;
    int morph_y = 1;
    int morph_x2 = 1;
    int morph_y2 = 3;
    int iteration = 1;
    int canny_max = 300;
    int min_width = 8;
    int flag;
    int flag2;
    string model_path = "./1.onnx";
    dnn::Net net;
    int rect_thresh = 0;
    int blocksize = 31;
    int C = 10;
    int a = 3;
    int c = 5;
    int b = -5;
    int d = 10;
    int softmax_thresh = 0.9;
    int _h = 1;
    int _w = 1;
    int _channel = 1;
    int RGB_mode = 0;//0为默认bgr
                     //unsigned char * img
    int showimg = 1;

public:
    ocr(void);
    ~ocr(void);
    
    void set_print();//打印参数
    void set_showimg(int x);

    void set_morph2(int x, int y);//canny用,默认即可,竖向断裂

    void set_morph(int x, int y);//同正负,正数变粗,负数变细
    void set_morph_easy(int x);//x=1: 3,1--字体横向变细,x=2:-3,-1--字体横向变粗, x=3:1,1--不变

    void set_blocksize(int _C, int _blocksize = 31);//阈值 blocksize自适应区块大小,影响背景过滤程度,默认31,C需人工调整,白色字体C为负数,深色字体正数,影响噪点
    void set_blocksize_easy(int x);//0:字体无粘连,背景无线条,自动阈值, x=1:31,15--字体深色   2:31,-15--字体浅色,背景深色   3:31,4--字体深色但较细

    void set_net(const string model_path);//const string 路径
                                          //void set_net_easy();





#include "stdafx.h"
#include "recognize.h"
#include <stdlib.h>  
#include <iostream>
#include <fstream>

#include "opencv2\opencv.hpp"
#include "opencv2\dnn.hpp"
#include "opencv2\dnn\all_layers.hpp"

#include <string>
#include <vector>
#include <algorithm>
#include <array>
#include<cmath>
#include<windows.h>
//#include "omp.h"  

using namespace std;
using namespace cv;
using namespace dnn;

ocr::ocr(void)
{
    //this->net0 = new Net();
    net = cv::dnn::readNetFromONNX(model_path);
}

ocr::~ocr(void)
{
    ;
    //delete this->net0;
    //~Net();
}

void ocr::set_print()
{
    //cout<<blur <<" blur2" << "\n";
    cout << "blur is" << this->blur2;
    cout << "morph_x, morph_y :" << this->morph_x << this->morph_y << "\n";
    cout << "morph_x2 , morph_y2 :" << this->morph_x2 << this->morph_y2 << "\n";
    //cout << "model_path :" << this->model_path << "\n";
    cout << "rect_thresh :" << this->rect_thresh << "\n";
    cout << "blocksize, C :" << this->blocksize << C << "\n";
    cout << "softmax_thresh :" << this->softmax_thresh << "\n";
}
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

3条回答 默认 最新

  • qfl_sdu 2021-10-08 15:55
    已采纳

    无法正常退出一般是由于子线程死循环或者子线程无法及时退出导致的。
    应该是调用opencv接口后,后台启动了子线程,程序退出时子线程无法及时结束。
    可以通过获取进程ID后,调用系统函数杀死进程,但是不建议这么做,这种做饭太粗暴了,建议还是看看是否是接口使用有问题。

    打赏 评论
  • 爱晚乏客游 2021-10-12 11:21

    你的动态链接类写的有问题。建议使用接口来完成。给你个简单的参考
    首先生成dll,需要新建4个文件,分别是interface.h和cpp这两个用来当做接口,然后是makedll.h和cpp,这两个是相当于具体类的实现方法。

    //interface.h
    #pragma once
    #ifdef    INTERFACE_EXPORT
    #define INTERFACE__API _declspec(dllexport)
    #else
    #define INTERFACE__API _declspec(dllimport)
    #endif
    #ifndef _IOSTREAM_H_
    #include<iostream>
    #endif 
    
    class Interface {
    public:
        Interface() {};
        virtual ~Interface() {};
        virtual void showimg(std::string img_path) = 0; //纯虚函数,子类必须要实现纯虚函数
        virtual void readNet() = 0;
    
    };
    extern "C" INTERFACE__API Interface* Export(void);
    
    //interface.cpp
    #include "stdafx.h"  
    #include "Interface.h"  
    #include "makedll.h"
    Interface* Export(void) {
        return (Interface*)new ocr();
    }
    
    //makedll.h
    #pragma once
    #include "interface.h"
    #include<opencv2/opencv.hpp>
    #include<iostream>
    #include<windows.h>
    class ocr : public Interface {
    public:
        ocr() {};
        virtual ~ocr() {};
        virtual void readNet();
        virtual void showimg(std::string img_path);
    private :
        std::string model_path = "1.onnx";
        cv::dnn::Net net;
    };
    
    //makedll.cpp
    #include "stdafx.h"
    #include "makedll.h"
    using namespace std;
    using namespace cv;
    using namespace dnn;
    
    void ocr::readNet() {
        try {
            net = readNetFromONNX(model_path);
            cout << "read net OK!" << endl;
        }
        catch (const std::exception& e) {
            cout << "Failed to read net" << endl;
            return;
        }
        
    
    }
    void ocr::showimg(string img_path) {
        Mat a = imread(img_path);
        cv::imshow("a", a);
        cv::waitKey();
    }
    
    

    上面的代码新建dll程序,生成一下,然后再debug里面找下makedll.dll,和上面的interface.h一起拷贝到测试工程下面,然后写个简单的测试程序

    //test.cpp
    
    #include "stdafx.h"
    #include<iostream>
    #include<windows.h>
    #include "interface.h"
    using namespace std;
    
    using pExport = Interface *(*)(void);
    int main()
    {
        HINSTANCE hdll;
        hdll = LoadLibrary(_T("makedll.dll"));
        if (hdll == NULL)
        {
            cout << "load dll failure!" << endl;
            FreeLibrary(hdll);
            return -1;
        }
        pExport pOcr = (pExport)GetProcAddress(hdll, "Export");//指针指向函数首地址
        if (pOcr == NULL) {
            cout << "get address failure" << endl;
        }
        Interface *test = pOcr();
        test->readNet();
        test->showimg("./1.bmp");
        system("pause");
        return 0;
    }
    

    结果显示并没有你说的问题

    img

    1 打赏 评论
  • 於黾 2021-10-08 15:30

    net = readnetFromonnx(path)
    如果你把dll中的对象丢给外面,就不能随便释放,释放了外面还怎么用呢,除非你dll里只有一些函数而不保存任何状态,只返回值不返回对象
    如果你的对象存在外部引用,那么你需要在析构函数里写释放的代码,这样什么时候外部不存在对你的类的引用了,它就会释放
    或者你把释放函数暴露出来让引用方显式调用

    打赏 评论

相关推荐 更多相似问题