ブログ

割とコンピュータよりの情報をお届けします。

Python

Pythonのsounddeviceその2

Pythonでnumpyの信号をスピーカーから出力するのはどうすればよいのかを探していると,sounddeviceというのが見つかった.
これを使って前は少し遊んでみるた
sounddeviceにはplayrecという関数があって再生と録音を同時にできる.

音を入力出力する例としては,sd.playrec(z, samplerate=fs, channels=1)に変えるだけである.
下の例ではz2が録音されたデータの列になっている.

注意すべき点は,秒単位ではタイミングがずれることである.タイミング関係は使用している環境や負荷によるかもしれない.
import numpy as np
import sounddevice as sd
import matplotlib.pyplot as plt

fs = 44100
fbase = 4000;
t = np.linspace(0, 44099, num=44100, endpoint=False);
z = 0.2 * np.sin(t * 2 * np.pi * (fbase)) + 0.2 * np.sin(t * 2 * np.pi * (fbase + 1)) + 0.2 * np.sin(t * 2 * np.pi * (fbase + 2)) + 0.2 * np.sin(t * 2 * np.pi * (fbase + 3));

z2 = sd.playrec(4 * z, fs, channels=1)#sd.rec(int(duration * fs), samplerate=fs, channels=1)
sd.wait()

sd.play(z2, fs);
# 表示
Fz = np.fft.fft(z2) / z2.shape[0] * 2; # 折り返すのでパワーが2分の1になっている.
Fz[0] = Fz[0] / 2; # 平均成分は折り返さない.
Fz_abs = np.abs(Fz);

freq = np.fft.fftfreq(len(z), d=1.0/fs);
tim = np.arange(0, z2.shape[0]) * 1.0 / fs;
plt.figure(1)
plt.subplot(211);
plt.plot(tim, z2);

plt.subplot(212);
plt.plot(freq, Fz_abs * 1e6);
plt.xlim([0, 20000])

plt.show();


 

参考: sounddeviceでPythonを使って録音する

≫ 続きを読む

2019/07/21 コンピュータ   TakeMe
タグ:Python

熱電対の温度と電圧の変換式を作る2

前回の記事では温度から熱起電力を求める式と熱起電力から温度を求める関数を用意していたが,逆が遅い.

とりあえず多項式近似なら,
前回

import numpy as np
import scipy as scipy
from scipy.optimize import fmin
import matplotlib
import matplotlib.pyplot as plt
import time

def GetK_Type_T2E(t):
    return -17.600413686 + 38.921204975 * t + 0.018558770032 * t ** 2 \
        + -0.000099457592874 * t ** 3  + 0.00000031840945719 * t ** 4 \
        + -5.6072844889E-10 * t ** 5 + 5.6075059059E-13 * t ** 6 \
        + -3.2020720003E-16 * t ** 7 + 9.7151147152E-20 * t ** 8 \
        + -1.2104721275E-23 * t ** 9 \
        + 118.5976 * np.exp(-0.0001183432 * (t - 126.9686) ** 2);

def f(t, *args):
    return np.abs(GetK_Type_T2E(t) - args[0])

def GetK_Type_E2T(e):
    t = e / 38.921204975;
    res  = scipy.optimize.minimize_scalar(f, bracket=None, bounds=(t - 0.7, t + 0.7), args=(e), method='brent', tol=1e-10);
    #res  = scipy.optimize.minimize_scalar(f, bracket=None, bounds=(t - 0.7, t + 0.7), args=(e), method='golden', options={'xtol': 1e-15});
    print(res)
    return res.x;

if __name__ == '__main__':
    start = time.time()
    t = 20
    e = GetK_Type_T2E(t);
    print(t)
    e0 = GetK_Type_T2E(20)
    print(e0)
    t0 = GetK_Type_E2T(e0)
    print(t0)
    elapsed_time = time.time() - start
    print ("elapsed: {0}".format(elapsed_time) + " s")

今回

import numpy as np
import scipy as scipy
from scipy.optimize import fmin
import matplotlib
import matplotlib.pyplot as plt
import time

