OpenCV 영상통신

파이썬 입문 공부 (문법, threading, PyQT5) 이후 OpenCV를 들여다 보면서 영상통신을 만들어 보기로

Android 단말기와 영상을 주고 받는 구조이고 Android 단말기는 jpg 형태의 압축파일을 전송 하기로 가정한다. jpg로 압축하지 않고 raw 영상을 보내면 PC 측에서 수신하는 Bytes 수가 많아 화면에 표시되는 속도가 느려지는 문제가 발생. jpg 형태로 압축하는 시간도 있기는 하겠지만 네트워크 전송량으로 인한 부하가 발생하는 것보다는 유리 할 것으로 생각.(어떻게 측정할 것인가의 문제도 있지만 jpg 압축 API 도 공부할 겸)

동작은 아래의 순서와 같이


  1. PC는 통신방식에서 Server 역할을 하고 통신 개시는 Android가 Client로 동작을 하고 연결을 시도한다.
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print('Socket created')
    server_socket.bind((HOST, PORT))
    print('Socket bind complete')
    server_socket.listen(10)
    print('Socket now listening')

  1. PC는 Client의 연결로 Socket 이 생성된다.
    client_socket, addr = server_socket.accept()
    print("wait accept to : ", addr[0], ":", str(addr[1]))
    print("Accept to : ", addr)
    print('wait image')

  1. Client는 jpg 형태로 압축된 영상을 보내므로 매 전송 시마다 전송하는 Byte 수가 다르므로 이후 얼만큼의 영상을 수신할지 알아야 한다.
    bytes_buf = receive_all(client_socket, 4)

  1. 알아낸 수 만큼 영상 데이터를 수신
    수신한 데이터를 OpenCV 포맷으로 변환한다.
    bytes_length = bytes_to_int(bytes_buf)
    print("Rx Length = {} ".format(bytes_length))
    byte_data = receive_all(client_socket, int(bytes_length))
    # convert jpg image to matix
    g_decode_img = np.frombuffer(byte_data, dtype=np.uint8)
    g_decode_img = cv2.imdecode(g_decode_img, cv2.COLOR_RGB2BGR)

  1. OpenCV 포맷으로 변환 한 이미지를 화면에서 표시하고 다시 jgp 형태로 만든다.
    화면 상에서 Mouse 버튼을 누르고 이동을 하면 그 영역만큼 사각형을 그린다. 사각형이 포함된 화면이 jpg 형태로 압축된다.
    cv2.imshow('Client', g_copy_img)
    cv2.waitKey(1)
    # convert Mat to byte for sending to client
    ret, imgencoded = cv2.imencode('.jpg', g_copy_img)
    byte_img = np.array(imgencoded)

  1. 압축된 jpg를 Client로 다시 전송한다.
    int_len_byte_img = len(byte_img)
    bytes_len_img = int_len_byte_img.to_bytes(4, byteorder='big')
    client_socket.send(bytes_len_img)
    client_socket.send(byte_img)

알게된 것들

OpenCV는 마우스 이벤트도 지원한다.
OpenCV 화면 색상은 R G B 순서가 아닌 B G R 순서이다.
압축을 하긴 했지만 1280 * 720 화소수는 통신을 하더라도 영상 표현이 꽤나 느리다.
? 어디에 응용하면 좋을까 ?

전체 소스는

import socket
import struct
import threading
import time
from _thread import *
import numpy as np
import cv2

mouse_rectangle = False


def receive_all(sock, count):
    buf = bytearray(b'')

    while count:
        newbuff = sock.recv(count)
        if not newbuff: return None
        buf += newbuff
        count -= len(newbuff)
    return buf


def bytes_to_int(bytes):
    result = 0

    for b in bytes:
        result = result * 256 + int(b)

    return result


def int_to_bytes(value, length):
    result = []

    for i in range(0, length):
        result.append(value >> (i * 8) & 0xff)

    result.reverse()

    return result


def onMouse(event, x, y, flags, parm):
    global mouse_rectangle, col, row, g_copy_img, g_decode_img

    if event == cv2.EVENT_LBUTTONDOWN:
        mouse_rectangle = True
        col, row = x, y

    elif event == cv2.EVENT_MOUSEMOVE:
        if mouse_rectangle:
            cv2.rectangle(g_decode_img, (col, row), (x, y), (0, 255, 0), 2)

    elif event == cv2.EVENT_LBUTTONUP:
        mouse_rectangle = False


def run_server_cv():
    global mouse_rectangle, g_copy_img, g_decode_img

    HOST = ''
    PORT = 20001

    cv2.namedWindow('Client')   # should create window object first
    cv2.setMouseCallback('Client', onMouse, 0)

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print('Socket created')

    server_socket.bind((HOST, PORT))
    print('Socket bind complete')
    server_socket.listen(10)
    print('Socket now listening')

    client_socket, addr = server_socket.accept()
    # print("wait accept to : ", addr[0], ":", str(addr[1]))
    print("Accept to : ", addr)
    print('wait image')

    while True:

        try:
            if mouse_rectangle is True:
                bytes_buf_null = receive_all(client_socket, 4)

                if bytes_buf_null is not None:
                    bytes_length_null = bytes_to_int(bytes_buf_null)
                    print("Clr Length = {} ".format(bytes_length_null))
                    byte_data_null = receive_all(client_socket, int(bytes_length_null))
            else:
                bytes_buf = receive_all(client_socket, 4)

                if bytes_buf is not None:
                    bytes_length = bytes_to_int(bytes_buf)
                    print("Rx Length = {} ".format(bytes_length))
                    byte_data = receive_all(client_socket, int(bytes_length))

                    # convert jpg image to matix
                    g_decode_img = np.frombuffer(byte_data, dtype=np.uint8)
                    g_decode_img = cv2.imdecode(g_decode_img, cv2.COLOR_RGB2BGR)

            g_copy_img = np.copy(g_decode_img)

            cv2.imshow('Client', g_copy_img)
            cv2.waitKey(1)

            # convert Mat to byte for sending to client
            ret, imgencoded = cv2.imencode('.jpg', g_copy_img)
            byte_img = np.array(imgencoded)

            int_len_byte_img = len(byte_img)
            bytes_len_img = int_len_byte_img.to_bytes(4, byteorder='big')

            client_socket.send(bytes_len_img)
            client_socket.send(byte_img)

        except IOError:
            client_socket.close()

            print("wait accept")
            client_socket, addr = server_socket.accept()
            print("accept to : ", addr[0], ":", str(addr[1]))
            print('wait image')

    server_socket.close()


if __name__ == "__main__":
    run_server_cv()

뭔가 좀 부족한데 뭘까 ?

좋은 웹페이지 즐겨찾기