Python で正規表現を使うときのエラー対策

正規表現を使うときは,正規表現のエラーチェックをきちんと実装しておいたほうが安全です

例外を使って実装すると次のようになります

import re

pattern = "^c++"
prog = None
try:
    prog = re.compile(pattern)
except re.error as msg:
    print("error in regex /%s/; %s" % (pattern, str(msg)))

行頭の c++ にマッチさせたいので正規表現として ^c++ と書いてありますが,これは正しくありません.

上記サンプルコードを実行すると,例外が発生して,エラーメッセージが表示されます.

error in regex /^c++/: multiple repeat at position 3

"multiple repeat"とあるように ++ が連続しているのは正規表現として正しくありません

上記の例だと c++ にマッチさせたいので ^c\+\+ と訂正する必要があります

Python でインストール済みのdebianパッケージを調べる方法

debianubuntu でインストール済みのdebパッケージの一覧を調べる方法です

(pythonのモジュールの一覧ではなくて,linuxdebパッケージの一覧です)

コマンドラインなら

$ dpkg -l

等になりますが、似たことをpythonで実装する場合は apt モジュールを使います

サンプルコード

import apt

cache = apt.Cache()

for pkg in cache:
     if not pkg.is_installed:
          continue

     name = pkg.name
     version = None
     for v in pkg.versions:
            if v.is_installed:
                 version = v
     print(name, version)

aptモジュールがインストールされていない場合は

$ sudo apt install python3-apt

でインストールできます

ワンライナーで動画編集 (ffmpeg の使い方一覧)

動画編集に便利な ffmpeg でよく使うワンライナーをまとめます.このエントリは今後も随時更新する予定です.

ffmpeg の基本

動画を MP4形式に変換する
$ ffmpeg -i 入力動画のファイル名   出力動画.mp4
  • 入力は "-i 入力ファイル名"で指定します.ファイル形式は自動判別されます
  • 出力はファイル名を指定します.出力フォーマットは,拡張子で自動判別されます
動画から音声だけ抽出
$ ffmpeg -i 入力動画のファイル名    出力音声ファイル.wav

wav とか mp3 のような拡張子を指定するだけで,音声だけが抽出できます.

映像と音声を合成する
$ ffmpeg -i video.mp4 -i audio.mp3  output.mp4
音声を差し替える

video.mp4 の音声を, audio.mp3 で差し替えます.

$ ffmpeg -i video.mp4 -i audio.mp3  -map [0:v] -map [1:a] output.mp4

"-map [0:v]" は 入力の0番目,つまりvideo.mp4 の映像(v)データを,出力 output.mp4 に割り当てるという意味です.

"-map [1:a]" は 入力の1番目,つまりaudio.mp3 の音声(a)データを,出力 output.mp4 に割り当てるという意味です.

これで video.mp4 (つまり [0:a])は無視されたファイルが出来上がります

音声と動画のタイミングを調整する

オプション -itsoffset を使います

$ ffmpeg -i video.mp4 -itsoffset ADELAY -i audio.mp3 -map [0:v] -map [1:a] output.mp4

音声audio.mp3をADELAY秒遅らせる場合は -i audio.mp3 の直前に -itsoffset ADELAY を指定します

"-itsoffset DELAY "はそのオプションの直後のストリームに対してのみ作用します

シーク

動画の一部を切り出す.ある時刻Aからある時刻Bまでを取り出す.

入力ファイル(INPUT) の時刻 START から D 秒間を切り出して,OUTPUT として保存する

$ ffmpeg -ss START  -i  INPUT -t D OUTPUT

例えば動画 input.mp4 の先頭3分30秒から 120秒を切り取って output.mp4 として保存するには

$ ffmpeg -ss 0:03:30  -i input.mp4  -t 120  output.mp4

となります.このように時間や時刻は 時:分:秒 の形式でも指定できます.

画像処理・画像変換

動画から映像だけ抽出.音声を消す
$ ffmpeg -i input.mp4  -vc -an  output.mp4
  • 映像(video)はそのままコピー(copy) で -vc
  • 音声(audio)は無し(None?) で -av

を指定します.

動画の一部を切り出す.画像の一部を取り出す

例えばフレームの右上だけを切り出す場合は filter の crop を使います.

$ ffmpeg -i input.mp4 -filter:v "crop=W:H:X:Y" output.mp4

