2018年09月30日

映画鑑賞 夏目友人帳 うつせみに結ぶ

「劇場版 夏目友人帳〜うつせみに結ぶ〜」を鑑賞。

公開初日の初回上映で舞台挨拶のライブビューイング付き。300席のスクリーンで空席60%くらい。客層は9割がた女性だったかな。LaLaの読者層を考えるとそんなものかも。

LaLaの読者というわけでもないし,SFでもないけれど,ニャンコ先生がかわいいのでアニメだけは録画して観ている。

TV版ではけっこう泣かせにくるエピソードが多いので劇場版も警戒していたけれど,ほのぼの路線であからさまな泣かせ・感動演出がなく,その点は良かったと思う。TV版含めて気に入っている点は,キーキャラクターである祖母,夏目レイコについて,友人帳の名前を返す際に過去の記憶が断片的にわかるだけで,あくまでも「過去の人」として描かれるところ。安直には「時を超えて声が伝わる」とかやりがちだけど,それをしないところが良い。

分裂小型ニャンコ先生は客寄せ的には必要だったのかもしれないけれど,ちょっとあざといというか,必然性がないというか,もっと自信を持ってストーリーで魅せてくれたら良かったのでは。

妖怪について。ファンタジーだとわかってはいても,つい考えてしまう。妖怪が見えない(一般の)人間には,妖怪が目の前にいても素通しで,背後の景色が歪むこともないし,触れることもできない。これではさすがに物理的実体があると考えるのは厳しいので,合理的解釈としては夏目(見える人)の脳内で作り出された幻覚...ということになるかな。

百歩譲って光学的に不可視の「霊的存在」を認めたとして,彼らはどういう存在なのか。一見人間のような見た目の妖怪(柊とか)もいれば,動物や昆虫が妖怪化したもの,植物(古木とか)の妖怪,信仰心などの思念が集まったものなど様々。一貫して個体寿命が極めて長いという描写があるので,繁殖したり群れたりしないというのはまあ合理的な描写かも。ただ妖怪の中にも明らかに序列や力の強弱,凶暴な個体などがある割に,知性のある弱い妖怪たちが団結したり文明化していないのはなぜなのか。そもそも妖怪は知性をどうやって獲得するのか。人間の学校に忍び込んだりして学ぶのか?人語を解することからその可能性はあるし,明らかに頭の悪そうな妖怪も居るのでそれらは勉強が足りなかったのだろう。しかし「生まれたて」の妖怪の場合はどうする。親子関係のある妖怪(子ギツネとか)はいいとして,それ以外はやはり何らかの「妖怪保育所」的な組織があるのか。妖怪が最低限の社会性を獲得するための組織的仕組みが地域ごとにあると見るべきかもしれない。

ところで「うつせみ」ってどういうニュアンスで捉えれば良いんですかね。英訳できますか?

IMG_4926_w560.JPG

参考サイト:劇場版 夏目友人帳 公式サイト
タグ:アニメ
posted by ソウヘイ at 14:59| Comment(0) | 映画

2018年09月22日

SF本読了 ダスト

「ダスト(上・下)」(H.ハウイー)を読了。Kindle版。

ウール3部作の完結編。面白かった...けどウール,シフト,ダストと,これでもか,これでもかと苦難の連続で,最後...は希望が見えてまあ良かったとはいえ,犠牲も大きかったし,ヘヴィだったかな。

ツイッターで話題になっている「七人のイヴ」3部作も,いくら面白いと言われても「月が七つに分裂した!人類滅亡の危機...」と書かれてしまうと,辛い場面しか思い浮かばず「ちょっとしんどそうだな...」と思ってなかなかポチれずにいる。こういうのは完全に若い頃より苦手になった。座右の書イーガンの「ディアスポラ」でも,ガンマ線バーストから肉滅の場面だけは苦手。

というわけで今回の早川のセールではあまり辛くなさそうな作品を4点ほどポチっておいた。現実は仕事など辛いことも多いので,小説の中では楽しい場面が多い方が良い。

