Scientia Conditorium

[소켓 프로그래밍001] opencv를 이용한 동영상 이미지 전송 프로그램 본문

프로그래밍/Socket

[소켓 프로그래밍001] opencv를 이용한 동영상 이미지 전송 프로그램

크썸 2022. 5. 5. 19:51

[소켓 프로그래밍001] opencv를 이용한 동영상 이미지 전송 프로그램

 

우연히 소켓 프로그래밍을 해야할 일이 생겼다.

비전공자 출신으로 네트워크 수업도 들어본 적 없으며 뭐가뭔지 한참을 헤맸지만 조금씩 헤쳐나갔다.

윈도우에서 간단하게 서버/클라이언트를 생성하여 동영상 이미지를 프레임 단위로 전송하는 간단한 프로그램을 만들어보았다.

 

먼저 서버다.

#include <iostream>

#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/background_segm.hpp>
#include <opencv2/video/tracking.hpp>

#define FRAME_WIDTH     640
#define FRAME_HEIGHT    480
cv::Mat cameraFeed;
SOCKET server, client;
int height, width, IM_HEIGHT, IM_WIDTH, imgSize;

void Server()
{
    WSADATA WSAData;
    if (WSAStartup(MAKEWORD(2, 0), &WSAData) != 0)
    {
        return;
    }

    server = socket(AF_INET, SOCK_STREAM, 0);

    SOCKADDR_IN serverAddr, clientAddr;
    serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(7200);

    int retval = bind(server, (SOCKADDR*)&serverAddr, sizeof(serverAddr));
    if (retval == SOCKET_ERROR)
    {
        std::cout << "bind() error" << std::endl;
        return;
    }

    retval = listen(server, 0);
    if (retval == SOCKET_ERROR)
    {
        std::cout << "listen() error" << std::endl;
        return;
    }

    std::cout << "Listening for incoming connections..." << std::endl;

    char buffer[1024];
    int clientAddrSize = sizeof(clientAddr);
    if ((client = accept(server, (SOCKADDR*)&clientAddr, &clientAddrSize)))
    {
        std::cout << "Client connected!" << std::endl;
    }
}

int main()
{
    Server();
    cv::VideoCapture cap("simulation.mp4");

    if (!cap.isOpened())
    {
        std::cout << "Can't open the camera" << std::endl;
        return -1;
    }
    
    while (true)
    {
        //cap >> cameraFeed;
        if (!cap.read(cameraFeed))
        {
            std::cout << "Video Ended" << std::endl;
            break;
        }

        height = cameraFeed.rows;
        width = cameraFeed.cols;
        IM_HEIGHT = FRAME_HEIGHT;
        IM_WIDTH = FRAME_WIDTH;
        cv::resize(cameraFeed, cameraFeed, cv::Size(IM_WIDTH, IM_HEIGHT));
        imgSize = cameraFeed.total() * cameraFeed.elemSize();
        
        int retval = send(client, reinterpret_cast<const char*>(cameraFeed.data), imgSize, 0);
        if (retval == SOCKET_ERROR)
        {
            std::cout << "send() error" << std::endl;
        }

        cv::imshow("Server", cameraFeed);

        if (cv::waitKey(30) == 27)
        {
            closesocket(client);
            std::cout << "Client disconnected" << std::endl;
            break;
        }
    }

    return 0;
}

 

다음은 클라이언트다.

#include <iostream>
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h.>
#include <Windows.h>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/background_segm.hpp>
#include <opencv2/video/tracking.hpp>

#define FRAME_WIDTH     640
#define FRAME_HEIGHT    480

int main()
{
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 0), &wsa) != 0)
    {
        return -1;
    }

    SOCKET server;
    server = socket(AF_INET, SOCK_STREAM, 0);

    SOCKADDR_IN addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(7200);
    int retval = inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr.S_un.S_addr));
    if (retval == SOCKET_ERROR)
    {
        std::cout << "inet_pton() error" << std::endl;
        return -1;
    }

    retval = connect(server, (SOCKADDR*)&addr, sizeof(addr));
    if (retval == SOCKET_ERROR)
    {
        std::cout << "connect() error" << std::endl;
        return -1;
    }

    std::cout << "Connected to server!" << std::endl;

    int bytes = 0;
    while (true)
    {
        int IM_HEIGHT = FRAME_HEIGHT;
        int IM_WIDTH = FRAME_WIDTH;
        cv::Mat img = cv::Mat::zeros(FRAME_HEIGHT, FRAME_WIDTH, CV_8UC3);
        int imgSize = img.total() * img.elemSize();

        const int ah = 921600;
        char sockData[ah];

        for (int i = 0; i < imgSize; i += bytes)
        {
            std::cout << bytes << std::endl;
            if ((bytes = recv(server, sockData + i, imgSize - i, 0)) == SOCKET_ERROR)
            {
                std::cout << "recv() error" << std::endl;
            }
        }

        int ptr = 0;
        for (int i = 0; i < img.rows; ++i)
        {
            for (int j = 0; j < img.cols; ++j)
            {
                img.at<cv::Vec3b>(i, j) = cv::Vec3b(sockData[ptr + 0], sockData[ptr + 1], sockData[ptr + 2]);
                ptr = ptr + 3;
            }
        }

        cv::namedWindow("Client", cv::WindowFlags::WINDOW_AUTOSIZE);
        cv::imshow("Client", img);
        
        if (cv::waitKey(30) == 27)
        {
            closesocket(server);
            std::cout << "Server disconnected" << std::endl;
            break;
        }
    }

    return 0;
}

 

 

위 두 프로그램을 각각 다른 솔루션으로 작성하여 실행하면 아래와 같은 결과가 나온다.

 

우선은 코드만 올려놓고 설명은 다음 포스팅에서 하는 것으로 하겠다.