pythonメニュー

Desktop画面をTCP通信で配信する検討

pyautogui as pag # デスクトップスクリーンを制御するモジュールを使って、得られた画像をTCP通信配信する作品の例です。

初期検討ソース

# MySrcTcpServer.py
import socket
import threading
import numpy as np
import cv2
import pyautogui as pag # デスクトップスクリーンを制御するモジュール

server_ip = "127.0.0.1"
server_port = 80
listen_num = 5

tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(type(tcp_server), "のソケットオブジェクトの作成")

tcp_server.bind((server_ip, server_port))
print("ソケットにIPアドレス:{}ポート:{}を紐づける".format(server_ip,server_port))

tcp_server.listen(listen_num)
print("listenで接続可能状態へ移行")

def rec_send(client,head,body): # 受信はしていません。head,bodyをclientのソケットで送信する関数です。
    client.send(head) # 送信
    lst=np.frombuffer( head , dtype="int16")#2バイト、リトルエンディアンで変換
    print("-------------", lst)
    client.send(body) # 送信
    client.close()

while True:
    img=pag.screenshot() # デスクトップ画面キャプチャ
    img=np.asarray(img)
    img=cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
    cv2.imwrite("scr.png", img) # 画像ファイル生成
    print( img.shape ) # 例:(1080, 1920, 3)
    a=np.array(img.shape, dtype="int16") # aに「array([1080, 1920,    3], dtype=int16)」がセット
    head=b''.join( a ) # bytes型 設定例:b'8\x04\x80\x07\x03\x00' 
    size=img.shape[0]*img.shape[1]*img.shape[2] # 設定例:1080*1920の2073600が記憶
    body=img.reshape(size,-1) 
    body=np.frombuffer(  body , dtype="int16")#2バイト、リトルエンディアンで変換
    #
    client,address = tcp_server.accept()
    print("クライアント接続許可:",type(client), address)
    #
    t_id = threading.Thread(target=rec_send, args=(client, head, body))
    t_id.start()



サーバ作品:tcp_desktopserver.py

上記を変更して作った作品です。
変更点1:上記はクライアント接続時にキャプチャしていますが、以下は1秒ごにキャプチャしてファイル化しています。
そして、クライアント接続時は、ファイルを送信しています。(接続が多い時に余計なキャプチャをさせないためです。)
ファイル("scr.jpg")は、ファイル全体をバイナリーイメージで送るだけにしました
変更点2:キースレッドで、強制終了やトリミング指定制御を追加しました。

# tcp_desktopserver.py
import pyautogui as pag # デスクトップスクリーンを制御するモジュール
import numpy as np
import cv2
import time
import shutil
import sys
import threading
import socket

host = socket.gethostname() # ホスト名を取得、表示
print("ホスト名:", host)

info = socket.gethostbyname_ex(host) # ipアドレスを取得、表示
print(type(info), ":", info)
s=input("使用するデバイスの添え字>") ; ip_idx=0 if s == "" else int(s)
server_ip = info[2][ip_idx]

s=input("ポート番号(defual:3000)>"); server_port  = 3000  if s == "" else int(s)
print(f"使用するIPアドレス:ポート番号={ server_ip}:{server_port }")
listen_num = 20 # 接続クライアント数

# 1.ソケットオブジェクトの作成
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2.作成したソケットオブジェクトにIPアドレスとポートを紐づける
tcp_server.bind((server_ip, server_port))
print("server : bind")

# 3.作成したオブジェクトを接続可能状態にする
tcp_server.listen(listen_num)

flag_Trimming = False # 画像をトリミングするか?のフラグ
def stop_start():  # スレッドでキー入力を行い、'Q'の入力で終了できるようにしている。('T'入力でトリミングの切り替え)
   global flag_Trimming
   while True:
      s = input("")
      print("Q ENTERのキー入力で終了:(T:トリミング)>>", end="")
      if s == "T" : flag_Trimming = not flag_Trimming
      if s == "Q" :
         tcp_server.close()
         print("強制終了しました。!!")
         sys.exit()