def GetK_Type_T2E(t):
    return -17.600413686 + 38.921204975 * t + 0.018558770032 * t ** 2 \
        + -0.000099457592874 * t ** 3  + 0.00000031840945719 * t ** 4 \
        + -5.6072844889E-10 * t ** 5 + 5.6075059059E-13 * t ** 6 \
        + -3.2020720003E-16 * t ** 7 + 9.7151147152E-20 * t ** 8 \
        + -1.2104721275E-23 * t ** 9 \
        + 118.5976 * np.exp(-0.0001183432 * (t - 126.9686) ** 2);

def GetK_Type_E2T(e):
    global w;
    return np.polyval(w, e);

if __name__ == '__main__':
    start = time.time()
    ts = np.linspace(-40, 240, num=2801);
    es = GetK_Type_T2E(ts);
    
    w = np.polyfit(es, ts, 18)
    pvts = np.polyval(w, es)

    elapsed_time = time.time() - start
    print ("elapsed: {0}".format(elapsed_time) + " s")
    
今回の方が2801点計算しているのに同じ程度の時間で処理できている.

≫ 続きを読む

2019/04/11 コンピュータ   TakeMe
タグ:Python

熱電対の温度と電圧の変換式を作る

JIS C1602 熱電対 に熱起電力の多項式近似が載っているのでそれをもとに温度-起電力の変換式を用意した.

Pythonで書いてみた.
(数値の一部桁はわざと変えてある.可能性がある)細かい数値はJISを参照していただくとして,...
変換関数自体は問題ないのだが,逆関数は載っていない.
一応 数値最適化を用いて逆関数を作ってみた.
中に,numpyを入れたのはまずかったかもしれない.

import numpy as np
import scipy as scipy
from scipy.optimize import fmin

def GetK_Type_T2E(t):
    return -17.600413686 + 38.921204975 * t + 0.018558770032 * t ** 2 \
        + -0.000099457592874 * t ** 3  + 0.00000031840945719 * t ** 4 \
        + -5.6072844889E-10 * t ** 5 + 5.6075059059E-13 * t ** 6 \
        + -3.2020720003E-16 * t ** 7 + 9.7151147152E-20 * t ** 8 \
        + -1.2104721275E-23 * t ** 9 \
        + 118.5976 * np.exp(-0.0001183432 * (t - 126.9686) ** 2);

def f(t, *args):
    return np.abs(GetK_Type_T2E(t) - args[0])

def GetK_Type_E2T(e):
    t = e / 38.921204975;
    res  = scipy.optimize.minimize_scalar(f, bracket=None, bounds=(t - 0.7, t + 0.7), args=(e), method='brent', tol=1e-10);
    #print(res)
    return res.x;

if __name__ == '__main__':

    t = 20
    e = GetK_Type_T2E(t);
    print(t)
    e0 = GetK_Type_T2E(20)
    print(e0)
    t0 = GetK_Type_E2T(e0)
    print(t0)

このやり方だと逆関数の計算が順方向の数十倍負荷がかかってしまう. もっと時間がかかるが,以下のようにした方がずれが小さくなる(かも).

res  = scipy.optimize.minimize_scalar(f, bracket=None, bounds=(t - 0.7, t + 0.7), args=(e), method='golden', options={'xtol': 1e-15});

≫ 続きを読む

2019/04/06 コンピュータ   TakeMe
タグ:Python

C#からPythonの関数を呼び出し3

前のC#からのPythonの呼び出しの記事から新たに気が付いたことがあったので備忘録も含めて記載する.

使用するのはPython for .NETである.

git clone https://github.com/pythonnet/pythonnet.git

でgithubからpythonnetをダウンロードする.前にも述べている通りPythonの最新版は更新が速くてNugetパッケージは更新が追い付いていない.
ダウンロードされたフォルダの中にpythonnet.slnファイルがあるはずなのでこれを開き(Visual Studio 2017).
その後,ビルドの構成はReleaseWinPY3でx64(実際には実行環境に合わせる)でビルドする.するとbinフォルダにPython.Runtime.dllが出来上がる.

