20210605のPythonに関する記事は30件です。

残プロ 第-4回 ~生存確認をLINENotifyで~

番外編ってことで 今回は初回につくったもの(mementomori.py)を利用して生存確認システムを作ります.このご時世,いつどこで死ぬかわかりませんからね. システムといっても使うものはpythonとタスクスケジューラのみです. こんな感じで超シンプルに作成していきます. LINENotify部分 こちらの素晴らしい記事を参考にしてトークンの発行まで行ってください.超簡単! あらかじめ「生存確認用」のライングループを作成しておき,トークンはそのグループ宛てで発行してください. [超簡単]LINE notify を使ってみる https://qiita.com/iitenkida7/items/576a8226ba6584864d95 pythonプログラム notify.py import requests if __name__ == '__main__': send = "\nPCが起動しました" send_dict = {'message': send} TOKEN = 'ここにトークンをペースト' url = 'https://notify-api.line.me/api/notify' TOKEN_dict = {'Authorization': 'Bearer ' + TOKEN} requests.post(url, headers=TOKEN_dict, data=send_dict) ※pythonの環境が古い場合,実行時にSSLErrorがでる可能性があります.新しく環境を構築する等して対応してください タスクスケジューラへ登録 1.タスクスケジューラを起動. 2.基本タスクの作成. 3.タスク名を記入."生存確認"等,任意で. 4.トリガーを「コンピューターの起動時」に設定 5.操作を「プログラムの開始」に設定 6.プログラムにはpython.exeを.  引数の追加に実行する.pyファイル名.  開始に.pyファイルまでのpathを記入. 7.下記画像のようになっていれば完了 LINEグループに招待 あとは家族なり,親しい友人なりを生存確認グループに追加し趣旨を伝えて完成です. これで孤独死も怖くない!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

scikit-learn導入時にOSErrorが出たときのメモ

ちょっとした回帰の実装をしてみようと思い、scikit-learnをpipでローカル環境(Windows10)に導入することにしました。 C:\Users\[ユーザー名]>pip install scikit-learn しかしエラー。 scikit-learnインストール時のエラーメッセージ ERROR: Could not install packages due to an OSError: [Errno 2] No such file or directory: エラーの原因はインストール先のパスが長すぎることらしいです。 Windows10ではデフォルトでパス名の文字数が260文字までに制限されているので、以下の投稿を参考に文字数制限を解除しました。 やったことは以下の通りです。 スタートメニューから [グループポリシーの編集] へ [コンピューターの構成] > [管理用テンプレート] > [システム] > [ファイルシステム] [Win32 の長いパスを有効にする] をダブルクリック [有効] のラジオボタンをonにして、画面下部の [OK] をクリック 以上でパス名の文字数制限を撤廃できます。もう一度インストールすると… scikit-learnのインストール成功 C:\Users\[ユーザー名]>pip install scikit-learn Collecting scikit-learn Downloading scikit_learn-0.24.2-cp37-cp37m-win_amd64.whl (6.8 MB) |████████████████████████████████| 6.8 MB 3.3 MB/s ・・・(中略)・・・ Installing collected packages: scikit-learn Successfully installed scikit-learn-0.24.2 無事に導入できました。めでたしめでたし。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでつくる雑VTuber ~Jetson Nanoで顔認識~

何ができる? こんなのとか こんなの Web カメラから画像を取り込んで顔を検出、その上に指定した顔画像を貼り付けます。加工された画像は仮想カメラに送られ、通常の Web カメラと同様に動作します。これをリアルタイムでやることで低品質 VTuber の完成です。OBS や Zoom, LINE ビデオ通話など各種サービスと連携可能です。 動作のしくみ Windows PC と Jetson Nano1 の間でデータをやり取りし、最終的に Windows 内の仮想カメラに合成画像を出力します。Windows PC と Jetson Nano 間の通信にはソケット通信を使用します。処理のステップは以下の通り。 *[Win] は Windows 側、[Nano] は Jetson Nano 側の処理を表します。 [Win] 接続したWebカメラから画像を取り込む [Win] 取り込んだ画像を圧縮して送信する [Nano] 受信画像から顔を検出し、顔の位置情報を送り返す [Win] 受信した位置情報を元に顔画像を貼り付ける [Win] 加工後の画像を仮想カメラに送る なぜこんなやり方なのか? 顔の位置情報を取得するにあたり、後述のライブラリ jetson-inference が使いやすく、速度・精度ともに優秀なのが主な理由です。一方で普段カメラ出力が必要な Zoom や LINE などのソフトは Windows に入っており、カメラをNanoに繋ぎかえる手間も省きたいのでこの仕様になりました。結果として Windows 側のマシンスペックに依存しにくくなるというメリットも得られました。 もっと簡単にやりたい場合は、顔検出を OpenCV のカスケード分類器で実装するなどすれば Jetson Nano は不要になります。 実行環境 Windows PC, Webカメラ、Jetson Nano が必要です。ふつうの配信環境があれば2万円足らずであなたも VTuber に! え?Facerig と Live2D で 2000円足らずでなれるって……? Windows PC 項目 内容 OS Windows 10 Home 64bit CPU Intel Core i7-6700 3.4GHz GPU NVIDIA GeForce RTX 2060 メモリ 32GB カメラ Logicool C920n 一応スペックを書き下しましたが、GPU は使用してないですし、メモリもそんなに食いません(読み込む画像のボリュームにもよりますが)。要求スペックではなく参考として。 使用する外部ライブラリ 名称 [import名] 概要 PySimpleGUI [PySimpleGUI] GUI 処理を行う。Python 標準の tkinter より実装が楽 OpenCV [cv2] 画像ファイルの読込みや各種画像処理を行う NumPy [numpy] 数値計算を行う。OpenCV によって画像を配列として扱えるようになるので、基本的にセットで運用する Pillow [PIL] OpenCV と同じく画像処理を行う。OpenCV が日本語フォントに非対応のため(やむなく)導入 pyvirtualcam [pyvirtualcam] 仮想カメラに画像を転送する Terminal pip install pysimplegui pip install opencv-python pip install numpy pip install Pillow pip install pyvirtualcam その他 仮想カメラを動作させるために OBS2 のインストールが必要です(無償ダウンロード可能)。 Jetson Nano 項目 内容 モデル Jetson Nano 開発者キット JetPack SDK JetPack 4.5.1 CPU Quad-core ARM A57 1.43 GHz GPU 128-core Maxwell メモリ 4GB Jetson Nano 開発者キットはスイッチサイエンスで 13,000 円弱です(2021年6月時点)。他に電源、MicroSD カード、マウス・キーボード、(Wi-Fi や Bluetooth を使いたいなら)無線モジュールが必要になります。2GB版もありますが、そちらでは動作確認してません。 詳細仕様はマクニカ社のページで確認できます。 使用する外部ライブラリ Windows PC で触れたライブラリは省略します。 名称 [import名] 概要 jetson-inference [jetson] NVIDIA 社の提供する Jetson シリーズ向けのディープラーニング用ライブラリ その他 jetson-inference のチュートリアル Hello AI World を参照し、自分で書いたコードを実行できる状態にします。最低限、ビデオチュートリアルの Hello AI World Setup と Object Detection Inference の内容をおさえておけばOKです。 しかしこのままプログラム内で OpenCV を使おうとするとエラーが出るため、専用の Docker イメージを作成します。詳しいやり方は下記を確認してください。 Docker イメージ作成方法を表示 まずは docker フォルダまで移動して Dockerfile という名前のファイルを作成します。 Terminal cd ~/jetson-inference/docker nano Dockerfile 以下の内容を書き加えて保存します。Jetpack 4.5.1 を前提にしているので、他のバージョンを使用している場合は FROM dustynv/jetson-inference:r32.5.0 の部分を適宜読み替えてください。 Dockerfile FROM dustynv/jetson-inference:r32.5.0 RUN apt-get update RUN apt-get install python3-tk -y RUN pip3 install -U pip RUN pip3 install opencv-python RUN pip3 install pysimplegui ターミナルに戻り、Docker イメージをビルドします。ビルドが完了したら sudo docker images を実行し、イメージ一覧に jetson-inference-cv があることを確認します。 このイメージを使ってコンテナを起動するために、同じく docker フォルダにある tag.sh をエディタで編集します。事前に cp でバックアップをとっておきましょう。 Terminal sudo docker build -t jetson-inference-cv . # ... # Successfully built ************ # Successfully tagged jetson-inference-cv:latest sudo docker images # REPOSITORY TAG IMAGE ID CREATED SIZE # jetson-inference-cv latest ************ ******** ago 3.08GB # dustynv/jetson-inference r32.5.0 ************ ******** ago 2.89GB cp tag.sh tag.bak nano tag.sh 以下のようにDocker イメージをさきほど作成した jetson-inference-cv に置き換え、保存します。# TODO のある2箇所が修正対象です。 tag.sh #!/usr/bin/env bash # find L4T_VERSION source tools/l4t-version.sh # local container:tag name # CONTAINER_IMAGE="jetson-inference:r$L4T_VERSION" CONTAINER_IMAGE="jetson-inference-cv" # TODO # incompatible L4T version function version_error() { echo "cannot find compatible jetson-inference docker container for L4T R$L4T_VERSION" echo "please upgrade to the latest JetPack, or build jetson-inference natively from source" exit 1 } # get remote container URL if [ $L4T_RELEASE -eq 32 ]; then if [ $L4T_REVISION_MAJOR -eq 4 ]; then if [ $L4T_REVISION_MINOR -gt 4 ]; then CONTAINER_REMOTE_IMAGE="nvcr.io/ea-linux4tegra/$CONTAINER_IMAGE" elif [ $L4T_REVISION_MINOR -ge 3 ]; then CONTAINER_REMOTE_IMAGE="dustynv/$CONTAINER_IMAGE" else version_error fi elif [ $L4T_REVISION_MAJOR -eq 5 ]; then if [ $L4T_REVISION_MINOR -eq 0 ]; then CONTAINER_REMOTE_IMAGE="dustynv/$CONTAINER_IMAGE" elif [ $L4T_REVISION_MINOR -eq 1 ]; then # L4T R32.5.1 runs the R32.5.0 container # CONTAINER_IMAGE="jetson-inference:r32.5.0" CONTAINER_IMAGE="jetson-inference-cv" # TODO CONTAINER_REMOTE_IMAGE="dustynv/$CONTAINER_IMAGE" else CONTAINER_REMOTE_IMAGE="nvcr.io/ea-linux4tegra/$CONTAINER_IMAGE" fi elif [ $L4T_REVISION_MAJOR -gt 5 ]; then CONTAINER_REMOTE_IMAGE="nvcr.io/ea-linux4tegra/$CONTAINER_IMAGE" else version_error fi else version_error fi # check for local image if [[ "$(sudo docker images -q $CONTAINER_IMAGE 2> /dev/null)" == "" ]]; then CONTAINER_IMAGE=$CONTAINER_REMOTE_IMAGE fi これで完了です。あとはいつも通り run.sh を実行すれば OpenCV 対応のコンテナが起動します。一応 PySimpleGUI も動作するようにしてあります。 使いかた Jetson Nano 最初に Jetson Nano で顔検出プログラムを起動しておく必要があります。Hello AI World の手順にならって Docker コンテナからプログラムを実行します。事前にプログラムファイル server.py を ~/my-detection に保存しておいてください。 Terminal cd ~/jetson-inference docker/run.sh --volume ~/my-detection:/my-detection Docker cd /my-detection python3 server.py 推論モデル起動中のログがずらずらっと出てくるのでしばらく待ちます。Await connectionと表示されれば準備完了です。これ以降 Jetson Nano の操作は必要ありません。終了したいときは Ctrl + C で抜けてください。 Windows PC プログラムを実行するとウィンドウが表示されます。×ボタンで終了します。 Jetson Nano に接続 Jetson Nano 側のプログラムが稼働している状態で「Jetson Nano に接続」ボタンを押します。接続に成功するとデータ送受信のログが表示され、カメラのプレビューが開始されます。IPアドレスの記載が誤っていたり、相手側のプログラムが稼働していない場合はエラーログが出力されます。 # IPアドレスが適切でない場合 [WinError 10060] 接続済みの呼び出し先が一定の時間を過ぎても正しく応答しなかったため、接続できませんでした。または接続済みのホストが応答しなかったため、確立された接続は失敗しました。 # Jetson Nano 側のプログラムが稼働していない場合 [WinError 10061] 対象のコンピューターによって拒否されたため、接続できませんでした。 画像選択 「画像選択」ボタンを押すとファイルが選択できるので、置き換えたい顔画像を選びます。拡張子は .png 限定で、アルファチャンネルによる透過が有効です。 アニメーション 複数の画像を選択した場合、それらを順番に表示してアニメーションさせます。 ファイル名の昇順で表示されるので、連番画像を作っておくと良いです。表示順が狂わないように、数字は0埋めしておいてください。アニメーションの速さをフレーム数で指定することも可能です。 合成 ON / OFF トグルスイッチで顔画像合成の ON / OFF を切り替えられます。 人数制限 画面内に収める人数の上下限を指定できます。検出された顔の個数が範囲内にない間は、仮想カメラのフレームが更新されなくなります(ウィンドウ内のプレビュー画像は更新されます)。顔検出失敗による顔バレ対策を意図していますが、顔以外の場所が誤検出された場合は機能しなくなるのでご注意ください。またトグルスイッチで合成を OFF にしている間も機能しません。 合成サイズ・位置の調整 顔画像を合成する際のサイズと位置の調整が可能です。 字幕 選択した字幕が画面内に表示されます。「字幕解除」ボタンを押すと字幕が非表示になります。 動作確認 OBS で仮想カメラの動作確認ができます。OBSを起動し、「ソース」の+ボタン > 「映像キャプチャデバイス」 > 「新規作成」を選択して「OK」 > 「デバイス」に「OBS Virtual Camera」を指定して「OK」 と進むと仮想カメラの映像を確認できます。 LINE の場合は 「設定」 > 「通話」 > 「カメラ」に「OBS Virtual Camera」を選択 することでビデオ通話に仮想カメラが適用されます。 処理内容 項目ごとに別記事に分けて更新していく予定です。週に 1 個くらいのペースで上げられればと思ってますので、少々お待ちください。すまん! とりあえずコード全文だけ置いておきます。 パラメータ設定やプレビューの GUI 処理 Windows PC - Jetson Nano 間のデータ通信 jetson-inference による顔位置の推定 顔画像の合成 仮想カメラへの映像転送 Windows 側と Nano 側の非同期処理による処理速度 UP コード Windows PC 全文を表示 client.py import PySimpleGUI as sg from tkinter import filedialog import cv2 import numpy as np from PIL import ImageFont, ImageDraw, Image import pyvirtualcam import socket import pickle # 定数定義 SOCK_IP = "*.*.*.*" # TODO: Jetson NanoのIPアドレスを入れる SOCK_PORTS = (50001, 50002) # 通信に使用するポート CAMERA_ID = 0 # USBカメラのID CAMERA_SIZE = (864, 480) # キャプチャサイズ CAMERA_CODEC = 'H264' # キャプチャ時の動画圧縮コーデック CAMERA_BUFFER = 1 # バッファサイズを指定。ラグを減らしたいので下限値で設定 CAMERA_FPS = 30 # フレームレート VIRTUAL_CAMERA_FPS = 15 # 仮想カメラフレームレート SUBTITLES = [ '字幕1', '字幕2', '字幕3', '字幕4', '字幕5', ] # 字幕一覧 SUBTITLE_FONT = 'BIZ-UDGothicR.ttc' # 字幕フォント、該当フォントが入ってない場合は変更必要 SUBTITLE_FONT_COLOR = (255,255,255) # 字幕フォント色 SUBTITLE_FONT_SIZE = 20 # 字幕フォントサイズ SUBTITLE_CHAR_W = 20 # 字幕文字幅 SUBTITLE_CHAR_H = 20 # 字幕文字高さ SUBTITLE_MARGIN_TOP = 10 # 字幕背景マージン SUBTITLE_MARGIN_BOTTOM = 10 # 字幕背景マージン SUBTITLE_MARGIN_LEFT = 10 # 字幕背景マージン SUBTITLE_MARGIN_RIGHT = 0 # 字幕背景マージン SUBTITLE_TRANSPARENCY = 0.3 # 字幕背景透過率 # カメラ初期化 def init_cam( id, frame_size, codec, buffer_size, fps ): cam = cv2.VideoCapture(id) cam.set(cv2.CAP_PROP_FRAME_WIDTH, frame_size[0]) cam.set(cv2.CAP_PROP_FRAME_HEIGHT, frame_size[1]) cam.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*codec)) cam.set(cv2.CAP_PROP_BUFFERSIZE, buffer_size) cam.set(cv2.CAP_PROP_FPS, fps) return cam # Jetson Nano通信用ソケットの初期化 def init_socket(): client_data = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 処理結果を受け取るソケット client_status = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 相手が処理中かどうかを確認するソケット client_status.settimeout(0.1) return client_data, client_status # ソケットに接続 def connect_socket(): global connected try: d.print('接続開始') window.finalize() client_data.connect((SOCK_IP, SOCK_PORTS[0])) client_status.connect((SOCK_IP, SOCK_PORTS[1])) d.print('接続完了') connected = True except Exception as e: d.print(e) print(e) # ソケットの終了 def kill_socket(e=None): global connected if e: d.print(e) print(e) client_data.close() client_status.close() connected = False # 画像読込。cv2.imread()は日本語パスに対応していないのでその対策 def imread(filename, flags=cv2.IMREAD_UNCHANGED, dtype=np.uint8): try: n = np.fromfile(filename, dtype) img = cv2.imdecode(n, flags) return img except Exception as e: d.print(e) print(e) return None # アルファチャンネル付き画像合成 def blend(base, top, location=(0, 0)): ret = base.copy() if len(ret.shape) < 3: ret = cv2.cvtColor(ret, cv2.COLOR_GRAY2BGR) x, y = np.array(location, dtype=int) top = top[max(0, -y):, max(0, -x):] x, y = np.clip(location, 0, None).astype(int) th, tw = top.shape[:2] bh, bw = ret.shape[:2] top = top[:min(th, bh-y), :min(tw, bw-x)] th, tw = top.shape[:2] if len(top.shape) < 3: top = cv2.cvtColor(top, cv2.COLOR_GRAY2BGR) if top.shape[2] > 3: alpha = top[:,:,3:] else: alpha = np.ones((th, tw, 3), dtype=np.uint8)*255 alpha_inv = 255 - alpha ret[y:y+th, x:x+tw, :3] = np.array(ret[y:y+th, x:x+tw, :3]*(alpha_inv/255) + top[:, :, :3]*(alpha/255), dtype=np.uint8) return ret # 字幕の追加 def subtitle(img, text): ret = img.copy() def putText(img, text, org, fontFace, fontScale, color): def pil2cv(imgPIL): imgCV_BGR = np.array(imgPIL)[:, :, ::-1] return imgCV_BGR def cv2pil(imgCV): imgCV_RGB = imgCV[:, :, ::-1] imgPIL = Image.fromarray(imgCV_RGB) return imgPIL x, y = org b, g, r = color colorRGB = (r, g, b) imgPIL = cv2pil(img) draw = ImageDraw.Draw(imgPIL) fontPIL = ImageFont.truetype(font = fontFace, size = fontScale) draw.text(xy = (x,y), text = text, fill = colorRGB, font = fontPIL) imgCV = pil2cv(imgPIL) return imgCV x, y = (CAMERA_SIZE[0]//2, int(CAMERA_SIZE[1]/1.2)) h = SUBTITLE_CHAR_H w = len(text)*SUBTITLE_CHAR_W mt = SUBTITLE_MARGIN_TOP mb = SUBTITLE_MARGIN_BOTTOM ml = SUBTITLE_MARGIN_LEFT mr = SUBTITLE_MARGIN_RIGHT ret[y-mt:y+h+mb, int(x-w/2)-ml:int(x+w/2)+mr] = ret[y-mt:y+h+mb, int(x-w/2)-ml:int(x+w/2)+mr]*SUBTITLE_TRANSPARENCY ret = putText(ret, text, (int(x-w/2), y), SUBTITLE_FONT, SUBTITLE_FONT_SIZE, SUBTITLE_FONT_COLOR) return ret # 画像を表示(sg.Graph インスタンスメソッド) def draw_image_plus(self, img, location=(0,0)): if type(img) == np.ndarray: img = cv2.imencode('.png', img)[1].tobytes() id_ = self.draw_image(data=img, location=location) return id_ sg.Graph.draw_image_plus = draw_image_plus # ダイアログボックス d = sg.Multiline(size=(30, 11)) # プレビュー窓 canvas = sg.Graph((320, 180), (0, 1), (1, 0), '#000000') # 字幕一覧 sub_table = sg.Table( [[s] for s in SUBTITLES], num_rows=10, headings=['字幕'], auto_size_columns=False, col_widths=[60], justification='left', key='SUB', ) # GUIレイアウト layout = [ [ sg.Button('Jetson Nano に接続', size=(20, 1), key='CONNECT'), ], [ sg.Button('画像選択', key='SELECT_IMG'), sg.Input(size=(25, 1), disabled=True, key='CURRENT_IMG'), sg.Text(' アニメーション'), sg.Spin(list(range(1, 100)), 1, size=(5, 1), key='ANIME_RATE'), sg.Text('Fごとに更新'), ], [ sg.Text('合成 OFF'), sg.Slider((0, 1), 0, 1, size=(7, 20), disable_number_display=True, orientation='h', key='ENABLED'), sg.Text('ON'), sg.Text('  画面内人数'), sg.Spin(list(range(100)), 1, size=(5, 1), key='PERSONS_MIN'), sg.Text(' ≦ X ≦ '), sg.Spin(list(range(100)), 1, size=(5, 1), key='PERSONS_MAX'), ], [ sg.Text('サイズ'), sg.Slider((0.5, 3), 2, 0.1, size=(8, 20), orientation='v', key='SCALE'), sg.Text('  左右'), sg.Slider((-100, 100), 0, 1, size=(16, 20), orientation='h', key='SHIFT_X'), sg.Text('  上下'), sg.Slider((100, -100), 0, 1, size=(8, 20), orientation='v', key='SHIFT_Y'), ], [ sub_table, ], [ sg.Button('字幕解除', key='ERASE_SUB'), ], [ canvas, d, ], ] # ウィンドウ生成 window = sg.Window( 'PyTuber', layout, ) window.finalize() # ソケット準備 client_data, client_status = init_socket() # カメラ初期化 cam = init_cam( id=CAMERA_ID, frame_size=CAMERA_SIZE, codec=CAMERA_CODEC, buffer_size=CAMERA_BUFFER, fps = CAMERA_FPS ) # 変数初期化 face_list = [] connected = False prev_r = None response = None params = [] current_frame = 0 total_frame = 1 sub_frame = 0 # メイン処理 with pyvirtualcam.Camera(width=CAMERA_SIZE[0], height=CAMERA_SIZE[1], fps=VIRTUAL_CAMERA_FPS, print_fps=True) as vcam: while True: # ウィンドウ更新 event, values = window.read(timeout=1) # ×ボタンでループから抜ける if event is None: break # 字幕リセット if event == 'ERASE_SUB': window['SUB'].update(select_rows=[]) # Jetson Nano と接続 if event == 'CONNECT': connect_socket() # 顔画像選択 if event == 'SELECT_IMG': face_filenames = filedialog.askopenfilenames(filetypes=[('PNGファイル', '*.png')]) face_filenames = sorted(face_filenames) # ファイル名昇順で並び替え face_list = [] # 画像リスト生成 for filename in face_filenames: face_list.append(imread(filename)) total_frame = len(face_list) # 総フレーム数取得 if face_list: window['CURRENT_IMG'].update(face_filenames[0].split('/')[-1]) # 顔合成処理 if connected: # カメラ画像取り込み ret, capture_img = cam.read() # 送信用に変換 h, w = capture_img.shape[:2] send_img = cv2.cvtColor(capture_img, cv2.COLOR_BGR2GRAY) # 白黒に変換 send_img = cv2.resize(send_img, (w//2, h//2)) # 1/2に縮小 binary = cv2.imencode('.png', send_img)[1].tobytes() # バイナリ形式に変換 size = len(binary).to_bytes(4, 'little') # バイト長を取得 marged = size + binary # 先頭にバイト長情報を付加 # 相手に処理中の画像があるか確認、なければtimeoutするので描画をスキップ try: status = client_status.recv(1024) except socket.timeout: status = None except Exception as e: kill_socket(e) client_data, client_status = init_socket() continue # 処理中の画像があれば結果受信するまで待機 if status: try: response = client_data.recv(1024) except Exception as e: kill_socket(e) client_data, client_status = init_socket() continue d.print('受信 {} bytes'.format(len(response))) # キャプチャ画像を送信 client_data.sendall(marged) d.print('送信 {} bytes'.format(len(binary))) # 処理結果を反映 if status: # 顔画像の貼り付け if face_list and values['ENABLED']: # アニメーション処理 current_frame = min(sub_frame//values['ANIME_RATE'], total_frame - 1) current_face = face_list[current_frame] window['CURRENT_IMG'].update(face_filenames[current_frame].split('/')[-1]) sub_frame = (sub_frame + 1) % (total_frame*values['ANIME_RATE']) # 顔位置の取得、画像貼り付け params = pickle.loads(response) for param in params: x, y = [int(s*2) for s in param['center']] r = int(param['diagonal']*2) if prev_r: r = (prev_r*2 + r)/3 # 顔サイズの変動をなめらかにするため平均化する prev_r = r h, w = current_face.shape[:2] img_size = (w**2 + h**2)**0.5 # 顔画像の対角線長を測る scale = r/img_size*values['SCALE'] # 検出した顔の対角線との比を求める overlay = cv2.resize(current_face, (int(w*scale), int(h*scale))) # 算出したスケールでリサイズ pos = ( int(x + (-w/2 + values['SHIFT_X'])*scale), int(y + (-h/2 + values['SHIFT_Y'])*scale) ) prev_img = blend(prev_img, overlay, pos) # 顔画像を合成 # 字幕を表示 if values['SUB']: prev_img = subtitle(prev_img, SUBTITLES[values['SUB'][0]]) # 検出人数が適正なら仮想カメラを更新 if values['PERSONS_MIN'] <= len(params) <= values['PERSONS_MAX'] or not values['ENABLED']: img_rgb = cv2.cvtColor(prev_img, cv2.COLOR_BGR2RGB) vcam.send(img_rgb) vcam.sleep_until_next_frame() # プレビューを更新 canvas.erase() canvas.draw_image_plus(cv2.resize(prev_img, (320, 180))) prev_img = capture_img # 前フレーム画像を更新 window.close() kill_socket() おまけ SUBTITLES = [ 'バンガロール: 暑い日には鋼鉄の冷たさが堪らない。', 'バンガロール: スモークを焚いて敵を掃討する。', 'バンガロール: セーフティーを解除。自由に撃て。', 'バンガロール: 楽しむために来たの。遊びましょ!', 'バンガロール: このラウンドはこっちのもの。', 'バンガロール: 素人は出ていって、ここからは本物の戦いよ。', 'バンガロール: 硝煙の臭い、勝利の香りね。', 'バンガロール: 準備OK!さあ行くわよ。', 'バンガロール: 猛者はスコープを使わないって?私は大砲よ。', 'バンガロール: チームで勝ちに行く。忠義なき者には死を。', 'バンガロール: 射撃装填、その繰り返し。簡単でしょ?', 'バンガロール: 準備を!出撃するわ。', 'バンガロール: 準備して!出撃時間よ。', 'バンガロール: さあ、射撃の時間よ。', 'バンガロール: 私は戦場で戦果を上げてきた。', 'バンガロール: 訓練の成果を確かめる時が来た。', 'バンガロール: クリップは髪留めのこと。これはマガジンよ。', 'バンガロール: 敵を潰しに行くわよ。', 'バンガロール: これは欲しい。', 'バンガロール: これは使える。', 'バンガロール: 私のよ。', 'バンガロール: なんでもない。', 'バンガロール: 今のは忘れて。', 'バンガロール: 今のは取り消して。', 'バンガロール: このエリアを偵察しましょう。', 'バンガロール: ここに移動しましょう。', 'バンガロール: こっちへ行こう。', 'バンガロール: ここへ行きましょう。', 'バンガロール: ここに敵がいる。', 'バンガロール: 敵確認。', 'バンガロール: 敵を発見。近い。', 'バンガロール: 標的発見。近くにいる。', 'バンガロール: 標的発見。', 'バンガロール: みんなセーフティーを外して、敵よ!', 'バンガロール: 敵が来るわ、注意して!', 'バンガロール: あっちに誰かいる。集中して。', 'バンガロール: スモークを使う。', 'バンガロール: スモーク注意。', 'バンガロール: いぶしてあげる。', 'バンガロール: スモーク放出。', 'バンガロール: さあ皆。苦痛の始まりよ。', 'バンガロール: 一瞬で倒してやる。', 'バンガロール: 掃射開始。ガンガン撃つわよ。', 'バンガロール: くらいなさい。痛みを感じる間もないわよ。', 'バンガロール: シールドをリチャージ中。', 'バンガロール: 待って、シールドをリチャージする。', 'バンガロール: 回復中。援護して。', 'バンガロール: 少し止まる。応急措置を行うわ。', 'バンガロール: 助けが必要だわ。', 'バンガロール: ダウンした。救援を。', 'バンガロール: やられた。ダウンしてしまったわ。', 'バンガロール: まだ犠牲者リストには入れないでちょうだい。', 'バンガロール: 一等軍曹アニータ・ウィリアムズ、復活よ。まだ死ぬもんですか。', 'バンガロール: さて、派手にいきましょ。', 'バンガロール: 待たせたわね。', ] # 字幕一覧 Jetson Nano 全文を表示 server.py import jetson.inference import jetson.utils import cv2 import numpy as np import socket import pickle # 定数定義 SOCK_IP = "*.*.*.*" # TODO: Jetson NanoのIPアドレスを入れる SOCK_PORTS = (50001, 50002) # 通信に使用するポート DETECT_THRESHOLD = 0.6 # 顔検出しきい値 # Windows側の接続待ち def await_connection(): # ソケット初期化 server_data = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_status = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_data.bind((SOCK_IP, SOCK_PORTS[0])) server_status.bind((SOCK_IP, SOCK_PORTS[1])) server_data.listen(1) server_status.listen(1) # 接続待機 print('Await connection') try: client_data, client_address = server_data.accept() client_status, client_address = server_status.accept() except KeyboardInterrupt: print(' *** Keyboard Interrupt *** ') return None, None print('Connected') server_data.close() server_status.close() return client_data, client_status # 画像受信 def receive_image(): all_data = b'' while True: try: data = client_data.recv(4096) except Exception as e: print(e) client_data.close() client_status.close() return None # 先頭4バイトからファイルサイズを取得 if len(all_data) == 0: size = int.from_bytes(data[0:4], 'little') data = data[4:] # 受信データを連結 all_data += data # 受信サイズがファイルサイズに到達したら終了 if len(all_data) >= size: # 受信完了した瞬間にレスポンスを送信 client_status.sendall(b'1') return all_data # 初期化 net = jetson.inference.detectNet("facenet", threshold=DETECT_THRESHOLD) client_data, client_status = await_connection() # メイン処理 while True: # ソケット接続失敗していたら終了 if client_data is None: break # 画像受信 response = receive_image() # 受信失敗したら接続待ちに戻る if not response: client_data, client_status = await_connection() continue # バイナリ形式から画像を復元 img = np.frombuffer(response, dtype='uint8') img = cv2.imdecode(img, 1) # モノクロの場合はカラーに変換する if len(img.shape) < 3: img = cv2.cvtColor(img, cv2.GRAY2RGB) # cuda画像に変換して顔検出実行 img = jetson.utils.cudaFromNumpy(img) detections = net.Detect(img) # 検出結果を辞書形式のリストに変換 params = [ { 'left-top' : [s.Left, s.Top], 'right-bottom' : [s.Right, s.Bottom], 'center' : s.Center, 'diagonal' : (s.Width**2 + s.Height**2)**0.5 } for s in detections ] # バイナリ化して転送 client_data.sendall(pickle.dumps(params)) # ソケットを閉じる if client_data is not None: client_data.close() client_status.close() まとめ 開発初期はキャプチャした画像をごりっと原寸で転送していたり、Windows - Nano 間の同期待ちがあったりして表示レートが 5~6 fps と事務用ノート PC で Oblivion をやるがごとき遅さ3でした。なんやかんやで最終的には 14~15 fps になり、なんとかまともに見えるレートまでもっていけたかなという感じです。配信だと 30 fps とか 60 fps が主流なので若干のパワー不足は否めないですが、 Web 越しに相手をビビらせるには十分なスペックかと思います。いらすとや なんかは基本すべて透過PNGなので便利ですね。酔狂な方はWeb会議やLINE通話で試してみてはいかがでしょうか4。 調べてみると Jetson Nano で姿勢推定なんかもできるっぽいので、もう少しマシな VTuber も目指せそうです。気が向いたら試してみます。 またね~ 参考 NVIDIA JETSON NANO 開発者キットを動かすのに必要なもの https://denor.jp/nvidia-jetson-nano-開発者キットを動かすのに必要なもの Hello AI World https://github.com/dusty-nv/jetson-inference 【Docker】初めてのdockerでimageまで作成 https://qiita.com/irico/items/4677334879da859a7c24 Tkinterを使うのであればPySimpleGUIを使ってみたらという話 https://qiita.com/dario_okazaki/items/656de21cab5c81cabe59 Call Reference - PySimpleGUI https://pysimplegui.readthedocs.io/en/latest/call%20reference/ Python socket通信の使い方,サンプル解説:画像ファイル転送し、ディープラーニングの認識結果を取得【Keras,ラズパイ】 https://nine-num-98.blogspot.com/2020/03/ai-socket-03.html Python: オブジェクトを漬物 (Pickle) にする https://blog.amedama.jp/entry/2015/12/05/132520 アルファチャンネルつき png を透過画像で貼り付ける with Python/OpenCV https://qiita.com/smatsumt/items/923aefb052f217f2f3c5 OpenCVで日本語フォントを描写する を関数化する https://qiita.com/mo256man/items/82da5138eeacc420499d pyvirtualcam https://github.com/letmaik/pyvirtualcam NVIDIA 社のシングルボードコンピュータ。 ↩ Open Broadcaster Software, フリーの配信・録画ソフト。 ↩ 終わってるの意。 ↩ 本プログラムの利用において発生したいかなる精神的・社会的損失に対しても当方は責任を負えません。あしからず。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MaixduinoのESP32のファームウェアインストールとアナログ入力(Maixpy)

