qshinoの日記

Powershell関係と徒然なこと

ximagesrc in gstreamer

x11の画面を切り取るgstreamer plugin

ximagesrcはxからイメージを切り出すプラグイン。これを使って、モールスのデモを作成する。

x上でモールス信号を表示し、映像からモールスを復号する。

簡単だと思ったら、煩雑な処理が多く、作るかどうかは別途検討する。ここでは実現方法の検討まで。

全体の流れは

  1. テキストファイルをモールス変換
  2. モールスをウインドウに表示
  3. 表示されたイメージをモールス化
  4. モールスを復号し、テキスト表示

1,2,4は作るだけ。今回のメインは3.イメージのモールス化。

また、モールスは時間で長短の符号が変わるが、pcは時間に対して厳密な精度を出しにくいため、送受信共に時間的なバラツキの取り扱いが煩雑になる。

時間の同期処理を適宜盛り込むか、受信側で何らかの意味的適合処理を盛り込むか。意味的適合処理を入れるためには、文脈認識やその為の辞書の様な仕組みが必要になり、実装範囲が拡大する。

今回は意味的適合処理は考慮しない。

イメージのモールス化

イメージの取得はgstreamerのximagesrcを使う。切り取ったイメージをopencvで処理し、短点、長点、符号間、文字間、語間を認識し、モールス化する。

image切り取り

受信の入口はイメージ。先ずはイメージの切り取り方から。

image切り取りは、ximagesrcのstartx, starty, endx, endyを指定する。

# init

x=233
y=24 
w=$((x + 800 -1)) 
h=$((y + 480 -1))

# launch

 gst-launch-1.0 -e \
 ximagesrc display-name=:1 \
show-pointer=false startx=${x}\
 starty=${y} endx=${w} endy=${h} \
 ! videoconvert ! videoscale \
! video/x-raw, framerate=25/1, width=1920, height=1080, \
pixel-aspect-ratio=1/1, \
method=lanczos \
 ! fbdevsink sync=false 

切り取り範囲

切り取りには範囲を指定する。この範囲を取得する方法。

xlsclients : xクライントリスト

xprop : プロパティ

xwininfo : window info

lsclients -l 出力例

# xlsclients -l 
Window 0x180000d: 
  Command: /usr/X11R6/bin/kterm       
  Instance/Class: kterm/KTerm 
Window 0x1200001: 
  Name: MozillaFirefox-bin 
  Command: /usr/lib/mozilla-firebird/MozillaFirefox-bin 
  Instance/Class: MozillaFirefox-bin/MozillaFirefox-bin 
Window 0x2d0000d: 
  Name: xine Icon 
  Name: kterm 
  Command: /usr/bin/xine 
  Instance/Class: xine/Xine 

xlsclient -l出力から、idとnameを取得し、対象の名前のwindowを選択し、xwininfo -id xx によりsizeを取得する。

取得したsizeをximagesrcに指定する。

ref

https://www.hackinglinuxexposed.com/articles/20040608.html

opencv

ximagesrc出力をopencvで受け取り、イメージから、モールスを認識する。

opencvでのイメージの受け取り方。appsinkで受ける。

import cv2 

cap = cv2.VideoCapture("videotestsrc ! videoconvert ! appsink") 

while True:
  ret, img = cap.read() 
  if not ret: 
    break 

  cv2.imshow("",img)

  key = cv2.waitKey(1)
  if key==27: 
    #[esc] key
    break 

ref

https://qiita.com/stnk20/items/242e400853579d511ea3

モールス認識

さて、ココまででイメージをopecvで受け取れる。ここからモールスを認識する。

全体の流れは、

  1. オン/オフ判別
  2. 符号変換、短点、長点、符号間、文字間、語間判別
  3. 文字変換
  4. 語変換
  5. 文変換
  6. 終了判定

オンオフ判別は、イメージ全体の和が閾値以上かどうかで判別。

短点、長点判別は、オン時間で判別。

符号間他の休符は、オフ時間で判別。

時間関係は後で動的処理に変更する事を前提に文内で動的に変わって良い変数とする。

短点の長さをsとすると、符号間はs, 長点は3s, 文字間は3s, 語間は7s, 終了は、終了コードまたはx s 最低x=20s程度とする。

符号変換は、再変換に対応する為、オン・オフ判別データを暫く(x 文程度)保持する。

保持するオン・オフデータは、一定の長さのリングバッファとする。要素は下記のオン・オフとその長さとする。

(オン・オフ、長さ、ch)

オン・オフ判別はタイムアウトを持ち、タイムアウトの場合、オフデータ、あるいはオンデータが続く場合がある。

ch は今後の拡張用で、複数チャネル対応時に使用する。Imageを領域分割、あるいは色で分割した場合等。今は0を入れる。

時刻

モールスの長短点判別には時間がキーとなる。pythonで作るとして、どの時計を使うかを検討する。

  • time.time()
  • time.process_time()
  • time.perf_counter()
  • time.clock()
  • time.monotonic()

とりあえず、5種類ある。環境毎に調べて適切な物を利用する。

ref

https://qiita.com/takeopy/items/170d0e1ddbf02ef9fbb9

オン・オフ判別再び

オン・オフ判別を動体検知で。

import numpy as np

 import cv2
 cap = cv2.VideoCapture('vtest.avi')

 #動体検出器の生成

 fgbg = cv2.createBackgroundSubtractorMOG2() 

while(1): 
    #動画を1フレーム読み込む
    ret, frame = cap.read()

    #「Escが押される」または「動画フレームがない場合」終了 
    k = cv2.waitKey(30) & 0xff
    if k == 27 or not ret: break 

    #動体、背景、影に分ける

    fgmask = fgbg.apply(frame)   

    cv2.imshow('frame',fgmask) 

cap.release() 
cv2.destroyAllWindows()

ref

https://www.tech-tech.xyz/background-subtraction2.html

サイズとintegral

最初にサイズを調べる。

import cv2 
import sys 

if __name__ == "__main__": 

    img = cv2.imread("lena.jpg",    
    cv2.IMREAD_UNCHANGED)

    # 画像ファイルの読み込みに失敗したらエラー終了
    if img is None:
        print("Failed to load image file.") 
        sys.exit(1) 

    # カラーとグレースケールで場合分け
    if len(img.shape) == 3: 
        height, width, channels = img.shape[:3] 
    else: 
        height, width = img.shape[:2]    
        channels = 1 

    # 取得結果(幅,高さ,チャンネル数,depth)を表示 

    print("width: " + str(width))     
    print("height: " + str(height)) 
    print("channels: " + str(channels)) 
    print("dtype: " + str(img.dtype))

次に、integralで総和を求める。integralは元の配列より、w,h共に1大きな配列を返す。つまり、sum(x,y)は、srcの(x0..x-1,y0..y-1)の和を返す。

よって、画素の総和は sum(w,h)の値になる。sum(w-1,h-1)では無いことに注意。 また、チャネル単位の和のため、カラーの場合は、チャネルの総和がイメージの総和となる。

性能が課題になる場合は、見直す。

#-*- coding:utf-8 -*-
import cv2 
import numpy as np 

def main(): 
    # 入力画像を読み込み
    img = cv2.imread("input.jpg") 

    # グレースケール変換
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) 

    # 画素値を表示
    print("gray=\n", gray)

    # 積分画像の作成 
    integral = cv2.integral(gray) 

    # 画素値を表示 
    print("integral=\n", integral) 


if __name__ == "__main__": 
    main()

基本的には、integralの結果を元にオン・オフ判断する。

実際には揺れがある為、閾値等で調整する。

まとめ

オン・オフ判別は、動体検知も良いが、インテグラルでも可能かもしれない。

  1. インテグラ
  2. 閾値確認
  3. 変化検知
  4. データ出力

しかし、閾値が背景により変動するため、動体検知を使い閾値を規格化し、環境影響を排除する方法もある。

送受信のどちらか、あるいは双方が移動している場合、背景も動き、更なる検討の余地がある。

ref

サイズ

https://github.com/atinfinity/lab/wiki/%5BOpenCV-Python%5D%E7%94%BB%E5%83%8F%E3%81%AE%E5%B9%85%E3%80%81%E9%AB%98%E3%81%95%E3%80%81%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E6%95%B0%E3%80%81depth%E5%8F%96%E5%BE%97

integral

https://algorithm.joho.info/programming/python/opencv-integral-image-py/

xterm in tkinter

from Tkinter import *
 import os

root = Tk() 
termf = Frame(root, height=400, width=500) 
termf.pack(fill=BOTH, expand=YES)

wid = termf.winfo_id() 
os.system('xterm -into %d -geometry 40x20 -sb &' % wid)

 root.mainloop()

ref

https://stackoverflow.com/questions/7253448/how-to-embed-a-terminal-in-a-tkinter-application

opencv draw text

http://opencv.jp/cookbook/opencv_drawing.html

ref

https://mattintosh.hatenablog.com/entry/20161003/1475421469