今回は呼び出した先のPythonのコードでmultiprocessingを使ってみた.
すると,Pythonで書かれている部分だけでなく.NET Frameworkで動いているプロセス全体が複製される.
C#から呼び出す先でマルチプロセスを使いたい場合には,プロセス全体が複製されることを前提に書いておく必要がある(複製された先からQueueなどの処理を行って処理を返さないと呼び出し元のプロセスはいつまでも待つ(特別な処理を書いていれば別だが)).

意図しない動作をしないように十分に注意する必要がある.

≫ 続きを読む

2019/03/28 コンピュータ   TakeMe
タグ:Python

Pythonのmultiprocessingの例2

Pythonではmultithreading よりもmultiprocessingの方がCPUのコアを有効に使用できるが,前の例だとCtrl+Cで停止したときにプロセスが残る.
WinPython 3.7では少し改善しているので対策コードが追加できる.

メインのプロセスから,terminate()を実行するととりあえず子プロセスは消える.
実際には残るらしい.
その場合にはProcessのコンストラクタにdaemon=Falseを指定する
最初に考えたコードは次のようなもの... しかし,実際にはうまくいっていない感がある

import numpy as np
from multiprocessing import Process
import multiprocessing as mp

def calc(queue, i_range, arg1, arg2, arg3):
    print('Start: {}\r\n'.format(mp.current_process().name))
    a = arg1[i_range] + arg2[i_range];
    b = arg2[i_range] + arg3[i_range];
    c = arg3[i_range] + arg1[i_range];
    queue.put([a, b, c])