1.はじめに SIPEEDのMaixduinoについては、以前、以下の記事を書きましたが、今回はESP32のファームウェアを書き込んでMaixduinoからPythonコードを書くことでアナログ入力の値を取得してみるところまでやってみたいと思います。 Maixduinoのセットアップに関する情報は、日本語のものはそこまで多くなく、英語または中国語で書かれたものしかないので、もしセットアップで苦戦されている方のお役に立てれば、と思います。なお、以下の内容の利用は、正誤の問題を含め、自己責任でお願いいたします。 2.読んでおくと良いもの(参考) 英語の情報にはなりますが、以下が参考になりました。英語が読める方はざっと目を通されるとイメージがわくと思います。 3.Maixduino全体を制御するための基本的事項 Maixduinoの中核部分は、 ①Sipeed M1 AIモジュール(K210を含む)と、 ②ESP32 の2つから構成されています。 ①のファームウェアについては、前回の記事でPort0からインストールをしたものです。統合開発環境のMaixPyを使って顔認識などのAIを使ったり、前回の記事のLチカのようにデジタル出力をするだけならこれだけで十分なのですが、それだけだとMaixduinoの半分の機能しか使っていないと言って良いでしょう。 ②のESP32は、センサーなどからの値を入力するために不可欠なアナログ入力端子やWifi通信・ブルートゥース通信などの機能を持っており、これらの機能を使おうとすると、別途、ESP32のファームウェアを別の方法でインストールする必要があります。 なお、SipeedM1モジュールは、ファームウェアを入れることでESP32とコミュニケーションができるようになっています。このため、ESP32の機能は、MaixpyでPythonのプログラムを書くことで制御できる、すなわち、Maixduinoのすべての機能(①と②)は1つのpythonプログラムで全体制御ができるようになっているのです。 4.全体手順と準備 Sipeed M1 AIモジュールのファームウェアはKFlashというSipeed公式のソフトウェアを使い、ファームウェアをインストールするだけの簡単なものでした。一方で、ESP32のファームウェアは、ちょっとしたコツが必要になります。(WINDOWSだとKFLASHのようなソフトウェアが使えるようですが) まず最初に、ESP32のファームウェアをここからダウンロードしておきます。 また、ファームウェアの書き込みには、esptools.pyというプログラムを使います。ターミナルから以下を入力してインストールしておきます。 % pip install esptool 5.ファームウェアの書き込み まず、PCのどのポートにボードが接続されているのかを調べます。Macのターミナルで、lsコマンドを使いますが、ls -lだけだと大量に出てくるので、以下のように実行します。 % ls -l /dev/tty.* crw-rw-rw- 1 root wheel 18, 0 6 5 08:38 /dev/tty.Bluetooth-Incoming-Port crw-rw-rw- 1 root wheel 18, 4 6 5 08:49 /dev/tty.usbserial-xel_sipeed0 crw-rw-rw- 1 root wheel 18, 2 6 5 09:03 /dev/tty.usbserial-xel_sipeed1 3つ出てきましたね。このうち、①tty.usbserial-xel_sipeed0は、Sipeed M1モジュールで、既にKFLASHを使ってファームウェアをインストールしたものです。ここでは、②tty.usbserial-xel_sipeed1に先ほどダウンロードしたファームウェアをインストールすることになります。私の環境の場合には、/dev/tty.usbserial-xel_sipeed1 がMSP32にアクセスするポートのパスになります(これは、ファームウェアの書き込みをするときのコマンドで使うことになります。)。 ファームウェアのインストール(フラッシュ)には、pipコマンドでインストールしたesptoolを使います。まっさらな状態にインストールすべきなので、インストール前にeraseをします。次を実行してみてください。 % esptool.py --chip esp32 --port /dev/tty.usbserial-xel_sipeed1 erase_flash esptool.py v3.1 Serial port /dev/tty.usbserial-xel_sipeed1 Connecting... Failed to get PID of a device on /dev/tty.usbserial-xel_sipeed1, using standard reset sequence. .....___ Chip is ESP32-D0WDQ6 (revision 1) Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None Crystal is 40MHz MAC: XX:XX:XX:XX:XX:XX Uploading stub... Running stub... Stub running... Erasing flash (this may take a while)... Chip erase completed successfully in 9.1s Hard resetting via RTS pin... うまく行けたでしょうか?たまに、esptool.FatalError: Timed out waiting for packet header とエラーが出てしまうケースがあるとの報告があるようです。私も一度出たことがありますが、USBとの接続を抜いて挿し直すとうまくいきました。Maixduino以外では、ブートボタンとリセットボタンを同時に押して、リセットボタンを話す技や、コンデンサを使う技があるようです。 いよいよファームウェアの書き込みを行います。以下のコマンドを入力してください。通信速度(baud)のところはPCの環境にもよりますが、Githubなどで紹介されている速度1500000ではエラーが出て書き込めなかったので、115200にしています。また、先ほど取得したボードのポートまでのパスもここで指定します。 % esptool.py --chip esp32 --port /dev/tty.usbserial-xel_sipeed1 --baud 115200 write_flash -z 0x0000 maixduino_esp32_firmware_v1.4.1_0x0.bin esptool.py v3.1 Serial port /dev/tty.usbserial-xel_sipeed1 Connecting... Failed to get PID of a device on /dev/tty.usbserial-xel_sipeed1, using standard reset sequence. .....__ Chip is ESP32-D0WDQ6 (revision 1) Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None Crystal is 40MHz MAC: XX:XX:XX:XX:XX:XX Uploading stub... Running stub... Stub running... Configuring flash size... Flash will be erased from 0x00000000 to 0x00118fff... Compressed 1150976 bytes to 630987... Wrote 1150976 bytes (630987 compressed) at 0x00000000 in 57.1 seconds (effective 161.3 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin... これでボードのファームウェアの書き込みは終了しました。 6 Maixpyでのコーディング アナログ入力A0~A5とグラウンド(GND)の間にかかっている電圧を取得するプログラムをMaixPyで書いてみます。以下がコードになります。 import network import utime from Maix import GPIO from fpioa_manager import * #MaixDuinoのアナログINのマッピング fm.register(25,fm.fpioa.GPIOHS10)#cs fm.register(8,fm.fpioa.GPIOHS11)#rst fm.register(9,fm.fpioa.GPIOHS12)#rdy fm.register(28,fm.fpioa.GPIOHS13)#mosi fm.register(26,fm.fpioa.GPIOHS14)#miso fm.register(27,fm.fpioa.GPIOHS15)#sclk nic = network.ESP32_SPI(cs=fm.fpioa.GPIOHS10,rst=fm.fpioa.GPIOHS11,rdy=fm.fpioa.GPIOHS12, mosi=fm.fpioa.GPIOHS13,miso=fm.fpioa.GPIOHS14,sclk=fm.fpioa.GPIOHS15) # ANALOG INのA0~A5までの入力値の取得、表示 while True: try: adc = nic.adc() except Exception as e: print(e) continue for v in adc: print("%04d" %(v), end=" ") print() utime.sleep_ms(100) 7. コードを走らせる前のボードの準備(とりあえず) とりあえず、以下の写真のようにA3とグランドを接続し、A3から0が出るかどうかを試してみたいと思います。 本来であれば、可変抵抗とオシロスコープをつないで電圧値を変更させ、それに応じて読み取った値が変化するかどうかを確認すべきですが、今回はとりあえず簡単な動作確認にとどめたいと思います。 8. 実行後のターミナル上の出力 では、6.で書いたプログラムを実行するとどのようになるか、やってみましょう。MaixPyでプログラムを実行する時に接続先のボードを選択するよう求められますが、この時は、ESP32(USB1)ではなく、Sipeed M1 AIモジュール(USB0)になります。(前述の通り、MaixPy側からESP32とコミュニケーションを取るため。) 実行すると、以下のように表示されると思います。A3のところをグラウンドと接続してあるので、A3(左から3つ目の列)はすべてゼロであることが確認できますね。 >>> [esp32_spi] use soft spi 0566 1315 0000 1195 0000 0000 0000 0594 0000 0219 0000 0000 0000 0285 0000 0000 0000 0000 0329 0797 0000 0438 0306 0067 0608 1291 0000 1205 0771 0066 0315 1066 0000 0914 0391 0000 0000 0409 0000 0000 0000 0000 0089 0439 0000 0055 0038 0000 0576 1150 0000 1007 0693 0167 0560 1291 0000 1271 0729 0000 0080 0767 0000 0528 0048 0000 0000 0238 0000 0000 0000 0000 0375 0822 0000 0539 0390 0131 0577 1237 0000 1168 0734 0029 0368 1115 0000 1049 0491 0000 0000 0400 0000 0000 0000 0000 0068 0405 0000 0031 0017 0000 0575 1136 0000 0993 0680 0164 0564 1303 0000 1278 0741 0000 0107 0832 0000 0579 0095 0000 0000 0259 0000 0000 0000 0000 9.最後に いかがでしたでしょうか?アナログ端子が使えると、センサーの入力などもできるようになるのでボードの活躍の幅が広がってくると思います。 今後、センサーをつなげてみたり、センサーからとったデータをWifi経由でデータベースに飛ばしてみたりと工夫をしていきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python]Seleniumを利用したWebページのPDF保存方法 メモ

Python + Seleniumを利用して複数のWebページにアクセスし、それらのページをPDF保存する方法をメモする。 Windows環境でGoogle Chromeを利用して実施する。 事前準備 こちらの手順でSelenium,Chromeドライバーをインストールしておく。 処理フロー 以下のような対象WebページのURL一覧(urls.txt)を用意する。 https://hogehoge.com/hoge.html https://fugafuga.com/fuga.html ... 上記ファイルに記載のURLにアクセスする。 アクセスしたURLをPDF保存する。 1,2を繰り返す。 コード ※有料ジャーナルなどを対象に実行しないこと。 from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from pathlib import Path import time import json # 保存対象URL一覧取得 urls = [] with open('urls.txt', mode='rt', encoding='utf-8') as f: urls = f.readlines() # Chrome の印刷機能でPDFとして保存 options = webdriver.ChromeOptions() # PDF印刷設定 appState = { "recentDestinations": [ { "id": "Save as PDF", "origin": "local", "account": "" } ], "selectedDestinationId": "Save as PDF", "version": 2, "pageSize": 'A4' } # ドライバへのPDF印刷設定の適用 options.add_experimental_option("prefs", { "printing.print_preview_sticky_settings.appState": json.dumps(appState), "download.default_directory": '~/Downloads' }) options.add_argument('--kiosk-printing') with webdriver.Chrome("./chromedriver.exe", options=options) as driver: # 任意のHTMLの要素が特定の状態になるまで待つ wait = WebDriverWait(driver, 15) for url in urls: driver.implicitly_wait(10) driver.get(url) # ページ上のすべての要素が読み込まれるまで待機 wait.until(EC.presence_of_all_elements_located) # PDFとして印刷 driver.execute_script('window.print()') # 待機 time.sleep(10) driver.quit() ダウンロードフォルダにPDFファイルが保存される。 参考情報 PythonのSeleniumでログインありのwebページをPDF保存する SeleniumとPythonを用いて複数のウェブサイトをPDF保存 PythonのSeleniumでhtmlをpdfにするときの設定メモ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python]Selenium概要およびインストール、基本操作方法 メモ

Windows環境でのPython + Selenium + Google Chrome利用方法や基本操作()についての備忘録記事。 Seleniumとは Webブラウザ操作を自動化するためのフレームワーク。 WebDriverと呼ばれるAPI群とプロトコルを利用してWebブラウザ操作を行う。 次の用途で利用される。 Webアプリケーションの UI テスト Webブラウザ操作タスクの自動化 Webサイトのクローリングなど サポート言語 Java Python C# Ruby JS Kotlin など 事前準備 ※Windows環境での例 1.Seleniumインストール pip install selenium 2.Google Chrome Webドライバーダウンロード こちらのサイトから自環境で利用しているGoogle Chromeと同一バージョンのドライバーをダウンロードする。 Zipファイルを解凍し、コードから参照できる場所にchromedriver.exeを配置する。 サンプルコード 「selenium」をGoogle検索するコード(公式参考) from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.expected_conditions import presence_of_element_located # Google Chromeを操作するためのWebドライバを読み込む with webdriver.Chrome("./chromedriver.exe") as driver: # 任意のHTMLの要素が特定の状態になるまで待機するための設定 wait = WebDriverWait(driver, 10) # Googleにアクセス driver.get("https://google.com") # "selenium"で検索 driver.find_element(By.NAME, "q").send_keys("selenium" + Keys.RETURN) # 1件目の検索結果を取得(描画されるまで待機) first_result = wait.until( presence_of_element_located((By.CSS_SELECTOR, "h3"))) print(first_result.get_attribute("textContent")) 参考情報 The Selenium Browser Automation Project
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サブディレクトリを含むディレクトリ内のファイルを別のディレクトリにコピー

import os import re, shutil import tkinter.filedialog def name_savefile_dirname(path): file_path = path.replace('/', os.sep) #パスの区切り文字を変換 for i in range(upper_dir_num+1): upper_dirname = file_path.split(os.sep)[-(upper_dir_num-i)-1] #ファイル名に使用するディレクトリ名を抽出 if i==0: file_name = upper_dirname #最初はアンダーバーなし else: file_name += '_' + upper_dirname #2回目以降はアンダーバーを挟んで結合 return file_name def copy_files(): file_list = [] #コピーするファイルのリスト filepath_list = [] #コピーするファイルパスのリスト for dirpath, dirnames, filenames in os.walk(org_dir): #コピー元のディレクトリを基準にtarget_extで指定した拡張子のファイルをリスト化 for file in filenames: m = re.search(re.escape(target_ext), file) #指定した拡張子のファイルを探す if m : file_list.append(file) #ファイル名をリストに追加 filepath_list.append(os.path.join(dirpath, file)) #コピー元ファイルのパスをリストに追加 for i in range(len(file_list)): save_file_path = os.path.join(save_dir, name_savefile_dirname(filepath_list[i])) #コピー先ディレクトリ名とコピー元ディレクトリを付けたファイル名を結合 shutil.copy2(filepath_list[i], save_file_path) #ファイルをコピー print(save_file_path) if __name__ == '__main__': upper_dir_num = 0 #コピーしたファイル名にコピー元の何個上のディレクトリまで使うか target_ext = '.csv' #コピーするファイルの拡張子 iDir = os.path.abspath(os.path.dirname(__file__)) #ダイアログ表示の初期ディレクトリ org_dir = tkinter.filedialog.askdirectory(initialdir=iDir, title='コピー元のディレクトリ') #コピー元のディレクトリのパス save_dir = tkinter.filedialog.askdirectory(initialdir=iDir, title='コピー先のディレクトリ') #コピー先のディレクトリのパス copy_files() #メイン関数 print('finished')
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bouncing balls(跳ねるたまたま)を増殖(たまたま^8)させてみた

基本方針 テキスト 「いちばんやさしいPython入門教室」大澤文孝著,(ソーテック社出版,2017)で,せっかく速くしたんで,「もっと増やせね?」というのが動機です.どうせなら「カラーグリグリ」は後付け. 動画の作り方 動画は,Narupenが気づかせてくれた ScreenToGifを利用 参照: 寝ログ記事 quicktime -> giftedを利用 cmd-ctrl-esc で録画停止 参照:【Mac】自分の画面を録画して、即gif動画化する方法 ** なんかqiitaに上がらね. :CUSTOM_ID: なんかqiitaに上がらね <!– ![bouncing_ball_128_small.gif]() –> 位置,速度,カラーの初期値をランダムに random.randint(0,9) を改良すればいいよね. random.randint(0, 800), random.randint(0, 600) #x,y位置 random.randint(0, 10)-5 # dx color1 = "#e8f" # rgb color これをどうやろう? # colorのrandom化 python color1 = "#"+ hex(random.randint(1, 15)).lstrip("0x") + hex(random.randint(1, 15)).lstrip("0x") + hex(random.randint(1, 15)).lstrip("0x") みたいにね.やってることは, 1. "#"というstringを用意して 2. 1..15(1..f)の整数を出して, 3. hexで"0xe"とかの16進数(hexadecimal)に変換して 4. lstripで左(l)の"0x"をstrip(剥がす) - hex+lstripの参照サイト たくさんのボール あとはたくさんのボールをballsにappend(追加). balls = [] for j in range(100): balls.append(Ball(x, y, dx, dy, color) うまく生成しない 実は,3回に2回は失敗しています. File "C:\Users\shige\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 2567, in __init__ self.tk.call( _tkinter.TclError: invalid color name "#12" カラー文字生成で失敗(修正済み,たぶん). ソースコード 全てを晒しておきます. # Copyright (c) 2020 daddygongon # Released under the MIT license # https://opensource.org/licenses/mit-license.php import tkinter as tk import random speed = 1 size = 10 class Ball: global speed def __init__(self, x, y, dx, dy, color): self.x, self.y = x, y self.dx, self.dy = dx*speed, dy*speed self.color = color self.shape = None def move(self, canvas): self.erase(canvas) self.x += self.dx self.y += self.dy self.draw(canvas) if (self.x >= canvas.winfo_width() or self.x <= 0): self.dx = -self.dx if (self.y >= canvas.winfo_height() or self.y <= 0): self.dy = -self.dy def erase(self, canvas): canvas.delete(self.shape) def draw(self, canvas): self.shape = canvas.create_oval(self.x - size, self.y - size, self.x + size, self.y + size, fill=self.color, width=0) def loop(): for b in balls: b.move(canvas) root.after(10, loop) def color_maker(init, fin): color1 = "#"+hex(random.randint(init, fin) ).lstrip("0x")+hex(random.randint(init, fin) ).lstrip("0x") + hex(random.randint(init, fin) ).lstrip("0x") return color1 root = tk.Tk() root.geometry("800x600") canvas = tk.Canvas(root, width=800, height=600, bg=color_maker(1, 2)) canvas.place(x=0, y=0) balls = [] for j in range(2**8): # balls.append(Ball(random.randint(390, 410), random.randint(290, 310), balls.append(Ball(random.randint(0, 800), random.randint(0, 600), random.randint(0, 10)*2-10, random.randint(0,10)*2-10, color_maker(8, 15))) root.after(10, loop) root.mainloop() idleでは最後のmainloopもいらないそうです. source ~/Desktop/lecture_21s/CompAInfo/tmp.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonのforループでインデックス番号を取得する

enumerate関数はカウントと値をタプルで返却する。 https://docs.python.org/ja/3/library/functions.html#enumerate sample.py seasons = ['Spring', 'Summer', 'Fall', 'Winter'] print(list(enumerate(seasons))) >> [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')] enumerate関数を利用してforループでインデックス番号を取得できる。 sample.py for i, v in enumerate(seasons): print(i, v) >> 0 Spring >> 1 Summer >> 2 Fall >> 3 Winter
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「いちばんやさしいPython入門教室」修正

テキスト修正 「いちばんやさしいPython入門教室」1で演習をすすめてきました.このテキストはひとつひとつのcodeがどのような考え方でそこにあるのかを丁寧に解説してくれています.なので,coding初心者がcodingを理解するために,じっくり読むのにはとてもいいテキストです.ただ,論理の流れを重視しているためか,すこしcodingの常識からずれているところがあります.その修正です. example07-08-01.pyの修正(tag版) このあたりのコード,まずいです. 長くやったり,たくさんやったりしたらもたもたしてきます.はじめは,cpuとかのせいかとおもってあきらめてたんですが,あまりに遅くって... 修正のアイデアはどっかにあったんですが,もうだいぶむかしに修正しちゃったのでなにが原典だったかわかりません. -タグとバインドが最初のアイデアとしては同じです. 問題は, canvas.create_oval(self.x - 20, self.y - 20, self.x + 20, self.y + 20, fill="white", width=0) です.'white'で上書きして,消しています.これはcodeで絵をかくときの常とう手段です.画面全体をpictとして管理して書いていくときには効率がいいんです.でも,objectに対して,これをやるとobjectがどんどん増殖します.結果として必要なメモリーが増えて,描画がもたもたしてくるんです.たぶん なんで正しくはobjectをdeleteします. まず,create_ovalで識別のためにtagをつけて生成します. canvas.create_oval(self.x - 20, self.y - 20, self.x + 20, self.y + 20, fill=self.color, width=0, tag=self.color) それを=white=で上書きする代わりに,つぎのようにしてdeleteします. canvas.delete(self.color) これで試してみてください.とっても速くなるはずです. ただ,windowsでは形がいびつな感じがするかもしれません.これは,描画ライブラリのせいのようです2.mac版ではばっちりです. さらに正統派(obj版) さらにobjectを消すという考え方の方がわざわざtagで識別するより筋が良さそうです. -https://pythonprogramming.altervista.org/delete-an-object-on-the-canvas/?doing_wp_cron=1591052096.4730188846588134765625 基本的には, canvas.delete(self.ball) ... 中略 ... self.ball = canvas.create_oval(self.x - 20, self.y - 20, self.x + 20, self.y + 20, fill=self.color, width=0) としてself.ballというobjectにしてしまって,それを,deleteするだけです.でも,元テキストからはcodeの順番を相当いじる必要があります. 完成形をつけておきますので,時間のある人は,どこがどうなっているか,なんでか悩んで見てください.順番は, 上のように修正してみる 2. self.ballがないと怒られるので,initでnoneしておく 3.canvasがないと怒られるので,ballsを生成する前に持っていく です. import tkinter as tk speed = 10 class Ball: global speed def __init__(self, x, y, dx, dy, color): self.x = x self.y = y self.dx = dx self.dy = dy self.color = color self.ball = None def test(self): print(self.x) print(self.y) def move(self, canvas): canvas.delete(self.ball) self.x = self.x + self.dx self.y = self.y + self.dy self.ball = canvas.create_oval(self.x - 20, self.y - 20, self.x + 20, self.y + 20, fill=self.color, width=0) if self.x >= canvas.winfo_width(): self.dx = -speed if self.x < 0: self.dx = +speed if self.y >= canvas.winfo_height(): self.dy = -speed if self.y < 0: self.dy = +speed root = tk.Tk() root.geometry("600x400") canvas = tk.Canvas(root, width=600, height=400, bg="white") canvas.place(x=0, y=0) balls = [ Ball(300, 400, 1, 1, "red"), Ball(200, 400, 2, 1, "green"), Ball(300, 200, 1, 2, "blue") ] def loop(): for b in balls: b.move(canvas) root.after(10, loop) root.after(10, loop) root.mainloop() Footnotes 1 「いちばんやさしいPython入門教室」大澤文孝著,(ソーテック社出版,2017). 2 (2020/06/16追記)バックを黒くして,ボールを明るくするとうまく出ます.人間の目のせいでしょうか.. source ~/Desktop/lecture_21s/CompAInfo/tmp.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pandasでfor文を回したものの、遅くてやってられなかった時の話

題目の通りです。 DataFrameを使って各行に色々処理を施したい。 そんなとき、for文を使った行ごとに処理していきますよね。 例えば、 for k in range(len(df)): if df.loc[k,0]>= 20 and df.loc[k,0] <= 29: df.loc[k,0] = 0 elif df.loc[k,0] >= 30 and df.loc[k,0] <= 39: df.loc[k,0] = 1 こんな感じ。これで、tqdmを導入するとわかりますがめっちゃ重い。仕事にならん。 それで下記のように書いたら、同義なんだけどめっちゃ早い。 for index, row in dx.iterrows(): if row[pattern]>= 20 and row[pattern] <= 29: row[pattern] = 0 elif row[pattern] >= 30 and row[pattern] <= 39: row[pattern] = 1 これで大きく変わるなんて。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SpotifyAPIに見る女性声優勢力図【上坂すみれ女史を震源にして】

はじめに どうも、ボン♡キュッ♡ボンは僕のモノ♡ プラチナ☆みゆきである。 さて、開始早々読者諸氏に断っておきたいことがある。 ※本記事は技術記事に見せかけた女性声優考察記事である。 Spotifyとアニソン さて、突然だがアニソンとSpotifyは相性が良いように思う。 なぜなら、アニオタは比較的箱推しの人が多いというのとアニソンだったらなんでも聞く雑食の人が多いように思えるからだ。 現に私もアニソンは特定の曲を聞くというよりはメドレーで聞くということの方が多い。 そういう用途ではApple等で特定の音楽を聞くよりも、Spotifyでレコメンドされたものや他の人が作ったプレイリストを聞く方が面白かったりするんじゃないかと思う。 Spotifyと女性声優 Spotifyが面白い理由の一つに豊富なAPIを提供している事が挙げられる。 割といろんな機能があるのだが、今回はアーティストの関連アーティストを取得することに関心がいった。 (APIレファレンスに関してはこちらSpotify API Reference) 女性声優の関連アーティストを掘っていったら女性声優の勢力図的なものが出来上がるんじゃないかと思ったのだ。 あとは思い立ったが吉日である。 「APIを叩けば解るんだ。いい加減に始めようぜ、魔術師!」 実施手順 OAuth2によるアクセストークンの発行 spotipyによるspotify-apiの活用 アーティストごとの関係データを作成 networkxによる女性声優勢力図の可視化 OAuth2によるアクセストークンの発行 まずSpotifyアカウントを持ってない人はアカウントを作ろう。2秒くらいで終わるだろう。 https://www.spotify.com/jp/ 次にデベロッパー向けサイトでアプリ登録を行う。登録からアクセストークンの発行まで20秒くらいで終わるから親切である。 https://beta.developer.spotify.com/dashboard/ spotipyによるspotify-apiの活用 次に、spotipyによってspotify-apiを活用していく。 spotipyはpythonでspotify-apiを利用するためのライブラリである。ドキュメントはこちら まず、pipからspotipyをインストールする。 $ pip install spotipy 次に、発行したアクセストークンで設定ファイルを作成する。"CLIENT_ID"と"CLIENT_SECRET"は各々のものに置換しよう。 config.py import spotipy client_id = "CLIENT_ID" client_secret = "CLIENT_SECRET" client_credentials_manager = spotipy.oauth2.SpotifyClientCredentials(client_id, client_secret) spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager) まずは試しに上坂すみれ女史の情報を取得してみるとしよう。 本題に入る前に、私の上坂すみれ女史に対する入れ込みについて解説しておく。 私が上坂すみれ女史をよく聞くようになったのは2013年1stシングル「七つの海よりキミの海」がリリースされた頃だったように思う。そこからすげえプロパガンダで啓蒙的な人だなあと思いドハマり。 以降は上坂すみれ女史に憧れて大学の第二言語にロシア語を選択するまでになった。 まことにХорошо! さて、ということで女史の名前は日本語かロシア語で検索したいところだが、あいにく英語名しか対応していないためここは素直に英語名で検索する。 from pprint import pprint from config import spotify name = 'Sumire Uesaka' spotapi_out = spotify.search(q='artist:' + name, type='artist') artist_items = spotapi_out['artists']['items'][0] pprint(artist_items) 結果がこちら {'external_urls': {'spotify': 'https://open.spotify.com/artist/4hRg5l2hXQl3lAzffFF8P8'}, 'followers': {'href': None, 'total': 30901}, 'genres': ['anime', 'denpa-kei', 'seiyu'], 'href': 'https://api.spotify.com/v1/artists/4hRg5l2hXQl3lAzffFF8P8', 'id': '4hRg5l2hXQl3lAzffFF8P8', 'images': [{'height': 640, 'url': 'https://i.scdn.co/image/7757cd7179ec94ac7f516ee6fe581694d0695ad0', 'width': 640}, {'height': 320, 'url': 'https://i.scdn.co/image/26ffea4fdd66890a3f9a7db53f0b474043a25663', 'width': 320}, {'height': 160, 'url': 'https://i.scdn.co/image/1973d72603d14f24d2bba61000aaf9b32f14b850', 'width': 160}], 'name': 'Sumire Uesaka', 'popularity': 51, 'type': 'artist', 'uri': 'spotify:artist:4hRg5l2hXQl3lAzffFF8P8'} ジャンル:[アニメ、電波系、声優]とある。 電波系か~。電波系なんだよな~~。 いやーそこが良いんだよな~~~!!! まったく電波ソングは最高だぜっ! とまあこんな感じで。 spotify-apiはアーティストやアルバム、あとはリコメンデーションなどの情報が取得できるのだ。 続いて、上坂すみれ女史の関連アーティスト情報を見てみよう。 from config import spotify name = 'Sumire Uesaka' spotapi_out = spotify.search(q='artist:' + name, type='artist') artist_items = spotapi_out['artists']['items'][0] artist_id = artist_items['id'] spotapi_out_related = spotify.artist_related_artists(artist_id) atrname_related_list = [] for artname_related in spotapi_out_related['artists']: atrname_related_list.append(artname_related['name']) print(atrname_related_list) 結果がこちら。 ['Maaya Uchida', 'Yui Ogura', 'Yukari Tamura', 'TrySail', '伊藤美来', '大橋彩香', 'Inori Minase', 'Kaori Ishihara', 'Plasmagica', '悠木 碧', 'Yui Horie', 'Suzuko Mimori', 'Wake Up, Girls!', 'Petit Milady', '亜咲花', 'Shiina Natsukawa', '中島愛', ' 田所あずさ', 'fhána', 'DIALOGUE+'] これはSpotifyの関連アーティストと同じ情報なので、本家のUIの検索結果を掲示しておく。 かわいいという意味でまあやたそと関連性があるし、 同じくかわいいという意味で小倉唯ちゃんと関連性があるし、 軍隊を保有するという意味でゆかりんと関連性があるから 妥当な結果だと言えるだろう。 アーティストごとの関係データを作成 上坂すみれ女史に関連するアーティスト情報を取得したところで、次に関連するアーティストの更に関連するアーティストを取得していく。このように関連するアーティストの関連データを作っていくのだ。 関連アーティストの情報はいったんpandasのSeriesとして採取し、DataFrameへと結合していく。 import pandas as pd import matplotlib.pyplot as plt import networkx as nx from pprint import pprint from config import spotify from tqdm import tqdm def find_artists(name): """ 最初の探索 """ artist_df = pd.DataFrame(columns=['artist_name', 'artist_ID', 'genres', 'popularity', 'related_artist_names']) spotapi_out = spotify.search(q='artist:' + name, type='artist') artist_items = spotapi_out['artists']['items'][0] artist_id = artist_items['id'] artid_list = [artist_id] atrname_related_list = [] spotapi_out_related = spotify.artist_related_artists(artist_id) for artname_related in spotapi_out_related['artists']: atrname_related_list.append(artname_related['name']) sr = pd.Series([artist_items['name'], artist_items['id'], artist_items['genres'], artist_items['popularity'], atrname_related_list], index=artist_df.columns) artist_df = artist_df.append(sr, ignore_index=True) return artid_list, artist_df def find_related_artists(depth): """ depth分類似するアーティストを探索する """ # 名前は英語名でないと正常に返ってこないので注意 artid_list, artist_df = find_artists('Sumire Uesaka') artid_list_tail = 0 for i in range(depth): artid_list_head = artid_list_tail artid_list_tail = len(artid_list) for artid in tqdm(artid_list[artid_list_head:artid_list_tail]): spotapi_out = spotify.artist_related_artists(artid) for artid_related in spotapi_out['artists']: # 類似のアーティストリストを作成 artname_related2_list = [] spotapi_out_related = spotify.artist_related_artists(artid_related['id']) for artname_related2 in spotapi_out_related['artists']: artname_related2_list.append(artname_related2['name']) artid_list.append(artid_related['id']) sr = pd.Series([artid_related['name'], artid_related['id'], artid_related['genres'], artid_related['popularity'], artname_related2_list], index=artist_df.columns) artist_df = artist_df.append(sr ,ignore_index=True) return artid_list, artist_df artid_list, artist_df = find_related_artists(1) print(artist_df) 得られたDataFrameがこちら。 次にDataFrameの中から関連アーティストの情報だけを抽出したdictデータを作る。 ###### ### 前回のコードの続きに書く ###### # アーティストの関係辞書を作る artdic = {} for i in range(len(artid_list)): artdic[artist_df.iloc[i,0]] = [] for artname_related in artist_df.iloc[i,4]: artdic[artist_df.iloc[i,0]].append(artname_related) print(artdic) 結果は長いので一部を以下に記載。 {'Sumire Uesaka': ['Maaya Uchida', 'Yui Ogura', 'Yukari Tamura', 'TrySail', '伊藤美来', '大橋彩香', 'Inori Minase', 'Kaori Ishihara', 'Plasmagica', '悠木 碧', 'Suzuko Mimori', 'Yui Horie', 'Wake Up, Girls!', 'Petit Milady', '亜咲花', 'Shiina Natsukawa', '中島愛', '田所あずさ', 'fhána', 'i☆Ris'], 'Maaya Uchida': ['Yui Ogura', 'Inori Minase', 'Alisa Takigawa', 'Sangatsu no Phantasia', 'mimimemeMIMI', 'Aimi', 'DIALOGUE+', 'Saori Hayami', 'Wake Up, Girls!', 'Yoshino Nanjo', 'i☆Ris', 'Shiina Natsukawa', 'Yui Horie', 'Petit Milady', 'Machico', 'Konomi Suzuki', 'Ayana Taketatsu', '大橋彩香', 'TrySail', 'Yu Serizawa'], 'Yui Ogura': ['Kaori Ishihara', 'Eri Kitamura', 'Maaya Uchida', 'Inori Minase', 'Yukari Tamura', 'Ayana Taketatsu', 'Yoshino Nanjo', 'Aimi', 'Saori Hayami', 'Amamiya Sora', 'Shiina Natsukawa', 'Yui Horie', 'Petit Milady', 'Yu Serizawa', 'Aki Toyosaki', '大橋彩香', 'TrySail', 'Momo Asakura', '伊藤美来', 'Suzuko Mimori'], 'Yukari Tamura': ['Yui Ogura', 'Inori Minase', 'Nana Mizuki', 'Yui Horie', 'KOTOKO', '栗林みな実', 'Miyuki Hashimoto', 'Petit Milady', '大橋彩香', 'Sphere', "May'n", 'TrySail', 'Maaya Uchida', '佐咲紗花', '田所あずさ', 'ELISA', '茅原実里', 'ChouCho', 'Eri Kitamura', 'Suzuko Mimori'], ... } networkxによる女性声優勢力図の可視化 最後に、前項で得たアーティストごとの関係辞書をもとにnetworkxを使って女性声優勢力図を可視化していく。 networkxというのはpythonでグラフを可視化するためのライブラリである。今回はちょっとしか使わないがかなり汎用性の高いライブラリであるように思う。 ドキュメントはこちら あと、初学者の方はこちらから読むと理解がスムーズかもしれない。 まずはnetworkxをインストール。 $ pip install networkx それでは上坂すみれ女史を震源にした関連アーティスト20人の関係データ。つまり重複を除かずに400人分のデータを可視化していく(実際には重複するアーティストは一意にするので400人よりは少ない) import pandas as pd import matplotlib.pyplot as plt import networkx as nx from pprint import pprint from config import spotify from tqdm import tqdm ###### ### 前回のコードの続きに書く ###### # nodeとedgeの設定 G = nx.Graph() G.add_nodes_from(list(artdic.keys())) for parent in artdic.keys(): relation = [(parent, child) for child in artdic[parent]] G.add_edges_from(relation) # sizeとcolorの設定 average_deg = sum(d for n, d in G.degree()) / G.number_of_nodes() sizes = [1000*d/average_deg for n, d in G.degree()] colors = [i/len(G.nodes) for i in range(len(G.nodes))] plt.figure(figsize=(20,20)) nx.draw(G, font_family='Yu Gothic', with_labels=True, node_size=sizes, node_color=colors) plt.savefig('depth1.png') plt.show() 得られた結果がこちら。 震源地である南の方の本土に女性声優が集まっているのがわかる(ここでは上下左右を東西南北と形容する) 上坂すみれ女史(Sumire Uesaka)は本土の少し上の方に位置している。 上坂すみれ女史を震源にしたが、本土の中心(女性声優の重心)は[内田真礼、大橋彩香、水瀬いのり、小倉唯、悠木碧、伊藤美来]などのいかにも女性声優達で構成されているのは面白い。 まあ、上坂すみれ女史は女性声優的特異点なので当然の帰結だろうか。 あとは関係ネットワークの特徴的に電車の路線図みたいに関係性のハブとなる人物が可視化されているのが面白い。 例えば、北部のSHOW BY ROCK!!島のハブを成すのはPlasmagicaであったり、西部のアニソンシンガーに接続するのは堀江由衣であったり、東部のプリパラ地方に向かうためには芹澤優から所属グループi☆Risを経由する必要があること等が伺える。 では最後に、次数を増やしておよそ8000人(20 ^ 3)規模の女性声優勢力図を確認していこう。 spotify_api.py import pandas as pd import matplotlib.pyplot as plt import networkx as nx from pprint import pprint from config import spotify from tqdm import tqdm def find_artists(name): """ 最初の探索 """ artist_df = pd.DataFrame(columns=['artist_name', 'artist_ID', 'genres', 'popularity', 'related_artist_names']) spotapi_out = spotify.search(q='artist:' + name, type='artist') artist_items = spotapi_out['artists']['items'][0] artist_id = artist_items['id'] artid_list = [artist_id] atrname_related_list = [] spotapi_out_related = spotify.artist_related_artists(artist_id) for artname_related in spotapi_out_related['artists']: atrname_related_list.append(artname_related['name']) sr = pd.Series([artist_items['name'], artist_items['id'], artist_items['genres'], artist_items['popularity'], atrname_related_list], index=artist_df.columns) artist_df = artist_df.append(sr, ignore_index=True) return artid_list, artist_df def find_related_artists(depth): """ depth分類似するアーティストを探索する """ # 名前は英語名でないと正常に返ってこないので注意 artid_list, artist_df = find_artists('Sumire Uesaka') artid_list_tail = 0 for i in range(depth): artid_list_head = artid_list_tail artid_list_tail = len(artid_list) for artid in tqdm(artid_list[artid_list_head:artid_list_tail]): spotapi_out = spotify.artist_related_artists(artid) for artid_related in spotapi_out['artists']: # 類似のアーティストリストを作成 artname_related2_list = [] spotapi_out_related = spotify.artist_related_artists(artid_related['id']) for artname_related2 in spotapi_out_related['artists']: artname_related2_list.append(artname_related2['name']) artid_list.append(artid_related['id']) sr = pd.Series([artid_related['name'], artid_related['id'], artid_related['genres'], artid_related['popularity'], artname_related2_list], index=artist_df.columns) artist_df = artist_df.append(sr ,ignore_index=True) return artid_list, artist_df artid_list, artist_df = find_related_artists(1) print(artist_df) # アーティストの関係辞書を作る artdic = {} for i in range(len(artid_list)): artdic[artist_df.iloc[i,0]] = [] for artname_related in artist_df.iloc[i,4]: artdic[artist_df.iloc[i,0]].append(artname_related) print(artdic) # nodeとedgeの設定 G = nx.Graph() G.add_nodes_from(list(artdic.keys())) for parent in artdic.keys(): relation = [(parent, child) for child in artdic[parent]] G.add_edges_from(relation) # sizeとcolorの設定 average_deg = sum(d for n, d in G.degree()) / G.number_of_nodes() sizes = [1000*d/average_deg for n, d in G.degree()] colors = [i/len(G.nodes) for i in range(len(G.nodes))] # 探索する次数によってfigsizeを変更 plt.figure(figsize=(50,50)) nx.draw(G, font_family='Yu Gothic', with_labels=True, node_size=sizes, node_color=colors) plt.savefig('depth2.png') plt.show() 結果がこちら。 多すぎてよく見えない。 少し拡大しよう。 左の紫色の箇所。ここが女性声優地方である。 中心部にはゆいかおり、悠木碧、田所あずさ、大橋彩香等で構成されているのがわかる。 その中心部に攻め入るように南東の方から女性声優第6世代の水瀬いのり、雨宮天、南西の方から第7世代の伊藤美来、少し離れて鬼頭明里、楠木ともりが位置しているのがわかる。 参考:女性声優における世代分け 参考:女性声優世代一覧 参考:女性声優界の黒船。第7世代から伊藤美来氏 さて、上図の解説に戻るが女性声優地方を左に見て右にあるのがアニソン地方・ゲーソン地方である。 女性声優地方から、今井麻美、田村ゆかり、Machico、亜咲花をハブにしてアニソン地方に接続していることがわかる。 アニソン地方を構成するのは、鈴木このみ、ChouCho、fhana、春奈るな、ClariS等であることがわかる。 そこからゲーソン地方に接続している。 アニソン地方を構成するのは、KOTOKO、川田まみ、fripside、彩音、橋本みゆき等だろうか。 ちなみに、私はゲーソンも大好きである。 ゲーソンは良いゾ~~~ さて、本土の女性声優・アニソン・ゲーソン地方を概観したところで最後に少し離れた地方を分析して終わろう。 まずは南部。 なんと言っても注目すべきは「Petit Rabbit's」 この世の癒しを全て詰め込んだような楽曲を提供するご存知ごちうさグループである。 参考:ご注文はうさぎですか? ご注文はうさぎに決まっている。なんならうさぎのうさぎ添え、うさぎマシマシまである() ちなみに、私のおすすめ曲は「魔法少女チノ」 明日も強く生きようという勇気を貰える気がするのだ。 次に北西部。 讃州中学勇者部やスタァライト九九組がハブになっていることがわかるだろう。また、バンドリアーティストも散見される。 最後にに西部。 ラブライブ!勢力が幅を利かせていることがわかる。 あとラブライブ!の横にミルキィホームズが位置しているのも面白い。ミルキィホームズはラブライブ!声優である三森すずこ、徳井青空が所属していることから実質ラブライブという意見もあった。 あとは、22/7という2.5次元アイドルユニットをハブにしてNGT48などの3次元アイドルに接続している様子も伺える。 おわりに これにて分析は終わりだが最後に一つ言っておきたいことがある。 北西部、西部。 ほとんど三森すずこじゃねぇか!!! いや、私は三森すずこ女史大好きだから良いんだけどね。 讃州中学勇者部もスタァライト九九組もラブライブ!もミルキィホームズも私から言わせれば三森すずこなのである。 なんという三森すずこ勢力、さすがはブシ〇ード。 (ちなみに私のおすすめ曲は「ファッとして桃源郷」) 参考:女性声優界の絶対強者。三森すずこ氏 Code 今回のCodeと結果画像はこちらに格納している。 記事では8,000人規模の紹介までに留めたが、160,000人規模のdepth3.pngまで掲載している。 参考 Spotify APIで好きなアーティストの繋がりを可視化してディグる 【図解】Python NetworkXの使い方【サンプルコード有】 | ネットワーク分析・可視化 [Python]NetworkXでQiitaのタグ関係図を描く spotipy Document networkx Tutorial
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】for文の"_"(アンダースコア)の処理速度

概要 普通のfor文と"_"を使ったfor文の処理速度を比較します。 処理速度の比較 それぞれ1億回ループ処理をします。 処理速度の比較 import time n = int(1e+9) normal = 0 under_score = 0 # 普通のfor文 start_normal = time.time() for i in range(n): normal += 1 print('normal:{:.5f} sec'.format(time.time() - start_normal)) # "_"を使ったfor文 start_under_score = time.time() for _ in range(n): under_score += 1 print('under_score:{:.5f} sec'.format(time.time() - start_under_score)) 出力結果 normal:79.93061 sec under_score:79.05743 sec 結論 "_"を使った方が約1%速い結果になりました。 おまけ 使っているところを見たことはないが、"_"は変数として機能しているようです。 変数名のルールは以下のようになっています。 ・使用できる文字は a ~ z 、 A ~ Z 、 0 ~ 9 、アンダーバー(_)、漢字など ・一文字目に数値(0~9)は使用できない ・一文字目にアンダーバーは使用できるが特別な用途で使用されているケースが多いので通常は使用しない方がいい ・大文字と小文字は区別される ・予約語は使用できない (引用:https://www.javadrive.jp/python/var/index1.html#section1) # 普通のfor文 print('---普通のfor文---') for i in range(3): print('値:{}, 型:{}'.format(i, type(i))) # "_"を使ったfor文 print('---"_"を使ったfor文---') for _ in range(3): print('値:{}, 型:{}'.format(_, type(_))) 出力結果 ---普通のfor文--- 値:0, 型:<class 'int'> 値:1, 型:<class 'int'> 値:2, 型:<class 'int'> ---"_"を使ったfor文--- 値:0, 型:<class 'int'> 値:1, 型:<class 'int'> 値:2, 型:<class 'int'>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サプライチェーンマネジメントのためのデータサイエンス/オペレーションズ・リサーチ入門

はじめに この記事では、会社でデジタルトランスフォーメーション(以下、DX)として、幸か不幸かサプライチェーンマネジメント(以下、SCM)に関わることになった人たちが知っておくと良さそうなデータサイエンス/オペレーションズ・リサーチに関する情報をまとめました。 仕事でSCMのDXを担当することになったけど、何から取り組んでいいかわからないという方の参考になれば幸いです。 目次 サプライチェーンマネジメント(SCM)とは SCMとデータサイエンスとオペレーションズ・リサーチ よくあるSCM課題とアプローチ SCM関係技術情報の収集場所 サプライチェーンマネジメント(SCM)とは サプライチェーンとは、製造業等における原料調達→生産→物流→販売商品までのプロセス全体のことです。 SCMとはこうした”モノの流れ”と”情報の流れ”を結びつけ、サプライチェーン全体で情報を連携・活用して全体最適化を図る経営手法です。 SCMとデータサイエンスとオペレーションズ・リサーチ 前述のように、SCMでは”モノの流れ”と”情報の流れ”が重要になります。 この”情報の流れ”から得られるデータの分析・活用にデータサイエンス(統計や機械学習など)が関わってきます。 また、SCMはオペレーションズ・リサーチ(以下、OR)とも密接な関係があります。 ORとは数理最適化・シミュレーションなど、人間の高度な意思決定を支援する数学的アプローチのことです。 例えば、在庫や配送の最適化など、いくつかの制約がある状況の中でコストや時間といった目標を最大化するときなど、SCMで扱うプロセスの最適化にORは使われています。 様々なデータがあらゆる場所で発生し、複雑に絡みあう現代のサプライチェーンを最適化するためには、データサイエンスとORを両輪とした複合的なアプローチが必要です。 よくあるSCM課題とアプローチ 以下ではよくあるSCM課題とアプローチ方法についてPythonコードも交えて紹介していきます。 ここでは世の中にあるごく一部を紹介しますが、詳しくは末尾の文献が参考になりますので適宜ご参照ください。  需要予測 需要予測では代表的なものとして、移動平均法や回帰分析といったデータサイエンスのアプローチがよく使われます。 移動平均法 移動平均法では窓の大きさ分だけデータを切り出してその区間の平均を取りますので、変化の傾向を捉えることができます。(よく株価のチャートでも見かけますね) 移動平均は株式チャートや月次の在庫量履歴のように、データの前後に時系列的な関係性があるデータ(時系列データ)で使えます。 移動平均/移動標準偏差 #例として、データ列から12個分のデータを取り出して平均や標準偏差を計算。 #窓を一行ずつスライドさせていくことで、移動平均が求められる。 rolling_mean = df.rolling(window = 12).mean() rolling_std = df.rolling(window = 12).std() 回帰分析 需要予測の手法としては回帰分析が一般的です。 例えば、知りたい需要(目的変数)とそれに関係しそうなデータ(説明変数)があれば、Scikit-learnライブラリのLinerRegression()やPLSRegression()を使って簡単に回帰分析ができます。 ここでは、部分的最小2乗法(Partial Least Squares Regression:PLS)についてコード例を書きたいと思います。 回帰分析(PLS) #サンプルデータセットをimport from sklearn import datasets #bostonにデータを格納 boston = datasets.load_boston() #pandasのデータフレーム形式に変更してx_dfに格納、目的変数をy_df x_df = pd.DataFrame(boston.data, columns=boston.feature_names) y_df = boston.target #PLS regression from sklearn.cross_decomposition import PLSRegression #モデルをセット、PLSの潜在変数(n_component)は今回は適当に2程度に。 pls = PLSRegression(n_components=2) #トレーニング用データを入れてトレーニング→トレーニング済みモデルができる。 pls.fit(x_df, y_df) #最後はトレーニングしたPLS回帰モデルにテストデータ(x_test)を入れて未知のデータに対しての目的変数(y_pred)を予測する。 y_pred = pls.predict(x_test) 生産計画・在庫量の最適化 最適化問題では線形計画法のような目的関数と制約条件からなる問題をシンプレックス法などのアルゴリズムで解く、というようなORのアプローチがよく使われます。 また、最適化計算は専用のソフトを買わなくても無料で使えるPythonライブラリが充実していますので、まずはPythonでやってみると良いと思います。 線形計画法 線形計画法とは下図のような線形の目的関数(x1+2*x2)と線形の制約条件(①~⑤)からなる最適化問題(線形計画問題)を解く手法です。 ここでは、参考例として下図のような単純な線形計画問題をPythonライブラリのPuLPを使って解いていきます。 線形計画問題の一般化した式や細かな概念については参考文献1、PuLPの詳細については参考文献2を参照いただければと思います。 線形計画法 import pulp #数理モデル(線形計画)の箱を作成。今回は最大化問題。 problem = pulp.LpProblem("sample_problem", pulp.LpMaximize) #変数を設定。x1とx2の2変数。どちらも最小値0。 x1 = pulp.LpVariable("x1", lowBound=0)#④ x2 = pulp.LpVariable("x2", lowBound=0)#⑤ #目的関数を設定 problem += x1 + 2*x2 #制約条件を設定 problem += x1 + x2 <= 6#① problem += x1 + 3*x2 <= 12#② problem += 2*x1 + x2 <= 10#③ #ソルバーで上で設定した線形計画問題を解く。 problem.solve() #結果確認 print("Result") print("x1 :",x1.value())#3.0 print("x2 :", x2.value())#3.0 整数計画法 ナップサック問題に代表される組み合わせ最適化の問題を解く手法です。 ナップサック問題とは「大きさの決まったナップサックに、どの品物を選んで詰めると価値の合計が最大になるか?」という問題で、先程の線形計画問題と違って、変数(品物)は小数点以下の数字を取れません。 解き方としては動的計画法や貪欲法、近似解法、ソルバーによる解法など色々ありますが、ここではPuLPを使って、以下のような価値最大化のナップサック問題を解いてみます。 整数計画問題 import pulp #牛乳、おにぎり、サンドイッチの容量のリスト。それぞれ一個ずつある。 w=[1, 0.7, 0.5] #牛乳、おにぎり、サンドイッチの値段のリスト v=[200, 150, 120] #制約条件:ナップサックの容量 W = 2 #後で使うfor loopのためにリストの長さを変数rに格納 r = len(w) #数理モデルの箱を作成。今回は最大化問題。 problem = pulp.LpProblem(sense = pulp.LpMaximize) #変数の設定。xというリストを使う。 x = [pulp.LpVariable("x%d"%i, cat = LpBinary) for i in range(r)] #目的関数を設定 problem += pulp.lpDot(v, x) #制約条件を設定 problem += pulp.lpDot(w, x) <= W #ソルバーで上で設定した整数計画問題を解く。 problem.solve() #結果確認 print('最大価値:{} / 組み合わせ:{}'.format(value(problem.objective), [i for i in range(r) if value(x[i]) > 0.5])) #最大価値:350.0 / 組み合わせ:[0, 1] →つまりリストの0番目と1番目にある牛乳とおにぎりの組み合わせが最適。 混合整数線形計画法 倉庫配置問題や配送問題、ネットワーク設計問題などに取り組む際に使える手法です。 読んで字のごとく線形計画法と整数計画法をあわせたもので、変数の一部は整数でなければならないという制約があるような問題です。 残念ながら手元にお手軽にできそうなコンパクトな例が無いので、コードでの紹介は割愛しますが(例が見つかったら追記します)、ブレインパッドブログにて在庫問題と配送問題を組み合わせた混合整数線形計画問題について参考になる例がありましたので、リンク先を参考にしていただければと思います。 SCM関係技術情報の収集場所 日本オペレーションズ・リサーチ学会 私自身、何から取り組めばいいかわからないときに探索していてたどり着きましたが、SCMに限らず幅広い技術(数理最適化、金融工学、グラフ・ネットワークなどなど)を扱う学会です。 過去の学会誌はPDFで公開されており、良質な情報収集ができます。 ブレインパッドの数理最適化ブログ 最近、数理最適化に力を入れているブレインパッドのデータサイエンティストたちが数理最適化(先に紹介した線形計画法などの総称)についてまとめています。 数理最適化手法の紹介のみならず、SCM系のデータサイエンティストとしての心構えなど大変参考になる記事がたくさんあります。 その他(随時更新します) おわりに 今回はSCMに取り組むにあたって、データサイエンスやORといったデータの取扱に関する技術的な側面をピックアップして紹介しました。 一方、サプライチェーンのプロセス一つ一つの現場の実務において最も重要なことは、AIを使って需要予測を精度良くすることや数理的に最適な配送計画を出すことではなく、現実に発生しうる誤差を管理して、異常事態が発生したら適切な行動が取れるように準備をすることです。 データサイエンスやORはあくまで意思決定のための手段であることを念頭に、全体最適を意識してSCMに取り組むことが重要です。 以上、浅学ながら私がSCMに取り組むにあたって収集した情報・学んだことの中で参考になりそうなものを紹介させていただきましたが、皆様のご参考になっていれば幸いです。 参考文献/記事 Python言語によるサプライ・チェイン・アナリティクス サプライチェーンマネジメント(SCM)とは何か? オペレーションズ・リサーチとは 需要予測の手法 単体法を理解しよう! 線形計画法入門 部分的最小二乗回帰(Partial Least Squares Regression, PLS)~回帰分析は最初にこれ!~ 線形計画法超入門 Python言語による実務で使える100+の最適化問題 ナップサック問題を色々な方法で解いてみた PuLPによる数理最適化超入門
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python][OpenCV]グレースケール画像を単色濃淡画像に変換する