サイロの電源について。「発電機」は最下層にあって,地下から石油を組み上げて燃料としている,という記述があるので,ディーゼルエンジン発電機かガソリンエンジン発電機かと思う。これは大規模な石油火力発電(汽力発電)ではなくて,内燃機関の動力で発電機を回す方式。検索すると常用ディーゼル発電機は6MWクラスで重量150tくらいの製品があるようだ。これ1台でサイロの生活レベルなら5000世帯分くらいは賄えそう。地下の燃料は油田の上にサイロを作って,軽油などを蒸留している...と考えるよりは,備蓄タンクを底に埋めておいたとする方が自然かな。いずれにしろ予備部品だけで数百年間機械を稼動状態で維持するのは大変じゃないかな。缶詰は100年くらいは保った例があるらしいけど。...とまあ,想像力を掻き立てられるシチュエーションは,すごく魅力的な作品であった。

posted by ソウヘイ at 22:29| Comment(0) | 読書

2018年09月17日

アマチュア無線(XPO記念コンテスト2018)

XPO記念コンテスト(規約)への参加は7回目。JARLの主要コンテスト以外では珍しくコンスタントに参加している。

第2常置場所で登録してある固定機のFT-1021Xをたまには使ってあげようと,今回は第2固定から参加。エレメントを自作したダイポールアンテナも,経年で圧着部分がちぎれたりしており,たまに確認が必要。

幸いFT-1021Xの調子は問題なく,CWのセミブレークイン,フルブレークインイともに正常。ただフルブレークインは相変わらず苦手。




期待した14MHzは残念ながらまったく聞こえず。一瞬6エリアがカスカスに入ってきたけれど,交信には至らず。7MHzはそこそこ聞こえるものの,バンド内びっしり,という感じには程遠いコンディション。10分ほどで呼べる局は全部呼んでしまえた。

7.031MHzあたりでCQを出してみたら,8エリア(北海道)からコールあり。でもその1局だけ。

今回はリグとアンテナの動作確認が目的だったので,あまり頑張らずに1.5時間ほどで撤収。15局と交信できた。




Freq. Point  Multi  QSO
----------------------------------------
7MHz 15 9 15
----------------------------------------
Total 15 9
Score 15 x 9 = 135

マルチ
7MHz: 三重,大阪,奈良,兵庫,和歌山,京都,香川,広島,後志


【参考リンク】
過去のXPO記念コンテストの様子:2017年2016年2015年2014年2013年2012年
posted by ソウヘイ at 21:24| Comment(0) | アマチュア無線

2018年09月15日

夏の工作 Raspberry Pi (Twitterのメンションに反応して動作する)

Twitterで部屋の環境をつぶやくbotと,留守番猫カメラができたので,これらを組み合わせて,Twitter経由でカメラの制御ができるようにする。

Twitterでbot宛のメンションを監視して,「カメラ」「留守番」等のコマンドを検出したら,特定の動作を行うようにしたい。

メンションにリプライを返すbotはいろいろとネットに情報があって例えば,

参考サイト:TweepyとStreaming APIで自動リプライbotをつくる

などを参考にやってみたのだが,どうも動いたり動かなかったりする。変だな変だなと思っていたら,実はTwitterのAPIの仕様が2018年8月で変更になって,上記で使われているStreaming APIが廃止になってしまったらしい。

代わりにTweepy(pythonのライブラリ)で,api.mentions_timelineを使うことで,自分宛のメンションのタイムラインを取得できる。これをcronで定期的に実行するようにして,自分宛の新しいメンションがあったら,その内容をチェックして,「カメラ」,「留守番」等のキーワードが含まれていたら,所定の動作(留守番カメラの起動,写真撮影など)をするスクリプトを書いた。

参考サイト:[(元)情報学科大学生のブログ] Tweepyを使ってTwitterのbotを作る