切り出す範囲は矩形で指定します. 左上の座標が (X,Y) 右下の座標は(X+W,Y+H)となります.
言い換えると,output.mp4 のサイズは W × H となります

画像を小さくする.解像度を下げる.スケーリングする.リサイズする.

例えば フレームの幅を320ピクセルにするには

$ ffmpeg -i input.mp4 -vf scale=320:-1  output.mp4

とします.アスペクト比は保存されます

アスペクト比を無視したい場合は

$ ffmpeg -i input.mp4 -vf scale=320:240  output.mp4

両方の画素数を指定します.

連番画像から動画を作る
$ ffmpeg -r 29.97 -start_number 123 -i path/to/%08d.jpg  -r 29.97 output.mp4

ffmpeg のオプションには癖があります.注意が必要です

  • 入力に対するオプションは 入力ファイルの前で指定する
    • 開始フレーム番号の -start_number オプション
    • 入力のフレームレートの -r オプション
  • 出力に対するオプションは 出力ファイルの直前で指定する
    • 出力のフレームレートの -r オプション

上記の例では,-r オプションが2回指定されてる点に注意してください

音声処理・音声変換

動画から音声だけ抽出

mp3で抽出

$ ffmpeg -i input.mp4    output.mp3

オプションをつけないと拡張子で良きに計らってくれます.

オプションを明示する場合は

$ ffmpeg -i input.mp4  -vn -ac output.mp4

となります.

つまり

  • 映像(video)は無し(None?) で -vn
  • 音声(audio)はそのままコピー(Copy) で -ac

となります

音量を調整する.音を大きくする.音を小さくする.

一度音量を確認します

$ ffmpeg -i input.mp4 -vn -af volumedetect -f null -

ボリュームは デシベルで指定します

デシベル 音量の倍率
-20dB 0.1倍
-8dB 0.4倍
-6dB 0.5倍
0dB 1倍
6dB 2倍
8dB 2.5倍
20dB 10倍
40dB 100倍

例えば 40dB (100倍)にする場合は

$ffmpeg -i input.mp4 -vcodec copy -af volume=40dB output.mp4

となります

別撮りのモノラル音声を結合してステレオ音声にする

左と右で,別々にモノラル録音した音声データを結合してステレオ音声にする場合

ffmpeg -i left.wav -i right.wav \
               -filter_complex "[0:a][1:a]join=inputs=2:channel_layout=stereo[aout]"  \
               -map "[aout]" \
               output.mp3

入力は左音声がleft.wav ,右音声が right.wav です.

"-filter_complex "[0:a][1:a]join=inputs=2:channel_layout=stereo[aout]" "で

[0:a]つまり left.wav の音声と, [1:a] つまり right.wavの音声を取り出して,結合しています
結合したステレオ音声は "[aout]" で名前をつけています

"[aout]"の音声データは -map "[aout]" で output.mp3 に保存されます.

応用

音ズレを直す (原理説明)

まず原理を説明します.

音ズレがある元ファイルの名前を original.mp4 とします.

手順1) 音声と映像の分離

まずoriginal.mp4 から映像データを抜き出します.

$ ffmpeg -i original.mp4 -vc -an v.mp4

これで映像だけが v.mp4 として保存されます.

この際のログをよく見ると,映像の再生時間がわかります.

frame=233871 fps=0.0 q=-1.0 Lsize=  4416826kB time=01:05:00.57 bitrate= 4875 kb/s

例えば,上記の表示なら,映像 v.mp4 の長さは 01:05:00.57 となります.

次に original.mp4 から音声データを抜き出します.

$ ffmpeg -i original.mp4 -vn -ac a.mp4

これで音声だけが a.mp4 として保存されます.

こちらも再生時間を調べます.

size=  731596kB time=01:05:01.84 bitrate=1336.0kbits/s speed= 1745x

音声の長さは 01:05:01.84 となります.

元の動画ファイル original.mp4 の長さも調べましょう

$  ffprob -i original.mp4
 Duration: 01:05:01.85, start: 0.000000, bitrate: 2020 kb/s

01:05:01.85です

時間から秒への換算してみます.換算は "qalc" コマンドがオススメです

$ qalc
> 01:05:01.85 h to s

  (3901.85 / 3600) * hour = 3901.85 s

表でまとめてみます

再生時間 再生時間(秒)
元動画 original.mp4 01:05:01.85 3901.85
映像データ 01:05:00.57 3900.57
音声データ 01:05:01.84 3901.84


