openCV、C++ 并发、windows

Posted

技术标签:

【中文标题】openCV、C++ 并发、windows【英文标题】:openCV, C++ concurrency, windows 【发布时间】:2014-02-20 13:43:38 【问题描述】:

我们正在构建一个程序,该程序具有来自客户端的服务器流式视频。我们在 Visual Studio 中使用 C++。在调试模式下运行会消除所有奇怪的症状。

OBS:在发行版中运行,但使用 /0d 进行优化仍然会出现症状。

症状:调用 imshow("blabla", image);在服务器中,我们收到以下错误:“newCVS2.exe 中 0x54F26AF8 (opencv_highgui248d.dll) 处的未处理异常:0xC0000005:访问冲突读取位置 0x69577265。” 但是,如果我们执行 imwrite("example.jpg", otherimage);在我们没有收到此错误之前。 image 和 otherimage 是不同的图像。我们已经尝试单步执行代码,我所知道的没有发生奇怪的跳转。

在下面的代码中搜索 imwrite("test.jpg",tmp3) 使用下面的代码我们会得到错误,如果 imwrite 未注释我们不会。我包含了所有代码以供参考。

OBS:imwrite 解决了它不是原因的问题! 提前致谢,感谢任何 cmets。

代码示例:

    // newCVS2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#undef UNICODE

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>

#include <iostream>

#include <process.h>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>


#pragma comment (lib, "Ws2_32.lib")

using namespace std;
using namespace cv;

void streamServer(void* arg);
void  quit(string msg, int retval);
int connect();

//HANDLE hMutex1;
Mat frame;
Mat img;

SOCKET ListenSocket;
SOCKET ClientSocket;
WSADATA wsaData;
int iResult;

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"

int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
int is_data_ready = 0;

HANDLE hMutex1 = CreateMutex( NULL, FALSE, NULL );

HANDLE syncMutex = CreateMutex( NULL, FALSE, NULL );


void streamServer(void* arg)

    int  imgSize = img.total()*img.elemSize();
    char* sockData = new char[imgSize];
    int  bytes=0;

    int errorMsg = connect();
    if(errorMsg != 0)
        cout << "error on: " << errorMsg;
    

    SYSTEMTIME before;
    SYSTEMTIME after;

    // Receive until the peer shuts down the connection
    memset(sockData, 0x0, sizeof(sockData));
    do 
        GetSystemTime(&before);
        for(int i = 0; i < imgSize; i += iResult)
            iResult = recv(ClientSocket, sockData  + i, imgSize - i, 0);
            if(iResult == -1)
                printf("resv failed");
            
        

        GetSystemTime(&after);
        cout << (((after.wSecond * 1000) + after.wMilliseconds) - ((before.wSecond * 1000 ) + before.wMilliseconds)) << " time for recv \n";

        //int ptr = 0;
        GetSystemTime(&before);
        WaitForSingleObject( hMutex1, INFINITE );
                    for(int i = 0; i < img.rows; i++)
                        //row = sockData.part(
                        for(int j = 0; j < img.cols; j++)

                            (img.row(i)).col(j) = (uchar)sockData[((img.cols)*i)+j];
                            //img.at<cv::Vec3b>(i,j) = cv::Vec3b((uchar)sockData[ptr+ 0],(uchar)sockData[ptr+1],(uchar)sockData[ptr+2]);
                            //ptr = ptr + 3;
                        
                    

        cout << img.rows << " number of rows ";
        GetSystemTime(&after);

        is_data_ready = 1;
        memset(sockData, 0x0, sizeof(sockData));

        ReleaseMutex( hMutex1 );

        cout << (((after.wSecond * 1000) + after.wMilliseconds) - ((before.wSecond * 1000 ) + before.wMilliseconds)) << " time for annoying shit \n";

        if (iResult > 0) 
            printf("Bytes received: %d\n", iResult);
        


        //Close the connection
        else if (iResult == 0)
            printf("Connection closing...\n");
        //Fail during connection
        else  
            printf("recv failed with error: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
        

     while (iResult > 0);

    // shutdown the connection since we're done
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) 
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
    

    // cleanup
    closesocket(ClientSocket);
    WSACleanup();


int main()

    int width, height;
    width = 640;  
    height = 480; 
    img = Mat::zeros( height,width, CV_8UC1);



    is_data_ready = 0; // not needed



    _beginthread( streamServer, 0, NULL );
    //needed to make sure that server does not close
    while(true)
        WaitForSingleObject( hMutex1, INFINITE );
                if(is_data_ready)

                    cout << img.rows << " number rows before show \n";
                    cout << img.empty() << " empty? \n";



                    WaitForSingleObject( syncMutex, INFINITE );

                    Mat tmp0, tmp1, tmp2, tmp3, tmp4;

                    tmp4 = img;

                    bilateralFilter(img, tmp0, -1, 50, 5); 
                    Canny(tmp0, tmp1, 35, 200, 3); 

                    /*imwrite("test.jpg",tmp3);
                    waitKey(1);*/

                    ReleaseMutex( syncMutex );

                    WaitForSingleObject( syncMutex, INFINITE );

                    imshow("ServerWindow", tmp1);

                    ReleaseMutex( syncMutex );

                    waitKey(1);
                    cout << img.cols << " number cols after show \n";
                    is_data_ready = 0;
                

        ReleaseMutex( hMutex1 );
        waitKey(1);
    
    //connect();
    return 0;



