SERVER
# 필요한 패키지 import
# SERVER
import socket # 소켓 프로그래밍에 필요한 API를 제공하는 모듈
import struct # 바이트(bytes) 형식의 데이터 처리 모듈
import pickle # 객체의 직렬화 및 역직렬화 지원 모듈
import cv2 # OpenCV(실시간 이미지 프로세싱) 모듈
# 서버 ip 주소 및 port 번호
ip = '127.0.0.1'
port = 5000
# 1. 소켓 객체 생성
server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2. 바인딩
server_socket.bind((ip, port))
# 3. 접속 대기
server_socket.listen(10)
# 4. 접속 수락(클라이언트 연결안되면 여기서 멈춰있음)
client_socket, address = server_socket.accept()
# 수신한 데이터를 넣을 버퍼(바이트 객체)
data_buffer = b""
# calcsize : 데이터의 크기(byte)
# - L : 부호없는 긴 정수(unsigned long) 8 bytes
# data_size = struct.calcsize("L")
data_size = struct.calcsize("Q")
# struct.calcsize("L")은 부호 없는 긴 정수(unsigned long)의 크기를 바이트 단위로 계산하는 함수
# 64비트 아키텍처를 사용하고 있다면 8바이트가 나온다.
print(f'{data_size} 나는 data_size 8')
print(f'{data_buffer} 나는 data_buffer')
while True:
# 설정한 데이터의 크기보다 버퍼에 저장된 데이터의 크기가 작은 경우
# 현재까지 수신된 데이터의 크기가 기대되는 데이터의 크기보다 작을 때까지 계속해서 데이터를 수신하는 무한 루프입니다
while len(data_buffer) < data_size:
# 5. 데이터 수신
data_buffer += client_socket.recv(4096)
# 이 부분은 클라이언트 소켓에서 최대 4096바이트의 데이터를 수신하여 data_buffer에 추가하는 역할을 합니다
# recv() 메서드는 소켓으로부터 데이터를 읽어오는 역할
# 버퍼의 저장된 데이터 분할
packed_data_size = data_buffer[:data_size]
# print(f'data_buffer: {data_buffer}')
print(f'packed_data_size: {packed_data_size}') # \x00\x00T\x0e\x80\x04\x95\x03
print(f'packed_data_size int로: {int.from_bytes(packed_data_size, byteorder="big")}')
data_buffer = data_buffer[data_size:]
# print(f'data_buffer 끝 배열: {data_buffer}')
# struct.unpack : 변환된 바이트 객체를 원래의 데이터로 반환
# - > : 빅 엔디안(big endian)
# - 엔디안(endian) : 컴퓨터의 메모리와 같은 1차원의 공간에 여러 개의 연속된 대상을 배열하는 방법
# - 빅 엔디안(big endian) : 최상위 바이트부터 차례대로 저장
# - L : 부호없는 긴 정수(unsigned long) 4 bytes
frame_size = struct.unpack(">Q", packed_data_size)[0]
print(f'frame_size: {frame_size}') # 23215 출력됨
# 프레임 데이터의 크기보다 버퍼에 저장된 데이터의 크기가 작은 경우
while len(data_buffer) < frame_size:
# 데이터 수신
data_buffer += client_socket.recv(4096)
# 프레임 데이터 분할
frame_data = data_buffer[:frame_size]
data_buffer = data_buffer[frame_size:]
print("수신 프레임 크기 : {} bytes".format(frame_size))
# loads : 직렬화된 데이터를 역직렬화
# - 역직렬화(de-serialization) : 직렬화된 파일이나 바이트 객체를 원래의 데이터로 복원하는 것
frame = pickle.loads(frame_data)
# imdecode : 이미지(프레임) 디코딩
# 1) 인코딩된 이미지 배열
# 2) 이미지 파일을 읽을 때의 옵션
# - IMREAD_COLOR : 이미지를 COLOR로 읽음
frame = cv2.imdecode(frame, cv2.IMREAD_COLOR)
# 프레임 출력
cv2.imshow('Frame', frame)
# 'q' 키를 입력하면 종료
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
# 6. 접속종료
client_socket.close()
server_socket.close()
print('연결 종료')
# 모든 창 닫기
cv2.destroyAllWindows()
CLIENT
import cv2
import struct
import pickle
import socket
ip = '127.0.0.1'
port = 5000
capture = cv2.VideoCapture(0)
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# 1. 소켓 생성
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
# 2. 접속
client_socket.connect((ip, port)) # 서버와 연결
# 메시지 수신
while True:
# 프레임 읽기
retval, frame = capture.read()
# retval: 프레임을 읽어오는데 성공했는지 여부
# frame: 읽어온 프레임 자체를 나타내는 이미지 배열(array)
print(f'{retval}하고 {frame}')
# imencode : 이미지(프레임) 인코딩
# 1) 출력 파일 확장자
# 2) 인코딩할 이미지
# 3) 인코드 파라미터
# - jpg의 경우 cv2.IMWRITE_JPEG_QUALITY를 이용하여 이미지의 품질(0 ~ 100)을 설정
# - png의 경우 cv2.IMWRITE_PNG_COMPRESSION을 이용하여 이미지의 압축률(0 ~ 9)을 설정
# [return]
# 1) 인코딩 결과(True / False)
# 2) 인코딩된 이미지
retval, frame = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 25])
# 이미지를 JPEG 형식으로 인코딩하는 함수
# jpg: 인코딩할 이미지의 확장자를 나타내는 문자열
# frame: 인코딩할 이미지를 나타내는 NumPy 배열(numpy array)입니다. 이는 OpenCV에서 읽어온 이미지입니다.
# JPEG 인코딩 옵션을 나타내는 리스트입니다. 여기서는 이미지의 품질(quality)을 설정합니다.
# cv2.IMWRITE_JPEG_QUALITY는 JPEG 압축 품질을 지정하는 상수로, 값은 0에서 100까지 가능합니다.
# 높은 값일수록 이미지의 품질이 좋아지지만 파일 크기가 커집니다. 여기서는 90을 지정하여 상대적으로
# 고품질의 JPEG 이미지를 생성하도록 합니다.
# dumps : 데이터를 직렬화
# - 직렬화(serialization) : 효율적으로 저장하거나 스트림으로 전송할 때 데이터를 줄로 세워 저장하는 것
# 이미지 데이터를 직렬화 19\xa6\xd1@\x0e\xcd\x1b\x8d6\x96\x80?\xff\xd9\x94t\x94b...
frame = pickle.dumps(frame)
print(f'전송 프레임 크기 : {len(frame)} bytes') #21884 bytes
# print(frame) 은 e\xcd\x1b\x8d6\x96\x80?\xff\xd9\x94t\x94b... 이런거 나옴
# sendall : 데이터(프레임) 전송
# - 요청한 데이터의 모든 버퍼 내용을 전송
# - 내부적으로 모두 전송할 때까지 send 호출
# struct.pack : 바이트 객체로 반환
# - > : 빅 엔디안(big endian)
# - 엔디안(endian) : 컴퓨터의 메모리와 같은 1차원의 공간에 여러 개의 연속된 대상을 배열하는 방법
# - 빅 엔디안(big endian) : 최상위 바이트부터 차례대로 저장
# 3. 데이터 송신
client_socket.sendall(struct.pack(">Q", len(frame)) + frame)
# struct.pack(">Q", len(frame)) 부분은 len(frame)을 부호 없는 긴 정수로 변환한 후에
# 빅 엔디안(big endian) 방식으로 바이트로 묶은 것
# 클라이언트 소켓을 통해 이미지 데이터를 서버에 전송하는 코드
# 이미지 데이터의 길이를 네트워크로 전송하기 위해 패킹하는 과정
# len(frame): 이미지 데이터의 길이를 구하는 함수로, frame은 이미지 데이터를 나타내는 바이트 배열
# struct.pack(">L", len(frame))은 이미지 데이터의 길이를 네트워크 바이트 순서에 맞춰 패킹
# ">L"은 빅 엔디안(big-endian) 방식으로 unsigned long(부호 없는 긴 정수)를 나타내며,
# 데이터를 네트워크로 보낼 때 사용하는 표준적인 바이트 순서
# + frame: 이 부분은 이미지 데이터 자체를 뒤에 이어붙이는 과정입니다.
# 앞서 패킹된 이미지 데이터의 길이 정보와 실제 이미지 데이터를 연결하여 전송할 데이터를 완성합니다.
# Broken pip: sendall() 함수는 한 번에 모든 데이터를 소켓을 통해 전송하며, 데이터가 모두 전송될 때까지 블로킹됩니다.
# 파이프(또는 소켓)의 한쪽이 데이터를 보내고 있는 동안 다른 쪽이 연결을 끊었을 때 발생
# 메모리를 해제
capture.release()
'Python' 카테고리의 다른 글
excel to pdf python(엑셀을 pdf로 변환하기)_linux environment not window (0) | 2024.08.28 |
---|---|
opencv streaming 카메라로 받아서 localhost에서 띄우기 (0) | 2024.05.30 |
opencv 과제1 (0) | 2024.05.28 |
python에서 Input (0) | 2024.03.11 |
지니뮤직 렝크,제목,가수 크롤링(QUIZ) (0) | 2022.06.18 |