t_id2 = threading.Thread(target=stop_start)
t_id2.start() # 上記キー入力スレッド起動

import time # 1秒ごとにキャプチャーする制御で使う
prve_time=0 # 繰り返しにおいて前のキャプチャー時間を記憶
count = 0 # キャプチャー回数のカウント
img = None # これにイメージがある時、接続クライアント送信する。

Quality = 20 # cv2でjpgファイル化する時のクオリティ指定
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), Quality]
while True:
   n_time= time.time()#現在時間取得
   if n_time - prve_time > 1.0: # 1秒ごとにキャプチャーする制御
      prve_time = n_time
      try:
         img=pag.screenshot() # デスクトップキャプチャ
         img=np.asarray(img)
         img=cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
         h, w, ch = img.shape
         w=1000 if w > 1000 else w
         h=1000 if h > 1000 else h
         if flag_Trimming : img=img[:h , :w ] # トリミング
         filename = "scr.jpg"
         result, encimg = cv2.imencode(".jpg", img, encode_param)
         img=cv2.imdecode(encimg, cv2.IMREAD_UNCHANGED)
         cv2.imwrite(filename, img) # jpgのファイル書き込み
         print(type(img), end="のキャプチャから" ) 
         f=open(filename,"rb")
         img = f.read()
         print(type(img) ,end=f"のメージ生成。トリミング:{flag_Trimming}\n" )
         f.close()
         count+=1
         print(f"●キャプチャ回数:{count} サイズ:{len(img)} ++++++++++{server_ip}:{str(server_port)}")
      except Exception as e:
         print(e)
   
   if not img : continue

   print("server: wait for accept", end="")
   client,address = tcp_server.accept()
   print("[*] Connected!! [ Source : {}]".format(address))
   
   # 7.クライアントへ送る
   client.send(img)
   
   # 8.接続を終了させる
   client.close()


上記のクライアント作品(Java)


getpaint.zip 以下を実行可能ファイルにしたファイル(getpaint.jar)は、ここからダウンロードできます。
拡張子.zipを、.jarに変更して使ってください。
package getp;
// Java の最初に作られたGUIがawtで、そのTCP接続したサーバから受信した画像をCanvasに描画する繰り返しの作品
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import java.net.*;
import java.nio.ByteBuffer;