このように動画に含まれている,映像データと音声データとで再生時間が違うと,音ズレが発生します.

音ズレを修正するには,映像と音声どちらかの再生速度を変えて,再生時間を揃えればOKです.

再生速度の変換は音声処理の方が早いので,今回は映像はそのままで,音声の再生速度を変えることにします

映像に合わせて,音声の再生速度を x 倍に変換すると考えると
x = (音声の再生時間)/(映像の再生時間)
となります

$ qalc 
> 3901.84/3900.57

 3901.84 / 3900.57 = approx. 1.0003256

計算すると x=1.0003256 です

この値で,音声の再生速度を変換します.

$ ffmpeg -i a.mp4 -filter:a "atempo=1.0003256"  a_fixed.mp4 

確認します

$ ffprobe -i a_fixed.mp4
 Duration: 01:05:00.58, bitrate: 1536 kb/s

1/100秒ほどズレが残っていますが,まあ誤差の範囲としましょう.

変換した音声データと,元の映像データを結合します

$ ffmpeg -i v.mp4 -i a_fixed.mp4 -vc -ac output.mp4
音ズレを直す (運用版)

まず映像,音声の長さを調べます

$ ffprob -i original.mp4

音声の再生速度 x を求めます

変換します

$ ffmpeg -i original.mp4 -filter_complex "[0:a]atempo=1.0003256[a]" -map "[0:v]" -map "[a]"  output.mp4

オプションの -filter_complex "[0:a]atempo=1.0003256[a]" で音声の再生速度を変換しています
"[0:a]" は -i オプションで最初に指定した入力 original.mp4 の 音声(a),という意味です.
それを 1.0003256倍の速度で再生,その結果に "[a]"という名前をつけています

オプション -map "[0:v]" は,入力 original.mp4 の 映像(v)を,出力 output.mp4 に割り当てるという意味です.

オプション -map "[a]" は,変換後の音声データ "[a]"を,出力 output.mp4 に割り当てるという意味です.

これで中間ファイルを作成することなく音ズレが修正できます.

pythonでWebスクレイピングする方法

pythonでウェブページ(ホームページ)にアクセスして,必要な文字列だけ抽出する方法です.いわゆる Web scraping です.

いくつか方法がありますが,今回は python で lxml モジュールを使いました.

サンプルコード

サンプルコードです.python 3 で動作します.

実行すると,www.kernel.orgにアクセス,Linuxの最新バージョンを抽出,print で結果を表示します.

#!/usr/bin/env python3

import requests
import lxml.html

url = 'http://www.kernel.org/'
response = requests.get(url)

html = lxml.html.fromstring(response.content)

htmltag = html.xpath("//tr[2]/td[2]/strong")

for e in htmltag:
    print(e.text)

エラーが出る場合は lxml モジュールを追加でインストールしてください

$ pip install lxml 

解説

6行目から7行目で,Webサーバにアクセスして HTMLをダウンロードします.

9行目で, lxml 使ってHTMLを解析します.解析結果は変数html に保存されています.

xpath の求め方

抽出する文字列は xpath で指定します.上記のサンプルコードなら "//tr[2]/td[2]/strong" が xpath となります.

xpath は,firefox などのWebブラウザ経由で調べるのが簡単です.

firefox を使う場合は

  1. firefox で該当ページにアクセス
  2. 抽出したい要素(文字列)をマウスで選択.
  3. 右クリックして「要素を調査」を選択
  4. 「インスペクタ」が起動する
  5. 「インスペクタ」のツリービューに表示されている要素を右クリック
  6. 「コピー」の「XPath」をクリック

これで xpathクリップボードにコピーされます.

MySQLでSELECT文の出力を CSVファイルに保存する方法

多くの場合,MySQLはファイル出力(つまりファイルの新規作成&ファイルへの書き込み)を許可していません.

理由はファイル出力を許可するとセキュリティ上の問題が出てくるからです.MySQLを使う場合は,先ず mysqld に接続して,mysqld 上でクエリを実行することになります.つまりファイルを出力するのは mysqld であり,生成されたファイルの所有者は mysqld になってしまいます.ファイルを作成してもそれを削除できるのは mysqld ユーザだけ,これはセキュリティ上何かと問題を起こします.