はじめに 目標:Pythonを用いて,グレースケール(またはカラー画像をグレースケール化したもの)を単色の濃淡で表したい.例えばこういう感じ (https://www.qam-web.com/?p=13378) 検索してもサンプルコードに行き当たらなかったので,実装した. 方法 画像をHSV色調で考えて,彩度に画素値を入れることで擬似的に表現した. 色相参考:https://www.peko-step.com/html/hsv.html 実行環境 Python 3.8.8 anaconda 4.10.1 opencv 4.0.1 OpenCV導入: https://qiita.com/SatoshiGachiFujimoto/items/94da93f88578b87f6a89 (上記のanacondaではデフォルトでインストール済みだった) 元画像 Lena (https://en.wikipedia.org/wiki/File:Lenna_(test_image).png) を使用. コードと同じディレクトリに入れておく. コード import numpy as np import cv2 def main(): img = cv2.imread('Lenna_(test_image).png', cv2.IMREAD_GRAYSCALE) img_color = gray2SingleColor(img, 0) # 第2因数をHSV色調のHで指定,0は赤 cv2.imshow("image", img_color) cv2.waitKey(0) cv2.destroyAllWindows() return def gray2SingleColor(img, h_hsv): # 黒の部分を指定色に置き換え height, width = img.shape[:2] hsv = np.zeros((height, width, 3)) hsv[:, :, 0] = h_hsv/2 # 0-179 for opencv hsv[:, :, 1] = (255-img) hsv[:, :, 2] = 255 im = cv2.cvtColor(np.array(hsv, np.uint8), cv2.COLOR_HSV2BGR) return im def gray2SingleColor_rev(img, h_hsv): # 画像強度が大きいところを指定色の濃さに置き換え height, width = img.shape[:2] hsv = np.zeros((height, width, 3)) hsv[:, :, 0] = h_hsv/2 # 0-179 for opencv hsv[:, :, 1] = img hsv[:, :, 2] = 255 im = cv2.cvtColor(np.array(hsv, np.uint8), cv2.COLOR_HSV2BGR) return im if __name__ == '__main__': main() 結果 グレースケール読み取り(変換前) 黒の部分を指定色に置き換え 画像強度が大きいところを指定色の濃さに置き換え
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DynamoDBのデータ型とPythonのデータ型の対応

DynamoDBのデータ型は以下があります。Pythonのboto3でDynamoDBからデータを取得したときに、Pythonではどういうオブジェクトになるのかを確認しました。 String Binary Number Boolean Null List Map Set of String Set of Binary Set of Number DynamoDBとPythonのデータ型の対応 boto3のclientを使う場合とresourceを使う場合とで、扱いが異なります。 session.client("dynamodb").get_item は値とともにデータ型の情報が付随してきます。 session.resource("dynamodb").Table(table_name).get_item は直接Pythonのオブジェクトに変換されて取得できます。 DynamoDBでの型 client resource String {'S': 'abc'} 'abc' Binary {'B': b'Hello,\nWorld!\n'} Binary(b'Hello,\nWorld!\n') Number {'N': '1234'} Decimal('1234') Boolean {'BOOL': True} True Null {'NULL': True} None List {'L': [{'S': 'abc'}, {'N': '123'}]} ['abc', Decimal('123')] Map {'M': {'entry1': {'S': 'abc'}, 'entry2': {'S': 'def'}}} {'entry1': 'abc', 'entry2': 'def'} Set of String {'SS': ['abc', 'def']} {'abc', 'def'} Set of Binary {'BS': [b'Hello,\n', b'World!\n']} {Binary(b'World!\n'), Binary(b'Hello,\n')} Set of Number {'NS': ['123', '456']} {Decimal('456'), Decimal('123')} 2列目は session.client("dynamodb").get_item で取得した結果です。 3列目は session.resource("dynamodb").Table(table_name).get_item で取得した結果です。 いずれもPythonの pprint での出力です。 Pythonコード 確認に使ったPythonのソースコードです。 from pprint import pformat import boto3 profile = "default" table_name = "sample" record_id = "test1234" session = boto3.session.Session(profile_name = profile) dynamodb_client = session.client("dynamodb") res = dynamodb_client.get_item( TableName = table_name, Key = { "Id": { "S": record_id, }, }, ) for col, value in res["Item"].items(): print(f"{col}: {pformat(value)}") print() dynamodb_resource = session.resource("dynamodb") table = dynamodb_resource.Table(table_name) res = table.get_item( Key = { "Id": record_id, }, ) for col, value in res["Item"].items(): print(f"{col}: {pformat(value)}")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nixとはなにか、パッケージマネージャとしての使い方の紹介(書きかけ)

はじめに Nixの説明と簡単な使いかたを紹介したいと思います。 NixというとパッケージマネージャやOSとしてのNixOSの意味がありますが、ここではパッケージマネージャとしてのNixを説明します。 Nixをパッケージマネージャとして使う場合は手元のWindows, Mac, LinuxでOSのインストールなしに利用することができます。 パッケージマネージャだけでなく、開発環境を含めた開発フローについてpythonを使った例を示します。 NixではパッケージのことをDerivationと呼びます注意してください。Nixでは独特の用語が多いので用語集をつけました参考にしてください。 Nixってなに?なにがいいの?Dockerよりなにがいいの? あるソフトを使う場合にOSのバージョンを気にしたり、関連するソフトをインストールするかと思います。 MacのユーザーとLinuxのユーザーの開発環境が違っていてつらいという状況もあるでしょう。 同じソフトだけどバージョン違いのものをいれて試したいこともあるでしょう。 Nixをつかうとシステムを汚さずに使いたいソフトをどこででもインストールできます。 Nixはパッケージの依存関係をハッシュで管理しています。 パッケージを作るのに必要なパッケージのリストとパッケージのソースコードのアーカイブのハッシュをつくってそれをもとに出力先を決めます。詳しくはこちらを参照。 Dockerとの違いが気になってくるでしょう。Dockerはパッケージをいれた時点のファイルを 再現性 バイナリキャッシュ 独自パッケージの提供が簡単 MacでもLinuxでも同じパッケージを利用可能 いいことばかりだけではなく、Nixではなにがつらいのか紹介しておきます。 使いたいパッケージが公式サイトで提供がない。 コミュニティーが小さい。 既存のパッケージのコードがよくわからない。 nix expressionのデバッグが難しい。 ストレージを多く使う傾向がある。 インストール方法 下記を実行するだけです。LinuxでもMacでも同様です。Windowsの場合はWSL2上で実行しましょう。 $ curl -L https://nixos.org/nix/install | sh パッケージの管理の仕組み パッケージ管理は非常にシンプルで次のフローで行います。 パッケージマネージャの使い方 パッケージマネージャとしてのコマンドを紹介します。 こちらのコマンドはユーザーの環境内でグローバルに設定されるので、 開発環境を統一したい場合には直接インストールすることは避けたほうがいいです。 インストール nix-env -i "パッケージ名" 特定のチャネルのパッケージをインストール nix-env -f https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz -iA firefox アンインストール nix-env -e "パッケージ名" インストール済みのパッケージの一覧 nix-env -q インストール可能なパッケージの一覧 nix-env -qa パッケージの構成 パッケージの作り方 Derivation Sandbox ファイル構成 再現性の高いパッケージの作り方(NivとFlakes) https://www.tweag.io/blog/2020-05-25-flakes/ https://nixos.wiki/wiki/Flakes https://serokell.io/blog/practical-nix-flakes 開発環境の作り方 Pythonの使い方(Wheelの利用方法) CUDAの使い方 OpenGLの使い方 用語集 nix : パッケージマネージャのコマンド nixpkgs : nixのパッケージ一覧をまとめたもの。 https://github.com/nixos/nixpkgs で提供しています。 derivation : パッケージのこと、debianであればdebファイルのこと。 NixOS : nixをパッケージマネージャとしてつかうlinuxのディストリビューション Nix言語 : パッケージを記述する言語、nix expressionともいう。 チャンネル:パッケージ一覧のセットorバージョン、ubuntuだったら18.04とか20.04とか。 Nixストア: パッケージをインストールしているディレクトリ(/nix/store) 参考文献 https://serokell.io/blog/what-is-nix https://nixos.wiki/wiki/Flakes https://github.com/Tokyo-NixOS/Tokyo-NixOS-Meetup-Wiki/wiki/terminology https://nixos.org/manual/nixos/stable/ https://nixos.org/guides/nix-pills/ https://nixos.org/manual/nix/unstable/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

D-Wave量子アニーリングマシンでマインスイーパ解いてみた

はじめに 東北大学で主催している量子アニーリングマシンのワークショップに参加していて、D-Waveマシンの使い方を覚えたのでマインスイーパを量子アニーリングで解いてみました。 とりあえず、作ってみた粗い内容なので説明追加の依頼があればできるだけ対応します。 下記は、ワークショップの公式ページと過去4回分の講義のYoutubeアーカイブです。 Youtubeの再生時間見るとびっくりしますが、参加者の質問に大関先生が文字通り”全て”答えています。 授業に使ったJupyter-notebookがgoogleコラボラトリーに用意されているので、プログラミング初心者の方でもはじめられる内容になっています。 授業でカナダのD-Waveマシンに指令出して計算する方法も解説しています。 これまで4回分の講義動画 この授業を無料で受けられる、しかも後から見直せるのは素晴らしい時代です! 本記事では、量子アニーリングや組合せ最適化問題の詳細な説明は、これら講義にお任せするとして、、 本格的な実応用を検討するワークショップの前に個人的に練習で作ってみた量子アニーリングでマインスイーパを解くプログラムを解説してみます。 D-Waveマシンの利用 D-Waveのクラウドサービスに登録すると無料で1分間分、量子アニーリングマシンを使うことができます。 1分間は少なく見えますが、一度の計算のはデフォルトで20μ秒なのでたくさん使えます。 本記時のコードをD-Waveの実機に計算させる場合は、アカウント登録してAPI-tokenを取得する必要があります。 量子アニーリングで解ける問題 詳しい解説は、大関先生の授業のYoutubeを見てください。 組合せ最適化問題を上手に解くことができます。 世の中のどんなことが組合せ最適化問題として扱えるのか考えていくことで量子アニーリングの利用が広がっていきそうです。 マインスイーパを量子コンピュータで解くために マインスイーパ Windowsのゲームにいて授業や仕事が退屈な時のお供だった、あのゲームです。 ルールを箇条書きにしてみると下のような感じです。 初期情報としてプレイヤーには設置された地雷の数が情報として与えられる。 地雷の入っていないと思うマスを開いていく。 開いたマスに地雷が無ければ、そのマスの周囲8マスにある地雷の数が与えられる。開けたマスの周囲に爆弾 がなければ、爆弾に隣接するマスまで一気に開く。 地雷の入っているマス以外全てのマスを開けるとプレイヤーの勝ち。 開いたマスに地雷があれば負け。 量子アニーリングで解く方法を考える マインスイーパを解くときはヒントして表示される8Cell(マス)の爆弾の数を頼りに、そこに隣接する8Cellの爆弾のあるなしを予想します。 量子アニーリングのqbitは0,1を出力してくれるので、 爆弾がある状態:1 爆弾がない状態:0 としてヒントのCellの隣接する8Cellを量子アニーリングで爆弾のある/なしを予測します。 ヒントの数字のCellを見つけて、その隣接で開いていないCellにqbitを配置する。 マインスイーパはそもそも確率の問題になる部分があるので、量子アニーリングで何回も解かせてその平均を確率として出させる。 基本の (隣接するCellの出力の合計) - (ヒントの数字) = 0 を満たすようにCellに0/1を配置すれば良いということになります。 ヒントの数字はたくさんあるので、重ね合わせてこれを満たすようにすれば良いということ。 ちゃんとまとめると下のような感じです。 QUBO行列 QUBO行列は、i番目のqbitからj番目のqbitへの関係を表す行列です。 マインスイーパでは、ヒントの数字を介して関係しているqbitとの接続を考える。 考え方のスライドを作ったので下記に示します。 i番目のqbitに着目して、隣接Cellにヒントの数字があるか調べる、そのヒントの数字Cellに隣接のCellをj番目のqbitとする。 エネルギーのEの式を展開して、 *i != jの時は Q[i][j] += lam *i == jの時は Q[i][j] += lam - 2 * lam * (ヒントの数字) となります。 あとはひたすらコーディグするだけ。。。 ソースコード コードの解説なしでいきなり全部載せてしまってます。 マインスイーパのコードと量子アニーリングのコード合わせたものになっています。 D-Waveではなく、OpenJIJを有効しています。 (D-Waveだと一瞬で無料枠の1分使い切りそうでした。。。) D-Waveに投げたい人は、コメントにしている、token, endpoint, samplerを有効して試してください。 minesweeper-qa.py import sys import numpy as np import random #from dwave.system import DWaveCliqueSampler from openjij import SQASampler #token = '***' # 自分のAPI-keyを入れる #endpoint = 'https://cloud.dwavesys.com/sapi/' Nsample = 100 # 量子コンピュータで解く回数 Dwave使うときは10くらいにした方がいいです lam = 1.0 # QUBO行列のハイパーパラメータ ## マインスイーパの設定 M = 10 # 横のCellの数 N = 10 # 縦のCellの数 nMines = 25 # Number of mines openFlagArray = np.zeros((M, N)) # 開かれているCellのフラグ, 0:not open, 1:open mineArray = np.zeros((M, N)) # 爆弾の入っているCell, 0:not mine, 1:mine mineNumArray = np.zeros((M, N)) # 周囲の爆弾の数を格納するArray ### minesweeper用の部品 ### # 盤面表示関数 def printBoard(openFlagArray, mineArray, mineNumArray): print(" ",end="") for i in range(M): print("{0:>2d}".format(i),end="") print("") for j in range(N): print("{0:4d}".format(j),end="") for i in range(M): if(openFlagArray[i][j] == 0): print("| ",end="") else: if(mineArray[i][j] == 1): print("|*",end="") ## 爆弾のCellは* elif(mineNumArray[i][j] == 0): print("|-",end="") ## 0のCellは- else: print("|{0:1d}".format(int(mineNumArray[i][j])),end="") print("|") # 解答表示関数 def printAnswer(mineArray, mineNumArray): print(" ",end="") for i in range(M): print("{0:>2d}".format(i),end="") print("") for j in range(N): print("{0:4d}".format(j),end="") for i in range(M): if(mineArray[i][j] == 1): print("|*",end="") ## 爆弾のCellは* elif(mineNumArray[i][j] == 0): print("|-",end="") ## 0のCellは- else: print("|{0:1d}".format(int(mineNumArray[i][j])),end="") print("|") # Cellを開く関数、ゼロ(周囲 8 Cellに爆弾なし)のCellは自動で開く def openCell(ox, oy, openFlagArray, mineArray, mineNumArray): if (ox < M and oy < N and ox >= 0 and oy >= 0): ## 問題サイズ内か判定 openFlagArray[ox][oy] = 1 ## 対象のCellの開封フラグを1に変更 if (mineNumArray[ox][oy] == 0): for i in range(-1, 2): for j in range(-1, 2): if ( (ox + i) < M and (oy + j) < N and (ox + i) >= 0 and (oy + j) >= 0 ): if ( openFlagArray[ox+i][oy+j] == 0 and mineNumArray[ox+i][oy+j] == 0 and mineArray[ox+i][oy+j] == 0 ): openCell(ox+i, oy+j, openFlagArray, mineArray, mineNumArray) openFlagArray[ox+i][oy+j] = 1 else: ## 問題サイズ外のエラー処理 print("out of range X, Y") # GAMEの終了判定 def endJudgment(ox, oy, openFlagArray, mineArray, mineNumArray): endFlag = 0 numOpenCells = M * N if(mineArray[ox][oy] == 1): print("### GAME OVER! ###") printAnswer(mineArray, mineNumArray) endFlag = 1 for i in range(M): for j in range(N): if(openFlagArray[i][j] == 1): numOpenCells -= 1 if (numOpenCells == nMines): print("GAME CLEAR!!!") endFlag = 1 return endFlag ### minesweeperの部品ここまで ### ### QA solver ### ### mineArray は爆弾の位置が入っているので使用禁止!!! ### ### mineNumArrayはopenFlagArray[i][j] = 1の部分のみ使用可!!! ### def qa_solver(openFlagArray, mineNumArray): # 探索するCellの選択, ヒントの数字の周りの部分を対象とする qFlag = np.zeros((M,N)) for i in range(M): for j in range(N): if (openFlagArray[i][j] == 1): for k in range(-1, 2): for l in range(-1, 2): if ( i + k >= 0 and i + k < M and j + l >= 0 and j + l < N ): if( openFlagArray[i + k][j + l] == 0 ): qFlag[i+k][j+l] = 1 # qbitの番号とminesweeprのcellの変換表作る numQbit = int(sum(map(sum, qFlag))) print( "num of qbit = {0:d}".format(numQbit) ) qList = np.zeros((M,N), dtype=int) qList[:][:] = -1 qAddr = np.zeros((int(numQbit),2), dtype=int) k = 0 for i in range(M): for j in range(N): if (qFlag[i][j] == 1): qList[i][j] = k qAddr[k] = [i, j] k += 1 # 頑張ってスパースなQUBO行列作る QUBO = np.zeros((numQbit,numQbit)) for i in range(numQbit): # i 番目のcellの周囲8cell探索してヒントの数字cellを見つける for k in range(-1, 2): for l in range(-1, 2): if (qAddr[i][0] + k >= 0 and qAddr[i][0] + k < M and qAddr[i][1] + l >= 0 and qAddr[i][1] + l < N): if (openFlagArray[qAddr[i][0]+k][qAddr[i][1]+l] == 1 ): # 数字cellの周囲8cellで開いていないcellと結合を持つ for m in range(-1, 2): for n in range(-1, 2): if (qAddr[i][0]+k+m >= 0 and qAddr[i][0]+k+m < M and qAddr[i][1]+l+n >= 0 and qAddr[i][1]+l+n < N): if (qFlag[qAddr[i][0]+k+m][qAddr[i][1]+l+n] == 1): if( i == qList[qAddr[i][0]+k+m][qAddr[i][1]+l+n] ): QUBO[(i,qList[qAddr[i][0]+k+m][qAddr[i][1]+l+n])] += lam - 2 * lam * mineNumArray[qAddr[i][0]+k][qAddr[i][1]+l] else: QUBO[(i,qList[qAddr[i][0]+k+m][qAddr[i][1]+l+n])] += lam # 辞書に変換 Qdict = {} for i in range(numQbit): for j in range(numQbit): if QUBO[i][j] != 0.0: Qdict[(i,j)] = QUBO[i][j] # openjijで解いてみる #sampler = DWaveCliqueSampler(solver='DW_2000Q_6', token=token, endpoint=endpoint) # d-waveマシンに投げるとき sampler = SQASampler(num_sweeps = 1000) # openjij sampleset = sampler.sample_qubo(Qdict, num_reads=Nsample) # Nsampleの平均で爆弾の存在確率っぽく出力する x = np.zeros(Nsample*numQbit).reshape(Nsample,numQbit) for k in range(len(sampleset.record)): x[k,:] = sampleset.record[k][0] prob = np.average(x, axis=0) print(" x y probability(mine)") for k in range(numQbit): print(qAddr[k],prob[k]) ### end qa_solver ### # ========== main routine ========== # # 初手の入力 printBoard(openFlagArray, mineArray, mineNumArray) print("input open cell (x y) => ",end="") inXY = list(map(int, input().split())) # 爆弾の初期配置 for i in range(nMines): x = random.randrange(M) y = random.randrange(N) # 既に爆弾が配置済みのCell及び初手のCellには爆弾を配置しない if (mineArray[x][y] == 1 or (x == inXY[0] and y == inXY[1])): i = i - 1 else: mineArray[x][y] = 1 mineNumArray[x][y] = -1 # 爆弾の数の表,例外判定のため-1 # i, j Cellの周囲8 Cellにある爆弾の数を計算 for i in range(M): for j in range(N): if (mineArray[i][j] != 1): for k in range(-1, 2): ## i,j Cellの周囲8 Cellの探索 for l in range(-1, 2): if ( i + k >= 0 and i + k < M and j + l >= 0 and j + l < N ): mineNumArray[i][j] += mineArray[i+k][j+l] openCell(inXY[0], inXY[1], openFlagArray, mineArray, mineNumArray) printBoard(openFlagArray, mineArray, mineNumArray) ## ゲーム終わりまで再起実行 fin = 0 while(fin!=1): qa_solver(openFlagArray, mineNumArray) print("input open cell (x y) => ",end="") inXY = list(map(int, input().split())) openCell(inXY[0], inXY[1], openFlagArray, mineArray, mineNumArray) printBoard(openFlagArray, mineArray, mineNumArray) fin = endJudgment(inXY[0], inXY[1], openFlagArray, mineArray, mineNumArray) 実際解いてみた様子 ヒントの隣接Cellの爆弾がありそうな確率を出力する テキストベースで格好悪いですが、こんな感じで動きます (誰か格好いいインターフェイス作って。。) 0 1 2 3 4 5 6 7 8 9 0| | | | | | | | | | | 1| | | | | | | | | | | 2| | | | | | | | | | | 3| | | | | | | | | | | 4| | | | | | | | | | | 5| | | | | | | | | | | 6| | | | | | | | | | | 7| | | | | | | | | | | 8| | | | | | | | | | | 9| | | | | | | | | | | input open cell (x y) => 3 4 0 1 2 3 4 5 6 7 8 9 0| | | | | | | | | | | 1| | | | | | | | | | | 2| | | | | | | | | | | 3| | | | | | | | | | | 4| | | |4| | | | | | | 5| | | | | | | | | | | 6| | | | | | | | | | | 7| | | | | | | | | | | 8| | | | | | | | | | | 9| | | | | | | | | | | num of qbit = 8 x y probability(mine) [2 3] 0.53 [2 4] 0.56 [2 5] 0.56 [3 3] 0.47 [3 5] 0.45 [4 3] 0.55 [4 4] 0.51 [4 5] 0.37 [2 3] 0.53とありますが、2 3のcellの爆弾が入っていそうか度合いを出力しています。 量子アニーリングで100回サンプリングしていて、その平均を確率っぽく出しています。 同じ条件でも量子ビットの機嫌で答えが毎回変わります。 この数字を元に爆弾の確率が少ないCellを開いていけばクリアできるかも? 失敗もある マインスイーパ自体が確率的なので、普通に失敗する時もあります。 input open cell (x y) => 4 5 0 1 2 3 4 5 6 7 8 9 0| | | | | | | | | | | 1| | | | | | | | | | | 2| | | | | | | | | | | 3| | | | | | | | | | | 4| | | |4| | | | | | | 5| | | | |3| | | | | | 6| | | | | | | | | | | 7| | | | | | | | | | | 8| | | | | | | | | | | 9| | | | | | | | | | | num of qbit = 12 x y probability(mine) [2 3] 0.57 [2 4] 0.68 [2 5] 0.52 [3 3] 0.61 [3 5] 0.47 [3 6] 0.43 [4 3] 0.64 [4 4] 0.5 [4 6] 0.42 [5 4] 0.42 [5 5] 0.34 [5 6] 0.42 input open cell (x y) => 5 5 0 1 2 3 4 5 6 7 8 9 0| | | | | | | | | | | 1| | | | | | | | | | | 2| | | | | | | | | | | 3| | | | | | | | | | | 4| | | |4| | | | | | | 5| | | | |3|*| | | | | 6| | | | | | | | | | | 7| | | | | | | | | | | 8| | | | | | | | | | | 9| | | | | | | | | | | ### GAME OVER! ### ヒントの情報量が多くなるといい感じになってくる ヒントの数字によって一意に爆弾の位置が特定できる場合は、ちゃんとサンプリング100回とも1 or 0を出してくれるようになる。 0 1 2 3 4 5 6 7 8 9 0| | | | | | | | | | | 1| | | | | | | |1|1|1| 2| | | | |2|2|1|1|-|-| 3| | |2|1|1|-|-|-|-|-| 4| | |1|-|-|-|-|-|-|-| 5| | |3|1|1|-|-|1|2|2| 6| | | | |2|1|1|2| | | 7| | | | | | | | | | | 8| | | | | | | | | | | 9| | | | | | | | | | | num of qbit = 25 (25,) x y probability(QA) [1 2] 0.48 [1 3] 0.01 [1 4] 0.49 [1 5] 0.5 [1 6] 0.96 [2 2] 0.02 [2 6] 0.02 [3 1] 0.03 [3 2] 0.99 [3 6] 1.0 [3 7] 0.33 [4 1] 0.96 [4 7] 0.38 [5 1] 0.03 [5 7] 0.28 [6 0] 0.01 [6 1] 1.0 [6 7] 0.32 [7 0] 0.0 [7 7] 0.4 [8 0] 0.02 [8 6] 1.0 [8 7] 0.28 [9 0] 0.98 [9 6] 1.0 おわりに 量子アニーリングは、たくさんある条件を一気に最適化するのに強いということで、マインスイーパもできそう、でやってみました。 実際やってみてQUBO行列の作り方が大事だと実感しました。forループの数がとんでもないことに・・・ 組合せ最適化としてはマインスイーパは次に開ける1マスを選ぶだけなので割と簡単な部類なのかもとも思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VRChatのログイン監視体制を作る②

概要 以下の記事を参照 本記事について 上記記事にてフレンドのログインを監視するところまで作成しているので、 本記事では通知の種類やサンプルコードを記載します。 メール通知 1つめはメールにて通知を行う方法を紹介します。 今回使用するシステムはSendGridです こちら、クラウドタイプのメール配信サービスで初回会員登録に承認が必要ですが、 無料プランでもある程度の件数のメールを送ることができるサービスです。 今回記載するスクリプトは以下環境にて動作させています。 ----環境---- OS : Linux Apacheバージョン : 2.4.6 PHP       : 7.4.13 今回はcURLを使用してapiをpostする方法でメールを送ります。 ※SendGrid公式ライブラリを使用する場合の方法はSendGrid公式に記載されています sendmail.php <?php $url = 'https://api.sendgrid.com/'; $sendgrid_apikey =SendGridのAPIキー; $mail_address = "送信先のメールアドレス"; $FILE = 'tmp_players.txt'; $BOARD = json_decode(file_get_contents($FILE)); $params = array( 'Authorization: Bearer' => $sendgrid_apikey , 'to' => $mail_address, 'subject' => "監視対象ログイン通知", 'html' => " 以下お友達がログインしました <br /> ".$BOARD. "早く会いに行きましょう", 'from' => 'vrclogintest@monitoring.jp', ); $request = $url.'api/mail.send.json'; if (!defined('CURL_SSLVERSION_TLSv1_2')){ define('CURL_SSLVERSION_TLSv1_2', 6); } // カールリクエストを生成する $session = curl_init($request); // SSLv3を使用しないようにPHPに指示します(代わりにTLSを選択します) curl_setopt($session, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); curl_setopt($session, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $sendgrid_apikey)); // CurlにHTTP POSTを使用するように指示します curl_setopt ($session, CURLOPT_POST, true); // これがPOSTの本文であることをcurlに伝えます curl_setopt ($session, CURLOPT_POSTFIELDS, $params); // Curlにヘッダーを返さないように指示しますが、応答を返します curl_setopt($session, CURLOPT_HEADER, false); curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // レスポンスを得る $response = curl_exec($session); curl_close($session); ?> これで、以下メールが送信されます 通知メールが来ることでログインしたことが分かるので早く会いに行きましょう デスクトップ通知 上記メールでの通知ですが、常に監視するためのLinuxサーバーが必要です。 PCを起動していないとログインの通知が来てもすぐにログインはできないので、 常にPCを起動しておく場合はデスクトップに通知を飛ばしたほうがいいですよね。 ということで、次にデスクトップに通知を飛ばす方法を以下にて解説します。 今回はplyerモジュールを使用します。 今回記載するスクリプトは以下環境にて動作させています。 ----環境---- OS : Windows Python : 3.8.8 Anaconda version : 4.10.1 plyer : 2.0.0 notification.py from plyer import notification user_file = open("tmp_players.txt", mode='r',encoding='utf-8') user_data = user_file.read() user_file.close() notification.notify( title = "監視対象ログイン通知", message=user_data + "\nがログインしています\n\n早く会いに行きましょう", timeout=5 ) 上記を実行することで以下通知が飛んできます こちらを一定時間ごとに自動実行することで常にログイン動向を確認することができるので ログイン通知が飛んで来たら早く会いに行きましょう 終わりに これは友達を監視ではなく見守るのための技術紹介記事なので 変なことに使用しないようにしましょう あくまで友達を見守りたい人向けです...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DeepLearningの超解像手法10種を同じデータセットで学習・比較してみる

概要 「今まで作ってきた超解像手法のうち10種類を、全く同じデータセットで学習させて検証をしてみた」という記事です。 色々実装していたので一度してみたかった内容になっています! なお、割と安易な考えなので、パラメータなども大体同じにして学習させています。 そのため、超解像の論文のベンチマークテストみたいに、きっちりかっちりしていないのでご了承ください。 ちなみに超解像は、解像度の低い画像から解像度を向上させた画像を出力する技術です。 もう少し噛み砕いて説明すると、画質の荒い画像を綺麗にするみたいな感じです。 コードはGithubに置いていますが、過去の実装のコードをひとまとめにしただけなので長いです。 今回比較したアルゴリズムの紹介 今回使用した10種類のアルゴリズムの概要を紹介します。 10種類全部紹介するので長めになります。結果だけチラ見したい方は飛ばしてください! まず、今回紹介するアルゴリズムは以下の通りです。 それぞれ自分が書いた実装記事をリンク付けしておくので、詳しく見たい方はそちらをご覧ください。 SRCNN FSRCNN ESPCN VDSR DRCN RED_Net DRRN VSRnet RVSR VESPCN これらのうち、VSRnet・RVSR・VESPCNは、動画像を対象とした超解像手法です。 高解像度化するフレームは1枚なので、同じデータセットで比較することができます。 以下で、各手法の概要を紹介します。 SRCNN SRCNNは初めて超解像分野に深層学習を導入したモデルで、Convolutionを3層組み合わせたモデルとなっています。 SRCNNのアルゴリズムの概要は以下の通りです。(図は論文から引用) 今回実装したSRCNNは、事前にbicubicで拡大処理を行います。 FSRCNN FSRCNNのアルゴリズムの概要は以下の通りです。(図は論文から引用) 上の図がSRCNN、下の図がFSRCNNのモデルの概要図となっています。 SRCNNでは、最初にBicubic法で画像を補間してからニューラルネットワークに入力しており、画像サイズが大きい状態で特徴抽出や返還を行っていました。 そのため、CNNのサイズが大きくなったり、計算が非効率になっているという問題がありました。 そこで、FSRCNNではSRCNNの大まかなモデルの構造はそのままに、 Feature extraction Shrinking Mapping Expanding Deconvolution のいくつかの層に分けており、効率化に成功しています。 ESPCN ESPCNのアルゴリズムの概要は以下の通りです。(図は論文から引用) ESPCNは畳み込み処理を行った後、Sub pixel convolutional layerを通して最終的な結果を出力します。 これによって、画像を拡大しています。 VDSR VDSRのアルゴリズムの概要は以下の通りです。(図は論文から引用) Convolution層を多数重ねることで、学習をさせていますが、 最後に入力画像と畳み込み演算の結果を足し合わせるSkip Connnectionを導入していることが大きな特徴です。 これを行う意図としては、多数の畳み込み演算によって生じる勾配消失を防ぎ、画像の特徴を失わないようにするためです。 最後に入力画像を足し合わせることで、画像の特徴は残しつつ処理を行うことができる、というわけです。 今回実装したVDSRは、事前にbicubicで補間処理をした画像を入力します。 DRCN DRCNのアルゴリズムの概要図は以下の通りです。(図は論文から引用) 主に、 Embedding net:特徴マップの生成ネットワーク Inference net:メインの超解像ネットワーク Reconstruction net:再構成のネットワーク + Skip_connection の3つのネットワークに分かれています。 図では多数のネットワークが描かれていますが、同じ畳み込み層を繰り返し利用するので、パラメータ数は控えめになっています。 このモデルのイメージとしては、繰り返しCNNに画像を入力することで、徐々に画像をきれいにしていく感じです。 もう少し詳しい概要図は以下の通りです。(図は論文から引用) 図の通り、Conv + ReLUが基本構成になっています。また、フィルター数やフィルターサイズは基本的には同じものを使用します。 今回実装したDRCNは、事前にbicubicで拡大処理を行います。 RED_Net RED-Netのアルゴリズムの概要は以下の通りです。(図は論文から引用) 同じ数のConvolution layerとDeconvolution layerで構成されており、 Deconvolutionを2回行うごとに、Skip connectionでConvolutionの結果を足し合わせています。 Skip connectionは、学習に伴う勾配消失の問題を防ぐために使用されています。 また、Skip connectionによる結果の足し合わせを行ったとき、以下の図の例のようにReLUの活性化関数を通します。 (図は論文から引用) 上の図では、実線がConvolution layerで破線はDeconvolution layerを表しています。 今回実装したRed-Netは、事前にbicubicで拡大処理を行います。 DRRN DRRNのアルゴリズムの概要は以下の通りです。(図は論文から引用) DRRNの全体図は左のようになっています。 DRRNは任意の数のRecursive Blockから成立しており、最後にConvolutionで結果を出力します。 今回の図だと、Recursive Blockの数は6つとなっています。(図のRBの数) Recursive Blockの中の構成は図の右のようになっています。 Residual Unitsという名前で論文では紹介されており、図のような構成になっています。 VSRnet VSRnetは、3層のConvolutionから構成されていますが、入力する3枚のフレームを結合する位置に応じてモデルが3つあります。 architecture(a)は、Convolution層に入力する前に結合します。 architecture(b)は、1回それぞれの入力フレームをConvolution層に入力した後に結合します。 architecture(c)は、2回それぞれの入力フレームをConvolution層に入力した後に結合します。 概要図を見ると分かりやすいので、以下で概要図を示します。(図は論文から引用) 今回実装したVSRnetはarchitecture(b)で、事前にbicubicで拡大処理を行います。 RVSR RVSRのアルゴリズムの概要は以下の通りです。(図は論文から引用) この超解像アルゴリズムは主に3つのパートに分かれています。 SR inference branch:高解像度画像の候補を出力。 Temporal modulation branch:weight mapの出力。 Temporal aggregation:最終的な結果の出力。 VESPCN VESPCNのアルゴリズムの概要は以下の通りです。(図は論文から引用) この超解像手法は、3枚の連続するフレームを入力して、1枚の高解像度画像を出力します。 前半のMotion estimationが動き補償などのデータ前処理の部分、 後半のSpatio-temporal ESPCNが超解像を行う部分になっています。 学習・検証に使用したデータセット 今回は、データセットにREDSを使用しました。 このデータセットは、動画像の超解像用のデータセットで、240種類の学習用データ、30種類の検証用データ、30種類のテスト用データの計300種類のデータセットです。 パスの構造はこんな感じです。 train_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... val_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... このデータをbicubicで縮小したりしてデータセットを生成しました。 今回、入力フレームが5枚のアルゴリズムがあったので、データセットも5フレームで1セットにしています。 そのため、単一画像用のアルゴリズムは中央のフレームを、入力フレームが3枚のアルゴリズムでは、中央に近い3フレームを入力することで、全く同じデータセットでの学習を行っています。 学習条件 今回学習を行った環境は以下の通りです。 PC環境 CPU : AMD Ryzen 5 3500 6-Core Processor memory size : 40GB GPU : NVIDIA GeForce RTX 2060 SUPER OS : Windows 10 ライブラリ環境 python : 3.7.9 tensorflow-gpu : 2.3.0 keras : 2.4.3 opencv-python : 4.4.0.43 また、今回設定したパラメータは以下の通りです。(コードの一部抜粋) まず、学習に関連するパラメータはtrain.pyに記載しています。 train.py parser.add_argument('--train_height', type = int, default = 48, help = "Train data HR size(height)") parser.add_argument('--train_width', type = int, default = 48, help = "Train data HR size(width)") parser.add_argument('--train_dataset_num', type = int, default = 30000, help = "Number of train datasets to generate") parser.add_argument('--train_cut_num', type = int, default = 10, help = "Number of train data to be generated from a single image") parser.add_argument('--train_path', type = str, default = "../../dataset/reds_train_sharp", help = "The path containing the train image") parser.add_argument('--LR_num', type = int, default = 5, help = "Number of LR frames") parser.add_argument('--learning_rate', type = float, default = 1e-4, help = "Learning_rate") parser.add_argument('--BATCH_SIZE', type = int, default = 32, help = "Training batch size") parser.add_argument('--EPOCHS', type = int, default = 1000, help = "Number of epochs to train for") ここでは、学習データのサイズ・データセットの数・学習率などを指定しています。 次に、検証用データに関連するパラメータを以下で示します。test.pyに記載しています。 test.py parser.add_argument('--test_height', type = int, default = 360, help = "Test data HR size(height)") parser.add_argument('--test_width', type = int, default = 640, help = "Test data HR size(width)") parser.add_argument('--test_dataset_num', type = int, default = 50, help = "Number of test datasets to generate") parser.add_argument('--test_cut_num', type = int, default = 1, help = "Number of test data to be generated from a single image") parser.add_argument('--test_path', type = str, default = "../../dataset/reds_val_sharp", help = "The path containing the test image") parser.add_argument('--LR_num', type = int, default = 5, help = "Number of LR frames") parser.add_argument('--mag', type = int, default = 2, help = "Magnification") 同様に、検証用データセットのサイズや数などを指定しています。 検証結果 今回は、学習率や学習回数、データセットの数など、同じにできる部分は全て同じ値で学習させました。 以下は、その前提で読んでいただけると幸いです。 今回、検証結果として2種類の結果を紹介します。 50枚の結果のPSNRの平均・ピックアップした1枚の画像のPSNRの2種類です。 検証用データ50枚の平均結果で比較 低解像度画像は、bicubicで補間して画像サイズを揃えました。 以下は、bicubicと各アルゴリズムのPSNRの結果一覧です。 PSNRの数値が高いほど元画像に近く、高解像度の画像であることを示しています。 補間・拡大処理 PSNR(dB) bicubic 30.88 SRCNN(2015) 31.90 FSRCNN(2016) 32.25 ESPCN(2016) 32.10 VDSR(2016) 31.61 DRCN(2016) 31.44 REDNet(2016) 31.96 DRRN(2017) 32.03 VSRnet(2016) 32.19 RVSR(2017) 31.84 VESPCN(2017) 31.35 まさかのFSRCNNのPSNRが一番高くなりました。 後発の論文ほど理論上数値が高くなるはずなのですが、やはり学習に依存されるみたいですね... 1枚の画像で比較 次は、上の画像から1枚をピックアップして比較していきます。 今回、比較に使用した画像は以下の通りです。 見た感じ、動物園で撮影した写真のようです。 よく見てみると、所々歪んでいることが分かります。 この画像を高解像度化した結果の比較は以下の通りです。 補間・拡大処理 PSNR(dB) bicubic 29.43 SRCNN(2015) 30.89 FSRCNN(2016) 31.30 ESPCN(2016) 31.21 VDSR(2016) 30.91 DRCN(2016) 30.24 REDNet(2016) 31.09 DRRN(2017) 31.17 VSRnet(2016) 31.24 RVSR(2017) 30.91 VESPCN(2017) 30.27 上記の平均結果と同様に、FSRCNNのPSNRが最も高い数値になりました。 最後に、高解像度画像の一覧を表示します。 横で比較しやすいように、bicubicだけ2回表示しています。 画像をみても、高解像度化がしっかりと行われていることが分かります。 コードの全容 前述の通り、Githubに載せています。 pythonのファイルは主に5つあります。 各ファイルの役割は以下の通りです。 data_create.py : データ生成に関するコード。 model.py : 超解像のアルゴリズムに関するコード。 train.py : 学習の際に使用するコード。 test.py : 検証の際にに使用するコード。 MyPReLU.py :PReLUの活性化関数のコード まとめ 今回は、10種類の超解像アルゴリズムを比較してみました。 割と安易な考えで比較してみましたが、色々比較してみれて楽しかったです。 今後も、ちょくちょく超解像のアルゴリズムの記事を書きたいなと思います! 記事が長くなってしまいましたが、最後まで読んでくださりありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M1 macにpythonの仮想環境を作りモジュールを導入

M1 macbookProを会社から貸与いただいたので、pythonの仮想環境を作ってみました。 ${your_name}@MacBook-Pro> python3 -m venv machineLearning sourceコマンドで仮想環境を起動すると、その環境に入れる。 ${your_name}@MacBook-Pro> source ./machineLeaning/bin/activate (machineLearning)${your_name}@MacBook-Pro> python3 -m venv machineLearning 仮想環境にnumpyモジュールをインストールする (machineLearning) ${your_name}@MacBook-Pro ~ % pip install numpy Collecting numpy Downloading numpy-1.20.3-cp38-cp38-macosx_10_9_x86_64.whl (16.0 MB) |████████████████████████████████| 16.0 MB 5.9 MB/s Installing collected packages: numpy Successfully installed numpy-1.20.3 しかし何故かtensorflowが失敗してしまう。次のように公式サイトどおりpipすると、 pip install --upgrade tensorflow Could not find a version that satisfies the requirement tensorflow (from versions: ) No matching distribution found for tensorflow のエラーメッセージでダメだった。バージョン指定しても同じ。 pip install --upgrade tensorflow Mac-optimized version of TensorFlow 2.4 https://note.com/npaka/n/n7f2dcf8b316d を拝見すると、 M1 MAC用のパッケージがあるようである。しかもこっちの方が、実行スピードが格段に速い。 git https://github.com/apple/tensorflow_macos のINSTALLATION にあるscriptを(次のように実行して)インストールできました。 $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/apple/tensorflow_macos/master/scripts/download_and_install.sh)"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Chrome] SeleniumでBasic認証を突破する

はじめに SeleniumでBasic認証が行われているページにアクセスする場合は、以下のようにするとアクセスできます(Chromeの場合)。 driver.get("http://username:password@example.com") この時、ユーザ名が例えば、mail@example.comのようなメールアドレスの場合は、@を%40に置き換えて、以下のようにします。 driver.get("http://mail%40example.com:password@example.com") @や#等、URLの中で特別な意味を持つ文字は、パーセントエンコーディングしてあげる必要があります。 参考:Percent-encoding (パーセントエンコーディング) | MDN Web Docs setExtraHTTPHeadersを使ってBasic認証に対応させる(※注意事項あり) ここからが本題で、以下の投稿で使ったChrome DevTools ProtocolにあるNetwork.setExtraHTTPHeadersメソッドを使って、Basic認証を突破することに挑戦してみます。 SeleniumとHeadless ChromeでページをPDFに保存する 【Chrome】Seleniumでページ全体のスクリーンショットを撮る Network.setExtraHTTPHeadersは、HTTPヘッダを付与することのできるメソッドで以下のように使います。 set-extra-http-headers.py custom_headers = { "X-Custom-Header1": "value1", "X-Custom-Header2": "value2", } driver.execute_cdp_cmd("Network.enable", {}) driver.execute_cdp_cmd("Network.setExtraHTTPHeaders", {"headers": custom_headers}) 最初に結論と注意事項としたことを書くと、以下のようになります。 - setExtraHTTPHeadersを使って、Basic認証は突破できる - ただし、認証対象ページ内にある別ドメインのリソース読み込み等で、ヘッダから認証情報が漏れる可能性がある Basic認証突破の作戦 Basic認証の仕組みについては、下記の記事に詳しく記載されていました。詳しくはリンク先記事をご参照ください。 Basic認証では「Authorization」ヘッダに認証情報を付けて、ブラウザからサーバに送信します。このヘッダの付与をsetExtraHTTPHeadersメソッドを使って実現する、というのが今回のBasic認証対応の試みです。 Basic認証を行うサイトの環境づくり まずは、検証用にBasic認証がかけられたページを準備します。 Docker公式のApacheイメージをベースに作成します。 Dockerfile FROM httpd:2.4 RUN echo 'AuthUserFile /usr/local/apache2/htdocs/.htpasswd\n\ AuthGroupFile /dev/null\n\ AuthName "Basic Auth"\n\ AuthType Basic\n\ Require valid-user' > /usr/local/apache2/htdocs/.htaccess RUN htpasswd -b -c /usr/local/apache2/htdocs/.htpasswd user@example.com secret RUN sed -i.bak -e "s/AllowOverride None/AllowOverride All/g" /usr/local/apache2/conf/httpd.conf 以下のようなHTMLコンテンツも用意しておきます。 jQueryは実際には使っていませんが、後ほどの確認用にCDNからの読み込みコードを入れておきます。 index.html <html> <head> <title>Basic Auth Page</title> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <body> <h1>Secret Page</h1> </body> 上記からDockerのイメージを作成し、index.htmlをドキュメントルートにマウントして起動します。 $ docker build -t basic-auth-apache . $ docker run -dit --name apache-app -p 8080:80 -v "$PWD/index.html":/usr/local/apache2/htdocs/index.html basic-auth-apache ブラウザで、http://localhost:8080/index.html にアクセスすると、認証ダイアログが表示され、設定したID/パスワードを入力するとページが表示されました。 Seleniumコード setExtraHTTPHeadersメソッドを利用して、Basic認証に対応させたページにアクセスするSeleniumのコードは下記です。 cdp-basic-auth.py import base64 import time from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager # 与えられた認証情報をもとに、Authorizationヘッダを作成するメソッド def get_auth_header(user, password): b64 = "Basic " + base64.b64encode('{}:{}'.format(user, password).encode('utf-8')).decode('utf-8') return {"Authorization": b64} # Webdriver ManagerでChromeDriverを取得 driver = webdriver.Chrome(executable_path=ChromeDriverManager().install()) # Authorizationヘッダを付与 driver.execute_cdp_cmd("Network.enable", {}) driver.execute_cdp_cmd("Network.setExtraHTTPHeaders", {"headers": get_auth_header("user@example.com", "secret")}) # Basic認証が必要なページにアクセス driver.get('http://localhost:8080/index.html') time.sleep(5) driver.close() driver.quit() 実行すると、Basic認証で保護されたページが表示されました。 気になっていること Chrome DevTools ProtocolのNetwork.setExtraHTTPHeadersメソッドの説明には、以下のような記述があります。 Specifies whether to always send extra HTTP headers with the requests from this page. この「always」という部分の記述が気になり、 常にAuthorizationヘッダが送られているのではないか? (=認証情報が外部のサイトに送信されている状態でないか。) という懸念を抱きました。 そこで、リクエスト内容を取得して確認することにしました。 リクエスト内容の取得 リクエスト内容の取得については、下記の記事を参考にさせていただきました。 Basic認証の検証用ページにアクセスし、その後、別サイトにアクセスするという流れの時のリクエスト内容(URLとAuthorizationヘッダの値)をファイルに記録するコードです。 get-network-log.py import base64 import time import json from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.desired_capabilities import DesiredCapabilities # 与えられた認証情報をもとに、Authorizationヘッダを作成するメソッド def get_auth_header(user, password): b64 = "Basic " + base64.b64encode('{}:{}'.format(user, password).encode('utf-8')).decode('utf-8') return {"Authorization": b64} # ネットワークに関するログを取得するための設定 capabilities = DesiredCapabilities.CHROME capabilities['goog:loggingPrefs'] = { 'performance': 'ALL' } # Webdriver ManagerでChromeDriverを取得 driver = webdriver.Chrome( executable_path=ChromeDriverManager().install(), desired_capabilities=capabilities ) # Authorizationヘッダを付与 driver.execute_cdp_cmd("Network.enable", {}) driver.execute_cdp_cmd("Network.setExtraHTTPHeaders", {"headers": get_auth_header("user@example.com", "secret")}) # Basic認証が必要なページにアクセス driver.get('http://localhost:8080/index.html') time.sleep(5) # 別のサイトにアクセス driver.get('https://qiita.com/') # ネットワーク情報からリクエストURL・Authorizationヘッダの値を抽出し、ファイルに書き出す with open('network.log', 'w') as f: for entry_json in driver.get_log('performance'): entry = json.loads(entry_json['message']) if entry['message']['method'] != 'Network.requestWillBeSent' : continue print(entry['message']['params']['request']['url'], # Authorizationヘッダがない場合は、「No Authorization header」と出力する entry['message']['params']['request']['headers'].get('Authorization', "No Authorization header"), file=f) driver.close() driver.quit() 確認結果 実行すると次のような結果が得られました。 localhostのコンテンツ以外にも、HTMLから読み込んでいるCDNのjQuery取得のリクエスト、別サイト(Qiita)へのアクセス時にも認証情報が付与されてしまっていることがわかりました。 http://localhost:8080/index.html Basic dXNlckBleGFtcGxlLmNvbTpzZWNyZXQ= https://code.jquery.com/jquery-3.6.0.min.js Basic dXNlckBleGFtcGxlLmNvbTpzZWNyZXQ= http://localhost:8080/favicon.ico Basic dXNlckBleGFtcGxlLmNvbTpzZWNyZXQ= https://qiita.com/ Basic dXNlckBleGFtcGxlLmNvbTpzZWNyZXQ= ・・・(省略)・・・ 上記から、setExtraHTTPHeadersメソッドでAuthorizationヘッダを設定する際の注意事項として、 Authorizationヘッダの認証情報が外部のサイトに送信される という点に注意が必要です。 一応、setExtraHTTPHeadersメソッドのheadersパラメータに{}を指定するとカスタムヘッダの付与はされなくなるようです。 # Authorizationヘッダを付与 driver.execute_cdp_cmd("Network.enable", {}) driver.execute_cdp_cmd("Network.setExtraHTTPHeaders", {"headers": get_auth_header("user@example.com", "secret")}) # Basic認証が必要なページにアクセス driver.get('http://localhost:8080/index.html') time.sleep(5) # カスタムヘッダの付与をクリア driver.execute_cdp_cmd("Network.setExtraHTTPHeaders", {"headers": {}}) # 別のサイトにアクセス driver.get('https://qiita.com/') time.sleep(5) 上記のコードで実行すると、QiitaへのAuthorizationヘッダの送信は止まりました。 同じページ内で、別ドメインからのjQueryのJSファイル読み込みについては、試した範囲では回避はできませんでした。 http://localhost:8080/index.html Basic dXNlckBleGFtcGxlLmNvbTpzZWNyZXQ= https://code.jquery.com/jquery-3.6.0.min.js Basic dXNlckBleGFtcGxlLmNvbTpzZWNyZXQ= http://localhost:8080/favicon.ico Basic dXNlckBleGFtcGxlLmNvbTpzZWNyZXQ= https://qiita.com/ No Authorization header ・・・(省略)・・・ おわりに 今回は、setExtraHTTPHeadersメソッドを試してみました。Basic認証は一応突破できたものの、ページ内で別ドメインからのリソース読み込みを行っている場合は、認証情報を送信してしまう恐れがあるため、適用場面は限られてしまうのかなと思います。 setExtraHTTPHeadersメソッド自体は、他のヘッダ付与にもちろん利用できるので、他の活用があるかもしれません。 参考 SeleniumでBasic認証有のサイトにログインする HTTP 認証 - HTTP | MDN Authorization - HTTP | MDN Percent-encoding (パーセントエンコーディング) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN 「HTTP」の仕組みをおさらいしよう(その4) 【Python】Selenium側からネットワーク情報を取得する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub Actions を使って Qiita のviews 数を Slack に通知するアクションを python で作った

JavaScript や TypeScript で作られたアクションは割とよく見るのですが、 Python で作られた Actions は見ないので(そもそも Python で書く必要がない?)今回は Python を使って Action コードを作ってみたいと思います。 動作環境 Actions は Docker コンテナのアクションまたは JavaScript アクションの2種類あります。Python で書くために今回は Docker コンテナのアクションを採用します。 Docker コンテナで Python が実行できるようにベースイメージには Python を設定します。 ディレクトリ構成 Python コードは src ディレクトリ配下に置き Docker のCOPY コマンドでコンテナ内にコピーさせます。action.ymlは GitHub Actions のメタデータファイルです。 https://docs.github.com/ja/actions/creating-actions/creating-a-docker-container-action#creating-an-action-metadata-file . ├── Dockerfile ├── action.yml ├── entrypoint.sh └── src ├── main.py ├── qiita.py ├── requirements.txt └── slack.py action.yml name: 'Slack Notify' description: 'You can notify slack of GitHub Actions.' runs: using: 'docker' image: 'Dockerfile' モジュール slackに通知するためのコードで sdk を使用できるようにするために、slack-sdkモジュールをインストールします。 requirements.txt slack-sdk==3.5.1 ビュー数の取得 Qiita 記事のビュー数を取得するコードを書いていきます。ビュー数を取得するためには個別記事を取得する必要があります。そのためにまずは記事一覧取得APIを実行して一覧を取得し、取得したデータから個別記事のIDを抜き出して個別記事取得APIを叩きビュー数を取得します。qiita api のトークンは環境変数設定して取得できるようにします。 qiita.py import urllib.request import json import os def sendRequest(url): req = urllib.request.Request(url) req.headers = { 'Authorization': 'Bearer ' + os.environ['TOKEN'] } with urllib.request.urlopen(req) as response: decode = json.loads(response.read().decode('utf-8')) return decode def getIds(url): decode = sendRequest(url) ids = [] for s in range(len(decode)): ids.append(decode[s]['id']) return ids def getViews(ids): originUrl = 'https://qiita.com/api/v2/items/' views = [] for s in range(len(ids)): url = originUrl + ids[s] decode = sendRequest(url) views.append({ "title": decode['title'], "views": decode['page_views_count'] }) return views def views(): url = 'https://qiita.com/api/v2/users/kiyo27/items?page=1&per_page=20' ids = getIds(url) return getViews(ids) slack に通知 slack から sdk が提供されているのでそれを使用します。webhook url は環境変数に設定しておきます。 slack.py import os from slack_sdk.webhook import WebhookClient def notify(views): url = os.environ['WEBHOOK'] webhook = WebhookClient(url) blocks = [] for s in range(len(views)): title = views[s]['title'] viewCount = views[s]['views'] text = title + "\n:star::star::star::star: " text = text + str(viewCount) + "views" blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": text } }) response = webhook.send(text="fallback", blocks=blocks) コンテナ作成 ベースイメージに python を設定して、qiita の view 数を取得するコードと slack に通知するコードをCOPYコマンドコンテナ内に取り込みます。コンテナ起動時に実行されるブートコードとしてentrypoint.shを設定しておきます。 Dockerfile FROM python:3.9.5-slim-buster COPY src /usr/app COPY entrypoint.sh /entrypoint.sh RUN pip install -r /usr/app/requirements.txt \ && chmod +x /*.sh ENTRYPOINT ["/entrypoint.sh"] entrypoint.shの中身。main.py を実行しています。 entrypoint.sh #!/usr/bin/env bash python /usr/app/main.py main.pyの中身。コマンドラインから実行されたとき、 run メソッドを呼び出します。 run メソッドでは qiita の個別記事の view 数を取得し、取得結果を slack に通知するロジックにしています。 main.ph import qiita import slack def run(): views = qiita.views() slack.notify(views) if __name__ == "__main__": run() Actions で実行 作成したアクションを実行するために、 Actions の workflow を作成します。usesに先ほど作成したアクションを指定します。サンプルアクションはこちらで GitHub に上がっています。 Qiita API トークンと Slack Webhook URL はコード内では環境変数から取得しているので、 workflow 構文でenvを使用して環境変数を設定しています。環境変数として設定する値は、GitHub の シークレットを利用して設定しておきます。設定の仕方は下のリンクから確認できます。あとはこの workflow ファイルを GitHub にプッシュすればアクションが実行されて slack に qiita の 個別記事ごとの views 数が記載された状態で通知がきます。 workflow.yml on: [push] jobs: slack_notify: runs-on: ubuntu-latest name: A job to send a message to slack steps: - name: Slack notification id: slack uses: kiyo27/action-slack-notify@main env: TOKEN: ${{ secrets.TOKEN }} WEBHOOK: ${{ secrets.WEBHOOK }}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【超簡単】Python初心者が、初めて自分の頭で考えて動いたじゃんけんゲーム

目的 ▶︎プログラミング初心者でも、少ないコード量で自分で動かした実感の持てるゲームを作る。 一通り覚えた知識の中から、じゃんけんゲームを一から作ってみようと思いエラーを出しまくりながらも初めて理想通りに動いたコードです。 キレイなコードとは言えないと思いますが、動くとモチベーションが上がります。 #『じゃんけーん』のあとに間を作るために必要な、sleepをtimeからimport #リストの中をランダムに表示させる、randomをimport from time import sleep import random #リストを作る cp = ['グー','チョキ','パー'] #randomのメソッドshuffleでリストcpをシャッフル random.shuffle(cp) #わかりやすく最初はグーと言ってもらう print('最初はグー!') #inputをsayに入れる say = input() #もしsayがじゃんけん(janken)だったら、(じゃんけんとこちらが言ったら)sleepで2秒間あけて #ポイ!!をプリントすると同時に、cpリストの0番目を表示する。 if say == 'janken': sleep(2) print('ポイ!!') print(cp[0]) コメントをなくすとこんな感じです。 from time import sleep import random cp = ['グー','チョキ','パー'] random.shuffle(cp) print('最初はグー!') say = input() if say == 'janken': sleep(2) print('ポイ!!') print(cp[0]) 結果 これで実行すると最初はグー!のあとインプット待ちとなるため、janken(言葉はじゃんけんと変えてもいい) と入力した後に、2秒間あけてポイ!!と同時にランダムにリストが表示されました。 最後に 今回こちらのコードで動きましたが、初心者が考えたものなのでもしかしたらもっとわかりやすい書き方もあるかもしれません。もしフォローしていただける方がいましたら、ぜひお声がけください。 ただ自分の頭でちゃんと考えて動もができた時の感動は最高ですね!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Anaconda仮想環境でconda installを使ってMeCabを使えるようにする

まえがき 以前も触れた書籍「すぐに使える!業務で実践できる! PythonによるAI・機械学習・深層学習アプリのつくり方 TensorFlow2対応」でMeCabが登場したので、早速インストールしようとしてみました。しかし、有名かつ古参のソフトウェアということで、ネット上の情報の時間軸が散らばっていて、結構導入に手こずりました。 同じようにAnaconda仮想環境でconda installを使いMeCabを導入したい方の参考になれば、ということで残しておきます。 実行環境 Anaconda3(64bit) Python3.8 導入方法 まずはこちらのサイトを参考にしつつMeCabのインストールと、Path通しを行ってください。サイト中にもありますが、デフォルトのshift_jisではなくutf-8でインストールしないと後でエラーを吐くのでご注意ください。 次にconda installを行いますが、自分がデフォルトとconda-forgeのチャンネルで探す限り、"mecab-python-windows"も"mecab-python3"も"mecab"も見つけることができませんでした。 そこでAnacondaの公式サイトで検索をかけたところ、このパッケージがでてきました。こちらに記載してあるコマンドの conda install -c mzh mecab-python3 でインストールしたところ、以下のコードが実行できました。 import MeCab # MeCabオブジェクトの生成 tagger = MeCab.Tagger() # 形態素解析 result = tagger.parse("紹介されているパッケージや環境がばらついていて、非常に苦労しました。") print(result) 出力 紹介 名詞,サ変接続,*,*,*,*,紹介,ショウカイ,ショーカイ さ 動詞,自立,*,*,サ変・スル,未然レル接続,する,サ,サ れ 動詞,接尾,*,*,一段,連用形,れる,レ,レ て 助詞,接続助詞,*,*,*,*,て,テ,テ いる 動詞,非自立,*,*,一段,基本形,いる,イル,イル パッケージ 名詞,一般,*,*,*,*,パッケージ,パッケージ,パッケージ や 助詞,並立助詞,*,*,*,*,や,ヤ,ヤ 環境 名詞,一般,*,*,*,*,環境,カンキョウ,カンキョー が 助詞,格助詞,一般,*,*,*,が,ガ,ガ ばらつい 動詞,自立,*,*,五段・カ行イ音便,連用タ接続,ばらつく,バラツイ,バラツイ て 助詞,接続助詞,*,*,*,*,て,テ,テ い 動詞,非自立,*,*,一段,連用形,いる,イ,イ て 助詞,接続助詞,*,*,*,*,て,テ,テ 、 記号,読点,*,*,*,*,、,、,、 非常 名詞,形容動詞語幹,*,*,*,*,非常,ヒジョウ,ヒジョー に 助詞,副詞化,*,*,*,*,に,ニ,ニ 苦労 名詞,サ変接続,*,*,*,*,苦労,クロウ,クロー し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ まし 助動詞,*,*,*,特殊・マス,連用形,ます,マシ,マシ た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ 。 記号,句点,*,*,*,*,。,。,。 相変わらずものすごい精度で驚くばかりです。 あとがき 書籍の方には「MeCabはWindowsだとセットアップが大変なので、VirtualBox上で仮想環境を作って導入してね」とありましたが、確かにそこそこ大変でした。 集合知に感謝。 参考サイト様(アルファベット順) Anaconda.org: MZH / packages / mecab-python3 1.0.3) .NET Columns: MeCabをPython3で使ってみよう|形態素解析について分かりやすく解説 MeCab: Yet Another Part-of-Speech and Morphological Analyzer teratail: mecab-python-windowsのインストールが上手くできない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エラーフリー変換あれこれ

まえがき 以前投稿した記事で丸め誤差を簡単に紹介しました。 本記事では,高精度計算の準備としてエラーフリー変換を紹介します. エラーフリー変換とは 浮動小数点数を用いた計算では多くの場合誤差が生じることを以前の記事で理解していただけたと思います.誤差は生じてしまうけれど,その誤差情報も計算結果として保持しておこう!というのがエラーフリー変換の考え方です.つまり,以下の数式のように2つの浮動小数点数 a, b の演算結果を近似解 x と誤差項 y に変換します. a \oplus b = x + y \\ これをエラーフリー変換といいます. 準備 以降で和と積に関するエラーフリー変換と高精度計算方法について紹介していきますが, その前に紹介中で用いる記号や関数・言葉について紹介しておきます. また,簡単のため演算中でオーバーフローやアンダーフローは起きないと仮定します. $\mathbb{F} \qquad \ $:浮動小数点数の集合 $\mathrm{u} \qquad \ $:単位相対丸め(float型なら $2^{-24}$, double型なら $2^{-53}$) FMA$\ \ \ :$ 融合積和演算(Fused-Multiply-Add) $xy+z$ を 1回の丸めで計算できる機能. flop $\quad \ :$ 浮動小数点演算回数(Floating-point Operation) 和に関するエラーフリー変換 和に関するエラーフリー変換として Dekker12 に紹介された FastTwoSum というアルゴリズムがあります. FastTwoSum $a$, $b \in \mathbb{F}$ について $|a|\geq |b|$ のとき $\rm{FastTwoSum}$ を実行すれば, a + b = x + y \ が成り立つ. FastTwoSum.py def FastTwoSum(a, b): x = a + b y = b - (x - a) return [x, y] FastTwoSum は加算する浮動小数点数の大小関係に制限があるものの 3 flop(通常の和の3倍)で計算できます.また,加算する浮動小数点数の大小関係に仮定を置かないバージョンとして Knuth3 の TwoSum というアルゴリズムもあります. TwoSum $a$, $b \in \mathbb{F}$ について$\rm{TwoSum}$ を実行すれば, a + b = x + y \ が成り立つ.(FastTwoSum の結果と同じ) TwoSum.py def TwoSum(a, b): x = a + b tmp = x - a y = (a - (x - tmp)) + (b - tmp) return [x, y] TwoSum では加算する浮動小数点数の制限はなくなりましたが 6 flop(通常の和の6倍)必要になります.しかし,多くの場合で加算する浮動小数点数の大小関係は非自明なので TwoSum を使用するケースが多いです. 積に関するエラーフリー変換 積に関するエラーフリー変換として, またまた Dekker1 により紹介された TwoProduct というアルゴリズムがあります.このアルゴリズムは FastTwoSum/TwoSum に比べると少し複雑ですが以下のようになります. TwoProduct $a$, $b \in \mathbb{F}$ について$\rm{TwoProduct}$ を実行すれば, a * b = x + y が成り立つ. TwoProduct.py def split(a): # s は浮動小数点数の (仮数部 bit 数 t // 2 ) + 1 で計算できる定数 # 倍精度なら t = 53, 単精度なら t = 24 t = 53 s = (t // 2) + 1 tmp = a * (s + 1) tmp = s * a ah = tmp - (tmp - a) al = a - ah return [ah, al] def TwoProduct(a, b): x = a * b [ah, al] = split(a) [bh, bl] = split(b) y =(al * bl) - ((x - (ah * bh) - al * bh) - ah * bl) return [x, y] TwoProduct で使用する split というアルゴリズムもエラーフリー変換で,簡単に説明すると $a \in \mathbb{F}$ を $a = a_h + a_l$ のように浮動小数点数の上位ビット/下位ビットに誤差なく分解するイメージです. split 中にはハイパーパラメータ $t$ が登場しますが,これは扱う浮動小数点数の仮数部のビット長を意味します.Python では浮動小数点数の精度が倍精度なので $t=53$ としています. また,TwoProduct では split で 8 flop,その他で 9 flop なので合計 25 flop(通常の積の25倍)必要です. 積のエラーフリー変換には割と演算コストがかかってしまうのですが,FMAを用いることで計算コストを抑えられます. TwoProductFMA $a$, $b \in \mathbb{F}$ について$\rm{TwoProductFMA}$ を実行すれば, a * b = x + y が成り立つ.(TwoProduct の結果と同じ) TwoProductFMA.py # pyfma はデフォルトでインストールされていないので # 事前に「pip3 install pyfma」しておく必要があります. from pyfma import fma def TwoProductFMA(a, b): x = a * b y = fma(a, b, -x) return [x, y] FMAを用いることで split も必要なくなり,非常にスッキリとしたアルゴリズムになりました.この方法ではわずか 2 flop(通常の積の2倍)で積のエラーフリー変換ができます.そのため,FMA がハードウェアサポートされている環境4であればこちらのアルゴリズムで高速に計算できます.(サポートされていなくても動作はしますが遅いです.) 実験してみた これまでに紹介したエラーフリー変換アルゴリズムを使って実際に動作確認してみようと思います. まず TwoSum への入力を $(a,\ b) = (1,\ 3 \mathbb{u})$ として実行します. この演算で誤差が生じなければ結果は $1 + 3 \mathbb{u}\ $ですが,実際は丸めにより $1 + 4\mathbb{u}$ となります.つまり,TwoSum により誤差 $y = -\mathbb{u} \ $となれば TwoSum が正しく動作していると言えます. test_TwoSum.py u = 2**-53 [x, y] = TwoSum(1.0, 3*u) if (x, y) == (1.0 + 4*u, -u): print("OK") 実行結果 OK 正しく動作しました. 続いて TwoProduct を入力 $(a,\ b) = (1+2\mathbb{u},\ 1+2\mathbb{u})\ $として実行します. この演算で誤差が生じなければ結果は $1 + 4 \mathbb{u} + 4 \mathbb{u}^2\ $ですが,こちらも実際は丸めにより $1 + 4\mathbb{u}\ $となります.つまり,TwoProduct により誤差 $y = 4\mathbb{u}^2 \ $となれば TwoProduct が正しく動作していると言えます. test_TwoProduct.py u = 2**-53 [x, y] = TwoProduct(1+2*u, 1+2*u) if (x, y) == (1.0 + 4*u, 4*u**2): print("OK") 実行結果 OK TwoProduct も正しく動作しました. GIthub にエラーフリー変換のソースを置いてありますので,実際に手元で動作させてみたい方は利用してください.(そのうち Python 以外の言語でも実装しようと思います.) 次回これらのエラーフリー変換を用いた高精度計算について紹介しようと思います. 参考 TJ Dekker, floating-point technique for extending the available precision, Numer. Math. 18, 224-242, 1971 ↩ 明に示したのは Dekker ですが, 1965年に Kahan が総和に関する論文の中で FastTwoSum をそれとなく使っています. ↩ D. E. Kuth, The Art of Computer Programming, Volume 2: Seminunierical Algorithms, volume 2, Addison Wesley, Reading, Massachusetts, 1981 ↩ Linux 系環境の方は lscpu | grep fma で fma がハードウェアサポートされているか確認できます. ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでシコる

最近、3D-CADで物つくりをと考えていて、CADのことを色々調べたうえでの自分の結論 FUSION360 今のところベスト、solidworksを職場で使っていて、モデル作成のイメージが近い BLENDER これはないな SKETCH-UP なんちゅうか、めんどい DraftSight ええっちゅう噂・・・ → すべて触った上で、見解を書くけど・・・ すべてのCADソフト=邪魔 結論 Pythonでモデル作れるCADが有難い → Cadqueryというものがある。 その他 Pythonで動画を作る さのうち、Pythonでシコれる日が来るだろう・・・
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Anacondaで複数pythonバージョンの仮想環境を作る

異なるpythonのバージョンやソフトウェアライブラリを個別に管理できる環境を構築するため、仮想環境を作ってみた。やったことを記録しておかないと忘れるため、またqiitaに投稿します。 Anaconda仮想環境を作成 まず、createコマンドでanacondaの仮想環境を作ります。 nameオプション.....環境名 python.....pythonのバージョンを指定 base) C:\Users\${your_username}>conda create --name anaconda_virtual_env python=3.6.7 すると、 certifi ,pip などのパッケージインストールから始まる。 Collecting package metadata (current_repodata.json): done Solving environment: failed with repodata from current_repodata.json, will retry with next repodata source. Collecting package metadata (repodata.json): done Solving environment: done ## Package Plan ## environment location: C:\Users\cqe01\.conda\envs\anaconda_virtual_env added / updated specs: - python=3.8.7 The following NEW packages will be INSTALLED: certifi pkgs/main/win-64::certifi-2020.12.5-py36haa95532_0 pip pkgs/main/win-64::pip-21.1.1-py36haa95532_0 python pkgs/main/win-64::python-3.6.7-h9f7ef89_2 setuptools pkgs/main/win-64::setuptools-52.0.0-py36haa95532_0 sqlite pkgs/main/win-64::sqlite-3.35.4-h2bbff1b_0 vc pkgs/main/win-64::vc-14.2-h21ff451_1 vs2015_runtime pkgs/main/win-64::vs2015_runtime-14.27.29016-h5e58377_2 wheel pkgs/main/noarch::wheel-0.36.2-pyhd3eb1b0_0 wincertstore pkgs/main/win-64::wincertstore-0.2-py36h7fe50ca_0 Proceed ([y]/n)? y Anaconda仮想環境を起動する 仮想環境を起動する。Anaconda Promptの先頭が仮想環境名(次の例ではanaconda_virtual_env)に代わり、いま操作しているのがどの環境かわかる。 C:\Users\${your_username}>conda activate anaconda_virtual_env (anaconda_virtual_env) C:\Users\${your_username}> Anaconda仮想環境にライブラリをインストールする 続いてライブラリを次々にインストールしていく。 仮想環境は、 C:\Users\${your_username}.conda\envs\anaconda_virtual_env に保存されたようです。 (anaconda_virtual_env) C:\Users\${your_username}>conda install pandas=0.22.0 (中略) (anaconda_virtual_env) C:\Users\${your_username}>conda install numpy=1.14.6 (中略) (anaconda_virtual_env) C:\Users\${your_username}>conda install matplotlib=2.1.2 (中略) (anaconda_virtual_env) C:\Users\${your_username}>conda install -c anaconda seaborn (中略) (anaconda_virtual_env) C:\Users\${your_username}>conda install -c conda-forge scikit-learn (中略) (anaconda_virtual_env) C:\Users\${your_username}>conda install -c conda-forge xgboost (中略) (anaconda_virtual_env) C:\Users\${your_username}>conda install -c conda-forge lightgbm (中略) (anaconda_virtual_env) C:\Users\${your_username}>conda install -c conda-forge pandas_profiling (中略) (anaconda_virtual_env) C:\Users\${your_username}>conda install -c conda-forge tqdm (中略) (anaconda_virtual_env) C:\Users\${your_username}>conda install -c conda-forge sqlalchamey (中略) 最後にjupyterをインストールする。 (anaconda_virtual_env) C:\Users\${your_username}>conda install jupyter jupyter入れると、パッケージがたくさんインストールさされる。 レスポンスは、こんな感じ。 Collecting package metadata (current_repodata.json): done Solving environment: failed with initial frozen solve. Retrying with flexible solve. Solving environment: failed with repodata from current_repodata.json, will retry with next repodata source. Collecting package metadata (repodata.json): done Solving environment: done ## Package Plan ## environment location: C:\Users\${your_username}\.conda\envs\pandas_book added / updated specs: - jupyter The following packages will be downloaded: package | build ---------------------------|----------------- argon2-cffi-20.1.0 | py36h2bbff1b_1 49 KB async_generator-1.10 | py36h28b3542_0 40 KB cffi-1.14.5 | py36hcd4344a_0 223 KB decorator-5.0.9 | pyhd3eb1b0_0 12 KB entrypoints-0.3 | py36_0 12 KB importlib-metadata-3.10.0 | py36haa95532_0 33 KB ipykernel-5.3.4 | py36h5ca1d4c_0 184 KB ipython-7.16.1 | py36h5ca1d4c_0 1017 KB jedi-0.17.0 | py36_0 778 KB jinja2-3.0.0 | pyhd3eb1b0_0 106 KB jupyter-1.0.0 | py36_7 6 KB jupyter_core-4.7.1 | py36haa95532_0 85 KB markupsafe-2.0.1 | py36h2bbff1b_0 24 KB mistune-0.8.4 | py36he774522_0 55 KB nbconvert-6.0.7 | py36_0 497 KB notebook-6.4.0 | py36haa95532_0 4.4 MB pandocfilters-1.4.3 | py36haa95532_1 14 KB pygments-2.9.0 | pyhd3eb1b0_0 721 KB pyrsistent-0.17.3 | py36he774522_0 92 KB pywin32-227 | py36he774522_1 5.6 MB pywinpty-0.5.7 | py36_0 50 KB pyzmq-20.0.0 | py36hd77b12b_1 393 KB terminado-0.9.4 | py36haa95532_0 26 KB traitlets-4.3.3 | py36_0 140 KB webencodings-0.5.1 | py36_1 19 KB widgetsnbextension-3.5.1 | py36_0 868 KB ------------------------------------------------------------ Total: 15.2 MB The following NEW packages will be INSTALLED: argon2-cffi pkgs/main/win-64::argon2-cffi-20.1.0-py36h2bbff1b_1 async_generator pkgs/main/win-64::async_generator-1.10-py36h28b3542_0 attrs pkgs/main/noarch::attrs-21.2.0-pyhd3eb1b0_0 backcall pkgs/main/noarch::backcall-0.2.0-pyhd3eb1b0_0 bleach pkgs/main/noarch::bleach-3.3.0-pyhd3eb1b0_0 cffi pkgs/main/win-64::cffi-1.14.5-py36hcd4344a_0 colorama pkgs/main/noarch::colorama-0.4.4-pyhd3eb1b0_0 decorator pkgs/main/noarch::decorator-5.0.9-pyhd3eb1b0_0 defusedxml pkgs/main/noarch::defusedxml-0.7.1-pyhd3eb1b0_0 entrypoints pkgs/main/win-64::entrypoints-0.3-py36_0 importlib-metadata pkgs/main/win-64::importlib-metadata-3.10.0-py36haa95532_0 importlib_metadata pkgs/main/noarch::importlib_metadata-3.10.0-hd3eb1b0_0 ipykernel pkgs/main/win-64::ipykernel-5.3.4-py36h5ca1d4c_0 ipython pkgs/main/win-64::ipython-7.16.1-py36h5ca1d4c_0 ipython_genutils pkgs/main/noarch::ipython_genutils-0.2.0-pyhd3eb1b0_1 ipywidgets pkgs/main/noarch::ipywidgets-7.6.3-pyhd3eb1b0_1 jedi pkgs/main/win-64::jedi-0.17.0-py36_0 jinja2 pkgs/main/noarch::jinja2-3.0.0-pyhd3eb1b0_0 jsonschema pkgs/main/noarch::jsonschema-3.2.0-py_2 jupyter pkgs/main/win-64::jupyter-1.0.0-py36_7 jupyter_client pkgs/main/noarch::jupyter_client-6.1.12-pyhd3eb1b0_0 jupyter_console pkgs/main/noarch::jupyter_console-6.4.0-pyhd3eb1b0_0 jupyter_core pkgs/main/win-64::jupyter_core-4.7.1-py36haa95532_0 jupyterlab_pygmen~ pkgs/main/noarch::jupyterlab_pygments-0.1.2-py_0 jupyterlab_widgets pkgs/main/noarch::jupyterlab_widgets-1.0.0-pyhd3eb1b0_1 libsodium pkgs/main/win-64::libsodium-1.0.18-h62dcd97_0 m2w64-gcc-libgfor~ pkgs/msys2/win-64::m2w64-gcc-libgfortran-5.3.0-6 m2w64-gcc-libs pkgs/msys2/win-64::m2w64-gcc-libs-5.3.0-7 m2w64-gcc-libs-co~ pkgs/msys2/win-64::m2w64-gcc-libs-core-5.3.0-7 m2w64-gmp pkgs/msys2/win-64::m2w64-gmp-6.1.0-2 m2w64-libwinpthre~ pkgs/msys2/win-64::m2w64-libwinpthread-git-5.0.0.4634.697f757-2 markupsafe pkgs/main/win-64::markupsafe-2.0.1-py36h2bbff1b_0 mistune pkgs/main/win-64::mistune-0.8.4-py36he774522_0 msys2-conda-epoch pkgs/msys2/win-64::msys2-conda-epoch-20160418-1 nbclient pkgs/main/noarch::nbclient-0.5.3-pyhd3eb1b0_0 nbconvert pkgs/main/win-64::nbconvert-6.0.7-py36_0 nbformat pkgs/main/noarch::nbformat-5.1.3-pyhd3eb1b0_0 nest-asyncio pkgs/main/noarch::nest-asyncio-1.5.1-pyhd3eb1b0_0 notebook pkgs/main/win-64::notebook-6.4.0-py36haa95532_0 packaging pkgs/main/noarch::packaging-20.9-pyhd3eb1b0_0 pandoc pkgs/main/win-64::pandoc-2.12-haa95532_0 pandocfilters pkgs/main/win-64::pandocfilters-1.4.3-py36haa95532_1 parso pkgs/main/noarch::parso-0.8.2-pyhd3eb1b0_0 pickleshare pkgs/main/noarch::pickleshare-0.7.5-pyhd3eb1b0_1003 prometheus_client pkgs/main/noarch::prometheus_client-0.10.1-pyhd3eb1b0_0 prompt-toolkit pkgs/main/noarch::prompt-toolkit-3.0.17-pyh06a4308_0 prompt_toolkit pkgs/main/noarch::prompt_toolkit-3.0.17-hd3eb1b0_0 pycparser pkgs/main/noarch::pycparser-2.20-py_2 pygments pkgs/main/noarch::pygments-2.9.0-pyhd3eb1b0_0 pyrsistent pkgs/main/win-64::pyrsistent-0.17.3-py36he774522_0 pywin32 pkgs/main/win-64::pywin32-227-py36he774522_1 pywinpty pkgs/main/win-64::pywinpty-0.5.7-py36_0 pyzmq pkgs/main/win-64::pyzmq-20.0.0-py36hd77b12b_1 qtconsole pkgs/main/noarch::qtconsole-5.0.3-pyhd3eb1b0_0 qtpy pkgs/main/noarch::qtpy-1.9.0-py_0 send2trash pkgs/main/noarch::send2trash-1.5.0-pyhd3eb1b0_1 terminado pkgs/main/win-64::terminado-0.9.4-py36haa95532_0 testpath pkgs/main/noarch::testpath-0.4.4-pyhd3eb1b0_0 traitlets pkgs/main/win-64::traitlets-4.3.3-py36_0 typing_extensions pkgs/main/noarch::typing_extensions-3.7.4.3-pyha847dfd_0 wcwidth pkgs/main/noarch::wcwidth-0.2.5-py_0 webencodings pkgs/main/win-64::webencodings-0.5.1-py36_1 widgetsnbextension pkgs/main/win-64::widgetsnbextension-3.5.1-py36_0 winpty pkgs/main/win-64::winpty-0.4.3-4 zeromq pkgs/main/win-64::zeromq-4.3.3-ha925a31_3 zipp pkgs/main/noarch::zipp-3.4.1-pyhd3eb1b0_0 Proceed ([y]/n)? スクリーンショットを撮影できませんでしたが、 パッケージインストール終わると、wundowsからpython.exeを実行していいか?聞かれます。 補完機能をインストールしたいので、それが入っているjupyter extension機能もインストールしておく。 conda install -y -c conda-forge jupyter_contrib_nbextensions コード補完に必要なpthonの language serverをインストールする conda install -c conda-forge python-language-server Anaconda仮想環境を停止する 起動コマンドのactivateに、反対の意味の接頭辞"de"をつけた、deactivateコマンドで停止する。 (anaconda_virtual_env) C:\Users\${your_username}>conda deactivate Anaconda仮想環境を確認する (base) C:\Users\${your_username}>conda info -e base * C:\ProgramData\Anaconda3 pandas_book C:\Users\${your_username}\.conda\envs\pandas_book Anacondaの不要になったパッケージやキャッシュを削除 conda clean コマンドを利用すると、使われていないパッケージやキャッシュを削除することができます。 削除できるものは全て削除するのが良い。次のコマンドにより500 Mbyte以上削除できた。 conda clean -all Anaconda仮想環境を簡単に切り替えられるようにする ( https://www.servernote.net/article.cgi?id=anaconda-jupyter-notebook-on-myenv より引用) (base) C:\Users\${your_username}>conda info -e # conda environments: # base * C:\ProgramData\Anaconda3 pandas_book C:\Users\${your_username}\.conda\envs\pandas_book kernel listに追加し、jupyter notebookのchange kernelで選べるようにする (https://qiita.com/tomochiii/items/8b937f15c79a0c3eae0e より引用) # 利用可能なカーネルの表示 (base) C:\Users\${your_username}>jupyter kernelspec list Available kernels: python3 C:\ProgramData\Anaconda3\share\jupyter\kernels\python3 仮想環境をアクティベートした状態で追加する。 (base) C:\Users\${your_username}>conda activate pandas_book (pandas_book) C:\Users\${your_username}>ipython kernel install --user --name=pandas_book --display-name=pandas_book 逆に追加したカーネルを削除する方法  https://qiita.com/tomochiii/items/8b937f15c79a0c3eae0e にあります。 baseを起動して、カーネルモジュールをインストール 仮想環境の切り替えを相互に行うため、base仮想環境と自分の仮想環境に上記モジュールが必要になる。 スタートメニュー→Anadonda3 64-bit→Jupyter Notebook(64bit)で起動されるのはbase環境のJupyterです。 Anaconda Promptを立ち上げ、カーネルをbaseとmyenvに両方に入れます。 (base) C:\Users\${your_username}>conda activate base (base) C:\Users\${your_username}>pip install jupyter environment_kernels Collecting environment_kernels Using cached environment_kernels-1.1.1.tar.gz (28 kB) Building wheels for collected packages: environment-kernels Building wheel for environment-kernels (setup.py) ... done Created wheel for environment-kernels: filename=environment_kernels-1.1.1-py3-none-any.whl size=30797 sha256=37a060aa3ca1829753a9d55a23df787c9d8341c52c851268b2e215c8742ffa11 Stored in directory: c:\users\${your_username}\appdata\local\pip\cache\wheels\e0\be\3e\601232ccfabd4e923027463dd67df3c263a6a91aace930a34d Successfully built environment-kernels Installing collected packages: environment-kernels Successfully installed environment-kernels-1.1.1 (base) C:\Users\${your_username}>conda deactivate デフォルト仮想環境baseでJupyter初期設定ファイルを生成・編集する まず仮想環境baseで、Jupyter初期設定ファイル(jupyter_notebook_config.py)を生成する。 conda activate base (base) C:\Users\${your_username}>jupyter notebook --generate-config Writing default config to: C:\Users\${your_username}\.jupyter\jupyter_notebook_config.py 作成されたJupyter初期設定ファイル(jupyter_notebook_config.py)をatomやterapadなどのテキストエディタで開き、次の項目を追加する。${user_username}は自分のPCに合わせて適宜変更してください。 c.NotebookApp.kernel_spec_manager_classに、先ほどインストールしたモジュール(environment-kernels)のカーネルマネージャを指定。 c.NotebookApp.kernel_spec_manager_class = 'environment_kernels.EnvironmentKernelSpecManager' c.EnvironmentKernelSpecManager.conda_env_dirsに 仮想環境の保存先フォルダを指定 c.EnvironmentKernelSpecManager.conda_env_dirs = ['C:\Users\${ure_username}\.conda\envs\'] 各仮想環境のJupyterでカーネル操作できるよう、各仮想環境に ipykernelのインストール C:\Users\${ure_username}\.conda\envs\${your_env_name}> conda install notebook ipykernel 以上で、jupyter notebookで仮想環境が切り替えられるようになった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pyenv備忘録