cronの実行周期を1分にしているので,botに対して@付きでつぶやいてからレスポンスがあるまで最長1分くらい時間がかかるけれど,瞬時に応答が必要な機能でもないので,特に問題は感じない。




注意点としては,自分宛のメンションのうち,特定のユーザID(自分)からのメンションにだけ反応するようにしておくこと。そうしないと知らない人が留守番猫カメラのON/OFFを制御できたりしてしまう。
タグ:Raspberry_Pi
posted by ソウヘイ at 19:24| Comment(0) | パソコン・インターネット

2018年09月11日

夏の工作 Raspberry Pi (欧文/和文モールス音源生成スクリプト)

留守番猫カメラからの続き。

せっかくのラズパイを何か無線関係に使うことはできないだろうか。D-starのノード局にして使う,というのは聞いたことがあるけれど,D-starが使えるリグもないし,そもそもphoneはあまり興味がない。

せっかくならCW関係の何か...といってもすぐ思いつくエレキー/メモリキーヤーなんかはありきたりだし,すでに電池駆動で使えるメモリキーヤーを持っているので,別に必要性もない。

そこで,ちょうど和文や欧文の聞き取り練習をしているところでもあるので,日本語の普通文をCWの音声ファイルに変換するスクリプトはどうだろう。

Windows用にA1A Breakerというすばらしいフリーソフトがあるけれど、確か日本語の平文(漢字仮名交じり文)を直接変換することはできなかったのでは。

というような思考を経て、次のような仕様を考えた。
1 欧文と和文を自動判定
2 和文の場合、漢字仮名交じり文をカナに変換
3 CWの音声ファイルを生成

1はまあ含まれている文字から簡単に判別できそう。
3も文字に対応した符号を生成して、sin波の入切で音声データを作るのはなんとかなりそう。

やっかいなのは2で、漢字仮名交じり文の読み仮名を解析してカナに変換する方法を考えなくてはいけない。

自力でそんなコードを書くのはほとんど無理だけど、世の中には親切な人がいるもので、MeCabというオープンソースの形態素解析エンジンがある。これは日本語の文章を単語に区切り、品詞を同定する処理をしてくれるもので、そのついでに読み仮名も出してくれる。

で、これを使って1、2、3それぞれの機能ごとに少しずつコードを書いていった。




この辺からPythonの日本語処理でハマることが多くなる。まずstrとunicodeと文字コードがごちゃごちゃになってよくわからない。出てくるエラーを見ながらググってはエラーを出し,print文を仕込んでordやtypeやencode文を使って確認しつつ,辻褄を合わせるデバッグ作業が続いた。

