This post is Private. Only a writer or those who know its URL can access this post.

Article information
Show article in Markdown
Report article
Help us understand the problem. What is going on with this article?

リモートカメラの映像を取得する

はじめに

こちらはアドベントカレンダー16日目の記事になります。
監視カメラを作成してみたので共有します。

環境

macOS Catalina Version 10.15.2
Python3.7.5

構成

/remote_camera
|
├── connection.ini
├── streaming_client.py
└── streaming_server.py

注意

悪用厳禁・自己責任でお願いします。

事前準備

pip install numpy
pip install opencv-python
pip install kivy 

ソースコード

初期設定ファイル

ipにはサーバ側のipアドレスを入力してください。
現在はループバックアドレスを指定してローカル環境で実行しています!!

setting.ini
[server]
ip = 127.0.0.1
port = 50080

[packet]
header_size = 4
image_width = 640
image_height = 480

クライアント

remote_client.py
from kivy.app import App
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.graphics.texture import Texture
from kivy.core.window import Window
import sys
import cv2
import numpy as np
import socket
import configparser

class View(Image):

    def __init__(self, server_ip, server_port, image_width, image_height, view_fps, view_width, view_height, **kwargs):
        super(View, self).__init__(**kwargs)

        #  通信設定
        self.buff = bytes()
        self.PACKET_HEADER_SIZE = 4
        self.soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.SERVER_IP = server_ip
        self.SERVER_PORT = server_port
        self.IMAGE_WIDTH = image_width
        self.IMAGE_HEIGHT = image_height

        #  表示設定
        self.allow_stretch = True
        self.VIEW_FPS = view_fps
        self.VIEW_WIDTH = view_width
        self.VIEW_HEIGHT = view_height

        Clock.schedule_interval(self.update, 1.0 / view_fps)

        try:
            self.soc.connect((self.SERVER_IP, self.SERVER_PORT))
        except socket.error as e:
            print('Connection failed.')
            sys.exit(-1)

    def update(self, dt):
        data = self.soc.recv(self.IMAGE_HEIGHT * self.IMAGE_WIDTH * 3)
        self.buff += data

        packet_head = 0
        packets_info = list()
        while True:
            if len(self.buff) >= packet_head + self.PACKET_HEADER_SIZE:
                binary_size = int.from_bytes(self.buff[packet_head:packet_head + self.PACKET_HEADER_SIZE], 'big')
                if len(self.buff) >= packet_head + self.PACKET_HEADER_SIZE + binary_size:
                    packets_info.append((packet_head, binary_size))
                    packet_head += self.PACKET_HEADER_SIZE + binary_size
                else:
                    break
            else:
                break

        if len(packets_info) > 0:
            packet_head, binary_size = packets_info.pop()
            img_bytes = self.buff[packet_head + self.PACKET_HEADER_SIZE:packet_head + self.PACKET_HEADER_SIZE + binary_size]
            self.buff = self.buff[packet_head + self.PACKET_HEADER_SIZE + binary_size:]

            # 画像を復元
            img = np.frombuffer(img_bytes, dtype=np.uint8)
            img = cv2.imdecode(img, 1)
            img = cv2.flip(img, 0)
            img = cv2.resize(img, (self.VIEW_WIDTH, self.VIEW_HEIGHT))
            # 画像を変換
            img = img.tostring()

            # 作成した画像をテクスチャに設定
            img_texture = Texture.create(size=(self.VIEW_WIDTH, self.VIEW_HEIGHT), colorfmt='bgr')
            img_texture.blit_buffer(img, colorfmt='bgr', bufferfmt='ubyte')
            self.texture = img_texture

    def disconnect(self):
        # サーバとの接続を切断
        self.soc.shutdown(socket.SHUT_RDWR)
        self.soc.close()

