シェルスクリプトマガジン

test

AWKでデジタル信号処理(Vol.81掲載)

投稿日:2022.11.25 | カテゴリー: コード

著者:斉藤 博文

プログラミング言語「AWK」は、データストリーム(データの流れ)を逐次処理するのに適しています。本連載では、電子回路の分野でその特徴を生かし、シェルスクリプトを組み合わせてデジタル信号を処理します。第3回は低周波成分を減らす「ハイパスフィルタ」について解説します。

シェルスクリプトマガジン Vol.81は以下のリンク先でご購入できます。

図7 ハイパスフィルタの移動平均プログラム(hpf.awk)

#! /usr/bin/gawk -f

BEGIN {
    # define number of order
    num_ord = num_ord ? num_ord : 255;

    # define length of ring buffers
    len_data_raw = num_ord + 1;
    len_data_hpf = 2;

    # initialize ring buffers
    for (i = 0; i < len_data_raw; i++) {
        arr_data_raw[i] = 0.0;
    }
    for (i = 0; i < len_data_hpf; i++) {
        arr_data_hpf[i] = 0.0;
    }

    # initialize index of ring buffers
    idx_data_raw = 0;
    idx_data_hpf = 0;

    # initialize number of data
    num_data_raw = 0;
}

{
    # add number of data
    num_data_raw++;

    # update index of ring buffers (write pointers)
    idx_data_raw = num_data_raw % len_data_raw;
    idx_data_hpf = num_data_raw % len_data_hpf;

    # clear number of data
    if (idx_data_raw == 0 && idx_data_hpf == 0) {
        num_data_raw = 0;
    }

    # store input raw data
    val_data_raw = $0;
    arr_data_raw[idx_data_raw] = val_data_raw;

    # apply high pass filter
    arr_data_hpf[idx_data_hpf] = hpf( \
            arr_data_raw, arr_data_hpf,
            idx_data_raw, idx_data_hpf,
            num_ord,
            len_data_raw, len_data_hpf);

    # print results
    print arr_data_hpf[idx_data_hpf];
}

# get value of ring buffer
function get_buffer(arr, idx, len) {
    if (idx < 0) {
        return arr[idx + len];
    }

    return arr[idx];
}

# high pass filter
function hpf(arr_x, arr_y, idx_x, idx_y, ord, len_x, len_y,       _ret, _gain) {
    _ret = 0.0;
    _gain = 1.0 / ord;

    _ret += get_buffer(arr_y, idx_y - 1, len_y);
    _ret -= _gain * get_buffer(arr_x, idx_x, len_x);
    _ret += get_buffer(arr_x, idx_x - int((ord - 1) / 2), len_x);
    _ret -= get_buffer(arr_x, idx_x - int((ord + 1) / 2), len_x);
    _ret += _gain * get_buffer(arr_x, idx_x - ord, len_x);

    return _ret;
}

図12 群遅延を補正したプログラム(hpf_gd.awk)

#! /usr/bin/gawk -f

BEGIN {
    # define number of order
    num_ord = num_ord ? num_ord : 255;

    # define group delay
    val_gd = int((num_ord - 1) / 2);

    # define length of ring buffers
    len_data_raw = num_ord + 1;
    len_data_hpf = val_gd + 1;

    # initialize ring buffers
    for (i = 0; i < len_data_raw; i++) {
        arr_data_raw[i] = 0.0;
    }
    for (i = 0; i < len_data_hpf; i++) {
        arr_data_hpf[i] = 0.0;
    }

    # initialize index of ring buffers
    idx_data_raw = 0;
    idx_data_hpf = 0;

    # initialize number of data
    num_data_raw = 0;
}

{
    # add number of data
    num_data_raw++;

    # update index of ring buffers (write pointers)
    idx_data_raw = num_data_raw % len_data_raw;
    idx_data_hpf = num_data_raw % len_data_hpf;

    # clear number of data
    if (idx_data_raw == 0 && idx_data_hpf == 0) {
        num_data_raw = 0;
    }

    # store input raw data
    val_data_raw = $0;
    arr_data_raw[idx_data_raw] = val_data_raw;

    # apply high pass filter
    arr_data_hpf[idx_data_hpf] = hpf( \
            arr_data_raw, arr_data_hpf,
            idx_data_raw, idx_data_hpf,
            num_ord,
            len_data_raw, len_data_hpf);

    # print results
    print get_buffer(arr_data_raw, idx_data_raw - val_gd, len_data_raw), arr_data_hpf[idx_data_hpf];
}

# get value of ring buffer
function get_buffer(arr, idx, len) {
    if (idx < 0) {
        return arr[idx + len];
    }

    return arr[idx];
}

# high pass filter
function hpf(arr_x, arr_y, idx_x, idx_y, ord, len_x, len_y,       _ret, _gain) {
    _ret = 0.0;
    _gain = 1.0 / ord;

    _ret += get_buffer(arr_y, idx_y - 1, len_y);
    _ret -= _gain * get_buffer(arr_x, idx_x, len_x);
    _ret += get_buffer(arr_x, idx_x - int((ord - 1) / 2), len_x);
    _ret -= get_buffer(arr_x, idx_x - int((ord + 1) / 2), len_x);
    _ret += _gain * get_buffer(arr_x, idx_x - ord, len_x);

    return _ret;
}

香川大学SLPからお届け!(Vol.81掲載)

投稿日:2022.11.25 | カテゴリー: コード

著者:永田 歩

今回は、米Google社が提供するメディアデータ向けの機械学習ライブラリ「MediaPiPe」を用いて、動画内にある人の顔を検出し、そのデータを基に3D CGアニメーションを生成するプログラムを紹介します。AIを3D CGに利用する試みの一つとして参考にしてください。

シェルスクリプトマガジン Vol.81は以下のリンク先でご購入できます。

図1 動画をフレームに分割するプログラム「video2img.py」のコード

import cv2 as cv
import os

video_path = './video/test_video.mp4'
dir_path = './image'
basename = 'img_frame'
ext = 'jpg'
# 動画を読み込んで存在しなければ終了
cap = cv.VideoCapture(video_path)
if not cap.isOpened():
  exit()
os.makedirs(dir_path, exist_ok=True)
base_path = os.path.join(dir_path, basename)
# 画像保存名のためにフレームの桁数をカウント
digit = len(str(int(cap.get(cv.CAP_PROP_FRAME_COUNT))))
n = 0
while True:
  ret, frame = cap.read()
  if ret:
    # 画像の保存
    cv.imwrite('{}_{}.{}'.format(base_path, str(n).zfill(digit), ext), frame)
    n += 1
  else:
    exit()

図2 顔のランドマーク座標を出力するコード「images2npy.py」のメイン部分

import cv2 as cv
import mediapipe as mp
import os
import glob
import numpy as np

# 使用する画像のパス名を取得してリスト化
resource_dir = r'./image'
file_list = glob.glob(os.path.join(resource_dir, "*.jpg"))
xyzval = []
for file_path in file_list:
  # 画像を読み込む
  image = cv.imread(file_path)
  xyzval.append(get_landmark(image, file_path))
# 3次元numpy配列のバイナリファイルとして保存
np.save('result/np_save', xyzval)

図3 get_landmark()関数の定義コード

def get_landmark(image, file_path):
  # 顔のランドマークを取得する準備
  mp_face_mesh = mp.solutions.face_mesh
  face_mesh = mp_face_mesh.FaceMesh(
    static_image_mode=True,
    max_num_faces=1,
    min_detection_confidence=0.5)
  rgb_image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
  # ランドマーク検出
  results = face_mesh.process(rgb_image)
  # 顔が検出されなければ終了
  if not results.multi_face_landmarks:
    exit()
  face_landmarks = results.multi_face_landmarks[0]
  file_path = os.path.splitext(file_path)[0]
  file_name = file_path.split('/')[-1]
  # 保存先のフォルダ作成
  result_txt_dir = 'result/txt'
  os.makedirs(result_txt_dir, exist_ok=True)
  txtfile_path = result_txt_dir+'/'+file_name+'.txt'
  # テキストファイルにRawデータを書き込む
  f = open(txtfile_path, 'w')
  f.write(str(face_landmarks))
  f.close()
  make_landmarkimg(image, face_landmarks,
                   file_name, mp_face_mesh)
  face_mesh.close()
  return get_original_scalexyz(image, txtfile_path)

図5 get_original_scalexyz()関数の定義コード

def get_original_scalexyz(image, txtfile_path):
  height, width = image.shape[:2]
  vers = []
  i = 0
  with open(txtfile_path) as ft:
    lines = ft.read()
    for l in lines.split("\n"):
      if i%5 == 0 or i%5 == 4:
        i += 1
        continue
      elif i%5 == 1:
        tmp = l.split()  
        x = f'{float(tmp[1]) *width:.5f}'
      elif i%5 == 2:
        tmp = l.split()
        y = f'{float(tmp[1]) *height:.5f}'
      else:
        tmp = l.split()
        z = f'{float(tmp[1]) *width:.5f}'
        vers.append([float(x), float(y), float(z)])
      i += 1
  return vers

図6 make_landmarkimg()関数の定義コード

def make_landmarkimg(image, face_landmarks, file_name, mp_face_mesh):
  result_image_dir = 'result/image'
  os.makedirs(result_image_dir, exist_ok=True)
  # 顔のランドマークを画像上に出力する準備
  mp_drawing = mp.solutions.drawing_utils
  drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)
  # 元画像のコピーを作成
  annotated_image = image.copy()
  # 顔のランドマークを描画した画像を生成
  mp_drawing.draw_landmarks(
    image=annotated_image,
    landmark_list=face_landmarks,
    connections=mp_face_mesh.FACEMESH_TESSELATION,
    landmark_drawing_spec=drawing_spec,
    connection_drawing_spec=drawing_spec)
  cv.imwrite(result_image_dir+'/'+file_name+'.png', annotated_image)

図8 3D CGアニメーションを作成するコード「npy2blender.py」のメイン部分

import numpy as np
import bpy
filepath = r'3次元numpy配列のバイナリファイルのパス名'
# 例 → r'C:/Users/user1/python/shellmag/result/np_save.npy'
xyzval = np.load(filepath)
ver_count = len(xyzval[0])
frame_count = len(xyzval)
generate_ver(ver_count)
move_ver_anyframe(xyzval, frame_count)

図9 generate_ver()関数の定義コード

def generate_ver(ver_count):
  # 点群オブジェクトの生成
  bpy.data.meshes.new(name='shellmag_Mesh')
  bpy.data.objects.new(name='shellmag_Obj',
                       object_data=bpy.data.meshes['shellmag_Mesh'])
  bpy.context.scene.collection.objects.link(bpy.data.objects['shellmag_Obj'])
  Obj = bpy.data.objects['shellmag_Obj'].data
  Obj.vertices.add(ver_count)    
  return

図10 move_ver_anyframe()関数の定義コード

def move_ver_anyframe(xyzval, frame_count):
  ob = bpy.data.objects['shellmag_Obj'].data
  for frame_num in range(frame_count):
    i = 0
    # XYZ座標を各点ごとに定義してフレームとして保存
    for p in xyzval[frame_num]:
      X = p[0]
      Y = p[1]
      Z = p[2]
      ver = ob.vertices[i]
      ver.co.x = X
      ver.co.y = Y
      ver.co.z = Z
      i += 1
      ver.keyframe_insert('co',index = -1,frame = frame_num) 
  return

Pythonあれこれ(Vol.81掲載)

投稿日:2022.11.25 | カテゴリー: コード

筆者:飯尾 淳

本連載では「Pythonを昔から使っているものの、それほど使いこなしてはいない」という筆者が、いろいろな日常業務をPythonで処理することで、立派な「蛇使い」に育つことを目指します。その過程を温
かく見守ってください。皆さんと共に勉強していきましょう。第11回では、永続的な非同期通信を簡単に実現できる「WebSocket」というプロトコルを利用するシンプルなチャットアプリの作成に挑戦しま
す。

シェルスクリプトマガジン Vol.81は以下のリンク先でご購入できます。

図1 エコーサーバーを実現するコード

import asyncio
import websockets

async def echo(soc):
  async for msg in soc:
    print(f'RECV: {msg}')
    await soc.send(msg)

async def main():
  async with websockets.serve(echo, port=8765, ping_timeout=None):
    await asyncio.Future()

asyncio.run(main())

図2 エコーサーバーに接続するクライアントのコード

import asyncio
import websockets

async def client():
  uri = "ws://localhost:8765/"
  async with websockets.connect(uri, ping_timeout=None) as soc:
    while True:
      msg = input('> ')
      await soc.send(msg)
      msg = await soc.recv()
      print(f'< {msg}')

asyncio.run(client())

図4 チャットサーバーのコード

import asyncio
import websockets

clients = {}

async def echo(soc):
  async for msg in soc:
    if not (soc in clients):
      clients[soc] = msg
      print(f'{msg} is registered')
      for s in clients:
        await s.send(f'{clients[soc]} が参加しました')
    else:
      print(f'RECV: {msg}')
      for s in clients:
        await s.send(f'{clients[soc]}: {msg}')

async def main():
  async with websockets.serve(echo, port=8765, ping_timeout=None):
    await asyncio.Future()

asyncio.run(main())

図5 チャットクライアントのHTMLコード

<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>A Simple BBS</title>
  <meta name="description" content="WebSockets TEST">
  <meta name="author" content="Jun IIO">
  <script src="jquery-3.6.1.min.js"></script>
  <script src="scripts.js"></script>
</head>
<body>
  <h1>シンプル・チャット・システム</h1>
  <div>
    名前:
    <input id="regname" type="text">
    <button id="register">登録</button>
  </div>
  <div>
    <textarea id="msgarea" cols=80 rows=10
              readonly disabled="disabled"></textarea>
  </div>
  <div>
    <input id="chatmsg" type="text" size=80 disabled="disabled">
    <button id="send" disabled="disabled">送信</button>
  </div>
</body>
</html>

図6 チャットクライアントのJavaScriptコード

$(function(){
  let socket = new WebSocket("ws://localhost:8765/");

  $("#register").on("click", function(event) {
    name = $("#regname").val();
    if (name != "") {
      socket.send(name);
      $("#register").prop("disabled", true);
      $("#regname").prop("disabled", true);
      $("#msgarea").prop("disabled", false);
      $("#chatmsg").prop("disabled", false);
      $("#send").prop("disabled", false);
    };
  });

  socket.onmessage = function(event) {
    text = $("#msgarea").val();
    $("#msgarea").val(text + event.data + "\n");
  };

  $("#send").on("click", function(event) {
    msg = $("#chatmsg").val();
    if (msg != "") {
      socket.send(msg);
      $("#chatmsg").val("");
    };
  });
});

Raspberry Piを100%活用しよう(Vol.81掲載)

投稿日:2022.11.25 | カテゴリー: コード

著者:米田 聡

小型コンピュータボード「Raspberry Pi」(ラズパイ)向けにさまざまな拡張ボードが発売されています。その拡張ボードとラズパイを組み合わせれば、ラズパイでいろいろなことが簡単に試せます。第14回は、高品質なアナログ音声を出力する拡張基板を扱います。

シェルスクリプトマガジン Vol.81は以下のリンク先でご購入できます。

図7 MP3形式の音楽ファイルを再生するPythonプログラム(player.py)

import time
from pydub import AudioSegment
from pydub.playback import _play_with_simpleaudio

audio = AudioSegment.from_file("sample.mp3", "mp3")
play = _play_with_simpleaudio(audio)

try:
    while True:
        time.sleep(1)

except KeyboardInterrupt:
    pass

play.stop()

特集1 本格的なホームサーバーを構築(Vol.81記載)

投稿日:2022.11.25 | カテゴリー: コード

著者:麻生 二郎

小型コンピュータボードの最上位モデルである「Raspberry Pi 4 Model B」の4G/8Gバイト版と、人気のLinuxディストリビューション「Ubuntu」のサーバー版を組み合わせて、本格的なサーバーを構築しましょう。本特集では、サーバーの可用性や信頼性を向上する方法を紹介します。

シェルスクリプトマガジン Vol.81は以下のリンク先でご購入できます。

図A3 ラズパイサーバーの初期設定(ubuntu_init1.sh)

#!/bin/sh

##日本のタイムゾーン設定
sudo timedatectl set-timezone Asia/Tokyo

##全ソフトウエア更新
sudo apt update
sudo apt -y upgrade
sudo apt -y autoremove
sudo apt clean

##ファームウエアアップデート
sudo apt -y install rpi-eeprom
sudo rpi-eeprom-update

##完了後の再起動
read -p "再起動しますか [y/N]:" YN
if [ " $YN" = " y" ] || [ " $YN" = " Y" ]; then
  sudo reboot
fi

図A4 ラズパイサーバーの初期設定(ubuntu_init2.sh)

#!/bin/sh

##固定IPアドレスとルーターのIPアドレス
IP_ADDRESS="192.168.10.100"
ROUTER_IP="192.168.10.1"

##旧設定バックアップ
mkdir -p ~/old_settings
sudo mv /etc/netplan/50-cloud-init.yaml ~/old_settings/.