public class PaintImage extends Frame implements ActionListener, Runnable {
    BufferedImage bufferedImage = new BufferedImage(800,800,BufferedImage.TYPE_INT_RGB);
    int width, height;
    Panel panel = new Panel();
    Label labelIP= new Label("IP Address:");
    TextField textFieldIP= new TextField("192.168.71.8");
    Label labelPort= new Label("   Port No:");
    TextField textFieldPort= new TextField("3000");
    Button btnCtrl= new Button("START");
    Label labelMsg= new Label("It is an application that receives and displays images.");
    ScrollPane scrPane = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); //常にスクロールバー表示
    Thread thread = null;
    Canvas canvas = new Canvas(){
        public void paint(Graphics graphics) {
            graphics.drawImage(bufferedImage, 0, 0, null);//描画処理
        }
    };
    public static void main(String [] args)  throws Exception  {
        new PaintImage();
    }
    PaintImage() throws Exception {
        super("PaintImage");
        setLayout(new BorderLayout());
        panel.add(labelIP);
        panel.add(textFieldIP);
        panel.add(labelPort);
        panel.add(textFieldPort);
        panel.add(btnCtrl);
        add(panel,BorderLayout.NORTH);
        scrPane.add(canvas);
        add(scrPane,BorderLayout.CENTER);
        add(labelMsg,BorderLayout.SOUTH);
        loadTxt();
        setSize(800, 800); setVisible(true);
        btnCtrl.addActionListener(this);
        this.addWindowListener( new WindowAdapter(){
           public void windowClosing(WindowEvent e){// 閉じるボタンで
                thread = null;
                saveTxt();
                PaintImage.this.dispose();//破棄して、終了させる。
            }
        });
    }

    void saveTxt(){
        try {
            FileOutputStream os=new FileOutputStream("getpaint.txt");
            String s="IP:" + textFieldIP.getText() + "\r\n";
            s += "PORT:" + textFieldPort.getText() + "\r\n";
            os.write( s.getBytes() );
            os.close();
        } catch(Exception e){
        }
    }

    static String getValue(String s,String key){
        int i1 = s.indexOf(key);
        int i2 = s.indexOf("\r\n", i1);
        if(i1 == -1 || i2 == -1) return "";
        return s.substring(i1+key.length(), i2);
    }

    void loadTxt(){
        try {
            File file = new File("getpaint.txt");
            FileInputStream is=new FileInputStream(file);
            byte []buf = new byte[(int) file.length()];
            is.read(buf);
            String s = new String(buf);
            textFieldIP.setText(getValue(s, "IP:"));
            textFieldPort.setText(getValue(s, "PORT:"));
        } catch(Exception e){
        }
    }


    public void run(){
        while( thread != null ){
            try {
                Thread.sleep(1000);
                labelMsg.setText("Waiting for connection");
                getImage();
                canvas.repaint();
                labelMsg.setText("OK("+ width + "," + height +")");
            } catch(Exception err){
                String msg = err.getMessage();
                System.out.println(msg);
                labelMsg.setText(msg);
                thread = null;
                btnCtrl.setLabel("START");
                break;
            }
        }
    }

    public void actionPerformed(ActionEvent e){
        if(e.getSource() == btnCtrl){
            if( thread != null){
                thread = null;
                btnCtrl.setLabel("START");
            } else {
                labelMsg.setText("started !");
                thread = new Thread(this);
                thread.start();// runの実行
                btnCtrl.setLabel("STOP");
            }
        }
	}

    public void getImage() throws Exception {
           String ip= textFieldIP.getText();
           int port = Integer.parseInt(textFieldPort.getText());
           System.out.println(ip + ":" + port);
           Socket socket = new Socket(ip,port);
           
           ImageInputStream inputStream = ImageIO.createImageInputStream(socket.getInputStream());

           bufferedImage = ImageIO.read(inputStream);
           width = bufferedImage.getWidth(this);
           height = bufferedImage.getHeight(this);
           canvas.setSize(width,height);
           System.out.println("bufferedImage" + bufferedImage.toString());
    }

    public void getImage_ok(){
        try{
            
            Socket socket = new Socket("127.0.0.1",3000);
          
            InputStream inputStream = socket.getInputStream();

            bufferedImage = ImageIO.read(inputStream);

            System.out.println("bufferedImage" + bufferedImage.toString());
        }
        catch(Exception e){
        }    
    }

    public void getImage_2(){
        try{
            byte []buf = new byte[1000000];
            int  count = 0;
            Socket socket = new Socket("127.0.0.1",3000);
            InputStream inputStream = socket.getInputStream();
            for(;;){
               int c = inputStream.read();
               if( c == -1 ) break;
               buf[count++] = (byte) c;
           }
           System.out.println("受信サイズ:" + count);

           System.out.println("bufferedImage" + bufferedImage.toString());
        }
        catch(Exception e){
        }    
    }

    public void getImage_1(){
        try{
           InputStream inputStream = new FileInputStream(new File("cat.jpg"));
           bufferedImage = ImageIO.read(inputStream);
           System.out.println("bufferedImage" + bufferedImage.toString());
        }
        catch(Exception e){
        }    
    }
}
getpaint.MF.txtの名前でマニフェストファイルを作ります。
Main-Class: getp.PaintImage
Class-Path: getpaint.jar .
上記を 「PaintImage.java」 と、「getpaint.MF.txt」を 「getp」の名前のフォルダに入れて、次のように実行すれば、「getpaint.jar」の作品ができます。
javac -source 1.6 -target 1.6 getp\PaintImage.java
jar cmvf .\getp\getpaint.MF.txt .\getpaint.jar .\getp\*.class
getpaint_work.zipからソースなどがダウンロードできます。