教育用GM管開発を振り返って(10)

apgmman.hatenablog.com

放射線教育支援サイト「らでぃ」の実験集に掲載した「Pythonで反復計数の実験」のPythonプログラムについて解説する。Pythonやライブラリーのインストール方法や注意点、Pythonの文法の特徴などは「教育用GM管開発を振り返って(9)」に記載した。


Pythonで反復計数の実験」のプログラム例


10秒間の計数を、キーqの入力で停止するまで繰り返し、計数率を画面表示するとともに、csvファイルに出力する計数の基本プログラムについて解説する。


まず、必要なライブラリーをインポートする。このうち、numpyはPythonのインストールで同時にインストールされるので、ライブラリーとして別途インストールする必要はない。#以下はコメント文である。

import pyaudio #マイク入出力処理
import numpy as np
import matplotlib.pyplot as plt #グラフ処理
import cv2 #カメラ画像処理

次に、グローバル変数を宣言し、初期値を決める。閾値の変数thresholdは、パルス波高を予めモニターして数値を決め、キーボードから入力する。

global m, threshold #回数と閾値の変数をグローバルと宣言
m = 1 #測定回数
threshold = input('Threshold?') #閾値を入力

出力ファイルcpm_data.csvを開き、ヘッダーを書き込んでおく。出力ファイルはExcelなどの処理ソフトを利用することを念頭にcsvファイルとした。

with open('cpm_data.csv', mode='w') as fc: #出力ファイルを新規モードで開く
    fc.write('Time(sec)' + ',' + 'CPM' + '\n') #ヘッダーを書き込む

次に一連の音声入力をストリーミングとしてデータ化し、計数に至るまでを関数化する。CHUNKは入力単位でここでは、1024個ごとにサンプリングする。サンプリング周波数は変えられるが、デフォルトのままにした。録音時間は10秒である。

def streamandcount(): #マイク入力から計数までの処理
#stream
    CHUNK = 1024 #入出力の長さ
    FORMAT = pyaudio.paInt16 #2Byte整数
    CHANNELS = 2 #ステレオ
    RATE = 44100 #サンプリング周波数(Hz)
    RECORD_SECONDS = 10 #録音時間(秒)

ストリーミングをオープンし、CHUNKごとのデータdata-rawをframesというlistに追記していく。

    p = pyaudio.PyAudio()

    stream = p.open(format=FORMAT,
                    channels=CHANNELS,
                    rate=RATE,
                    input=True, #入力のみ
                    frames_per_buffer=CHUNK) #ストリームを開く

    frames = [] #空のlistを用意

10秒分のデーターを加算して、framesに追記していく。

    for i in range(int(RATE * RECORD_SECONDS // CHUNK)): #CHUNK単位で切り捨て
        data_raw = stream.read(CHUNK) #CHUNK単位で音声信号をサンプリング
        frames.append(data_raw) #430回分を加算

    frames_int16 = np.array(frames) #listをnumpyアレイに変換

ここでストリームを閉じる。

    stream.stop_stream() #ストリームを閉じる
    stream.close()
    p.terminate()

以下は、計数と計数率の画面とファイルへの出力のための演算で、まず、2進数を10進数に変換し、データは片チャンネルだけでよいので、ステレオのLチャンネルのデータだけを抽出する。

#count
    pulse = 0 #計数を初期化
    data = np.frombuffer(frames_int16, dtype='int16') #バイトデータを整数に変換
    data_l = data[::2] #ステレオのLチェンネルを取り出す
    onflag = False #閾値超過フラッグの初期値

データが閾値を超えるとフラッグを立て、閾値を下回るとカウント1を加算し、フラッグを降ろす。閾値を超えたところから下回ったところまでを1カウントとするので、複数パルスでも重複カウントすることはない。

    for k in range(len(data_l)): #data_l全体が範囲
        dat = data_l[int(k)] #data_lのk番目の値
        if int(dat) > int(threshold) and not onflag: #閾値を超えたら
            pulse = pulse + 1 #1カウントを加算
            onflag = True #閾値超過フラッグを立てる
        if int(dat) <= int(threshold): #閾値以下になったら
            onflag = False #閾値超過フラッグを降ろす

計数率はCPMで表示することとしたので、10秒のカウント数を6倍して画面に出力する。

    cpm = pulse / RECORD_SECONDS * 60 #CPMに換算
    print('CPM = ' + str(int(cpm))) #CPMを画面に出力

同時に計数率をファイルに出力する。フォーマットは、回数、計数率となっている。

    with open('cpm_data.csv', mode='a') as fc: #出力ファイルを追記モードで開く
            fc.write(str(10 * m) + ',' + str(cpm) + '\n')

キーボードからqを入力するまで、計数を反復する。プログラムの停止には、キーボードからqを入力する。

while True: #キー入力まで反復する
    if cv2.waitKey(1) & 0xFF == ord('q'): #qを入力すると
        break #プログラムが終了
    streamandcount() #処理を繰り返す
    m = m + 1 #回数のカウントを1増やす

Pythonの実行は、Windows Power ShellまたはCMDを開いて、ファイル名を入力することで行う。そうすれば、終了時にも計数率は画面上に残っているので、結果を確認することができる。Pythonファイルをダブルクリックして実行した場合は、画面上に残らないので結果は出力ファイルで確認することになる。学校の授業で、距離の実験や遮へいの実験を行う場合は、条件を変えて10秒率で6回の測定を繰り返すので、6回測定後に一旦停止して、再度再開できるプログラムの方が適している。しかし、キーボード入力でコントロールする方法に確実性がないために、できていない。うまい方法があれば教えてほしい。


(※id:TJOid:apgmmanから受領したWord原稿を元に再構成、代理投稿したものです)