##新ネットワーク設定作成
cat << EOF | sudo tee /etc/netplan/50-cloud-init.yaml > /dev/null
network:
  ethernets:
    eth0:
      dhcp4: false
      addresses: [ip_address/24]
      gateway4: router_ip
      nameservers:
        addresses: [8.8.8.8]
  version: 2
EOF
sudo sed -i -e "s%ip_address%$IP_ADDRESS%" /etc/netplan/50-cloud-init.yaml
sudo sed -i -e "s%router_ip%$ROUTER_ip%" /etc/netplan/50-cloud-init.yaml

##ネットワーク設定反映
sudo netplan apply

シェルスクリプトマガジンvol.81 Web掲載記事まとめ

投稿日:2022.11.25 | カテゴリー: コード

004 レポート Windows 11が動くVirtualBox
005 レポート Webブラウザ動作のPostgreSQL
006 製品レビュー 玩具「coemo(コエモ)」
007 NEWS FLASH
008 特集1 本格的なホームサーバーを構築/麻生二郎 コード掲載
018 特集2 Google AppSheet入門/伊藤勇斗、石野耀久、江口隆司
038 特集3 さくらのクラウド/前佛雅人
050 特別企画 MySQL HeatWave Database Serviceの新機能/生駒眞知子
057 Hello Nogyo!
058 Raspberry Piを100%活用しよう/米田聡 コード掲載
062 Pythonあれこれ/飯尾淳 コード掲載
066 法林浩之のFIGHTING TALKS/法林浩之
068 中小企業手作りIT化奮戦記/菅雄一
074 メタバース/桑原滝弥、イケヤシロウ
076 タイ語から分かる現地生活/つじみき
080 香川大学SLPからお届け!/永田歩 コード掲載
086 AWKでデジタル信号処理/斉藤博文 コード掲載
094 ユニケージ通信/田渕智也、高橋未来哉
098 Bash入門/大津真
104 Techパズル/gori.sh
105 コラム「ユニケージの学術論文が出ました」/シェル魔人

Vol.81

投稿日:2022.11.25 | カテゴリー: バックナンバー

 ホームサーバーも本格的な運用を始めるなら、常時動き続けるための「可用性」と、データなどの保護を目的とした「信頼性」の向上は不可欠です。特集1では、小型コンピュータボードの「Raspberry Pi 4 Model B」で構築したホームサーバーの可用性や信頼性を高める方法を解説しています。
 特集2では、注目のノーコード開発ツール「Google AppSheet」を紹介しています。Google AppSheetでは、とても簡単な操作で、Webブラウザ、スマートフォンやタブレットなどからアクセスできるWebアプリケーションを開発できます。。また、ローコード開発ツールの「Google Apps Script」で作成したアプリケーションと組み合わせることで、クラウド上のさまざまなシステムと連携したシステムの構築が可能です。
 特集3では、国産パブリッククラウドサービス「さくらのクラウド」を紹介しています。AWSやGCP、Azureなど、海外の企業のクラウドサービスが人気ですが、国産クラウドにも良い点が多数あります。特に、日本人が運用して管理している点で、日本人に適したクラウドサービスになっています。
 特別企画では、米Oracle社が提供するデータベースクラウドサービス「MySQL HeatWave Database Service」(旧名は「MySQL Database Service」)を紹介しました。機械学習による「AutoPilot」や「HeatWave ML」が追加されて、さらに大規模なOLTPやOLAPの処理をより効率良くできるようになった点を詳しく解説しています。
 このほか、業務システムの開発手法「ユニケージ」の連載が開始しました。ユニケージは、エンジニアが生み出した手法ではなく、業務という現場から生まれた手法です。そのため、エンジニアには理解しにくい部分もあります。この連載では、エンジニアが疑問に感じる点に焦点を当てながらユニケージ流のやり方を紹介していきます。
 今回も読み応え十分のシェルスクリプトマガジン Vol.81。お見逃しなく!

※記事掲載のコードはこちら。記事の補足情報はこちら

※読者アンケートはこちら

Vol.81 補足情報

投稿日:2022.11.25 | カテゴリー: コード

執筆・編集後記

p.103の執筆・編集後記のページが広告になっていました。以下が掲載する予定だった執筆・編集後記です。Kindle版やPDF版は差し替えています。お詫びして訂正いたします。

情報は随時更新致します。

  • shell-mag ブログの 2022年11月 のアーカイブを表示しています。

  • -->