pyenvを使うときのあれこれ インストール 「pyenv install」で検索 アップデート 「pyenv update」で検索 色々なコマンド pyenv で、可能なコマンドが表示される。 環境名を自分で指定 pyenv virtualenv 3.9.5 project とすると、python3.9.5で作った環境をprojectという名前で設定できる。(プラグイン要)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonとWinRT OCRで文字認識

インストール pip install winrt # Pillowを使う人はインストール #pip install pillow # OpenCVを使う人はインストール #pip install opencv-python # API経由で使う人はインストール #pip install fastapi uvicorn # Jupyter, Colaboratorerはインストール #pip install --upgrade jupyter_http_over_ws>=0.0.7 #jupyter serverextension enable --py jupyter_http_over_ws #jupyter notebook --NotebookApp.allow_origin='https://colab.research.google.com' --ip=0.0.0.0 --port=8888 --NotebookApp.port_retries=0 UbuntuやMacから使用する場合は、VMSなどからWindowsをVirtualBoxなどにインストールし、ポートフォワーディングなどを行う。 winocr.pyをダウンロード Usage winocrをインポート import winocr Pillow使い langパラメータ(第2引数)で認識対象の言語を指定可能です。 from PIL import Image img = Image.open('test.jpg') (await winocr.recognize_pil(img, 'ja')).text OpenCV使い import cv2 img = cv2.imread('test.jpg') (await winocr.recognize_cv2(img, 'ja')).text Colaboratoryへのローカルランタイムへの接続 API経由で使う人 python winocr.py を実行し、予めAPI Serverを起動させること! Python import requests f = open('test.jpg', 'rb') requests.post('http://localhost:8000/?lang=ja', f.read()).json()['text'] ./ngrok http 8000 でColaboratoryのランタイムでOCRを実行できます。 from PIL import Image from io import BytesIO img = Image.open('test.jpg') # 前処理する buf = BytesIO() img.save(buf, format='JPEG') requests.post('https://15a5fabf0d78.ngrok.io/?lang=ja', buf.getvalue()).json()['text'] import cv2 import requests img = cv2.imread('test.jpg') # 前処理する requests.post('https://15a5fabf0d78.ngrok.io/?lang=ja', cv2.imencode('.jpg', img)[1].tobytes()).json()['text'] Windows ServerでOCR Serverを動かすことも可能です。 Curl curl localhost:8000?lang=ja --data-binary @test.jpg JavaScript Chromeかつ英語だけの認識だけでよければ、Text Detection APIも検討できます。 // input要素の画像から認識 const file = document.querySelector('[type=file]').files[0] await fetch('http://localhost:8000/', {method: 'POST', body: file}).then(r => r.json()) // 画像をダウンロードして認識 const blob = await fetch('https://image.itmedia.co.jp/ait/articles/1706/15/news015_16.jpg').then(r=>r.blob()) await fetch('http://localhost:8000/?lang=ja', {method: 'POST', body: blob}).then(r => r.json()) 取得可能な情報 angle, text, line, word, BoundingBoxが取得できます。 import pprint result = await winocr.recognize_pil(img, 'ja') pprint.pprint({ 'text_angle': result.text_angle, 'text': result.text, 'lines': [{ 'text': line.text, 'words': [{ 'bounding_rect': {'x': word.bounding_rect.x, 'y': word.bounding_rect.y, 'width': word.bounding_rect.width, 'height': word.bounding_rect.height}, 'text': word.text } for word in line.words] } for line in result.lines] }) 言語のインストール # 管理者のPowerShellでインストールできます Add-WindowsCapability -Online -Name "Language.OCR~~~en-US~0.0.1.0" Add-WindowsCapability -Online -Name "Language.OCR~~~ja-JP~0.0.1.0" # インストールした言語の検索 Get-WindowsCapability -Online -Name "Language.OCR*" # State: NotPresentの言語はインストール出来ていないので必要に応じてインストールしてください Name : Language.OCR~~~hu-HU~0.0.1.0 State : NotPresent DisplayName : ハンガリー語の光学式文字認識 Description : ハンガリー語の光学式文字認識 DownloadSize : 194407 InstallSize : 535714 Name : Language.OCR~~~it-IT~0.0.1.0 State : NotPresent DisplayName : イタリア語の光学式文字認識 Description : イタリア語の光学式文字認識 DownloadSize : 159875 InstallSize : 485922 Name : Language.OCR~~~ja-JP~0.0.1.0 State : Installed DisplayName : 日本語の光学式文字認識 Description : 日本語の光学式文字認識 DownloadSize : 1524589 InstallSize : 3398536 Name : Language.OCR~~~ko-KR~0.0.1.0 State : NotPresent DisplayName : 韓国語の光学式文字認識 Description : 韓国語の光学式文字認識 DownloadSize : 3405683 InstallSize : 7890408 宗教上の理由でPythonが使えない、PowerShellだけで認識したい場合は、 こちら
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む