文字コード(ASCIIコード,UTF-8コード)に対応したモールス符号の生成(半角"ア" -> UTF-8"FF67 -> モールス符号"11011")は以下のサイトを参考にした。欧文は普通にASCIIコードで変換できる(ord("A")=65)んだけど,半角カナはord("ア")=177とはならず,ord("ア")=65393(FF71)で,これはUTF-8コード。この辺も最初はよくわからずハマった。

参考サイト:JH7UBCブログ ラズパイ Python モールス符号練習機

MeCabの読み変換はもちろん完璧ではなく,「大喜び」を「だいよろこび」と変換するなど,細かいミスはあるものの,一般的な文章なら相当精度良く変換できる。これは本当に素晴らしいなぁ。

和文符号だと濁点や半濁点を分離しないといけない(「ザ」は「サ゛」にする)必要があり,このために全角カナを半角カナにさらに変換。半角カナには濁点・半濁点付きの文字がないので,強制的に分離される。




sin波をつなげて音声を作っていくのは,以下のサイトを参考にした。信号ありをレベル1,無音部分を0として,ドットを基準の長さにして,ダッシュは3倍の長さ,というようにつなげていく。

参考サイト:[python] sin波の音をWAV形式で出力する

サンプリング周波数は最初サンプルと同じくCD音質の44100Hzとしていたけれど,これだと2分を超えるような符号列だと変換途中にメモリーエラーで落ちてしまうことが判明。よく考えたらCWのトーン周波数は700Hzなので,標本化定理に従えば1400Hzで充分再現できるはず。一応さらに倍の2800Hzとした。これで15分以上の長い文章でも一気に変換できるようになったと思う。

まだまだおかしいところはあると思うけれど,現状のコードを載せておく。いろいろなライブラリを読み込んでいるので,importに書かれているライブラリを事前にインストールしておく必要がある。

速度は変数[sec]に短点の秒数で指定。現状だと総通試験の和文の速度(1分間75字程度)になっていると思う。適宜調整してほしい。総通の試験で出ないと思われる符号はバッサリ削除している。音源はそのままでは完成度が低くて使えないかもしれないので,カナ変換した時点で生成されるout.txtをA1A Breakerなりに読み込ませて使うのも良いと思う。

実行方法は下記。引数に変換元のテキストファイルを入れておく。
python txt2cw.py momotaro.txt


ソースコード txt2cw.py
※「動きません」とか質問されても,当方もラズパイ,Pythonビギナーなのでお答えできない可能性が高いです。アドバイスは歓迎いたします。

#!/usr/bin/env python
# coding: utf-8

import numpy as np
import wave
import struct
import pydub
import sys
import MeCab
import mojimoji
import subprocess
import re
import codecs
import jaconv
import textwrap
import gc

#文字コードーモールス符号の変換テーブル
Morse_Code =[63,62,60,56,48,32,33,35,39,47,1,1,1,1,1,1,1,6,17,21,9,2,20,
11,16,4,30,13,18,7,5,15,22,27,10,8,3,12,24,14,25,29,19]
Wabun_Code =[30,59,6,12,61,34,14,57,7,22,54,59,6,12,61,34,18,37,24,29,31, #ヲ〜コ
53,43,55,46,23,5,20,22,58,36,10,21,16,27,28,17,51,19,2,9, #サ〜ホ
25,52,3,49,41,14,57,7,8,11,45,15,26,13,42,4,44, #マ〜ン
109,82,121,40,74,106] # ()ホレ ラタ 段落 区切り点

m = MeCab.Tagger('-Oyomi')

# 原文の読込み(第1引数にファイル名を指定)
f = codecs.open(sys.argv[1], 'r', 'utf-8')
lines = f.readlines()
f.close
bunsho = '\n'.join(lines)
print('*** 原文 ***')
print(bunsho)

# 試験で出ない符号は削除,またはスペースに置換する
bunsho = re.sub('[&%#+*/@$=],', '', bunsho)
bunsho = re.sub('[-\'\",.()]', ' ', bunsho)

#複数の連続するスペースは一つにする
bunsho = re.sub(r"\s+", " ", bunsho)

yomi = m.parse(bunsho.encode('utf-8'))
print('*** MeCab 読み仮名取得***')
print(yomi)
#一般名詞のひらがなで残ったものをカタカナにする
yomi2 = jaconv.hira2hkata(unicode(yomi,'utf-8'))
print('*** jaconv ひらがなtoカタカナ***')
print(yomi2)

#A1A Breaker用にテキストファイルを出力
with open('./out.txt', mode='w') as f:
s_wrap_list = textwrap.wrap(yomi2,20)
f.write('\n'.join(s_wrap_list))

han = mojimoji.zen_to_han(yomi2.decode('utf-8'))
han = han[:len(han)-1]
print('*** mojimoji 全角to半角 ***')
print(han)

message=han
message_len=len(message)

#不要な変数を削除してメモリ解放
del lines
del bunsho
del yomi
del yomi2
del han
gc.collect()

A = 1 # 振幅
fs = 2800 #サンプリング周波数
f0 = 700 #基本周波数 CW信号のトーン
sec = 0.055 #秒 短点の基準

cw_tone = []
# アルファベットは全て大文字に変換
message = message.upper()

# 欧文/和文の判定
#regexp = re.compile(r'[^\x20-\x7E]')
regexp = re.compile(r'(?:\xEF\xBD[\xA1-\xBF]|\xEF\xBE[\x80-\x9F])')
result = regexp.search(message.encode('utf-8'))
if result != None:
wabun = 1 # 和文のときは1 欧文は0
else:
wabun = 0

print(wabun)

if wabun ==1:
message = u'<' + message + u'>' # 和文の場合はホレ,ラタを追加

#和文中のアルファベットは削除(総通の問題には出ないので)
message = re.sub(r'[a-zA-Z]+', r'', message)
message_len=len(message)

#和文中の試験に出ない符号は削除する
message = re.sub('[?!()]', '', message)
message_len=len(message)
print(message)

#sin波
def create_wave(A,f0,fs,t): #A:振幅,f0:基本周波数,fs:サンプリング周波数,再生時間[s]
#nポイント
point = np.arange(0,fs*t)
sin_wave =A* np.sin(2*np.pi*f0*point/fs)

sin_wave = [int(x * 32767.0) for x in sin_wave] #16bit符号付き整数に変換

return sin_wave

def create_cwfile(data): # wavファイルの作成と書き出し
#バイナリ化
binwave = struct.pack("h" * len(data), *data)

#サイン波をwavファイルとして書き出し
w = wave.Wave_write("cw.wav")
p = (1, 2, fs, len(binwave), 'NONE', 'not compressed')
w.setparams(p)
w.writeframes(binwave)
w.close()
print(len(data))

#mp3に変換
sound = pydub.AudioSegment.from_wav("cw.wav")
sound.export("cw.mp3", format="mp3")

def dot(): #短点
cw_tone.extend(create_wave(A,f0,fs,sec))

def dash(): #長点
cw_tone.extend(create_wave(A,f0,fs,3*sec))

def space(): #文字間隔
cw_tone.extend(create_wave(0,f0,fs,2*sec))

#文字コードを対応するモールス符号に変換
try:
while True:
for i in range(message_len):
char_code = ord(message[i])
print(char_code)
if char_code == 0x20: #space code
space()
elif wabun == 0: # 欧文
Mcode = Morse_Code[char_code - 48]
while Mcode != 1:
mark = Mcode & 1
if mark == 0:
dot()
else:
dash()
cw_tone.extend(create_wave(0,f0,fs,sec)) # 符号間のスペース(1点)
Mcode >>=1
cw_tone.extend(create_wave(0,f0,fs,2*sec)) # 文字間のスペース(3点分)
elif wabun == 1: # 和文
if char_code >= 48 and char_code <= 57: # 数字
Mcode = Morse_Code[char_code - 48]
elif char_code >= 65 and char_code <= 90: # 和文中のアルファベット
Mcode = Morse_Code[char_code - 48]
elif char_code == 60: #ホレ
Mcode =121
elif char_code == 62: #ラタ
Mcode =40
elif char_code == 40 or char_code == 65378: #下向きカッコ
Mcode =109
elif char_code == 41 or char_code == 65379: #上向きカッコ
Mcode =82
elif char_code == 65377: #段落(。)
Mcode =74
elif char_code == 10 or char_code == 13: #単なる改行は無視
Mcode =1
elif char_code == 65380 or char_code == 65381: #区切り点,中黒
Mcode =106
else:
Mcode = Wabun_Code[char_code - 65382]
while Mcode != 1:
mark = Mcode & 1
if mark == 0:
dot()
else:
dash()
cw_tone.extend(create_wave(0,f0,fs,sec)) # 符号間のスペース(1点)
Mcode >>=1
cw_tone.extend(create_wave(0,f0,fs,2*sec)) # 文字間のスペース(3点分)
create_cwfile(cw_tone)
break

except KeyboardInterrupt:
pass


出力されたmp3をffmpegで動画にしたもの。
原文はももたろうの前半部分。
posted by ソウヘイ at 20:34| Comment(0) | パソコン・インターネット