この欠点を無視して無理やりファイル出力を許可するためには,次の二つの条件を満たす必要があります.

  • 条件1) mysqlのサーバーの設定で,ファイル出力が許可されていること.
  • 条件2) SQLを実行するユーザに権限 FILE が付与されていること

以下,具体的方法をまとめます.

条件1) ファイル出力の許可

以下のSQLを実行して

SELECT @@global.secure_file_priv;

NULL と表示される場合は,ファイル出力が許可されていません.

許可には,mysqlのサーバ設定の変更が必要です.

設定ファイルは /etc/mysql/my.cnf です.ただし debian の場合は /etc/mysql/conf.d/以下のファイルもincludeするようになっているので
/etc/mysql/conf.d/mysqld.cnf などの設定ファイルを新しく作った方が無難です.

設定ファイルに以下の設定を追加します

[mysqld]
secure-file-priv="/tmp"

これで "/tmp/" ディレクトリにファイルが出力できるようになります.
"/tmp"は誰でもアクセス可能なディレクトリなのでセキュリティ上リスクがあります.しかし今回は動作確認ということで /tmp を使います.

mysql を再起動して設定を反映させます.

以下のSQLを実行して確認しましょう

SELECT @@global.secure_file_priv;

"/tmp”と表示されれば成功です.NULLの場合はsecure-file-privの設定が有効になっていません.

条件2) FILE権限の付与

ユーザ hogehoge@localhost にファイル出力の権限を付与します

権限を付与するので管理者権限で mysql に接続して,GRANT文を実行します.

$ sudo mysql -u root 
GRANT FILE ON *.* TO 'hogehoge'@'localhost';

MYSQLの仕組み上,データベース単位,テーブル単位での細かな権限設定は出来ません.

CSVファイルに保存してみる.

条件1) 条件2)が満たせれば以下のような形で "INTO OUTFILE ファイル名” でクエリの出力をファイルに保存できるようになります.

 SELECT * FROM テーブル名  INTO OUTFILE '/tmp/filename.csv'


/tmp/filename.csv というファイルが出来上がります.ファイルのowner を確認してみましょう

$ ls -lha  /tmp/filename.csv
-rw-rw-rw- 1 mysql mysql 4.9K Mar 18 00:00 /tmp/filename.csv

所有者は mysql になっています.パーミッションも微妙です.

このようにファイル出力はセキュリティ上問題があるので注意しましょう.

CSVファイルの形式の調整

カンマ区切り,ダブルクオートで囲む場合

 SELECT * FROM テーブル名  INTO OUTFILE '/tmp/filename.csv' fields terminated by ',' optionally enclosed by '"';

エラーが出る場合

ERROR 1290  The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

条件1) を見直しましょう

 ERROR 1045  Access denied for user 'hogehoge'@'localhost' (using password: YES) 

条件2)を見直しましょう

デスクトップLinuxのサウンドをラズパイで再生する方法

デスクトップLinuxの音声出力を,Volumio のようなサウンドサーバーに転送&再生する方法です.