void  quit(string msg, int retval)


int connect()

    /*WSADATA wsaData;
    int iResult;

    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET ClientSocket = INVALID_SOCKET;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    int iSendResult;
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;*/


    struct addrinfo *result = NULL;
    struct addrinfo hints;

    ListenSocket = INVALID_SOCKET;
    ClientSocket = INVALID_SOCKET;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) 
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if ( iResult != 0 ) 
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    

    // Create a SOCKET for connecting to server
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) 
        printf("socket failed with error: %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    

    // Setup the TCP listening socket
    iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) 
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    

    freeaddrinfo(result);

    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) 
        printf("listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    

    // Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) 
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    

    // No longer need server socket
    closesocket(ListenSocket);
    return 0;

编辑: 如果按照建议添加测试如果 tmp1 为空,我会得到另一个异常:

bilateralFilter(img, tmp0, -1, 50, 5); 
                    Canny(tmp0, tmp1, 35, 200, 3); 
                    //ytp = frame;
                    //testImage = frame;

                    imwrite("test.jpg",tmp3);
                    waitKey(1);

                    waitKey(500);
                    ReleaseMutex( syncMutex );

                    WaitForSingleObject( syncMutex, INFINITE );

                    if (tmp1.empty())  
                        std::cout << "tmp1 is empty" << std::endl; 
                        break;
                    
                    imshow("ServerWindow", tmp1);

程序现在在 imwrite 上崩溃并抛出异常: newCVS2.exe 中 0x67BDFF1F (msvcr110d.dll) 处的未处理异常:0xC0000005:访问冲突读取位置 0x67706A2E。

这让我觉得访问垫子有问题

【问题讨论】:

【参考方案1】:

检查tmp1是否不为空,例如

if (tmp1.empty)  
    std::cout << "tmp1 is empty" << std::endl; 
    break;

我注意到 OpenCV 的 imshow 函数不是线程安全的(至少在 Windows 机器上)。例如,如果我从两个不同的线程调用imshow,我的程序就会崩溃。如果我在一个线程中调用namedWindow() 命令,然后在另一个线程中调用imshow() 命令,程序也会崩溃。所以确保你不要那样做。作为旁注,最好在调用imshow 之前使用namedWindow("ServerWindow") 创建一个输出窗口。仅在调用imshow 的同一线程中创建此窗口一次(在for 循环之外)。这将提高性能并可能解决问题。

【讨论】:

感谢您的好评。我尝试添加 tmp1.empty 行,现在程序崩溃并给出异常:newCVS2.exe 中 0x67BDFF1F (msvcr110d.dll) 处的未处理异常:0xC0000005:访问冲突读取位置 0x67706A2E。程序在 imwrite 行崩溃。这太奇怪了。我已经用信息更新了主帖 如果我真的打电话:namedWindow("ServerWindow",CV_WINDOW_AUTOSIZE);我得到了异常:newCVS2.exe 中 0x66796AF8 (opencv_highgui248d.dll) 处的未处理异常:0xC0000005:访问冲突读取位置 0x69577265。在那条线上 @Arnagath 您可能正在从多个线程访问tmp1 指向的数据。您的程序使用imwrite 的原因可能是因为imwrite 访问磁盘,因此执行时间较长。在imwrite执行期间,其他线程可能完成对共享数据的访问(例如tmp1指向的数据),所以没有例外。尝试使访问Mat 线程安全。最重要的是,通过保护共享数据不被同时访问,确保您的程序是线程安全的。 另外请注意,当您分配 Mat image = other_image; 时,imageother_image 都指向的不是完全相同的数据。所以通过更改imageother_image 也将被更改。【参考方案2】:

因为tmp3只是初始化,没有任何数据。尝试编写 tmp0tmp1 将在此代码中起作用。

【讨论】:

你误会了,imwrite 不会导致问题,使用 imwrite 可以解决问题。我们有一个空的 tmp3 的原因是为了减少保存图像所需的时间。仅当我们之前没有 imwrite 时,程序才会在 imshow 上崩溃。写 tmp0, tmp1 tmp3 一切正常,这是我什么都不写的时候。哈哈,我看到它令人困惑,但不知道如何更好地制定:P 你有没有尝试在使用imwrite的区域使用waitkey(500)? (保持 imwrite 被注释掉) 我刚试过,waitkey(5000) 无济于事,仍然在imshow 崩溃。即使我没有看到它在单步执行时跳到任何地方,也感觉像是一个并发问题。

以上是关于openCV、C++ 并发、windows的主要内容,如果未能解决你的问题,请参考以下文章

c++结合opencv能给载入图像并处理,我想知道,c++结合啥,可以实现处理gif图像

无法使用 OpenCV 4 (C++) 创建 FisherFaceRecognizer

图像轮廓检测错误:OpenCV、C++

带有 C++ 的 OpenCV 无法编译

OpenCV(C++):如何显示 yuv 文件?

Borland C++ 2009 中的 OpenCV