if __name__ == '__main__':
    

    x = np.linspace(0, 10000, 10000);
    y = np.linspace(10000, 0, 10000);
    z = np.linspace(0, 10000, 10000) - 5000;

    mp_size = 4
    ps = [];
    queue = mp.Queue()
    results = dict()
    for i in range(mp_size):
        ps.append(mp.Process(target=calc, args=(queue, range(x.shape[0]*i//mp_size, x.shape[0]*(i+1)//mp_size), x, y, z), name="{}".format(i+1), daemon=False))
        
    for p in ps:
        p.start()

    try:
        for i in range(mp_size):
            results[i] = queue.get();
        
    except KeyboardInterrupt:
        for p in ps:
            p.terminate()

    a = 0;
    b = 0;
    c = 0;
    for i in range(mp_size):
        a += np.asscalar(np.sum(results[i][0]));
        b += np.asscalar(np.sum(results[i][1]));
        c += np.asscalar(np.sum(results[i][2]));
    
    print('{},{},{}'.format(a, b, c))

無理やり止めるコードを追加しなおしたのが以下のコードである.
無理やり止めているのでidleでデバッグしていると例外などが発生したって確認できなくなる.ただしエンドユーザには関係なくなる.

import numpy as np
from multiprocessing import Process
import multiprocessing as mp
import signal
import sys

ps = [];

def calc(queue, i_range, arg1, arg2, arg3):
    print('Start: {}\r\n'.format(mp.current_process().name))
    a = arg1[i_range] + arg2[i_range];
    b = arg2[i_range] + arg3[i_range];
    c = arg3[i_range] + arg1[i_range];
    queue.put([a, b, c])

def handler(signum, frame):
    global ps;
    for p in ps:
        if p.is_alive():
            try:
                p.terminate()
            except AssertionError:
                pass
            print('terminate {}'.format(p.pid));
    sys.exit(0);

if __name__ == '__main__':
    
    signal.signal(signal.SIGINT, handler);
    
    x = np.linspace(0, 10000, 10000);
    y = np.linspace(10000, 0, 10000);
    z = np.linspace(0, 10000, 10000) - 5000;

    mp_size = 4
    
    queue = mp.Queue()
    results = dict()
    for i in range(mp_size):
        ps.append(mp.Process(target=calc, args=(queue, range(x.shape[0]*i//mp_size, x.shape[0]*(i+1)//mp_size), x, y, z), name="{}".format(i+1), daemon=False))
       
    for p in ps:
        p.start()
        
    for i in range(len(ps)):
        results[i] = queue.get();
        
    a = 0;
    b = 0;
    c = 0;
    for i in range(mp_size):
        a += np.asscalar(np.sum(results[i][0]));
        b += np.asscalar(np.sum(results[i][1]));
        c += np.asscalar(np.sum(results[i][2]));
    
    print('{},{},{}'.format(a, b, c))
    

実はこれでも対策は不十分でどうにもならないことがある.子プロセスの準備中などに止められると異常終了して意図通りに止まらないことがあるのだ.(is_alive()のせいかも)

≫ 続きを読む

2019/03/02 コンピュータ   TakeMe
タグ:Python

Pythonのmultiprocessingの例

Pythonではmultithreading よりもmultiprocessingの方がCPUのコアを有効に使用できる.

Pythonではマルチスレッドのプログラムが簡単.
よほどのことがない限り,異常は発生してなかったが,
GILのためという理由で,なかなか性能が発揮できない場面が大かった.

おそらくこの例が,マルチプロセスに直した場合になっている.

(これを作ったときには,nameに文字列以外を挿入して大失敗した.ちゃんと文字列を入れましょう)

import numpy as np
from multiprocessing import Process
import multiprocessing as mp

def calc(queue, i_range, arg1, arg2, arg3):
    print('Start: {}\r\n'.format(mp.current_process().name))
    a = arg1[i_range] + arg2[i_range];
    b = arg2[i_range] + arg3[i_range];
    c = arg3[i_range] + arg1[i_range];
    queue.put([a, b, c])

if __name__ == '__main__':
    x = np.linspace(0, 10000, 10000);
    y = np.linspace(10000, 0, 10000);
    z = np.linspace(0, 10000, 10000) - 5000;

    mp_size = 4
    ps = [];
    queue = mp.Queue()
    results = dict()
    for i in range(mp_size):
        ps.append(mp.Process(target=calc, args=(queue, range(x.shape[0]*i//mp_size, x.shape[0]*(i+1)//mp_size), x, y, z), name="{}".format(i+1)))
        
    for p in ps:
        p.start()
    
    for i in range(mp_size):
        results[i] = queue.get();

    
    a = 0;
    b = 0;
    c = 0;
    for i in range(mp_size):
        a += np.asscalar(np.sum(results[i][0]));
        b += np.asscalar(np.sum(results[i][1]));
        c += np.asscalar(np.sum(results[i][2]));
    
    
    print('{},{},{}'.format(a, b, c))

なんの問題もなく終了するときは良いが,強制終了がかかると子プロセスが残る問題が生じる.この件は次の記事で扱う.

≫ 続きを読む

2019/03/01 コンピュータ   TakeMe
タグ:Python

C#からPythonの関数を呼び出し2

C#からPythonの関数を呼び出し」の最後の方で,「デバッグの時は云々」の部分をもう少し詳しく書く.

まずはコードを示してみる.

using System;
using System.Collections.Generic;
using Python.Runtime;

namespace TEST
{
    class Program
    {
        // TEST.exe C:\python-3.7.0-embed-amd64\
        // numpy入りのpython embeddableを指定してください.
        static void Main(string[] args)
        {
            var path = Environment.GetEnvironmentVariable("PATH");
            path = path + args[0] + ";";
            Environment.SetEnvironmentVariable("PATH", path);

            using (Py.GIL())
            {
                dynamic sys = Py.Import("sys");
                sys.path.append(args[0]);

                dynamic np = Py.Import("numpy");
                dynamic a = np.array(new List<double> { 1, 2, 3 });
                Console.WriteLine(a.dtype);
                dynamic b = np.array(new List<int> { 6, 5, 4 }, dtype: np.int32);
                Console.WriteLine(b.dtype);
                Console.WriteLine(a * b);
                Console.ReadKey();
            }
        }
    }
}

まずは,環境変数PathにPython Embeddableのインストールパスを設定する.
さらに,sys.path.appendでPythonモジュールのパスも指定する.
このようにすると,実行時の引数にPython embeddableのインストールパスを指定すると実行ができる.
 

≫ 続きを読む

2019/02/11 コンピュータ   TakeMe
タグ:Python

C#からPythonの関数を呼び出し

C言語からPythonの関数呼び出し4」から発展を考えていたら,C#から呼び出しの記事を書いていないことに気が付いたので備忘録も含めて記載する.
なお,参考は「Pythonから.NETを呼び出す方法とついでにその逆も(https://qiita.com/y-tsutsu/items/0c4561d6478be32e6f8e)」なのだ.

使用するのはPython for .NETである.これ自体は前にも扱っている.このときには,Pythonから.NETを呼び出すというのをやっていた.
今回はその逆である.

git clone https://github.com/pythonnet/pythonnet.git

でgithubからpythonnetをダウンロードする.
ダウンロードされたフォルダの中にpythonnet.slnファイルがあるはずなのでこれを開く(Visual Studio 2017).

その後,ビルドの構成はReleaseWinPY3でx64(実際には実行環境に合わせる)でビルドする.するとbinフォルダにPython.Runtime.dllが出来上がる.

次に,.NETのプロジェクトを作る.
この時,アッセンブリの参照に先のPython.Runtime.dllを加えてやる(これが大事).参考のページではNugetパッケージをとってきているが,Python 3.7もPython for .NETもGitHubの方の更新が速いのでこのNugetパッケージは更新が全く追い付いていない.

続いて,「Pythonから.NETを呼び出す方法とついでにその逆も」からサンプルコードを頂く.
例えば以下のようなコードになる.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Python.Runtime;

namespace TEST
{
    class Program
    {
        static void Main(string[] args)
        {
            using (Py.GIL())
            {
                dynamic np = Py.Import("numpy");
                dynamic a = np.array(new List<double> { 1.0, 2.1, 3.2 });
                Console.WriteLine(a.dtype);
                dynamic b = np.array(new List<float> { 4, 5, 6 }, dtype: np.int32);
                Console.WriteLine(b.dtype);
                Console.WriteLine(a * b);
                Console.ReadKey();
            }
        }
    }
}

実行時には,NumpyをインストールしたPython Embeddableのフォルダの場所にexeファイルとdllファイル突っ込めば実行できる.(Visual Studioのデバッグの際にはプロジェクトのプロパティで「デバッグ」の設定で,同等の構成になるように作業フォルダなどを調整する)

≫ 続きを読む

2019/02/10 コンピュータ   TakeMe
タグ:Python

C言語からPythonの関数呼び出し4

久しぶりの「C言語からPythonの関数呼び出し」記事として,WinPythonとVisual Studio 2017でPython embeddable用の実行ファイルを作るということを再検討していた.

まず,Python側のコードの例は以下のようにした.
今回もlistを引数としてPythonに渡して,listを返り値としてもらうという例になっている.

# -*- coding: utf-8 -*-

def SampleFunc1(t, f):
    output = [10, 9, 7, 5, 3, 1];
    return output

C(C++)言語側のコードは以下のようにした.

#include <stdio.h>
#include <windows.h>

#define _USE_MATH_DEFINES
#include <cmath>
#ifdef _DEBUG
#undef _DEBUG
#include <Python.h>
#define _DEBUG
#else
#include <Python.h>
#endif

int main()
{
    char Path[MAX_PATH + 1];
    char Dir[MAX_PATH + 1 + 32];
    char drive[MAX_PATH + 1]
        , dir[MAX_PATH + 1]
        , fname[MAX_PATH + 1]
        , ext[MAX_PATH + 1];
    if (0 != GetModuleFileNameA(NULL, Path, MAX_PATH)) {
        _splitpath_s(Path, drive, dir, fname, ext);

        int j = 0;
        for (int i = 0; i < MAX_PATH + 1; i++) {
            Dir[j] = dir[i];
            j++;
            if (dir[i] == '\\') {
                Dir[j] = dir[i];
                j++;
            }
            else if (dir[i] == '\0') {
                break;
            }
        }
        sprintf_s(dir, "%s%s", drive, Dir);
        printf("ディレクトリ パス : %s\n", dir);
    }
    char buf1[2000];
    sprintf_s(buf1, "import sys\r\nsys.path.append('%s')\r\nprint(sys.path)\r\n", dir);
    // 実行ファイルと同じフォルダにあるpyモジュールも使いたいので,前回より少し手を加えた.

    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pArg, *pArg2, *pValue, *pValue2;

    Py_Initialize();
    PyRun_SimpleString(buf1);
    pName = PyUnicode_DecodeFSDefault("SampleFunc");

    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) {
        pFunc = PyObject_GetAttrString(pModule, "SampleFunc1");

        if (pFunc && PyCallable_Check(pFunc)) {
            pArgs = PyTuple_New(2); // 関数fncの引数の数を引数にする

            pArg = PyList_New(1000);
            pArg2 = PyList_New(1000);
            for (int i = 0; i < 1000; i++) {
                double value = sin(2.0 * M_PI * 0.01 * 1.0 * i);
                pValue = PyFloat_FromDouble(value);
                pValue2 = PyFloat_FromDouble(2.0);
                if (!pValue) {
                    Py_DECREF(pArgs);
                    Py_DECREF(pArg);
                    Py_DECREF(pArg2);
                    Py_DECREF(pModule);
                    return 1;
                }
                PyList_SetItem(pArg, i, pValue);
                PyList_SetItem(pArg2, i, pValue2);
            }
            if (PyTuple_SetItem(pArgs, 0, pArg2) != 0)
                return 1;
            if (PyTuple_SetItem(pArgs, 1, pArg) != 0)
                return 1;

            pValue = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pValue != NULL) {
                //printf("%d\n", PyList_CheckExact(pValue));
                Py_ssize_t len = PyList_Size(pValue);

                for (int i = 0; i < len; i++) {
                    //printf("%I32d ", PyFloat_Check(PyList_GetItem(pValue, i)));
                    printf("%.4g\n", PyFloat_AsDouble(PyList_GetItem(pValue, i)));
                }
                //printf("return: %s\n", (char*)PyUnicode_DATA(pValue));
                Py_DECREF(pValue);
            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                fprintf(stderr, "Call failed\n");
                return 1;
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function\n");
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load module\n");
        return 1;
    }
    if (Py_FinalizeEx() < 0) {
        return 120;
    }
    return 0;
}

プロジェクトのプロパティを次のように設定しておくとF5キーでデバッグがかかるようになる.
(Python部分はデバッグできない) 「ビルド後のイベント」のコマンドライン
copy .\*.py $(TargetDir)

「デバッグ」の「環境」にPython Embeddableのインストールパスを設定する.例えば
PATH="$(Path)C:\python-3.7.2.post1-embed-amd64\;"

なお,[C/C++]の[追加インクルードディレクトリ]にはWinPythonのインストールパスを設定する.例えば
C:\WPy-3710Zero\python-3.7.1.amd64\include
[リンカー]の[追加のライブラリディレクトリ]には 例えば
C:\WPy-3710Zero\python-3.7.1.amd64\include
[リンカー]の[追加の依存ファイル]には
python3.lib
を設定する.

 

なお途中にでている「手を加えた」としている部分は次の記事ですこし考え直した.

≫ 続きを読む

2019/02/02 コンピュータ   TakeMe
タグ:Python

Python zipimportでpydは読み込めない

前に,「Python embeddable用に追加モジュールのzipファイルを追加」というのを紹介していたが,どうもうまくいくものとうまくいかないものがある.

Python embeddableにはpython**.zipなるファイルが付いている.
なんだこれと思っていたらこれにPythonのモジュールなどのフォルダをそのまま突っ込むと使用できるようになる.
もちろん展開に時間がかかるので多少レスポンスがおちる.

小さなモジュール,Pure Pythonモジュール類は問題なく追加できるが,NumpyやScipyはそれができない.
実はpydの実態がWindows dllであるということ.
Windowsではzipファイルからdllをロードできなくなったのでpydをロードできない.したがって,NumpyやScipyはzipファイルに入れては使用できない(厳密にはpydを使っていない部分だけなら使用できるが,内部でどのように依存しあっているかは外からはわからない).

≫ 続きを読む

2019/01/30 コンピュータ   TakeMe
タグ:Python