転送には pulseaudio を使います.たとえば

  • サウンドサーバとして, Volumioで動いている raspbery PI (つまり debianベースのLinux
  • デスクトップとして, debianベースのPC

という環境があるとして,デスクトップからLAN経由で音声データをラズパイ側に転送,ラズパイにつながったスピーカーで再生,ということができるようになります.

設定方法

  • volumio 側が pulseaudio のサーバ
  • デスクトップ側が pulseaudio のクライント

とします.

pulseaudio サーバ側の設定 (つまりVolumio側)

インストール

必要なパッケージをインストールします

$ sudo apt-get install pulseaudio pulseaudio-module-zeroconf avahi-daemon
pulseaudio の設定

設定ファイルは /etc/pulse/system.pa です

pulseaudio の認証方式は

  • 接続元IPで制限する方法
  • クッキーを使って認証を行う方法

などがあります


前者の接続元IPで制限する方法を使う場合は, /etc/pulse/system.pa の末尾に以下の2行を追加します.

load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.0.0/24
load-module module-zeroconf-publish

これで192.168.0.0/24 からの接続を無条件に許可します.言うまでもありませんが,IPアドレスは必要に応じて適宜変更してください

pulseaudio の自動起動

systemd を使います

まず pulseaudio の設定ファイル /etc/pulse/daemon.conf を確認します

local-server-type = user

の行があれば削除(コメントアウト)します




次に systemd 用の設定ファイルを作ります.

以下の内容を,/lib/systemd/system/pulseaudio.service というファイルに保存します.

[Unit]
Description=pulseaudio sound server
[Service]
Type=forking
PIDFile=/var/run/pulse/pid
ExecStart=/usr/bin/pulseaudio --system --daemonize
[Install]
WantedBy = multi-user.target

設定を有効にします

$ sudo systemctl enable pulseaudio.service
$ sudo systemctl start pulseaudio
確認
$ sudo systemctl status pulseaudio

で Active: active (running)
と出力されていればOKです

pulseaudio クライアント側の設定 (つまりデスクトップLinux側)

インストール

必要なパッケージをインストールします

$ sudo apt install pulseaudio-module-zeroconf avahi-utils pasystray
動作確認

まずラズパイ側の pulseaudio に接続できるか確認します

$  pactl -s <ラズパイのホスト名>  info

volumioを使っている場合は,デフォルト値としてラズパイのホスト名が volumio.local と設定されています.


うまく接続できると以下のような出力になります

$ pactl -s volumio.local info

Server String: volumio.local
Library Protocol Version: 33
Server Protocol Version: 29
Is Local: no
Client Index: 29
Tile Size: 65472
User Name: pulse
Host Name: volumio
Server Name: pulseaudio
Server Version: 5.0
Default Sample Specification: s32le 2ch 48000Hz
Default Channel Map: front-left,front-right
Default Sink: alsa_output.platform-soc_audio.analog-stereo
Default Source: alsa_output.platform-soc_audio.analog-stereo.monitor
Cookie: xxxx:xxxx

次に avahi の名前空間を確認します.

$ avahi-browse -a

LAN内で稼働しているサービスの一覧が出力されます.

ラズパイ側が正常に動いている場合は以下の行があるはずです.

 eth0 IPv4 pulse@volumio                                 PulseAudio Sound Server local

このように "pulse@ホスト名" で pulseaudio のサーバーが見つかれば成功です

音を再生してみる

試しに mplayer コマンドを使って, test.mp3 を再生してみます.

$ mplayer -ao pulse:volumio.local   test.mp3

デスクトップの設定

GUIがあります.

$pasystray 

これでタスクトレイにスピーカーのアイコンが出てきます."default slink" がデフォルトの音声出力先デバイスになります.ここから volumio を選択します.

環境変数を使った pulseaudio サーバーの切り替え

pulseaudio は,環境変数 PULSE_SERVER, PULSE_SINK でpulseaudioのサーバーを選択しています.

ですから例えば

$ PULSE_SERVER=volumio.local mplayer -ao pulse test.mp3

という方法でサーバーを変更できます.


PULSE_SERVER環境変数の値は pax11publish でも変更できます

ハイレゾ化(?)

/etc/pulse/daemon.conf で,音声データを転送する際のサンプリングレート(≒ビットレート)が指定できます

デフォルト値は16bit @ 44.1KHz です

たとえば32bit @ 48KHz にあげるには /etc/pulse/daemon.conf の該当箇所を

default-sample-format = s32le
default-sample-rate = 48000

と書き換えます

2つのpdfファイルを重ねて一つのpdfファイルとして保存する

原稿 A.pdf に,別ファイル B.pdf の内容を重畳させて,新しいファイル C.pdf として保存する方法です

pdf形式で文章や印刷データを扱っていると

  • 各ページに,フッターやヘッダーを自動で追加する
  • pdf形式で作成したポスターに印刷&裁断用のトンボを付ける

といった作業が必要になることが多々あります

これらの作業は イラストレーターのようなアプリをつかうよりも,pdf編集用のコマンドラインツールを使ったほうが簡単かつ確実です

方法

pdftk というコマンドを使うのが簡単です

debian系なら

$ sudo apt install pdftk

ですぐにインストールできます

windowsmac なら公式のインストーラがあります
https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/

コマンド

  • A.pdf 手前のレイヤとなるpdfファイル名
  • B.pdf 後側(背景, background)のレイヤになるpdfファイル名
  • C.pdf 出力ファイル名

とすると,実行するコマンドは

pdftk A.pdf  background B.pdf output  C.pdf

となります

これだけで C.pdfが出来上がります


またbackgroundではなく,watermark としてレイヤを加える方法もあります.コマンドは

pdftk A.pdf stamp watermark.pdf output C.pdf

となります