class ClientApp(App):

    def __init__(self, view_fps, view_width, view_height, **kwargs):
        super(ClientApp, self).__init__(**kwargs)
        self.VIEW_FPS = view_fps
        self.VIEW_WIDTH = view_width
        self.VIEW_HEIGHT = view_height

    def build(self):
        config = configparser.ConfigParser()
        config.read('./connection.ini', 'UTF-8')
        config_server_ip = config.get('server', 'ip')
        config_server_port = int(config.get('server', 'port'))
        config_header_size = int(config.get('packet', 'header_size'))
        config_image_width = int(config.get('packet', 'image_width'))
        config_image_height = int(config.get('packet', 'image_height'))

        Window.size = (self.VIEW_WIDTH, self.VIEW_HEIGHT)

        # ビューの生成
        self.stream_view = View(
            server_ip=config_server_ip,
            server_port=config_server_port,
            image_width=config_image_width,
            image_height=config_image_height,
            view_fps=self.VIEW_FPS,
            view_width=self.VIEW_WIDTH,
            view_height=self.VIEW_HEIGHT
        )
        return self.stream_view

    def on_stop(self):
        # サーバとの接続を切断
        self.stream_view.disconnect()

if __name__ == '__main__':
    ClientApp(view_fps=30, view_width=800, view_height=600).run()

サーバー

remote_server.py
import socket
import numpy as np
import cv2
import time
import configparser

config = configparser.ConfigParser()
config.read('./connection.ini', 'UTF-8')

FPS = 12
INDENT = '    '

CAMERA_ID = 0
CAMERA_FPS = 12
CAMERA_WIDTH = 1280
CAMERA_HEIGHT = 720

SERVER_IP = config.get('server', 'ip')
SERVER_PORT = int(config.get('server', 'port'))

HEADER_SIZE = int(config.get('packet', 'header_size'))

IMAGE_WIDTH = int(config.get('packet', 'image_width'))
IMAGE_HEIGHT = int(config.get('packet', 'image_height'))
IMAGE_QUALITY = 30

cam = cv2.VideoCapture(CAMERA_ID)
cam.set(cv2.CAP_PROP_FPS, CAMERA_FPS)
cam.set(cv2.CAP_PROP_FRAME_WIDTH, CAMERA_WIDTH)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, CAMERA_HEIGHT)

print('Camera {')
print(INDENT + 'ID    : {},'.format(CAMERA_ID))
print(INDENT + 'FPS   : {},'.format(cam.get(cv2.CAP_PROP_FPS)))
print(INDENT + 'WIDTH : {},'.format(cam.get(cv2.CAP_PROP_FRAME_WIDTH)))
print(INDENT + 'HEIGHT: {}'.format(cam.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print('}')

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((SERVER_IP, SERVER_PORT))
s.listen(1)
soc, addr = s.accept()

print('Server {')
print(INDENT + 'IP   : {},'.format(SERVER_IP))
print(INDENT + 'PORT : {}'.format(SERVER_PORT))
print('}')

print('Client {')
print(INDENT + 'IP   : {},'.format(addr[0]))
print(INDENT + 'PORT : {}'.format(addr[1]))
print('}')

while True:
    loop_start_time = time.time()

    # 送信用画像データ作成
    flag, img = cam.read()
    resized_img = cv2.resize(img, (IMAGE_WIDTH, IMAGE_HEIGHT))
    (status, encoded_img) = cv2.imencode('.jpg', resized_img, [int(cv2.IMWRITE_JPEG_QUALITY), IMAGE_QUALITY])

    packet_body = encoded_img.tostring()
    packet_header = len(packet_body).to_bytes(HEADER_SIZE, 'big') 
    packet = packet_header + packet_body

    #  送信
    try:
        soc.sendall(packet)
    except socket.error as e:
        print('Connection closed.')
        break

    # FPS制御
    time.sleep(max(0, 1 / FPS - (time.time() - loop_start_time)))

s.close()

問題点

サーバ側のMacがカメラ起動時にLEDランプついちゃうんで消したい!!
プロセスがバレバレなので隠したい!!

参考

TCPでカメラからの映像を送信(Python)

感想

Python初学者のためあまりわかりません。。。
指摘などいただければ修正します!
読んでいただきありがとうございました!!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした