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

FastAPI+React+DockerでQiitaみたいなサイトを作ってみたい -6日目-

目次 1日目 - 初期構成 2日目 - /docsの作成 3日目 - DBとModelの作成 4日目 - CRUDの作成 5日目 - Testの導入 テストの導入 色々と仕様や導入方法などを調べていたらだいぶ時間が経ってしまったが、 前回大体の機能の実装ができたので、まずはテストを導入していく テストの実行 とりあえずテストを実行するまで ライブラリの導入 backendの環境の中に入って以下でpytestをインストールする bash poetry add pytest テストの実行 backendの環境内に入って以下のコマンドでテストを実行できる bash poetry run pytest テスト前の準備 今までずっと無視していたがpytestを行う上で各ディレクトリにinit.py(ディレクトリをライブラリとして認識するための空ファイルらしい)を作成する必要があるので、今まで作った以下のディレクトリにinit.pyの空ファイルを作っておく。 backend/api/__init__.py backend/api/v1/__init__.py backend/api/v1/tests/__init__.py テストの作成 一旦/のルーティングにあるhalloworldのレスポンスを返すエンドポイントのテストを書いていく テストファイルを以下のように作成する。 (テストファイルはファイル名をtest_***のようにするのが慣例らしい) api/v1/tests/test_main.py from fastapi.testclient import TestClient from api.v1.main import app client = TestClient(app) def test_root_path(): response = client.get('/') assert response.status_code == 200 FastApiで用意されているTestClientを利用してappに対して各リクエストをテストし、responseの内容をチェックしていくことでテストを行なっていくことができる。 データベースを利用したテスト データベースを利用したテストを行なっていく場合、テスト用のDBを用意する必要がある。 今回はsqliteを利用する。 今までrouter内でDBを利用する際はget_dbというメソッドを依存注入していたのがここで効いてくる。 test用のconfファイルとしてtestconf.pyを以下のように作成する。 api/v1/tests/testconf.py import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session, sessionmaker from sqlalchemy.orm.session import close_all_sessions from api.v1.main import app from api.v1.db import get_db class TestingSession(Session): def commit(self): # commitをオーバーライドする self.flush() self.expire_all() @pytest.fixture(scope="function") def test_db(): engine = create_engine("sqlite:///./test_sqlite.db",connect_args={"check_same_thread":False}) TestSessionLocal = sessionmaker(class_=TestiongSession,autocommit=False,autoflush=False,bind=engine) db = TestSessionLocal() def get_test_db(): try: yield db db.commit() except SQLAlchemyError as e: assert e is not None db.rollback app.dependency_overrides[get_db] = get_test_db yield db db.rollback() close_all_sessions() engine.dispose() これでこのメソッドを先程のTestClientから作成したclientの引数に撮ることでget_dbをオーバーライドしてテスト時はsqliteを利用するように変更することができる。 テスト用のDBに各テーブルを作成する このままだと何のテーブルもないDBなので、ここでテーブルを作成する記述を追加する api/v1/tests/testconf.py # ~~~ from api.v1.models import user, post, lgtm # ~~~ def test_db(): engine = create_engine("sqlite:///./test_sqlite.db",connect_args={"check_same_thread":False}) user.Base.metadata.create_all(bind=engine) post.Base.metadata.create_all(bind=engine) lgtm.Base.metadata.create_all(bind=engine) TestSessionLocal = sessionmaker(class_=TestingSession,autocommit=False,autflush=False,bind=engine) # ~~~ これで各Modelに対応するテーブルが作成できる テストを作成 DBを利用したエンドポイント(GET /user/{login_id})のテストを書いてみる api/v1/tests/routers/test_user.py from fastapi.testclient import TestClient from api.v1.main import app from api.v1.tests.testconf import test_db from api.v1.models.user import User client = TestClient(app) def test_get_user_path(test_db): user1 = User(login_id="test_user_1",name="TestUser1",password_hash="unsecurepass") user2 = User(login_id="test_user_2",name="TestUser2",password_hash="unsecurepass") test_db.add_all([user_1,user_2]) response = client.get(f'/user/{user1.login_id}') assert response.status_code == 200 assert response.json()["login_id"] == "test_user_1" assert response.json()["name"] == "TestUser1" assert response.json()["password_hash"] is None これでDBを利用したケースのテストが書けるようになった。 ちなみにPostメソッドなどのリクエストにbodyをつけるようなリクエストの場合は以下のように書く。 response = client.post('/users',json={ "login_id":"test_user", "name":"TestUser", "password":"unsecurepass", "description":"fugafugafuga" }) multipart/form-dataなどの情報をリクエストするときはこんな感じ response = client.post('/token',data={ "username":"hogehoge", "password":"fugafuga" }) 認証が必要なエンドポイントへのテスト 認証を通した状態を再現するためにtestconf.pyにメソッドを追加する。 api/v1/tests/testconf.py # ~~~ import api.v1.cruds.auth as crud_auth # ~~~ def authorized_client(client:TestClient,user.User): access_token = crud_auth.create_access_token(data={"sub":user.login_id}) client.headers = { **client.headers, "Authorization":f"Bearer {access_token}" } return client def deauthorized_client(client:TestClient): del client.headers["Authorization"] return client テストの作成 認証が必要なPost /postsへのテストを作成する api/v1/tests/test_post.py from fastapi.testclient import TestClient from api.v1.main import app from api.v1.tests.testconf import test_db,authorized_client,deauthorized_client from api.v1.models.user import User from api.v1.models.post import Post client = TestClient(app) def test_post_posts_path(test_db): user_1 = User(login_id="test_user1",name="TestUser1",password_hash="unsecurepass") test_db.add(user_1) test_db.flush() test_db.commit() authorized_client(client,user_1) before_count = test_db.query(Post).count() response = client.post("/posts",json={ "title":"hogehoge", "context":"fugafugafugafuga" }) after_count = test_db.query(Post).count() assert response.status_code == 200 assert after_count-before_count == 1 assert response.json()["title"] == "hogehoge" assert response.json()["context"] == "fugafugafugafuga" assert response.json()["user"]["login_id"] == "test_user1" deauthorized_client(client) これで認証を通した状態でのテストが書けるようになった。 大体のテストはこのパターンで書けるので、これを参考に他のエンドポイントへのテストを追加していく。 backendの実装はあとはバリデーション周りの実装を行えば、次はフロントエンドに手をつけられそう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python] Youtube APIを使って動画の視聴回数動向をExcel出力しグラフ化

はじめに こんにちは!KDDIプラットフォーム技術部の山口です! 今回はPythonを使ってYoutubeのAPIを呼び出して視聴回数動向のグラフを作成するプログラムを備忘として残しておきます。 スクリプト中に無駄な部分があるのは許してください。笑 対象者 Pythonを使ったYoutubeAPIの使い方に興味がある人 グラフ作成を自動化してみたい人 項目 1.概要 2.スクリプトとグラフ 3.おわりに 概要 流れ:①YoutubeAPIKEYを作成、②任意の動画のIDをURLから取得、③プログラムを作成、④実行してファイルに出力(私の場合はExcel) API Keyの作成方法については以下のURLを参考にしました。 1.【データサイエンス】YouTubeのAPIを使ってYouTube内のデータを収集してみよう https://blog.codecamp.jp/programming-api-youtube 2.Youtube Data API Key の取得手順 http://piyohiko.webcrow.jp/kids_tube/help/index.html 本スクリプトは、API Keyと動画のIDのXXXの部分を任意の値に編集、インポートが必要なモジュールが使える、Excelが使える環境であれば、コピペで動作させることができます。 スクリプトとグラフ Sample.py ''' YoutubeのAPIを使って、Youtube内の特定の動画の情報を取得し、 Excelに結果を吐き出して、その値を使って自動的にグラフを生成するプログラム  ''' import json import requests import datetime from apiclient.discovery import build #GoogleAnalyticsAPI   from openpyxl import Workbook from openpyxl.chart import BarChart, Reference import numpy as np import time YOUTUBE_API_KEY = "XXXXX" #自分で作成したAPI KEY YOUTUBE_MOVIE_ID = 'XXXXXX' #取得したい動画のID API_SERVICE_NAME = "youtube" API_VERSION = "v3" youtube = build(API_SERVICE_NAME, API_VERSION, developerKey=YOUTUBE_API_KEY) list_statistics =[0] list_dt2 =[0] snippet = youtube.videos().list(part = 'snippet', id = YOUTUBE_MOVIE_ID).execute()['items'][0]['snippet'] #print('動画タイトル : ',snippet['title']) for i in range(10): statistics = youtube.videos().list(part = 'statistics', id = YOUTUBE_MOVIE_ID).execute()['items'][0]['statistics'] list_statistics.append(statistics['viewCount']) dt2 = datetime.datetime.now() list_dt2.append(dt2) wb = Workbook() sheet = wb.create_sheet("sheet01",0) #sheet名 sheet.cell(row = 1, column = 1, value = "取得順序") sheet.cell(row = 1, column = 2, value = "再生回数") X = np.arange(10) len_data = len(X) for i in range(1, len_data): sheet.cell(row= i + 1, column = 1, value = X[i]) sheet.cell(row= i + 1, column = 2, value = int(list_statistics[i])) chart = BarChart() values = Reference(sheet, min_row=1, min_col=2, max_row=11, max_col=2) chart.add_data(values, titles_from_data=True) xvalues = Reference(sheet, min_row=2, min_col=1, max_row=11, max_col=1) chart.set_categories(xvalues) sheet.add_chart(chart, "E15") wb.save("視聴動向回数グラフ.xlsx") #左記ファイル名で保存 このグラフは以下のyoutube(アーティストのLisaさんの炎のFIRST TAKE)の視聴回数を取得しました(URL末尾のv=XXXXXの部分が動画のIDになります)。https://www.youtube.com/watch?v=4Q9DWZLaY2U なお、再生回数が一定なのは、取得期間である、10秒以内に回数が上昇していないからです。 おわりに 今回はPythonを使ってYoutubeAPIを呼び出し、取得したデータをエクセルに出力後、グラフ化するプログラムを紹介しました。 カラムの位置とか割とつまずきましたが、意外と簡単にできますね! 引き続き、アウトプットたくさん作れるように努めていきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Youtube APIを使って動画の視聴回数動向をExcel出力しグラフ化

はじめに こんにちは!KDDIプラットフォーム技術部の山口です! 今回はPythonを使ってYoutubeのAPIを呼び出して視聴回数動向のグラフを作成するプログラムを備忘として残しておきます。 スクリプト中に無駄な部分があるのは許してください。笑 対象者 Pythonを使ったYoutubeAPIの使い方に興味がある人 グラフ作成を自動化してみたい人 項目 1.概要 2.スクリプトとグラフ 3.おわりに 概要 流れ:①YoutubeAPIKEYを作成、②任意の動画のIDをURLから取得、③プログラムを作成、④実行してファイルに出力(私の場合はExcel) API Keyの作成方法については以下のURLを参考にしました。 1.【データサイエンス】YouTubeのAPIを使ってYouTube内のデータを収集してみよう https://blog.codecamp.jp/programming-api-youtube 2.Youtube Data API Key の取得手順 http://piyohiko.webcrow.jp/kids_tube/help/index.html 本スクリプトは、API Keyと動画のIDのXXXの部分を任意の値に編集、インポートが必要なモジュールが使える、Excelが使える環境であれば、コピペで動作させることができます。 スクリプトとグラフ Sample.py ''' YoutubeのAPIを使って、Youtube内の特定の動画の情報を取得し、 Excelに結果を吐き出して、その値を使って自動的にグラフを生成するプログラム  ''' import json import requests import datetime from apiclient.discovery import build #GoogleAnalyticsAPI   from openpyxl import Workbook from openpyxl.chart import BarChart, Reference import numpy as np import time YOUTUBE_API_KEY = "XXXXX" #自分で作成したAPI KEY YOUTUBE_MOVIE_ID = 'XXXXXX' #取得したい動画のID API_SERVICE_NAME = "youtube" API_VERSION = "v3" youtube = build(API_SERVICE_NAME, API_VERSION, developerKey=YOUTUBE_API_KEY) list_statistics =[0] list_dt2 =[0] snippet = youtube.videos().list(part = 'snippet', id = YOUTUBE_MOVIE_ID).execute()['items'][0]['snippet'] #print('動画タイトル : ',snippet['title']) for i in range(10): statistics = youtube.videos().list(part = 'statistics', id = YOUTUBE_MOVIE_ID).execute()['items'][0]['statistics'] list_statistics.append(statistics['viewCount']) dt2 = datetime.datetime.now() list_dt2.append(dt2) wb = Workbook() sheet = wb.create_sheet("sheet01",0) #sheet名 sheet.cell(row = 1, column = 1, value = "取得順序") sheet.cell(row = 1, column = 2, value = "再生回数") X = np.arange(10) len_data = len(X) for i in range(1, len_data): sheet.cell(row= i + 1, column = 1, value = X[i]) sheet.cell(row= i + 1, column = 2, value = int(list_statistics[i])) chart = BarChart() values = Reference(sheet, min_row=1, min_col=2, max_row=11, max_col=2) chart.add_data(values, titles_from_data=True) xvalues = Reference(sheet, min_row=2, min_col=1, max_row=11, max_col=1) chart.set_categories(xvalues) sheet.add_chart(chart, "E15") wb.save("視聴動向回数グラフ.xlsx") #左記ファイル名で保存 このグラフは以下のyoutube(アーティストのLisaさんの炎のFIRST TAKE)の視聴回数を取得しました(URL末尾のv=XXXXXの部分が動画のIDになります)。https://www.youtube.com/watch?v=4Q9DWZLaY2U なお、再生回数が一定なのは、取得期間である、10秒以内に回数が上昇していないからです。 おわりに 今回はPythonを使ってYoutubeAPIを呼び出し、取得したデータをエクセルに出力後、グラフ化するプログラムを紹介しました。 カラムの位置とか割とつまずきましたが、意外と簡単にできますね! 引き続き、アウトプットたくさん作れるように努めていきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

googlecolabで画像を複数一気にアップロードして表示させる方法

はじめに googlecolabを使っているとローカルから画像をアップロードしたくなることがありますよね。 そんな時、ちゃんとアップできたか確認するために画像をすべて表示したくなることがあります。 複数パスの取得など躓いたところがあったので書いておきます。 コード こちらの記事を参考にしました。 アップロードについてはfiles.upload()ででき、以下のようなものが表示されるようになります。 この際、複数ファイルを選択してしたり、ファイル選択の部分に複数ファイルをドラックアンドドロップすることでまとめてアップロードすることができて便利です。 画像名の一括取得は以下のようにできます。 from google.colab import files uploaded_files = files.upload() uploaded_file_names = list(uploaded_files.keys()) これを利用して画像の表示までしたものが以下のコードです。 from google.colab import files uploaded_files = files.upload() uploaded_file_names = list(uploaded_files.keys()) uploaded_file_names.sort() print(uploaded_file_names) import matplotlib.pyplot as plt from PIL import Image import numpy as np fig = plt.figure(figsize=(30, 40)) images=[] for i in range(len(uploaded_file_names)): img = Image.open(uploaded_file_names[i]) images = np.asarray(img) ax = fig.add_subplot(10, 10, i+1, xticks=[], yticks=[]) image_plt = np.array(images) ax.imshow(image_plt) ax.set_xlabel(str(i), fontsize=20) plt.show() plt.close()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

食の自社データでクロスモーダル解析やってみた

はじめに 初めまして、あらほし(bamboo nova)です。 今回は初めてorganizations連携で投稿します。 普段はSARAHで食に関するデータベース基盤やデータサイエンス基盤の設計や検証を行っています。今回は「何か新しいことをやりたい」ということで、クロスモーダル解析を実装して、text(menu)-to-image retrievalを検証しました。 クロスモーダル解析 機械学習の代表的なアプローチ教師としてあり学習が出てくると思います。教師あり学習だと、一般的にはデータに対してラベルが付与されていて、データを学習してラベルを回答するアプローチになっています。 しかし、ラベルはあくまで離散的な情報であるという制限があります。例えば、ハンバーグの画像に対してハンバーグのラベルを付与して学習を行い、ハンバーグのラベルを推定するのが代表的な教師あり学習でした。ところが、ハンバーグでも細かくみていくとデミグラスソースのハンバーグかもしれませんし、国産牛のハンバーグかもしれません。もしかしたら、ハンバーグにマッシュポテトが副菜として存在しているかもしれません。 これらの付加的な情報もマルチラベル(一つのデータに対して複数のラベルを紐づける)にすることで対応できますが、もっと細かい情報まで学習して推定したいとなると限界が出てきてしまいます。また、このようなデータ基盤を人手でラベリングして実現するとなると膨大なコストが発生してしまいます。 もう一つの課題として、データとして活用したい情報が必ずしも画像だけとは限りません。例えば、SARAHでは「おいしい一皿が集めるグルメコミュニティアプリ」としてお店で提供されてる一皿に対する画像やレビュー、ジャンルやハッシュタグなどの複数の情報を同時期に取得しています。本当はこれらの複数の情報を活かしたデータ分析ができれば理想ですが、一般的な教師あり学習の枠組みでは複数のモダリティを組み合わせたアプローチを行うことができません。自然言語処理だと近年ではword2vec(元論文)やfasttext(元論文)のような分散表現を用いて単語や文章を多次元ベクトルで表現する優れた手法がありますが、これらの手法で表現される潜在空間についても実世界で観測される様々な事象やデータ構造に対応できる訳ではありません グルメの画像だと、テキストでは分からない情報でも画像でわかる事があり、画像では分からないけどテキストからだと分かる事があります。例えば、コース料理の画像があればどんな料理があるか分かりますが、テキストにメインディッシュの話しかなければどんな料理が出ていたのか全てを把握するのは難しいでしょう。あるいは、下記の例のようにヒレカツ定食でテキストにはヒレカツに豚汁が付いてくると記載されていれば良いですが、メインのトンカツしか写真になければ画像から得られる情報は断片的になってしまい、豚汁の情報は手に入らなくなってしまいます。 今までの問題点をまとめると、以下のようになります。 一般的な教師あり学習のアプローチだと、推論すべきラベルが有限なので限界がある 同時期に取得した複数のデータを組み合わせて解析したいが、一般的な教師あり学習だと手法の入出力仕様の問題でうまく扱えず、片方だと完全な情報が得られないケースが存在してしまう そこで、クロスモーダル解析(あるいはマルチモーダル解析)が登場しました。ここでは複数のモダリティの情報を使って学習を行い共有潜在空間を構築します。こうすることで、あるモダリティの情報から他のモダリティの情報を検索したり生成することが可能になります(テキストから画像を生成するtext-to-image retrievalなどが一例) 近年でクロスモーダル解析が注目されるようになった理由ですが、動画と音声のように観測時刻が同期している場合は「同一対象を観測している」という強い仮説が成立します。したがって、手動での正解データの作成をしなくても大量のデータに基づいた共有潜在空間を元にして変換や検索が可能になるのが注目されている理由になります。 今回のクロスモーダル解析では、主にテキストと画像に用いて学習を行い、学習モデルを通して得られたモダリティの特徴量間の距離を最小化することでメニュー名から類似した画像データを生成できるかどうかを検証しました。 注) 一般的にはMetric Learning(距離学習)と同じ仕組みですが、よく記事とかで紹介されているMetric Learningは画像同士やテキスト同士で学習を行なっている事例がほとんどで、異なるモダリティ(例えばテキストと画像、あるいは動画と音声)で分析事例を紹介してる記事はとても少ないと思います。 Material and Methods 具体的には、以下の論文を参考にして実装を行いました。 Carvalho, M. et al., Images & recipes: Retrieval in the cooking context. Proc. - IEEE 34th Int. Conf. Data Eng. Work. ICDEW 2018 169–174 (2018) 概要図はこちらになります(上の論文から抜粋)。 今回はお試しとしてランダムに抽出した約4万5000枚のSARAHの画像及びテキストデータを使用しました。pytorchで実装を行い、テキストデータ(上図の左側)はメニュー名を使用してLSTMで学習を行いました。また、画像の学習モデルはefficientnet-v2ベースのpre-trainedモデルを使用しました。 ロス関数についてはtriplet lossを採用しました。この手法では、anchorとなるデータを元に、positiveデータとのロス(距離)が小さくなるように学習し、negativeデータとのロス(距離)が大きくなるように学習します。今回はanchorデータをメニュー名、positiveデータをanchor(メニュー名)に紐づく画像データ、negativeをanchorとは食カテゴリが異なるメニュー名の画像データを使用しました。 トレーニングについてですが、epoch=20で学習を行い、過学習の挙動を見るために訓練データと検証データを8:2で分割しました。また、今回はテストデータは用いず、何かしらのテキストを入れたらどんな画像が出力されるのかの体感を確かめました。 訓練結果 epoch=20で実施した学習結果は以下のようになっています(横軸はepoch数で縦軸がロスの値です)。最初は順調に訓練データと検証データのロスが減っていましたが、途中から両者の損失が乖離してしまったので過学習が起きていました。こちらに関してはデータの持ち方なども変えていくと思いますが、バッチサイズを小さくしたり、学習率を下げたり等の探索チューニングが必要になると思います。 実際の結果を見てみた 今回はメニュー名ということもあったので、試しに「カルビ」と入力したときにどんな画像が出てくるかを可視化してみました。上位五件を可視化してみたのですが、カルビっぽい見た目をした画像が出力されたのは驚きで、メニュー名だけの入力からでも近い見た目の食べ物を取り出してくれました! でも実はこの結果、メニュー名を取り出してみると以下のようになっています。 ['山形黒毛和牛サーロインステーキ', 'シンシン カメノコウ', 'きんきの一夜干し', 'プライムリブ東京カット', 'ダチョウの心臓焼き'] 皆さんは最初に上記の結果を見たとき、きんきの一夜干しではなくてお肉を想像したのではないでしょうか(僕も可視化された時はお肉にしか見えませんでした笑) このようにメニュー名と画像のペアのみからクロスモーダル解析してるので精度改善の余地はありますが、カルビ(お肉)に関連した画像と見た目が出てくることが結果から確認することができました。 あと、もう一つの例で試しに「ベーコンチーズバーガー」も入力してみました。取り出せてる割合的にチーズ>ベーコンですが、途中でベーコンチーズバーガーも無事に取り出せていることが確認できました。 出てきたメニュー名: ['チーズバーガー', 'エッグサルサとローストトマトのチーズバーガー', 'ハンバーガー+モッツァレラチーズ', 'ブラウンシュガーベーコンチーズバーガー', 'パインチリソースバーガー+チーズ'] 今回の解析の課題と方針 用いたデータセット数が少ない 今回はお試しとしてランダムに取得した約45000枚の画像とテキストのデータを使って実施してみましたが、実際のSARAHにはその20倍近いデータが蓄積されています。今後はデータを増やして学習を行うことで、より精度の高いクロスモーダル解析を実現していきます テキストデータにメニュー名しか使ってない 今回はテキストデータとしてメニュー名のみを用いていますが、今後はレビューの情報も含めた解析を進める予定です。 食のデータには食べ物の情報以外にも食感や見た目、味などの様々な副次的な情報が紐づいていて、それらの情報を活かしたマーケティングを多くの食品企業が展開しています。クロスモーダル解析でも画像に紐づくメニュー名だけではなくレビュー内容も取り入れることで、より精度の高いクロスモーダル解析を実現することができると考えています ファインチューニングの必要性 今回はお試しだったので使いませんでしたが、NLP側も画像処理側もSARAH独自のデータを用いてファインチューニングされた学習モデルを使って学習を行った方が効率的です。 例えばSARAHのサービスではカメラロールで食べ物を判定する学習モデルなどをファインチューニングして実装していますが、今後の検証では随時改善してるファインチューニングされたモデルを使って検証を回して比較を行なっていきたいと考えています まとめ 今回はSARAHで公開されているテキストデータと画像の一部を用いてクロスモーダル解析を実施してみました。メニュー名だけからそれに似た画像の生成(text-to-image retrieval)ができていたので驚きましたが、今後はファインチューニングされた独自のモデルやレビューなどの情報を用いて精度を改善して、プロダクトに載せられる形に昇華したり研究事例として学会などで発信していきたいと考えています。 SARAHでは一皿に特化した独自のデータを解析してくれるような方をインターンや正社員を問わず募集しておりますので、興味のある方はぜひカジュアル面談に来ていただけると嬉しいです。皆さんと一緒に働けるのを楽しみにしています!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCodeのPrettier+black環境の作り方

Formatterとは コードの種類に応じて、自動で空白とか改行などの整形を行う拡張機能のこと。 有名なものでは、Prettier(JS, TS, yaml, html等が対応)やBlack(python用)などがある。 基本的にはPrettierを用いれば、大抵のプログラムは対応しているが、 PrettierはPythonに非対応のため、Pythonの時のみBlackを使うように設定する必要がある。 Prettierのインストール VSCodeでPrettierを使用するには、拡張機能をインストールします。 まずVS CodeのExtension panelでPrettier-Code Formatterを検索しインストールします。 default formatterをPrettierに設定 画面左上のタブからCode→基本設定→設定を開き、 editor.defaultFormatterと検索して、Prettier-Code Formatterを選択 これでdefault formatterをPrettierに変更することができた。 次にeditor.formatOnSaveと検索してこれをTrueに変える。(ファイル保存時にFormatterを適用する設定) Blackのインストール 下記コマンドでBlackをインストールする。 pip3 install black 次に画面左上のタブからCode→基本設定→設定を開き、 python.formatting.providerと検索して、blackを選択 しかしこのままだと、Pythonであろうとdefault formatterであるPrettierが適用されてしまい、エラーが出る。 このため、pythonコードの場合のみ、prettierではなくblackを適用するよう下記の設定を追加する必要がある。 python時のみdefault formatterを適用しない設定の追加 画面左上のタブからCode→基本設定→設定を開き、右上の3つのアイコンの一番左のものを押して、settings.jsonを開く。 そこで、一番下に "[python]": { "editor.defaultFormatter": null, }, というPythonの時のみ動作する例外設定を追加する。 最終的にsettings.jsonが以下のようになっていることを確認する。 settings.json { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "python.formatting.provider": "black", "[python]": { "editor.defaultFormatter": null, }, } formatterの動作確認 pythonファイルのフォーマット(black) 下記のようなフォーマットが壊れたpythonファイルを作成して、保存するとちゃんと整形されることを確認する。 test.py(整形前) a = [ 1, 2, 3] test.py(整形後) a = [1, 2, 3] その他ファイルのフォーマット(prettier) 下記のようなフォーマットが壊れたjsファイルを作成して、保存するとちゃんと整形されることを確認する。 test.js(整形前) const test = 3; const func =() => { return 0; }; test.js(整形後) const test = 3; const func = () => { return 0; };
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bullseyeで緊急避難的なTFlite環境

1.はじめに Raspberry Pi OSをBullseyeに変えたら、OpenCV(が要求するライブラリ)のインストールができなくてこまったりしたので、BullseyeでTFLite動かすための緊急避難的な覚書。ちなみにBusterならこちらやこちらで動く(はず)。 OSのバージョンはこちら。 $ lsb_release -a No LSB modules are available. Distributor ID: Raspbian Description: Raspbian GNU/Linux 11 (bullseye) Release: 11 Codename: bullseye 2.OpenCVインストール OpenCV(opencv-python)自体はpipでインストールできるのですが、依存ライブラリのうちlibqt4関連がBullseyeに対応してないっぽいのでインポートできません。ちょっとずるですが、OAK-DのDepthAI動作環境をインストールするとOpenCVも一緒にインストールしてくれるのでこれを流用したらいいじゃない、ってことで $ git clone https://github.com/luxonis/depthai.git $ cd depthai $ python3 install_requirements.py $ python Python 3.9.2 (default, Mar 12 2021, 04:06:34) [GCC 10.2.1 20210110] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import cv2 >>> cv2.__version__ '4.5.5' >>> importできました。versionは4.5.5、最新ですね。 3.TFLiteのインストール https://www.tensorflow.org/lite/guide/python#install_tensorflow_lite_for_python こちらもBusterを推奨していますが、公式の方法でruntimeを入れてみます。 $ pip3 install --extra-index-url https://google-coral.github.io/py-repo/ tflite_runtime インストールはできましたのでインポートしてみます。 $ python Python 3.9.2 (default, Mar 12 2021, 04:06:34) [GCC 10.2.1 20210110] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import cv2 >>> import tflite_runtime.interpreter as tflite >>> importもできましたね。 4.動作確認 TFLiteの動作確認はPINTO神さまのリポジトリが簡単なのでお借りします。 $ git clone https://github.com/PINTO0309/TensorflowLite-bin $ cd TensorflowLite-bin $ python3 mobilenetv2ssd.py time: 0.08984041213989258 [[(128, 223), (320, 542), 0.96484375, 'dog'], [(114, 135), (564, 432), 0.953125, 'bicycle'], [(459, 80), (694, 173), 0.87890625, 'car']] coordinates: (128, 223) (320, 542). class: "dog". confidence: 0.96 coordinates: (114, 135) (564, 432). class: "bicycle". confidence: 0.95 coordinates: (459, 80) (694, 173). class: "car". confidence: 0.88 無事動きましたね。同じフォルダにできているresult.jpgに認識後の画像も生成されています。 5.動作確認2 USBカメラ入出力 import cv2 import time capture = cv2.VideoCapture(0) if capture.isOpened() is False: raise IOError start_time = time.time() while(True): try: ret, frame = capture.read() if ret is False: raise IOError elapsed_time = time.time() - start_time start_time = time.time() cv2.putText(frame, "Elapsed Time : " + '{:.1f}'.format(elapsed_time * 1000) + "ms" + " / " + '{:.1f}'.format(1 / elapsed_time) + "fps" , (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2, cv2.LINE_AA) cv2.imshow('frame',frame) cv2.waitKey(1) except KeyboardInterrupt: # 終わるときは CTRL + C を押す break capture.release() 以前やった単眼震度推定も動きました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【多クラス分類問題】LightGBMにてパラメータチューニングの有無で精度を比較

1.目的 MNISTデータ(0から9までの手書き数字)を分類するモデル(多クラス分類モデル)をLightGBMを用いて実装する。 その際にハイパーパラメータをチューニングしないモデルと、チューニングしたモデルの精度を比較する。 チューニングはOptunaを用いて行う。その使い方等をメモ程度に置いておく。 2.MNISTとは MNISTはMixed National Institute of Standards and Technology databaseの略で、手書き数字画像60,000枚とテスト画像10,000枚を集めた、画像データセット。 0~9の手書き数字が教師ラベルとして各画像に与えられている。 詳しくはこちら 今回はKerasで公開されているMNISTを使う。 3.使用ライブラリ import lightgbm as lgb import pandas as pd import numpy as np from tensorflow.keras.datasets import mnist from sklearn.model_selection import train_test_split #評価指標は以下4つ from sklearn.metrics import accuracy_score from sklearn.metrics import recall_score from sklearn.metrics import precision_score from sklearn.metrics import f1_score 4.モデル作成(チューニング無しVer) コードは以下の通り。 # Kerasに付属の手書き数字画像データをダウンロード (X_train, y_train), (X_test, y_test) = mnist.load_data() # training set を学習データ(X_train, y_train)と検証データ(X_valid, y_valid)に8:2で分割する X_train,X_valid,y_train,y_valid = train_test_split(X_train, y_train, test_size=0.2, random_state=0) # 各データを1次元に変換 X_train = X_train.reshape(-1,784) X_valid = X_valid.reshape(-1,784) X_test = X_test.reshape(-1,784) #正規化 X_train = X_train.astype('float32') X_valid = X_valid.astype('float32') X_test = X_test.astype('float32') X_train /= 255 X_valid /= 255 X_test /= 255 # 訓練・検証データの設定 train_data = lgb.Dataset(X_train, label=y_train) eval_data = lgb.Dataset(X_valid, label=y_valid, reference=train_data) #パラメータ設定 params = { 'task': 'train', #トレーニング用 'boosting_type': 'gbdt', #勾配ブースティング決定木 'objective': 'multiclass', #目的:多値分類 'num_class': 10, #分類クラス数 'metric': 'multi_logloss' #評価指標は多クラスのLog損失 } #モデル作成 gbm = lgb.train( params, train_data, valid_sets=eval_data, num_boost_round=100, early_stopping_rounds=10 ) こうして出来たモデルを用いて、testデータの予測を行う。 そのコードは以下。 #予測 preds = gbm.predict(X_test, num_iteration=gbm.best_iteration) y_pred = [] for x in preds: y_pred.append(np.argmax(x)) この予測y_predと教師データy_testを用いて評価指標を計算し、出力する。 ここでは説明を省くが、各評価指標についてはこちらでわかりやすく解説されている。 #正解率など評価指標の計算 print('正解率(accuracy_score):{}'.format(accuracy_score(y_test, y_pred))) #適合率、再現率、F1値はマクロ平均を取る print('再現率(recall_score):{}'.format(recall_score(y_test, y_pred, average='macro'))) print('適合率(precision_score):{}'.format(precision_score(y_test, y_pred, average='macro'))) print('F1値(f1_score):{}'.format(f1_score(y_test, y_pred, average='macro'))) 出力結果は以下の通りである。 正解率(accuracy_score):0.9766 再現率(recall_score):0.9764353695446533 適合率(precision_score):0.9764619714676158 F1値(f1_score):0.9764381593889576 ハイパーパラメータをチューニングしなくても、正解率が97.6%とかなり高い数字になっている。 また、モデルの作成もわずか数分で終わったのでlightGBMの凄さが分かる。 ちなみに、上の4つの指標はclassification_reportを使うことで以下のように一気に計算できることが分かった。0から9の各数字についての指標も出してくれる。 from sklearn.metrics import classification_report print(classification_report(y_test, y_pred, digits=5)) #digitsは表示桁数 実行結果 precision recall f1-score support 0 0.97778 0.98776 0.98274 980 1 0.99120 0.99207 0.99163 1135 2 0.97282 0.97093 0.97187 1032 3 0.96961 0.97921 0.97438 1010 4 0.97955 0.97556 0.97755 982 5 0.97860 0.97422 0.97640 892 6 0.98115 0.97808 0.97961 958 7 0.97835 0.96693 0.97260 1028 8 0.96735 0.97331 0.97032 974 9 0.96822 0.96630 0.96726 1009 accuracy 0.97660 10000 macro avg 0.97646 0.97644 0.97644 10000 weighted avg 0.97661 0.97660 0.97660 10000 5.モデル作成 (チューニング有り) 次に、ハイパーパラメータをチューニングしてモデルを作る。 Optunaを用いてチューニングをするため、以下をインポート。 # LightGBM Optunaを使ってハイパーパラメタチューニング import optuna.integration.lightgbm as lgb_o あとはパラメータ設定までは同じコードになる。 # Kerasに付属の手書き数字画像データをダウンロード (X_train, y_train), (X_test, y_test) = mnist.load_data() # training set を学習データ(X_train, y_train)と検証データ(X_valid, y_valid)に8:2で分割する X_train,X_valid,y_train,y_valid = train_test_split(X_train, y_train, test_size=0.2, random_state=0) # 各データを1次元に変換 X_train = X_train.reshape(-1,784) X_valid = X_valid.reshape(-1,784) X_test = X_test.reshape(-1,784) #正規化 X_train = X_train.astype('float32') X_valid = X_valid.astype('float32') X_test = X_test.astype('float32') X_train /= 255 X_valid /= 255 X_test /= 255 # 訓練・検証データの設定 train_data = lgb_o.Dataset(X_train, label=y_train) eval_data = lgb_o.Dataset(X_valid, label=y_valid, reference=train_data) # パラメータ設定 params = { 'task': 'train', 'boosting_type': 'gbdt', 'objective': 'multiclass', 'num_class': 10, 'metric': 'multi_logloss' } Optunaでパラメータチューニングをする。 チューニングされたパラメータをbest_paramsとして表示させてみる。 この実行はかなり時間がかかるため注意。(特徴量が784列分もあるためだろうか。。?) best_params= {} #モデル作成 gbm = lgb_o.train( params, train_data, valid_sets=[train_data, eval_data], #ここがチューニングしない場合と違う num_boost_round=100, early_stopping_rounds=10 ) best_params = gbm.params best_params チューニングされたパラメータが次のようになった。 実行結果 {'task': 'train', 'boosting_type': 'gbdt', 'objective': 'multiclass', 'num_class': 10, 'metric': 'multi_logloss', 'feature_pre_filter': False, 'lambda_l1': 1.4206763386954986e-08, 'lambda_l2': 0.0004970847920575475, 'num_leaves': 37, 'feature_fraction': 0.784, 'bagging_fraction': 0.9001098370804291, 'bagging_freq': 3, 'min_child_samples': 20, 'num_iterations': 100, 'early_stopping_round': 10} 先と同じように、このモデルを用いて予測してみる。 #予測 preds = gbm.predict(X_test, num_iteration=gbm.best_iteration) y_pred = [] for x in preds: y_pred.append(np.argmax(x)) #正解率など評価指標の計算 print('正解率(accuracy_score):{}'.format(accuracy_score(y_test, y_pred))) #適合率、再現率、F1値はマクロ平均を取る print('再現率(recall_score):{}'.format(recall_score(y_test, y_pred, average='macro'))) print('適合率(precision_score):{}'.format(precision_score(y_test, y_pred, average='macro'))) print('F1値(f1_score):{}'.format(f1_score(y_test, y_pred, average='macro'))) チューニング後のモデルでの評価指数が以下となる。 チューニング後 正解率(accuracy_score):0.978 再現率(recall_score):0.9778036992589507 適合率(precision_score):0.9778601989002492 F1値(f1_score):0.9778154759497815 チューニング前と比べてみる チューニング前 正解率(accuracy_score):0.9766 再現率(recall_score):0.9764353695446533 適合率(precision_score):0.9764619714676158 F1値(f1_score):0.9764381593889576 いずれも0.0014ほど上昇していることが分かる。 が、チューニングせずともかなり良い精度で分類できていたので、あまりその恩恵を感じられなかった。。 次は違うデータセットでも試してみたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なぜディープラーニングをするのか?について調べてみた件2(ロジスティック回帰)

先日は論理ゲートを使ってなぜディープラーニングをするのかについてコードを動かしてみました。今回は、人工的にですがデータを使って線形分離が不可能な物を3層のニューラルネットワーク(ディープラーニングと言っておきながらディープじゃないですね)を使って確認しています。なお、参考にしたのは和歌山大学で知能システム演習の講義を持たれている八谷大岳先生の物です。先生のブログとYouTube、GitHubを参考にさせてもらっています。元の資料のリンクはcolab内に貼っていますのでそちらを確認して頂ければと思います。 Colab:なぜディープラーニングをするのか?について調べてみた件2(ロジスティック回帰) 今回は先生のGitHubをフォークした物を講義資料(ブログとYouTube)に従って、コードを追加したり、自分で課題を作って修正したりしています。このような投稿は初めてですので不備があるかと思います。何か決定的な不備があったり、気になる所がありましたら遠慮なく指摘して頂けると幸いです。 参考 なぜディープラーニングするのか?を自分なりに調べてみた件
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python+opencvで画像処理の勉強5 幾何学的変換

pythonとopencvを使って画像処理を勉強していきます。 前回 python+opencvで画像処理の勉強4 周波数領域におけるフィルタリング 周波数フィルタリングについて学びました。 今回は幾何学的変換について学び、最後は応用として画像のつなぎ合わせを行います。 使用画像は旭川動物園のペンギンの散歩の写真です。 線形変換 線形変換の一般系 座標$(x,y)$の位置の点が、変換により座標$(x',y')$に移動するとする。 そのとき、以下の式で表される変換を線形変換と呼ぶ。 \left[\begin{array}{c} x'\\ y' \end{array}\right] = \left[\begin{array}{cc} a&b\\ c&d \end{array}\right] \left[\begin{array}{c} x\\ y \end{array}\right] 拡大・縮小 拡大・縮小は、以下の式で表される。 \left[\begin{array}{c} x'\\ y' \end{array}\right] = \left[\begin{array}{cc} s_{x}&0\\ 0&s_{y} \end{array}\right] \left[\begin{array}{c} x\\ y \end{array}\right] ここで$s_{x},s_{y}$はx方向、y方向の拡大(縮小)率である。 width = img_rgb.shape[1] height = img_rgb.shape[0] x_scale = [2,1,3] y_scale = [2,2,1] fig, ax = plt.subplots(1, 3, figsize=(16, 9)) for i in range(3): dst = cv2.resize(img_rgb, (width*x_scale[i], height*y_scale[i])) ax[i].imshow(dst) ax[i].set_title('x_scale={} y_scale={}'.format(str(x_scale[i]), str(y_scale[i]))) ax[i].set_xticks([]) ax[i].set_yticks([]) 回転 原点を中心に、反時計回りに角度$\theta$だけ回転する変換は、以下の式で表される。 \left[\begin{array}{c} x'\\ y' \end{array}\right] = \left[\begin{array}{cc} \cos{\theta}&-\sin{\theta}\\ \sin{\theta}&\cos{\theta} \end{array}\right] \left[\begin{array}{c} x\\ y \end{array}\right] ここでは、画像の左下を中心に回転した例を示す。 fig, ax = plt.subplots(1, 2, figsize=(10, 5)) center = (0, height) affine_trans = cv2.getRotationMatrix2D(center, 20.0, 1.0) dst = cv2.warpAffine(img_rgb, affine_trans, (width, height)) ax[0].imshow(dst); ax[0].set_xticks([]); ax[0].set_yticks([]); affine_trans = cv2.getRotationMatrix2D(center, -30.0, 1.0) dst = cv2.warpAffine(img_rgb, affine_trans, (width, height), flags=cv2.INTER_CUBIC) ax[1].imshow(dst); ax[1].set_xticks([]); ax[1].set_yticks([]); 鏡映 ある直線に対して、対象な位置に反転する変換を鏡映と呼ぶ。 x軸に関する変換 \left[\begin{array}{c} x'\\ y' \end{array}\right] = \left[\begin{array}{cc} 1&0\\ 0&-1 \end{array}\right] \left[\begin{array}{c} x\\ y \end{array}\right] y軸に関する変換 \left[\begin{array}{c} x'\\ y' \end{array}\right] = \left[\begin{array}{cc} -1&0\\ 0&1 \end{array}\right] \left[\begin{array}{c} x\\ y \end{array}\right] 直線y=xに関する変換 \left[\begin{array}{c} x'\\ y' \end{array}\right] = \left[\begin{array}{cc} 0&1\\ 1&0 \end{array}\right] \left[\begin{array}{c} x\\ y \end{array}\right] fig, ax = plt.subplots(1, 4, figsize=(20, 5)) ax[0].imshow(img_rgb) ax[0].set_xticks([]) ax[0].set_yticks([]) flip_n = [0, 1, -1] for i in range(1,4): dst = cv2.flip(img_rgb, flip_n[i-1]) ax[i].imshow(dst) ax[i].set_title('flip={}'.format(str(flip_n[i-1]))) ax[i].set_xticks([]) ax[i].set_yticks([]) スキュー 長方形を傾けて平行四辺形にするような変換をスキューまたはせん断と呼ぶ。 x軸方向へのスキューは以下の式で表される。 \left[\begin{array}{c} x'\\ y' \end{array}\right] = \left[\begin{array}{cc} 1&b\\ 0&1 \end{array}\right] \left[\begin{array}{c} x\\ y \end{array}\right] 傾ける角度を$\theta$とすると、$b=\tan{\theta}$の関係がある。 また、y軸方向へのスキューは以下の式で表される。 \left[\begin{array}{c} x'\\ y' \end{array}\right] = \left[\begin{array}{cc} 1&0\\ c&1 \end{array}\right] \left[\begin{array}{c} x\\ y \end{array}\right] 傾ける角度を$\theta$とすると、$c=\tan{\theta}$の関係がある。 fig, ax = plt.subplots(1, 2, figsize=(10, 5)) mat = np.array([[1, 0, 0], [0.3, 1, 0]], dtype=np.float32) dst = cv2.warpAffine(img_rgb, mat, (width, int(height + width * 0.3))) ax[0].imshow(dst); ax[0].set_xticks([]); ax[0].set_yticks([]); mat = np.array([[1, 0.6, 0], [0, 1, 0]], dtype=np.float32) dst = cv2.warpAffine(img_rgb, mat, (int(width + height * 0.6), height)) ax[1].imshow(dst); ax[1].set_xticks([]); ax[1].set_yticks([]); 合成変換 ここまでに示した各種変換は、ベクトルと行列を記号で置き換えることで、以下のように表される。 \boldsymbol{x'}=\boldsymbol{A}\boldsymbol{x} ここで、 \boldsymbol{x}= \left[\begin{array}{c} x\\ y \end{array}\right], \boldsymbol{x'}= \left[\begin{array}{c} x'\\ y' \end{array}\right], \boldsymbol{A}= \left[\begin{array}{cc} a&b\\ c&d \end{array}\right] 線形変換は2×2の行列$\boldsymbol{A}$で表現される。 座標$x$が変換$\boldsymbol{A}$により、座標$\boldsymbol{x'}$に移ったとすると以下のように表される。 \boldsymbol{x'}=\boldsymbol{A}\boldsymbol{x} さらに、座標$\boldsymbol{x'}$が、変換$\boldsymbol{B}$により座標$\boldsymbol{x''}$に移ったとすると、以下のように表される。 math \boldsymbol{x''}=\boldsymbol{B}\boldsymbol{x'} 以下の式が導かれる。 \boldsymbol{x''}=(\boldsymbol{BA})\boldsymbol{x}=\boldsymbol{Cx} ここで、$\boldsymbol{C}$は$\boldsymbol{C}=\boldsymbol{BA}$なる2×2行列であり、これも1つの線形変換を表すことになる。 任意の線形変換の組み合わせは、やはり線形変換となる。一般に、個々の順番を入れ替えると、結果が異なることに注意する。 同次座標とアフィン変換・射影変換 平行移動 x軸方向の移動量$t_{x}$、y軸方向の移動量を$t_{y}$としたとき、この平行移動は以下の式で表される。 x'=x+t_{x}\\ y'=y+t_{y} 単純なものであるが、線形変換の一般系で表現することはできない。 fig, ax = plt.subplots(1, 2, figsize=(10, 5)) mat = np.array([[1, 0, 200], [0, 1, 50]], dtype=np.float32) dst = cv2.warpAffine(img_rgb, mat, (width, height)) ax[0].imshow(dst); ax[0].set_xticks([]); ax[0].set_yticks([]); mat = np.array([[1, 0, -100], [0, 1, -50]], dtype=np.float32) dst = cv2.warpAffine(img_rgb, mat, (width, height)) ax[1].imshow(dst); ax[1].set_xticks([]); ax[1].set_yticks([]); 同次座標 座標$(x,y)$に対し、要素を1つ増やした座標$(\xi_{1},\xi_{2},\xi_{3})$を、以下の関係式を満たすように定義する。 x=\frac{\xi_{1}}{\xi_{3}}\\ y=\frac{\xi_{2}}{\xi_{3}} ただし、$\xi_{1},\xi_{2},\xi_{3}$の少なくとも1つは0でないとする。 このように定義される座標を同次座標と呼ぶ。 同次座標において、$\lambda\neq0$となる任意の$\lambda$に対し、$(\lambda\xi_{1},\xi_{2},\lambda\xi_{3})$と$(\xi_{1},\lambda\xi_{2},\xi_{3})$は, 通常の座標に直したとき、ともに$(\frac{\xi_{1}}{\xi_{3}},\frac{\xi_{2}}{\xi_{3}})$となる。 同次座標系においては、定数倍をしても変わらないとみなせる。 このような関係を同値であると呼び、以下のように表す。 \left[\begin{array}{c} \xi_{1}\\ \xi_{2}\\ \xi_{3} \end{array}\right] \sim \left[\begin{array}{c} \lambda\xi_{1}\\ \lambda\xi_{2}\\ \lambda\xi_{3} \end{array}\right] ベクトル表記では、頭に~を付けて表す。 $$\boldsymbol{\tilde{\xi}}\sim\lambda\boldsymbol{\tilde{\xi}}$$ アフィン変換 同次座標を利用すると、平行移動は以下の式のように表すことができる。 \left[\begin{array}{c} x'\\ y'\\ 1 \end{array}\right] \sim \left[\begin{array}{ccc} 1&0&t_{x}\\ 0&1&t_{y}\\ 0&0&1 \end{array}\right] \left[\begin{array}{c} x\\ y\\ 1 \end{array}\right] また、線形変換は、同次座標を用いると以下のように表現される。 \left[\begin{array}{c} x'\\ y'\\ 1 \end{array}\right] \sim \left[\begin{array}{ccc} a&b&0\\ c&d&0\\ 0&0&1 \end{array}\right] \left[\begin{array}{c} x\\ y\\ 1 \end{array}\right] さらに、任意の線形変換と平行移動を組み合わせた変換をアフィン変換と呼ぶ。 アフィン変換の一般系は以下のように求めることができる。 \left[\begin{array}{ccc} 1&0&t_{x}\\ 0&1&t_{y}\\ 0&0&1 \end{array}\right] \left[\begin{array}{ccc} a&b&0\\ c&d&0\\ 0&0&1 \end{array}\right] \sim \left[\begin{array}{ccc} a&b&t_{x}\\ c&d&t_{y}\\ 0&0&1 \end{array}\right] a = [1, 2, 3] b = [4, -2, -5] c = [4, 0.5, 2] d = [-3, 2, 0] tx = [10, 1400, 1000] ty = [30, 0, 200] fig, ax = plt.subplots(1, 3, figsize=(16, 5)) for i in range(3): mat = np.array([[a[i], b[i], tx[i]], [c[i], d[i], ty[i]]], dtype=np.float32) dst = cv2.warpAffine(img_rgb, mat, (7*width, 7*height)) ax[i].imshow(dst) ax[i].set_xticks([]) ax[i].set_yticks([]) アフィン変換の特殊な例で、任意の回転と平行移動を組みわせたものをユークリッド変換と呼ぶ。 対象の大きさを保ったまま、任意に移動するような変換である \left[\begin{array}{ccc} \cos{\theta}&-\sin{\theta}&t_{x}\\ \sin{\theta}&\cos{\theta}&t_{y}\\ 0&0&1 \end{array}\right] thetas = [np.pi/6, -np.pi/3, np.pi/4] c = np.cos(thetas) s = np.sin(thetas) tx = [20, 70, 50] ty = [-100, 150, -150] fig, ax = plt.subplots(1, 3, figsize=(16, 5)) for i in range(3): mat = np.array([[c[i], -s[i], tx[i]], [s[i], c[i], ty[i]]], dtype=np.float32) dst = cv2.warpAffine(img_rgb, mat, (width, height)) ax[i].imshow(dst) ax[i].set_xticks([]) ax[i].set_yticks([]) ユークリッド変換に対し、縦横の倍率が等しい拡大・縮小を加えたものを相似変換と呼ぶ。 \left[\begin{array}{ccc} s\cos{\theta}&-s\sin{\theta}&t_{x}\\ s\sin{\theta}&s\cos{\theta}&t_{y}\\ 0&0&1 \end{array}\right] thetas = [np.pi/6, -np.pi/3, np.pi/4] c = np.cos(thetas) s = np.sin(thetas) p = [4, 2, 0.6] tx = [20, 150, 50] ty = [-150, 100, -150] fig, ax = plt.subplots(1, 3, figsize=(16, 5)) for i in range(3): mat = np.array([[p[i]*c[i], -p[i]*s[i], tx[i]], [p[i]*s[i], p[i]*c[i], ty[i]]], dtype=np.float32) dst = cv2.warpAffine(img_rgb, mat, (width, height)) ax[i].imshow(dst) ax[i].set_xticks([]) ax[i].set_yticks([]) 射影変換 同次座標を用いて、さらに一般的な変換を以下の式で表現することもできる。 この変換を射影変換(ホモグラフィ)と呼ぶ。 \left[\begin{array}{c} x'\\ y'\\ 1 \end{array}\right] \sim \left[\begin{array}{ccc} h_{11}&h_{12}&h_{13}\\ h_{21}&h_{22}&h_{23}\\ h_{31}&h_{32}&h_{33} \end{array}\right] \left[\begin{array}{c} x\\ y\\ 1 \end{array}\right] これをベクトルと行列の記号を用いて表すと、 $$\boldsymbol{\tilde{x}'}\sim\boldsymbol{H}\boldsymbol{\tilde{x}}$$ ただし、 \boldsymbol{\tilde{x}}= \left[\begin{array}{c} x\\ y\\ 1 \end{array}\right], \boldsymbol{\tilde{x}}'= \left[\begin{array}{c} x'\\ y'\\ 1 \end{array}\right] $\boldsymbol{H}$は任意の3×3の行列である。 座標$(x',y')$を求めると、 x'=\frac{h_{11}x+h_{12}y+h_{13}}{h_{31}x+h_{32}y+h_{33}}\\ y'=\frac{h_{21}x+h_{22}y+h_{23}}{h_{31}x+h_{32}y+h_{33}} pts1 = np.float32([[0,0], [width,0], [0,height], [width,height]]) pts2 = np.float32([[0,0], [width,50], [0,height], [x1,250]]) #M, _ = cv2.findHomography(pts1, pts2, cv2.RANSAC, 5.0) M = cv2.getPerspectiveTransform(pts1, pts2) dst = cv2.warpPerspective(img_rgb, M, (width,height)) plt.imshow(dst) plt.xticks([]); plt.yticks([]); 合成変換 線形変換の合成変換の性質は、同次座標を利用した射影変換による合成変換に対しても成り立つ。 math \boldsymbol{\tilde{x}''}\sim(\boldsymbol{H_{2}H_{1}})\boldsymbol{\tilde{x}} 画像の再標本化と補間 画像の再標本化 ディジタル画像に幾何学的変換を施すと、元の画像の画素位置は、一般に変換後は標本化位置からずれる。 変換後の画像を再び縦横等間隔に標本化された位置の値の集まりとして表現するために,再標本化が必要となる。 1. 変換後の出力画像におけるある画素位置に対し、適用する幾何学的変換の逆変換を行い、元の入力画像に対する位置を求める。 2. 上で求めた入力画像での位置は、一般に入力画像の画素位置からずれている。そこで、その位置の値を補間により周囲の画素値から求める。 3. 上の処理を、出力画像上のすべての画素位置に対して行う。 ニアレストネイバー 求めたい位置に最も近い画素位置の値をそのまま利用する補間法を、ニアレストネイバーと呼ぶ。 求めたい位置を$(x,y)$とすると、その位置の画素値$I(x,y)$は以下のように得られる。 I(x,y)=f([x+0.5],[y+0.5]) ただし、$f(i,j)$は入力画像の位置$(i,j)$の画素値を、記号[ ]はガウス記号で[ ]内の数字を超えない最大の整数値を返すものである。 処理が単純で高速であるが、滑らかなエッジがギザギザになって現れるジャギーが発生しやすい。 バイリニア補間 求めたい位置$(x,y)$の値$I(x,y)$を、まわりの4点の画素値を用い、以下のように求める。 I(x,y)= \left[\begin{array}{cc} [x]+1-x&x-[x] \end{array}\right] \left[\begin{array}{cc} f([x],[y])&f([x],[y]+1)\\ f([x]+1,[y])&f([x]+1,[y]+1) \end{array}\right] \left[\begin{array}{c} [y]+1-y\\ y-[y] \end{array}\right]\\ =([x]+1-x)([y]+1-y)f([x],[y])+([x]+1-x)(y-[y])f([x],[y]+1)\\ +([x]-x)([y]+1-y)f([x]+1,[y])+(x-[x])(y-[y])f([x]+1,[y]+1) これをバイリニア補間と呼ぶ。 まわりの4点の画素値の重み付きの平均値を求めることになるため、平滑化の効果が生じる。 そのため、ニアレストネイバーのようなジャギーが目立たなくなるが、エッジがなまってしまう傾向がある。 バイキュービック補間 バイキュービック補間では、求めたい位置$(x,y)$の値$I(x,y)$を、まわりの16点の画素値$f_{11},f_{12},\cdots,f_{44}$を用いて、以下の式により求める。 I(x,y)= \left[\begin{array}{cccc} h(x_{1})&h(x_{2})&h(x_{3})&h(x_{4}) \end{array}\right] \left[\begin{array}{cccc} f_{11}&f_{12}&f_{13}&f_{14}\\ f_{21}&f_{22}&f_{23}&f_{24}\\ f_{31}&f_{32}&f_{33}&f_{34}\\ f_{41}&f_{42}&f_{43}&f_{44} \end{array}\right] \left[\begin{array}{c} h(y_{1})\\ h(y_{2})\\ h(y_{3})\\ h(y_{4}) \end{array}\right]\\ ただし、 x_{1}=1+x-[x]\\ x_{2}=x-[x]\\ x_{3}=[x]+1-x\\ x_{4}=[x]+2-x\\ y_{1}=1+y-[y]\\ y_{2}=y-[y]\\ y_{3}=[y]+1-y\\ y_{4}=[y]+2-y 関数$h(t)$は、sinc関数を3次多項式で近似するもので、一般に以下の式が用いられる。 h(t)= \begin{cases} |t|^3-2|t|^2+1 & (|t|\leqq1) \\ -|t|^3+5|t|^2-8|t|+4 & (1<|t|\leqq2) \\ 0 & (2<|t|) \end{cases} バイリニア補間に比べて、よりシャープで自然な画像が得られる。 pols = [cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC] pol_names = ['INTER_NEAREST', 'INTER_LINEAR', 'INTER_CUBIC'] fig, ax = plt.subplots(1, 3, figsize=(16, 5)) for i in range(3): tmp = cv2.resize(img_rgb, dsize=None, fx=10, fy=10, interpolation = pols[i]) ax[i].imshow(tmp[500:1000, 500:1000,:]) ax[i].set_xticks([]) ax[i].set_yticks([]) ax[i].set_title(pol_names[i]) イメージモザイキング イメージモザイキングとその概略処理手順 複数の画像をつなぎ合わせて1つの画像とすることを、イメージモザイキングと呼ぶ。 概略の処理手順は、以下のようになる。 1. 特徴点と検出のマッチング 2. 幾何学的変換の推定 3. 画像の幾何学的変換と合成 特徴点の検出とマッチング 各入力画像に対し、各々から特徴点を検出する。つぎに、検出された特徴点に対し、画像間で対応を求める。 このように、画像間で対応を求めることをマッチングと呼ぶ。 幾何学的変換の推定 イメージモザイキングでは射影変換が良く利用される。 \left[\begin{array}{c} x'\\ y'\\ 1 \end{array}\right] \sim \left[\begin{array}{ccc} h_{11}&h_{12}&h_{13}\\ h_{21}&h_{22}&h_{23}\\ h_{31}&h_{32}&h_{33} \end{array}\right] \left[\begin{array}{c} x\\ y\\ 1 \end{array}\right] は、定数倍を許した方程式であるため$h_{33}=1$とおき、整理することで以下の式を得ることができる。 xh_{11}+yh_{12}+h_{13}-xx'h_{31}-x'yh_{32}=x'\\ xh_{21}+yh_{22}+h_{23}-xy'h_{31}-yy'h_{32}=y' 未知数の数は8個なので、対応する特徴点が4組以上あれば解を得ることができる。 まず、各対応点から得られる式を以下に示すように並べて行列表現する。 \boldsymbol{Ah}=\boldsymbol{b} ここで、 \boldsymbol{A}= \left[\begin{array}{cccccccc} x_{1}&y_{1}&1&0&0&0&-x_{1}x'_{1}&-x'_{1}y_{1}\\ 0&0&0&x_{1}&y_{1}&1&-x_{1}y'_{1}&-y_{1}y'_{1}\\ x_{2}&y_{2}&1&0&0&0&-x_{2}x'_{2}&-x'_{2}y_{2}\\ 0&0&0&x_{2}&y_{2}&1&-x_{2}y'_{2}&-y_{2}y'_{2}\\ &\vdots&&\vdots&&\vdots&&\vdots& \end{array}\right], \boldsymbol{h}= \left[\begin{array}{c} h_{11}\\ h_{12}\\ h_{13}\\ h_{21}\\ h_{22}\\ h_{23}\\ h_{31}\\ h_{32} \end{array}\right], \boldsymbol{b}= \left[\begin{array}{c} x'_{1}\\ y'_{1}\\ x'_{2}\\ y'_{2}\\ \vdots \end{array}\right] 最小二乗解は、以下の式で得られる。 \boldsymbol{h}=(\boldsymbol{A}^{T}\boldsymbol{A})^{-1}\boldsymbol{A}^{T}\boldsymbol{b} マッチングの結果には、一般にはアウトライヤと呼ばれる誤った対応が多数含まれる。 アウトライヤに影響されずに何らかのパラメータを推定する方法として、RANSAC(RAndom SAmple Conensus)と呼ばれる方法がよく利用される。 RNSACによるパラメータの推定の手順を、以下に述べる。 1. パラメータを推定するために必要最小限のデータをランダムに選択する。上の場合、4組の対応点を選択する。 2. 選択した4組の対応点座標を用いて、射影変換のパラメータ$h_{11},h_{12},\dots,h_{32}$を求める。 3. 求めた射影変換パラメータを用いて、他の全ての対応点が正しく変換されているかチェックする。 ここで、正しく変換された対応点はインライヤと呼ばれる。用いたパラメータに対するインライヤの数を記憶する。 4. 1~3の処理を繰り返し、最後に最もインライヤの数が多い射影変換パラメータとそのインライヤを結果として出力する。 画像を2つに分けて片方を変形してつなぎあわせてみます。 img1 = img_rgb[0:400, 0:400] img2 = img_rgb[50:350, 300:700] x1 = img2.shape[1] x2 = img2.shape[0] pts1 = np.float32([[0,0], [x1,0], [0,x2], [x1,x2]]) pts2 = np.float32([[0,0], [x1,30], [0,x2], [x1,280]]) #M, _ = cv2.findHomography(pts1, pts2, cv2.RANSAC, 5.0) M = cv2.getPerspectiveTransform(pts1, pts2) img2 = cv2.warpPerspective(img2, M, (x1, x2)) fig, ax = plt.subplots(1, 2, figsize=(8, 3)) ax[0].imshow(img1); ax[0].set_xticks([]); ax[0].set_yticks([]); ax[1].imshow(img2); ax[1].set_xticks([]); ax[1].set_yticks([]); ここで特徴点のマッチングなどを行いますが、これは次回以降で説明をします。 detector = cv2.AKAZE_create() matcher = cv2.DescriptorMatcher_create("BruteForce-Hamming") k1, d1 = detector.detectAndCompute(img1,None) k2, d2 = detector.detectAndCompute(img2,None) match = cv2.BFMatcher() matches = match.knnMatch(d2, d1, k=2) good = [] for m, n in matches: if m.distance < 0.8* n.distance: good.append(m) MIN_MATCH_COUNT = 10 if len(good) > MIN_MATCH_COUNT: src_pts = np.float32([ k2[m.queryIdx].pt for m in good ]) dst_pts = np.float32([ k1[m.trainIdx].pt for m in good ]) H = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)[0] else: print('Not enought matches are found - {}/{}'.format(len(good), MIN_MATCH_COUNT)) exit(1) img2_warped = cv2.warpPerspective(img2, H, (img1.shape[1] + img2.shape[1], img1.shape[0])) img_stitched = img2_warped.copy() img_stitched[:img1.shape[0], :img1.shape[1]] = img1 def trim(frame): if np.sum(frame[0]) == 0: return trim(frame[1:]) if np.sum(frame[-1]) == 0: return trim(frame[:-2]) if np.sum(frame[:,0]) == 0: return trim(frame[:, 1:]) if np.sum(frame[:,-1]) == 0: return trim(frame[:, :-2]) return frame img_stitched_trimmed = trim(img_stitched) img_key = cv2.drawKeypoints(img2, k2, None) draw_params = dict(matchColor=(0,255,0), singlePointColor=None, flags=2) img_match = cv2.drawMatches(img2, k2, img1, k1, good, None, **draw_params) plt.imshow(img_match) plt.imshow(img_stitched_trimmed) 画像の幾何学的変換と合成 画像をつなぎ合わせたとき、画像のつなぎ目が目立ってしまうことがある。 画像のつなぎ目の位置を適切に選ぶか、重なった部分で両方の画素値を混ぜあわせる方法がある。 後者の例として、両方の重なった部分でアルファブレンディングの考え方を利用し、画素値を重み付き平均する方法がある。 例えば、画像1の画素値を$I_{1}$、画像2の画素値を$I_{2}$、画像1の画像端からの距離を$d_{1}$、画像2の画像端からの距離を$d_{2}$とするとき、 以下の式により、つなぎ目を目立たなくする。 \frac{d_{1}I_{1}+d_{2}I_{2}}{d_{1}+d_{2}} これで幾何学変換についての勉強は終わります。 次回 2値画像処理 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python]docstringのGoogleスタイルの書き方

docstringとは docstringとは、関数やクラスに対する説明を記載する方法の1つ。docstringを記載することで、ドキュメントをHTML形式で自動生成することもできる。docstringの基本的な書き方としては、関数やクラス定義の先頭に'''または"""で説明を囲む形で記載する。慣例的に'''よりも"""が使用されることが多い。説明文を関数やクラス定義の先頭に記載せず、間に処理が記載されるとdocstringとして認識されなくなる。 def my_function(): """<関数の説明を記載>""" またdocstringには、引数や返り値などの書き方に以下のような3つのスタイルがある。 ・reStructuredTextスタイル ・NumPyスタイル ・Googleスタイル その中のGoogleスタイルの書き方について簡単にメモしておく。 Googleスタイル GoogleのPythonスタイルガイドで定義されているdocstringのスタイル。 Googleスタイルで定義されているセッションの一部を使用した記載例は以下となる。 class Sample(): """<クラスの概要> <クラスの詳細説明>   Attributes: <属性名> (<型>): <説明> """ def my_function(): """<関数の概要> <関数の詳細説明> Args: <引数名> (<引数の型>): <説明> Returns: <返り値の型>: <説明> Raises: <例外名>: <説明> """ <関数の処理内容>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#Python5

Python入門その5 tkinterでGUIをつくってみる。 GUIとは これから先、GUIだのCUIだのを聞くことが増えると思います。 まず、UIとはユーザーインターフェイスのことです。 ユーザインタフェースは、機械、特にコンピュータとその機械の利用者の間での情報をやりとりするためのインタフェースである。(Wikipediaより) もっとわかりやすく言うと、例えばアプリの使用者が入力をしたり、見て情報を受け取ったりする画面のことです。これをユーザーインターフェイスといいます。 その中で、GUIはグラフィカルユーザーインターフェイスのことです。 UIの中でも、画面のボタンを押したりすることができるインターフェイスのことを指します。マウスや指で操作することができるもの、と思ってください。 また、CUIはキャラクターユーザーインターフェイスのことを指します。 こちらは、すべての操作をキーボード入力で行うインターフェイスを指します。コマンドプロンプト(映画の中でハッカーとかがパチパチ打ってるあれ)などがCUIです。すべてキーボード入力で操作するものだと思ってください。 他にもTUIなどがありますが、今はややこしくなるので説明しません。興味があったら調べてみてください。 今回はGUIを試しにつくってみます。 Tkinter TkinterはPython からGUIを構築・操作するための標準ライブラリです。標準ライブラリとは、pythonの初期装備、くらいに思ってください。 上でも述べたように、ボタンとかをつくって入力することができる画面をPC上に作り出すことができます。 アプリ製作の第一歩だと思ってやってみてください。 テキスト表示する まずは、tkinterでテキスト表示させてみましょう。 下を書いてみてください。 #tkinterを使いますよ、という宣言。以降、tkとうつとtkinterが使える。 import tkinter as tk #画面を作成する root = tk.Tk(); #画面の大きさを設定する root.geometry("800x400") #テキストを作成する lbl = tk.Label(text = 'test') #テキストを配置する lbl.pack() #画面を出力する tk.mainloop() このような画面が出てくればOKです。 ex5-1 画面の大きさと表示の文字を好きなものに変えてみましょう。 ボタンを配置する 次にボタンを配置します。 上のテキスト表示に書き足すように記述してください。 import tkinter as tk root = tk.Tk(); root.geometry("800x400") lbl = tk.Label(text = 'test') #ボタンの作成。ボタン内テキストは'test' btn = tk.Button(text='test') lbl.pack() #ボタンの配置 btn.pack() tk.mainloop() ボタンの配置方法は上に書いてあるとおりです。 このようになればOK。 また、pack()でボタンやテキストの大きさを変えることができます。 たとえば、btn.pack()をbtn.pack(fill='x')に変えてみてください。ボタンが横いっぱいに広がったら成功です。 ex5-2 ・ボタンを複数つくってみましょう。 ・中の文字を変えたりしてみましょう。 ・下記サイトを参考にして、ボタンの位置をいろいろいじって実験してみてください。 https://imagingsolution.net/program/python/tkinter/widget_layout_pack/ ボタンで関数を呼び出す ボタンをおしたらテキストを変更する、という機能をつくってみます。 上のプログラムに書き足してみてください。 import tkinter as tk #関数作成 def changetxt(): #.configureでテキスト内容を変えることができる lbl.configure(text='change!') root = tk.Tk(); root.geometry("800x400") lbl = tk.Label(text = 'test') #command=○○でボタン押したときに関数を呼び出せる btn = tk.Button(text='test',command=changetxt) lbl.pack() btn.pack(fill='x') tk.mainloop() btn = tk.Button(text='',command=関数名)で関数を呼び出すことができます。 lbl.configure(text='変更後のテキスト')でテキストが変更できます。 ex5-3 ・ボタンをおすと、テキストが「大吉」「吉」「凶」いずれかに変化するおみくじをつくってみましょう。 ヒント: 1まずは関数をつくってボタンをおしたら呼び出せるようにします。 2一番上にimport randomと書くと乱数が使えるようになります。 3「大吉」「吉」「凶」が入ったリストを使います。 4text=random.choice(リスト名)でリストの中の要素をランダムでtextに返します。 5上で書いたプログラムに書き足す形でつくることができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#Python5_tkinterをさわってみる

Python入門その5 tkinterでGUIをつくってみる。 GUIとは これから先、GUIだのCUIだのを聞くことが増えると思います。 まず、UIとはユーザーインターフェイスのことです。 ユーザインタフェースは、機械、特にコンピュータとその機械の利用者の間での情報をやりとりするためのインタフェースである。(Wikipediaより) もっとわかりやすく言うと、例えばアプリの使用者が入力をしたり、見て情報を受け取ったりする画面のことです。これをユーザーインターフェイスといいます。 その中で、GUIはグラフィカルユーザーインターフェイスのことです。 UIの中でも、画面のボタンを押したりすることができるインターフェイスのことを指します。マウスや指で操作することができるもの、と思ってください。 また、CUIはキャラクターユーザーインターフェイスのことを指します。 こちらは、すべての操作をキーボード入力で行うインターフェイスを指します。コマンドプロンプト(映画の中でハッカーとかがパチパチ打ってるあれ)などがCUIです。すべてキーボード入力で操作するものだと思ってください。 他にもTUIなどがありますが、今はややこしくなるので説明しません。興味があったら調べてみてください。 今回はGUIを試しにつくってみます。 Tkinter TkinterはPython からGUIを構築・操作するための標準ライブラリです。標準ライブラリとは、pythonの初期装備、くらいに思ってください。 上でも述べたように、ボタンとかをつくって入力することができる画面をPC上に作り出すことができます。 アプリ製作の第一歩だと思ってやってみてください。 テキスト表示する まずは、tkinterでテキスト表示させてみましょう。 下を書いてみてください。 #tkinterを使いますよ、という宣言。以降、tkとうつとtkinterが使える。 import tkinter as tk #画面を作成する root = tk.Tk(); #画面の大きさを設定する root.geometry("800x400") #テキストを作成する lbl = tk.Label(text = 'test') #テキストを配置する lbl.pack() #画面を出力する tk.mainloop() このような画面が出てくればOKです。 ex5-1 画面の大きさと表示の文字を好きなものに変えてみましょう。 ボタンを配置する 次にボタンを配置します。 上のテキスト表示に書き足すように記述してください。 import tkinter as tk root = tk.Tk(); root.geometry("800x400") lbl = tk.Label(text = 'test') #ボタンの作成。ボタン内テキストは'test' btn = tk.Button(text='test') lbl.pack() #ボタンの配置 btn.pack() tk.mainloop() ボタンの配置方法は上に書いてあるとおりです。 このようになればOK。 また、pack()でボタンやテキストの大きさを変えることができます。 たとえば、btn.pack()をbtn.pack(fill='x')に変えてみてください。ボタンが横いっぱいに広がったら成功です。 ex5-2 ・ボタンを複数つくってみましょう。 ・中の文字を変えたりしてみましょう。 ・下記サイトを参考にして、ボタンの位置をいろいろいじって実験してみてください。 https://imagingsolution.net/program/python/tkinter/widget_layout_pack/ ボタンで関数を呼び出す ボタンをおしたらテキストを変更する、という機能をつくってみます。 上のプログラムに書き足してみてください。 import tkinter as tk #関数作成 def changetxt(): #.configureでテキスト内容を変えることができる lbl.configure(text='change!') root = tk.Tk(); root.geometry("800x400") lbl = tk.Label(text = 'test') #command=○○でボタン押したときに関数を呼び出せる btn = tk.Button(text='test',command=changetxt) lbl.pack() btn.pack(fill='x') tk.mainloop() btn = tk.Button(text='',command=関数名)で関数を呼び出すことができます。 lbl.configure(text='変更後のテキスト')でテキストが変更できます。 ex5-3 ・ボタンをおすと、テキストが「大吉」「吉」「凶」いずれかに変化するおみくじをつくってみましょう。 ヒント: 1まずは関数をつくってボタンをおしたら呼び出せるようにします。 2一番上にimport randomと書くと乱数が使えるようになります。 3「大吉」「吉」「凶」が入ったリストを使います。 4text=random.choice(リスト名)でリストの中の要素をランダムでtextに返します。 5上で書いたプログラムに書き足す形でつくることができます。 ex5-4 ・ex4-3でつくったじゃんけんゲームをtkinterで実装しなおしてみてください。 ・余裕があればボタンを見やすく配置してみましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なぜディープラーニングするのか?を自分なりに調べてみた件

雑な投稿ですが、なぜディープラーニングをするのかについて調べてみました。 要は線形分離不可能な物を分けるためですが、それをイメージするために3次元で表した物です。深層学習で可視化するとsigmoid的な曲線が描写されて面白いです。 https://drive.google.com/file/d/140DSzbIY0YyywwWmTGSv86-Qf1e2HG2m/view?usp=sharing 参考 なぜディープラーニングするのか?を自分なりに調べてみた件2(ロジスティック回帰)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【MediaPipe】FaceMeshで取得した468点の座標から3次元顔を再現できるか?

疑問 MediaPipeのFaceMeshを使うと、2次元人物画像から人の顔表面468点の3次元座標を推測してくれます。この座標と、該当する画素値を取得すれば、顔の3次元モデルを再現することができるのではないか?と思い、ためしてみました。 結論からいうと… ローポリの顔モデルを作ることができました。しかし、顔と認識できるものの、ローポリなため完全な再現はできませんでした。やはり、より多くの点群が必要だとわかります。 ・元画像 Photo by Yonas Bekele on Unsplash ・MediaPipeで取得したランドマーク ・点群からメッシュ化した3Dモデル(Octree depth=15) 3DCGに関して素人なので、ここからモデルを綺麗にする方法をどなたか教えていただきたいです… MediaPipeで座標と画素値を取得する全体コード import cv2 import numpy as np import pandas as pd from PIL import Image import mediapipe as mp # 初期設定 mp_holistic = mp.solutions.holistic holistic = mp_holistic.Holistic( static_image_mode=True, min_detection_confidence=0.5) mp_drawing = mp.solutions.drawing_utils drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1) def main(): image = cv2.imread('画像パス') results = holistic.process( cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) # ランドマークの座標dataframeとarray_imageを取得 df_xyz, landmark_image = landmark(image) # ランドマーク記載画像を整形、出力 landmark_image = cv2.cvtColor( landmark_image, cv2.COLOR_BGR2RGB) # BGRtoRGB landmark_image = Image.fromarray(landmark_image.astype(np.uint8)) # landmark_image.show() height, width, channels = image.shape[:3] # ランドマークの色情報を取得 df_rgb = color(image, df_xyz, height, width) # xyzとrgb結合 df_xyz_rgb = pd.concat([df_xyz, df_rgb], axis=1) df_xyz_rgb.to_csv('./点群.csv', header=False, index=False) # ランドマークの画素値を取得する def color(image, xyz, height, width): label = ['r', 'g', 'b'] data = [] for _ in range(len(xyz)): x = int(xyz.iloc[_, 0]*width) y = int(xyz.iloc[_, 1]*height) b = int(image[y, x, 0]) g = int(image[y, x, 1]) r = int(image[y, x, 2]) data.append([r, g, b]) df = pd.DataFrame(data, columns=label) return df # ランドマークの座標を取得する def face(results, annotated_image): label = ["x", "y", "z"] data = [] if results.face_landmarks: # ランドマークを描画する mp_drawing.draw_landmarks( image=annotated_image, landmark_list=results.face_landmarks, connections=mp_holistic.FACEMESH_TESSELATION, landmark_drawing_spec=drawing_spec, connection_drawing_spec=drawing_spec) for landmark in results.face_landmarks.landmark: data.append([landmark.x, landmark.y, landmark.z]) else: # 検出されなかったら欠損値nanを登録する data.append([np.nan, np.nan, np.nan]) df = pd.DataFrame(data, columns=label) return df # imageに対してmediapipeでランドマークを表示、出力する def landmark(image): results = holistic.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) annotated_image = image.copy() # ランドマーク取得 df_xyz = face(results, annotated_image) return df_xyz, annotated_image if __name__ == "__main__": main() 顔のRGB取得 全体コードのRGB取得についてのコードを説明します。 MediaPipeで取得した座標DataFrameは以下のようになっています。 x y z 0 0.481850 0.361779 -0.037970 1 0.458742 0.330488 -0.088316 2 0.466888 0.337605 -0.040462 3 0.423848 0.292867 -0.072854 4 0.451685 0.319499 -0.096578 .. ... ... ... 463 0.465720 0.238455 -0.005005 464 0.457777 0.244713 -0.014008 465 0.453392 0.249533 -0.024355 466 0.543307 0.207326 0.000718 467 0.548165 0.201184 0.000230 x, y座標はそれぞれ画像サイズで割られて0-1の値に正規化されているため、画素値取得にはもとの大きさに戻す必要があります。あとは、座標から元画像の画素値を抽出するだけです。 rgb取得 ''' ランドマークの画素値を取得する image:元画像 xyz:mediapipeで取得した座標DataFrame height:元画像の縦サイズ width:元画像の幅サイズ ''' def color(image, xyz, height, width): label = ['r', 'g', 'b'] data = [] for _ in range(len(xyz)): # 座標をもとの大きさに戻す x = int(xyz.iloc[_, 0]*width) y = int(xyz.iloc[_, 1]*height) b = int(image[y, x, 0]) g = int(image[y, x, 1]) r = int(image[y, x, 2]) data.append([r, g, b]) df = pd.DataFrame(data, columns=label) return df 画像ランドマークの座標と画素値を取得することができました。 df_xyz_rgb x y z r g b 0 0.481850 0.361779 -0.037970 184 126 124 1 0.458742 0.330488 -0.088316 184 144 119 2 0.466888 0.337605 -0.040462 178 136 114 3 0.423848 0.292867 -0.072854 163 94 78 4 0.451685 0.319499 -0.096578 196 157 142 .. ... ... ... ... ... ... 463 0.465720 0.238455 -0.005005 186 134 112 464 0.457777 0.244713 -0.014008 192 164 150 465 0.453392 0.249533 -0.024355 214 196 186 466 0.543307 0.207326 0.000718 162 145 129 467 0.548165 0.201184 0.000230 181 154 125 点群のメッシュ化 得られた点群.csvから点群をメッシュ化しました。メッシュ化にはフリーソフトである「Cloud Compare」を使用しました。 参考:https://amdlaboratory.com/amdblog/cg%E7%82%B9%E7%BE%A4%E3%81%AE%E3%83%A1%E3%83%83%E3%82%B7%E3%83%A5%E5%8C%96/ メッシュの精度を決める「Octree depth」をいろいろ弄ってみます。 ・Octree depth=8(デフォルト) ・Octree depth=15 ・Octree depth=20 理由はわかりませんが、Octree depth=20以上になると左上にこぶのようなものが出てきます。 まとめ 鼻・口まわりは点数が多いため再現度は高いですが、眼球や眉まわりは再現できていません。やはり、より多くの点数が必要になりそうです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonからTwitterと天気予報のAPIを使って定期ツイートをする

初めに タイトルの通りPythonを使用して定期ツイートをするプログラムを作りました。 使用したライブラリはtweepy、使用した天気予報APIは天気予報 API(livedoor 天気互換)です。 lolipopというレンタルサーバー上にアップロードして定期実行させています。 目次 1.Twitter APIの取得 2.tweepyのインストールとテスト 3.天気予報APIの取得とテスト 4.最後の仕上げ 5.サーバーにアップロード 6.サーバー側にtweepyをインストール 7.サーバー上で定期実行 1.Twitter APIの取得 これが一番大変でした。 Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ を参考にして登録しました。 元々持っていたアカウントを使用しました。手順は以下の通りです。 1.電話番号認証をする 2.Twitter Developerにアクセスしてログイン 3.必要事項の入力 4.権限の設定 5.キーの再発行 1-1.電話番号認証 そのままの意味です。電話番号認証をしないとAPIが使えません。 1-2.Twitter Developerのページにアクセス https://developer.twitter.com/ にアクセスして右上のボタンからログイン。 1-3.必要事項の入力 名前、住んでいる国、APIの使用目的などを入力します。 利用規約に同意してsubmitボタンを押すとメールが届いて、メールのリンクを踏むと完了です。 1-4.高レベルアクセス権の取得 どうやらEssentialからElevatedというものにならないとツイートできないらしいので申請していきます。 1.ここをクリック 2.必要事項の入力 ここで文章を書く必要が出てきます。 一応日本語でも通るらしいですが、英語推奨なのでDeepL翻訳やGoogle翻訳などを使うのをお勧めします。 私の書いた文章を載せておきます。(DeepL翻訳使用) I will use it to learn Python and API. I'm planning to make a program that tweets the weather retrieved from the weather forecast API at a fixed time every day. I do not intend to use it for spamming. PythonとAPIの勉強に使わせていただきます。 毎日決まった時間に天気予報APIから取得した天気をツイートするプログラムを作ろうと思っています。 スパムなどに使うつもりはありません。 続いて使う機能を選択します。ツイートするだけなら2個目以外はいらないはずです。私は政府機関云々のところ以外は入力しました。 以下、私の書いた文章です。 Are you planning to analyze Twitter data? Twitterのデータを分析する予定がありますか? I don't plan to use it now, but in the future I may output it to a text file using Python tweepy, etc. and use it for analysis. It will not be used for the purpose of collecting personal information. 今は使う予定はありませんが、将来的にはPythonのtweepyなどを使ってテキストファイルに出力し、解析に利用することもあるかもしれません。 個人情報収集の目的では使用しません。 Will your app use Tweet, Retweet, Like, Follow, or Direct Message functionality? アプリでツイート、リツイート、いいね、フォロー、ダイレクトメッセージの機能を使用しますか? Yes, I plan to create a program that will tweet the weather forecast retrieved by the API at a regular time every day. It will not be used for spamming purposes. はい、APIで取得した天気予報を毎日定時にツイートするプログラムを作成する予定です。 スパム目的では使用しません。 Do you plan to display Tweets or aggregate data about Twitter content outside Twitter? Twitterのコンテンツに関するツイートや集計データをTwitterの外部で表示する予定はありますか? I don't plan to use it now, but I may introduce it in my graduation research or blog in the future. If I do use it, I will process it so that no individual is identified before posting it. 今は使う予定はありませんが、今後、卒業研究やブログで紹介することがあるかもしれません。 使用する場合は、個人が特定されないように加工してから掲載するつもりです。 入力が終わったら規約に同意してsubmitを押せば完了です。 私の場合は一瞬で承認されました。 1-5.権限の設定 このままではツイートする権限が無いので権限を与えます。 訳あってスクショによって時系列が違いますが問題ないので気にしないでください。 1.プロジェクトの設定を選択 2.Set upを選択 3.OAuth 2.0、OAuth 1.0aにチェックを入れる 多分権限の設定だけならOAuth 1.0aだけでも大丈夫だと思います。 4.App permissionsの欄をRead and write and Direct messageに変更 ツイートするだけならRead and writeでも構いません。 5.Callback URI / Redirect URL と Website URLを入力 ここがよくわからなくて一番時間を使いました。 実際未だによく理解していないので有識者の方いらっしゃいましたらコメントしていただけると幸いです。 それっぽい説明の載っているサイト (私は何となくしか理解できませんでした...) 私は両方とも自分のTwitterアカウントのURLを入れました。(危険だったら教えてもらえると幸いです) 1-6.キーとトークンを再発行する 権限変えたら再発行が必要らしいので再発行します。 どこかにメモしておいてください。 以上でTwitter APIの設定は終わりです。 2.tweepyのインストールとテスト ターミナルで以下のコマンドを実行。 pip install tweepy インストールできたら次のようなコードを書きます。 import tweepy TEXT = "ツイートしたいテキスト" consumer_key = "取得したAPI key" consumer_secret = "取得したAPI Key Secret" access_token = "取得したAccess Token" access_token_secret = "取得したAccess Token Secret" auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) api.update_status(TEXT) 実行してツイートできていたら完了です。 3.天気予報APIの取得 天気予報 API(livedoor 天気互換)というサイトを使いました。 特にキーなどは必要なく、URLにアクセスするだけでjson形式で返ってきます。 次のようなコード書きます。クラス化していますが関数とかでも構いません。 リクエストURLの作成にformatを使用していますが、末尾に付け足すだけなので(URL + TOKYO)とかでも大丈夫です。 地域一覧はこちらを参照してください。 import requests class Translation: def __init__(self) -> None: self.URL = "https://weather.tsukumijima.net/api/forecast/city/{}" # 基本URL self.TOKYO = "130010" # 地域のID ここでは東京 self.URL_TOKYO = self.URL.format(self.TOKYO) # URLを作成 def get(self): link = self.URL_TOKYO # URLセット request = requests.get(link) # リクエストを投げる result = request.json() # jsonに変換 s = ( # タイトル (例:東京都 東京 の天気) result["title"] + "\n" # 天気 (例:くもり 昼過ぎ 一時 雪") replaceで全角スペースを半角に変換 + str(result["forecasts"][0]["detail"]["weather"]).replace(" "," ") + "\n" + "最低気温は" # 最低気温 5時以降はnullが返ってくるので注意 + str(result["forecasts"][0]["temperature"]["min"]["celsius"]) + "度" + " 最高気温は" # 最高気温 + str(result["forecasts"][0]["temperature"]["max"]["celsius"]) + "度です" ) return s if __name__ == "__main__": a = Translation() print(a.get()) 実行すると以下のような内容が返ってくると思います。 東京都 東京 の天気 くもり 昼過ぎ 一時 雪 最低気温はNone度 最高気温は3度です Noneが返ってくるのは仕様です。5時以降に叩くとNoneが返ってきます。 正常に返ってきていたら完了です。 4.最後の仕上げ もう一息で完成です。 tweepyのテストで書いたコードに少し書き足します。 クラス化せずに関数で書いた場合は2行目と3行目を消してください。 キーはベタ打ちでも問題ないですが、クラス化しておくと後々別の目的で使いたくなったときに楽です。 import tweepy import 天気予報APIのファイル translation = 天気予報APIのファイル.Translation() TEXT = translation.get() # 関数にした場合は TEXT = 関数名() consumer_key = "取得したAPI key" consumer_secret = "取得したAPI Key Secret" access_token = "取得したAccess Token" access_token_secret = "取得したAccess Token Secret" auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) api.update_status(TEXT) 実行してみてツイートされていたら完成です! 5.サーバーにアップロード ここから先はlolipop限定の話になります。ご了承ください。 アップロードする前に実行ファイルの一行目に以下の一文を書き足してください。 #!/usr/local/bin/python3.7 私はFFFTPを使ってアップロードしました。 FFFTPの環境構築をしたのがかなり前で何も覚えていないので詳しくはググってください。 アップロードしたらパーミッションを変更します。 FFFTP側でも設定できますが、ここではlolipop側で設定します。 1.サーバーの管理・設定 -> ロリポップ!FTP 2.実行ファイルを選択 -> 現在の属性を700に変更 777とかでも構いません。お好みでどうぞ。 以上でパーミッションの設定は完了です。 6.サーバー側にtweepyをインストール サーバーの管理・設定 -> SSH からSSHを有効にしたら、こちらのサイトを参考にしてpipの導入をしてください。(丸投げですみません) pipの導入が終わったら以下のコマンドを実行。 pip3 install tweepy 以上です。 7.サーバー上で定期実行 1.サーバーの管理・設定 -> cron設定 2.cronの設定をする こんな感じで設定します。時間などはお好みで。5時以降だと最低気温がNoneになってしまうのでそれよりも前をお勧めします。 以上で全て完了です! 終わりに 最後まで読んでいただきありがとうございます。 まともな記事を書くのはこれが初めてなので至らない点が多いと思います。何か気になることや間違っていることがありましたらコメントしていただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonでTwitterと天気予報のAPIを使って定期ツイートをする

初めに タイトルの通りPythonを使用して定期ツイートをするプログラムを作りました。 使用したライブラリはtweepy、使用した天気予報APIは天気予報 API(livedoor 天気互換)です。 lolipopというレンタルサーバー上にアップロードして定期実行させています。 目次 1.Twitter APIの取得 2.tweepyのインストールとテスト 3.天気予報APIの取得とテスト 4.最後の仕上げ 5.サーバーにアップロード 6.サーバー側にtweepyをインストール 7.サーバー上で定期実行 1.Twitter APIの取得 これが一番大変でした。 Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ を参考にして登録しました。 元々持っていたアカウントを使用しました。手順は以下の通りです。 1.電話番号認証をする 2.Twitter Developerにアクセスしてログイン 3.必要事項の入力 4.権限の設定 5.キーの再発行 1-1.電話番号認証 そのままの意味です。電話番号認証をしないとAPIが使えません。 1-2.Twitter Developerのページにアクセス https://developer.twitter.com/ にアクセスして右上のボタンからログイン。 1-3.必要事項の入力 名前、住んでいる国、APIの使用目的などを入力します。 利用規約に同意してsubmitボタンを押すとメールが届いて、メールのリンクを踏むと完了です。 1-4.高レベルアクセス権の取得 どうやらEssentialからElevatedというものにならないとツイートできないらしいので申請していきます。 1.ここをクリック 2.必要事項の入力 ここで文章を書く必要が出てきます。 一応日本語でも通るらしいですが、英語推奨なのでDeepL翻訳やGoogle翻訳などを使うのをお勧めします。 私の書いた文章を載せておきます。(DeepL翻訳使用) I will use it to learn Python and API. I'm planning to make a program that tweets the weather retrieved from the weather forecast API at a fixed time every day. I do not intend to use it for spamming. PythonとAPIの勉強に使わせていただきます。 毎日決まった時間に天気予報APIから取得した天気をツイートするプログラムを作ろうと思っています。 スパムなどに使うつもりはありません。 続いて使う機能を選択します。ツイートするだけなら2個目以外はいらないはずです。私は政府機関云々のところ以外は入力しました。 以下、私の書いた文章です。 Are you planning to analyze Twitter data? Twitterのデータを分析する予定がありますか? I don't plan to use it now, but in the future I may output it to a text file using Python tweepy, etc. and use it for analysis. It will not be used for the purpose of collecting personal information. 今は使う予定はありませんが、将来的にはPythonのtweepyなどを使ってテキストファイルに出力し、解析に利用することもあるかもしれません。 個人情報収集の目的では使用しません。 Will your app use Tweet, Retweet, Like, Follow, or Direct Message functionality? アプリでツイート、リツイート、いいね、フォロー、ダイレクトメッセージの機能を使用しますか? Yes, I plan to create a program that will tweet the weather forecast retrieved by the API at a regular time every day. It will not be used for spamming purposes. はい、APIで取得した天気予報を毎日定時にツイートするプログラムを作成する予定です。 スパム目的では使用しません。 Do you plan to display Tweets or aggregate data about Twitter content outside Twitter? Twitterのコンテンツに関するツイートや集計データをTwitterの外部で表示する予定はありますか? I don't plan to use it now, but I may introduce it in my graduation research or blog in the future. If I do use it, I will process it so that no individual is identified before posting it. 今は使う予定はありませんが、今後、卒業研究やブログで紹介することがあるかもしれません。 使用する場合は、個人が特定されないように加工してから掲載するつもりです。 入力が終わったら規約に同意してsubmitを押せば完了です。 私の場合は一瞬で承認されました。 1-5.権限の設定 このままではツイートする権限が無いので権限を与えます。 訳あってスクショによって時系列が違いますが問題ないので気にしないでください。 1.プロジェクトの設定を選択 2.Set upを選択 3.OAuth 2.0、OAuth 1.0aにチェックを入れる 多分権限の設定だけならOAuth 1.0aだけでも大丈夫だと思います。 4.App permissionsの欄をRead and write and Direct messageに変更 ツイートするだけならRead and writeでも構いません。 5.Callback URI / Redirect URL と Website URLを入力 ここがよくわからなくて一番時間を使いました。 実際未だによく理解していないので有識者の方いらっしゃいましたらコメントしていただけると幸いです。 それっぽい説明の載っているサイト (私は何となくしか理解できませんでした...) 私は両方とも自分のTwitterアカウントのURLを入れました。(危険だったら教えてもらえると幸いです) 1-6.キーとトークンを再発行する 権限変えたら再発行が必要らしいので再発行します。 どこかにメモしておいてください。 以上でTwitter APIの設定は終わりです。 2.tweepyのインストールとテスト ターミナルで以下のコマンドを実行。 pip install tweepy インストールできたら次のようなコードを書きます。 import tweepy TEXT = "ツイートしたいテキスト" consumer_key = "取得したAPI key" consumer_secret = "取得したAPI Key Secret" access_token = "取得したAccess Token" access_token_secret = "取得したAccess Token Secret" auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) api.update_status(TEXT) 実行してツイートできていたら完了です。 3.天気予報APIの取得 天気予報 API(livedoor 天気互換)というサイトを使いました。 特にキーなどは必要なく、URLにアクセスするだけでjson形式で返ってきます。 次のようなコード書きます。クラス化していますが関数とかでも構いません。 リクエストURLの作成にformatを使用していますが、末尾に付け足すだけなので(URL + TOKYO)とかでも大丈夫です。 地域一覧はこちらを参照してください。 import requests class Translation: def __init__(self) -> None: self.URL = "https://weather.tsukumijima.net/api/forecast/city/{}" # 基本URL self.TOKYO = "130010" # 地域のID ここでは東京 self.URL_TOKYO = self.URL.format(self.TOKYO) # URLを作成 def get(self): link = self.URL_TOKYO # URLセット request = requests.get(link) # リクエストを投げる result = request.json() # jsonに変換 s = ( # タイトル (例:東京都 東京 の天気) result["title"] + "\n" # 天気 (例:くもり 昼過ぎ 一時 雪") replaceで全角スペースを半角に変換 + str(result["forecasts"][0]["detail"]["weather"]).replace(" "," ") + "\n" + "最低気温は" # 最低気温 5時以降はnullが返ってくるので注意 + str(result["forecasts"][0]["temperature"]["min"]["celsius"]) + "度" + " 最高気温は" # 最高気温 + str(result["forecasts"][0]["temperature"]["max"]["celsius"]) + "度です" ) return s if __name__ == "__main__": a = Translation() print(a.get()) 実行すると以下のような内容が返ってくると思います。 東京都 東京 の天気 くもり 昼過ぎ 一時 雪 最低気温はNone度 最高気温は3度です Noneが返ってくるのは仕様です。5時以降に叩くとNoneが返ってきます。 正常に返ってきていたら完了です。 4.最後の仕上げ もう一息で完成です。 tweepyのテストで書いたコードに少し書き足します。 クラス化せずに関数で書いた場合は2行目と3行目を消してください。 キーはベタ打ちでも問題ないですが、クラス化しておくと後々別の目的で使いたくなったときに楽です。 import tweepy import 天気予報APIのファイル translation = 天気予報APIのファイル.Translation() TEXT = translation.get() # 関数にした場合は TEXT = 関数名() consumer_key = "取得したAPI key" consumer_secret = "取得したAPI Key Secret" access_token = "取得したAccess Token" access_token_secret = "取得したAccess Token Secret" auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) api.update_status(TEXT) 実行してみてツイートされていたら完成です! 5.サーバーにアップロード ここから先はlolipop限定の話になります。ご了承ください。 アップロードする前に実行ファイルの一行目に以下の一文を書き足してください。 #!/usr/local/bin/python3.7 私はFFFTPを使ってアップロードしました。 FFFTPの環境構築をしたのがかなり前で何も覚えていないので詳しくはググってください。 アップロードしたらパーミッションを変更します。 FFFTP側でも設定できますが、ここではlolipop側で設定します。 1.サーバーの管理・設定 -> ロリポップ!FTP 2.実行ファイルを選択 -> 現在の属性を700に変更 777とかでも構いません。お好みでどうぞ。 以上でパーミッションの設定は完了です。 6.サーバー側にtweepyをインストール サーバーの管理・設定 -> SSH からSSHを有効にしたら、こちらのサイトを参考にしてpipの導入をしてください。(丸投げですみません) pipの導入が終わったら以下のコマンドを実行。 pip3 install tweepy 以上です。 7.サーバー上で定期実行 1.サーバーの管理・設定 -> cron設定 2.cronの設定をする こんな感じで設定します。時間などはお好みで。5時以降だと最低気温がNoneになってしまうのでそれよりも前をお勧めします。 以上で全て完了です! 終わりに 最後まで読んでいただきありがとうございます。 まともな記事を書くのはこれが初めてなので至らない点が多いと思います。何か気になることや間違っていることがありましたらコメントしていただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cartopyで地図上にデータを描いてみる

はじめに  地図上にデータを重ねて描く方法をまとめました。気象データなどの解析をしていると、地図があったほうがわかりやすく、他人に説明する場合もイメージしてもらいやすいな、と感じるときがよくあります。  そこで、今回は次の、よく仕様する2点を順にまとめました。 ・正方位図法(Plate Carree) ・正距方位図法(北極、南極中心) 地図を図法ごとに描画する。 下準備 ・numpy ・matplotlib ・cartopy 次のように必要なライブラリを呼び出します。 import numpy as np import mayplotlib.pyplot as plt import matploblib.path as mpath import cartopy.crs as ccrs これだけです。 正方形図法(Plate Carree) まずは、もっともよく目にするであろう、緯線と経線が等間隔に直交している図法です。 fig = plt.figure(1, figsize=(8, 6), facecolor='white', dpi=300) ax = fig.add_subplot(projection=ccrs.PlateCarree(central_longitude=180.0)) ax.set_global() ax.coastlines() ax.gridlines() axを作成するときに、projectionで地図の形式(ここではPlateCarree)を指定します. central_longitudeは中心となる軽度を指定しています(ここでは東経180°)。 ax.set_global()で地球全体を指定し、ax.coastlines()で陸地の沿岸部(すなわち地図)を描きます。ax.gridlines()で、図にグリッド線を描きます(なくてもいいかもしれませんがあったほうがわかりやすいので一応)。 あとは軽度、緯度、データを描けば完成です。今回は、気象庁のJRA-55(Kobayashi et al.)を使用。 long = np.linspace(0, 360, 288) lat = np.linspace(90, -90, 145) Long, Lat = np.meshgrid(long, lat) contf = plt.contourf(Long, Lat, data.T, transform=ccrs.PlateCarree(), levels=np.linspace(230, 320, 50), cmap=cm.jet, extend='both') plt.colorbar(contf, orientation='vertical', shrink=0.6) long, latは解析したいデータの形です。transform=ccrs.PlateCarree()を忘れないでください。 今回は例として、地表面でのある日の気温を表示してみます。次のような結果になりました。 正距方位図法(Azimuthal Equidistant) 正方位図法とほとんど同じで、projectionの指定が異なります。また、切り取りを行わないと四角形の図になります。 phi = np.linspace(0, 2*np.pi, 100) center, radi = [0.5, 0.5] verts = np.concatenate([np.cos(phi)[:, np.newaxis], np.sin(phi)[:, np.newaxis]], axis=1) circle = mpath.Path(verts * radi + center) fig = plt.figure(1, figsize=(8, 6), facecolor='white', dpi=300) ax1 = fig.add_subplot(1, 2, 1, projection=ccrs.AzimuthalEquidistant(central_latitude=90.0)) ax1.set_boundary(circle, transform=ax.transAxes) ax1.coastlines() ax1.gridlines() ax1.set_extent([-180, 180, 0, 90], ccrs.PlateCarree()) 正距方位図法の場合、projection=ccrs.AzimutualEquidistant()と指定します。そして、central_latitudeでどの緯度を中心にするかを選択します。北極中心の場合、central_latitude=90.0です。 set_extentで、どの範囲で表示するかを決めます。 続いて、南極中心です。 ax2 = fig.add_subplot(1, 2, 2, projection=ccrs.AzimuthalEquidistant(central_latitude=-90.0) ax2.set_boundary(circle, transform=ax.transAxes) ax2.coastlines() ax2.gridlines() ax2.set_extent([-180, 180, -90, 0], ccrs.PlateCarree()) 変わったのはcental_latitudeの値のみです。ここまでこれば緯度経度の格子点を与え、データを描けば完了です。 全体のコードは次のようになります。 import numpy as np import mayplotlib.pyplot as plt import matploblib.path as mpath import cartopy.crs as ccrs phi = np.linspace(0, 2*np.pi, 100) center, radi = [0.5, 0.5] verts = np.concatenate([np.cos(phi)[:, np.newaxis], np.sin(phi)[:, np.newaxis]], axis=1) circle = mpath.Path(verts * radi + center) long = np.linspace(0, 360, 288) lat = np.linspace(90, -90, 145) Long, Lat = np.meshgrid(long, lat) fig = plt.figure(1, figsize=(8, 6), facecolor='white', dpi=300) ax1 = fig.add_subplot(1, 2, 1, projection=ccrs.AzimuthalEquidistant(central_latitude=90.0)) ax1.set_boundary(circle, transform=ax.transAxes) ax1.coastlines() ax1.gridlines() ax1.set_extent([-180, 180, 0, 90], ccrs.PlateCarree()) contf1 = ax1.contourf(lo, la, data.T, transform=ccrs.PlateCarree(), levels=np.linspace(230, 320, 50), cmap=cm.jet, extend='both') plt.colorbar(contf1, shrink=0.6, orientation='vertical') ax2 = fig.add_subplot(1, 2, 2, projection=ccrs.AzimuthalEquidistant(central_latitude=-90.0) ax2.set_boundary(circle, transform=ax.transAxes) ax2.coastlines() ax2.gridlines() ax2.set_extent([-180, 180, -90, 0], ccrs.PlateCarree()) contf2 = ax2.contourf(lo, la, data.T, transform=ccrs.PlateCarree(), levels=np.linspace(230, 320, 50), cmap=cm.jet, extend='both') plt.colorbar(contf2, shrink=0.6, orientation='vertical') plt.show() 結果は以下のようになりました。 こうすると気温の分布がわかりやすいですね。 また、この記事は以下の記事を参考にしました。 [cartopyとmatplotlibを使って緯度経度座標のデータ(気象データとかを想定)の可視化] https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwiGocSetKv1AhUur1YBHXn-C-oQFnoECAkQAQ&url=https%3A%2F%2Fqiita.com%2Fconvection%2Fitems%2F6d5fa546d81fd5c9b452&usg=AOvVaw2YPdWYGpdHFN1OrLd8RolQ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Day002 : Botterまでの道のり(使っているライブラリのメモ)

シリーズ前後5つ まだ まだ まだ まだ Day001 : Botterまでの道のり(LSTMについてぼんやり理解する) Day002 : Botterまでの道のり(使っているライブラリのメモ) <- 今ここ まだ まだ まだ まだ まだ 動機 モデルをどうやって作ろうかなと思ったときに,まずなんとなくでも理解する必要があると思って・・・。 シリーズ全体が200記事くらい書ければB級Botterにはなれるはず・・・。  ライブラリ一覧 機械学習系 機械学習ライブラリ PyTorch pip3 install torch==1.10.1+cu113 torchvision==0.11.2+cu113 torchaudio===0.10.1+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html 機械学習ライブラリ scikit-learn 多分最初から入っているはず。 conda install scikit-learn 最適化ライブラリ Optuna conda install -c conda-forge optuna 計算ライブラリ 経済指標計算ライブラリ TA-Lib conda install -c conda-forge ta-lib 配列計算ライブラリ CuPy pip install cupy-cuda113 #CUDAのバージョンに合わせて! お絵描き データ可視化ライブラリ Bokeh conda install bokeh データ可視化ライブラリ mplfinance 古い記事だとmatplotlibでCandlestickチャートを描いてますが、mplfinanceへ移行したようです。 Botterの定義 Have a good Botter Life!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[py2rb] itertools.product

はじめに 移植やってます。 ( from python 3.7 to ruby 2.7 ) product (Python) import itertools A = [('a', 'b', 'c'), ('d', 'e'), ('f', 'g')] print(list(itertools.product(*A))) # [('a', 'd', 'f'), ('a', 'd', 'g'), ('a', 'e', 'f'), ('a', 'e', 'g'), ('b', 'd', 'f'), ('b', 'd', 'g'), ('b', 'e', 'f'), ('b', 'e', 'g'), ('c', 'd', 'f'), ('c', 'd', 'g'), ('c', 'e', 'f'), ('c', 'e', 'g')] ジェネレータ式の入れ子になった for ループとおよそ等価です。たとえば product(A, B) は ((x,y) for x in A for y in B) と同じものを返します。 ふむふむ。 product 失敗 (Ruby) A = [['a', 'b', 'c'], ['d', 'e'], ['f', 'g']] print(A.inject(:product)) # [[["a", "d"], "f"], [["a", "d"], "g"], [["a", "e"], "f"], [["a", "e"], "g"], [["b", "d"], "f"], [["b", "d"], "g"], [["b", "e"], "f"], [["b", "e"], "g"], [["c", "d"], "f"], [["c", "d"], "g"], [["c", "e"], "f"], [["c", "e"], "g"]] 単に、injectすると、平坦化されていない状態で出力されます。 product + flatten 成功 (Ruby) A = [['a', 'b', 'c'], ['d', 'e'], ['f', 'g']] print(A.inject(:product).map(&:flatten)) # [["a", "d", "f"], ["a", "d", "g"], ["a", "e", "f"], ["a", "e", "g"], ["b", "d", "f"], ["b", "d", "g"], ["b", "e", "f"], ["b", "e", "g"], ["c", "d", "f"], ["c", "d", "g"], ["c", "e", "f"], ["c", "e", "g"]] tupleではないのですが、同様の処理ができました。 product 応用問題 (Python) import itertools A = [(('H-', 'a'), 'b', 'c'), ('d', 'e'), ('f', 'g')] print(list(itertools.product(*A))) # [(('H-', 'a'), 'd', 'f'), (('H-', 'a'), 'd', 'g'), (('H-', 'a'), 'e', 'f'), (('H-', 'a'), 'e', 'g'), ('b', 'd', 'f'), ('b', 'd', 'g'), ('b', 'e', 'f'), ('b', 'e', 'g'), ('c', 'd', 'f'), ('c', 'd', 'g'), ('c', 'e', 'f'), ('c', 'e', 'g')] 前回は、要素が一つずつでしたが、今回は要素数が異なります。 product 応用問題 (Ruby) A = [[['H-', 'a'], 'b', 'c'], ['d', 'e'], ['f', 'g']] print(A.inject(:product).map(&:flatten)) # [["H-", "a", "d", "f"], ["H-", "a", "d", "g"], ["H-", "a", "e", "f"], ["H-", "a", "e", "g"], ["b", "d", "f"], ["b", "d", "g"], ["b", "e", "f"], ["b", "e", "g"], ["c", "d", "f"], ["c", "d", "g"], ["c", "e", "f"], ["c", "e", "g"]] print(A.inject(:product).map{ _1.flatten(1) }) # 1 print(A[0].product(*A[1..-1])) # 2 # [[["H-", "a"], "d", "f"], [["H-", "a"], "d", "g"], [["H-", "a"], "e", "f"], [["H-", "a"], "e", "g"], ["b", "d", "f"], ["b", "d", "g"], ["b", "e", "f"], ["b", "e", "g"], ["c", "d", "f"], ["c", "d", "g"], ["c", "e", "f"], ["c", "e", "g"]] 単なるflattenですと平坦化しすぎですので、深さを指定します。 2番目の書き方は、こちらを参照しました。 メモ Python の itertools.product を学習した 百里を行く者は九十里を半ばとす
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ABC82メモ

ABC82 メモ A - Round Up the Mean $a$と$b$を足して2で割った計算結果を切り上げる。 これは$a+b+1$を2で割って切り捨てたものと等しい。 82A.py a, b = map(int, input().split()) ans = (a+b+1)//2 print(ans) B - Two Anagrams $s$と$t$を一度リストにして、$s$を昇順、$t$を降順にそれぞれ並び替え、文字列に戻す。 文字列同士の比較演算は、辞書順で比較されるため、問題文通りそのまま比較。 82B.py s = input() t = input() s = list(s) t = list(t) s.sort() t.sort(reverse=True) s = ''.join(s) t = ''.join(t) ans = 'No' if s < t: ans = 'Yes' print(ans) C - Good Sequence $x$が$x$個含まれない場合、全て取り除く必要があり、$x$個以上含まれている場合、その差分だけ取り除けば良い。そのためには$x$が何個含まれているかを数える必要がある。 ここで、$1\leq x \leq 10^9$より、リストで管理すると大きくなり過ぎてしまうので、辞書で管理する。 82C.py N = int(input()) alst = list(map(int, input().split())) d = dict() for i in range(N): a = alst[i] if a in d: d[a] += 1 else: d[a] = 1 ans = 0 for i in d.keys(): if d[i] >= i: ans += (d[i]-i) else: ans += d[i] print(ans) D - FT Robot まず、与えられた文字列を、移動と方向転換に分割する。つまり、Fの連続する数だけ進む移動を、Tで区切る、と考える。 例えば、"FFTFFFTFTTFFF"であれば、[2, 3, 1, 0, 3]と移動する。 移動を観察すると、偶数回目の移動においては$y$軸方向にしか移動できず、奇数回目の移動は$x$軸方向にしか移動できないことがわかる。また、$x$軸方向の移動は$y$軸方向の移動に制約を与えないので、$x$軸方向に$x$に到達できるか、$y$軸方向に$y$に到達できるかを独立に考えて、両方が可能である場合、指定された座標に到達可能である。 また、$x$軸方向への移動について、1回目の移動は$x$軸方向正の向きしか行えないが、2回目以降は、正の向き、負の向きをそれぞれ自由に選択できることがわかる。$x$軸方向に移動できる歩数のリストについて、合計を$S$とする。正の向きに移動するとしたものをリストからいくつか選択し、その時の合計を$A$とする。このとき、$A$だけ正の向きに移動し、$S-A$だけ負の向きに移動することがわかる。このとき、最終的な座標は$A-(S-A)=2A-S$より、$2A-S$であるとわかる。これが指定された$x$と一致するようなAが存在すれば良い。 つまり、「各軸方向の移動の数列について、うまく部分和をとることで、与えられた座標に到達できるか」と言い換えることができる。 部分和の計算はDPを用いて計算量が$O(|s|^2)$であり、今回の制約では間に合う。 (計算量の見積もりはあっているのかわからないです。) 82D.py def calc(lst, s): S = sum(lst) dp = [] for i in range(len(lst)+1): tmp = [0]*(S+1) dp.append(tmp) dp[0][0] = 1 for i in range(len(lst)): n = lst[i] for j in range(S+1): if dp[i][j]: dp[i+1][j] = 1 if j+n < S+1: dp[i+1][j+n] = 1 if (S+s)%2 or S < s or -S > s: return False else: return dp[-1][(S+s)//2] s = input() x, y = map(int, input().split()) lst = list(s.split('T')) x -= len(lst[0]) Xlst = [] Ylst = [] for i in range(1, len(lst)): if i%2: Ylst.append(len(lst[i])) else: Xlst.append(len(lst[i])) ans = 'No' if calc(Xlst, x) and calc(Ylst, y): ans = 'Yes' print(ans)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

テレビゲームのRTAにおける最適チャート追求の部分理論化:「進め!キノピオ隊長」の場合

改訂履歴 2022/01/12: 初版(計算結果なし) ネタバレ注意 本記事は、「進め!キノピオ隊長」というテレビゲームソフトや、そのほかのスーパーマリオシリーズのゲームソフトのネタバレを含みます。それでもかまわない方のみ、読み続けてください。 テレビゲームのRTAと数理最適化 テレビゲームの楽しみ方の一つに、何らかの設定された条件のもとでゲームソフト上の指定された地点や状態などから別の指定された地点や状態などに至るまでを人力操作によりできるだけ短時間で達成することを目指す「RTA」があります。例えば、「スーパーマリオブラザーズ」でゲームをスタートさせてからマリオやルイージを操作してできるだけ短時間でピーチ姫を救出する、などです。Speedrun.comに代表されるRTA記録集サイトには、日々、世界中から、多くのゲームソフトの高速プレイ録画リンクが提出され、録画動画の審査を経て、ランキング表が更新されています。RTAプレイヤーは、このランキング表の上位を目指して競っている、あるいは、自己ベストタイムを縮めることに挑戦をしています。 RTAにおいては、「チャート」と呼ばれる「短時間で目標を達成し得る操作の一連の流れ」を発見および改良することと、できるだけチャートどおりに操作を実践することの両面で、プレイヤーによる研究や鍛錬が進んでいます。前者については、コミュニティー内において、時折、チャートの(専門用語ではない通常の日本語のニュアンスとしての)最適化や、理論的に達成可能な最短タイム、といった言葉が登場します。ですが、理論的最短タイムを実現し得る真の最適チャートを探しだすことは、バグを含むゲームソフトのコードやそれが動くゲーム機の挙動の完全な把握が必要になることと、操作の選択が1秒間だけでも数十回(例えば、秒間60フレームのゲームソフトの場合、1秒間に60回)発生しそれがタイムぶん生じて膨大な総数になることから、たいへん困難です。そのため、チャートの発見・改良は、プレイヤーによる試行錯誤によるものがほとんどです。 さて、「何らかの与えられた条件のもとで目的とする評価値を最小あるいは最大にする解を求める」手法群である「数理最適化」が、チャートの発見・改良に役立てられないか?完全に理論的な最短タイムの導出は無理ではあるが、一部に仮定を置き、仮定していない部分について数理最適化を用いてチャートを導出することで、「もしその仮定が正しければこのチャートは理論的な最短タイムを実現し得る」という、条件付きの理論化ができないか?というのが、本記事の執筆動機になります。 ただし、ゲームソフトにはさまざまなジャンルやシステムのものがあり、一律に同一の数理最適化手法を適用することはできないので、本記事では特定の一つのソフトを対象にします。そして最後に、ほかのソフトへの応用可能性について言及します。 対象:「進め!キノピオ隊長」 本記事では、「進め!キノピオ隊長」というゲームソフトを対象にします。このゲームは「スーパーマリオ 3Dワールド」のスピンオフとして開発されたものです。Wii U版が2014年に、Nintendo Switch版とニンテンドー3DS版が2018年に発売されました。 ソフトのジャンルは、3D箱庭パズルアクションです。独立した複数のステージが存在し、各ステージについて、主人公であるキノピオ隊長やキノピコ、ギミックなどを操作して動かし、用意されたゴール地点を目指すものです。 数理最適化の適用範囲 「進め!キノピオ隊長」のチャートのどの部分を数理最適化を用いて導出するかを明確にするため、以下、整理をします。 RTAのカテゴリー RTAでは、通常、一つのゲームソフトに対して、達成目標が異なる複数の「カテゴリー」が設定されており、達成タイムのランキング表はカテゴリー別に掲載されています。本記事では、Speedrun.comで設定されているカテゴリーのうち、「ANY%, Switch (Solo)」を対象とします。これは、Nintendo Switch版を1人でプレイするという前提で、プレイヤーの操作キャラであるキノピオ隊長を動かし始めてから3-28のゴール地点というところに到達するまでの時間を計測するカテゴリーです。その道中に関しては特に何もルールはありません。 ゲームソフトの進行構成 ゲームソフトが進行していく様子の特徴をまとめます。番号付き箇条書き部分は抽象的に、番号なし箇条書き部分は「進め!キノピオ隊長」のRTAのANY%カテゴリーにおいて具体的に対応しているものを、それぞれ記載します。 ゲームソフトは複数のワールド(エピソード)からなる。最短タイムは、ワールド番号の小さいものから大きいものへという順番でクリアしなければ達成できない。 エピソード1~エピソード3 各ワールドには複数のステージ(ページ)がある。最短タイムは、ステージ番号の小さいものから大きいものへという順番でクリアしなければ達成できない。 エピソード1には、1-01~1-18の18ページ エピソード2には、2-01~2-18の18ページ エピソード3には、3-01~3-28の28ページ 各ステージには、1つあるいは複数の進行アイテム(ダイヤ)が配置されている。そのステージは進行アイテムを取得しなくてもクリアできる。 各ページに、3つのダイヤ 訪れてクリアをしなくてもよいワールドやステージが存在してもよい 1-06 (中略) 3-26 あるステージをクリアすることで、アンロックされる1つないし複数のステージがある 1-01をクリア → 1-02がアンロック (中略) 3-27をクリア → 3-28がアンロック さらに、一部のステージを訪れるには、一定数の進行アイテム(ダイヤ)を取得済みである必要がある 1-10を訪れるには、12個のダイヤが必要 (中略) 3-27を訪れるには、155個のダイヤが必要 仮定 必要となる仮定を厳密に網羅して列挙することは難しいのですが、少なくともこれはなければならないと思われるものを列挙します。 ゲームソフトの進行構成をスキップできるようなバグは存在しない 各ステージについて、進行アイテムを0個、1個、…、最大個取得する場合の最短クリアタイムは、それぞれわかっているものとする 適用範囲 仮定の2つ目が強すぎるというか、その部分の理論的な最短チャートこそ知りたいものだと思われるかもしれません。しかしながら、ステージ内においては、前述のように、操作の選択が発生する機会が1秒あたり数十回もあり、それらをゲームソフトのコードやそれが動くゲーム機の挙動と照らし合わせる必要があるため、ステージ内の動きについて理論的に最適なものを求めるのは困難です。 本記事では、各ステージ内でどう操作すればよいかはRTAプレイヤーによる試行錯誤で発見された一連の操作を(最適なものと仮定して)採用し、「どのステージをクリアないしスキップをし、クリアする各ステージでは何個の進行アイテムを取得するか」という部分の最適な選択を、数理最適化を用いて行います。 数理最適化によるステージ選択・進行アイテム取得 数理最適化には適用する対象に応じてさまざまな手法があります。本記事で扱う「進め!キノピオ隊長」のANY%カテゴリーのRTAにおける最適なステージ(ページ)選択と各ステージの進行アイテム(ダイヤ)取得数を導出できる手法もいくつかあります。どの手法を選んでも手計算ないしコンピューターで計算プログラムを動かすことになるので、計算時間が短くてすむ手法がよいです。そして、その手法が適用できるように、対象の捉え方を工夫する必要があります。 動的計画法の適用(今回の場合は、非巡回有向ネットワークに対するダイクストラ法でも可) 本記事で行う工夫として、どのステージをクリアしたか、進行アイテムを何個所持しているかという、RTAの道中の「状態」と、プレイを進めてある状態から別の状態へ「遷移」することを考えます。そして、状態と遷移を表現するにあたり、数学の「グラフ理論」における「グラフ」を拡張した「ネットワーク」(リンク1、リンク2)というものを使用することにします。 まず、状態として、RTAの「スタート」および、各ステージ(ページ)のクリア時点を考えます。ただし、同じステージのクリア時であっても、進行アイテム(ダイヤ)数の保有数に応じて状態を区別します。例えば、「1-01クリア時点、ダイヤ保有数0個」「1-01クリア時点、ダイヤ保有数1個」といったものです。ANY%カテゴリーではダイヤを155個保有すれば3-28のゴール地点まで到達できますし、それ以上のダイヤを取得するには余計なタイムがかかるため、ダイヤ保有数は0~155までを考えます。以下に図例を示します。丸印が状態を表します。 次に、状態間の遷移を、ゲームソフト中のいくつかの場面の例に挙げて考えます。まず、「スタート」から始めて次に遷移できる状態としては、その時点でアンロックされているステージ(ページ)が1-01しかなく、1-01で取得できる進行アイテム(ダイヤ)が最大3個なので、「1-01クリア時点、ダイヤ保有数0個」~「1-01クリア時点、ダイヤ保有数3個」までの状態に遷移できます。次に、1-01のクリアから次に遷移できる状態ですが、1-01をクリアすると1-02がアンロックされ、1-02でもダイヤを0~3個取得することができるため、「1-01クリア時点、ダイヤ保有数0個」からは「1-02クリア時点、ダイヤ保有数0個」~「1-02クリア時点、ダイヤ保有数3個」までが、(中略)、「1-01クリア時点、ダイヤ保有数3個」からは「1-02クリア時点、ダイヤ保有数3個」~「1-02クリア時点、ダイヤ保有数6個」までが、それぞれ可能です。以下に図例を示します。矢印が遷移を表します。 別の例として、ここでは保有ダイヤ数については説明を省略しますが、1-05をクリアすると1-06と1-07がアンロックされるため、1-05からは1-06と1-07の両方への遷移ができます。一方、1-06をクリアしてもどのページもアンロックされませんが、次に1-07を訪れることができるため、1-06から1-07に遷移ができます。似た例として、2-10をクリアすると2-11、2-12、2-13がアンロックされ、2-11と2-12はクリアしてもアンロックされるページがないという場面を挙げます。このとき、2-10からは2-11、2-12、2-13へ、2-11からは2-12、2-13へ、それぞれ遷移が可能です。ここで、2-12についてですが、2-12をクリアした後に2-11を訪れることはプレイ上は可能なものの、2-11をクリアした後に2-12を訪れたほうがゲームソフトのUI操作に要する時間が短いため、2-12から2-11への遷移はないものとします。ゲームソフトの進行構成のところで述べた、最短タイムはワールドやステージ番号の小さいものから大きいものへという順番でクリアしなければ達成できないという特徴の一例が、これにあたります。 ほかの例として、1-10への遷移を考えます。1-10を訪れるには、12個のダイヤが必要のため、「1-10クリア時点、ダイヤ保有数0個」~「1-10クリア時点、ダイヤ保有数11個」までの状態には遷移することができません。なので、「1-09クリア時点、ダイヤ保有数0個」~「1-09クリア時点、ダイヤ保有数11個」という状態たちは、遷移先がありません。 例に挙げたような方法、可能な遷移を網羅的に列挙したうえで、各遷移(上図の一つ一つの矢印)について、仮定により判明している最短クリアタイムをひも付けます。例えば「スタート」から「1-01クリア時点、ダイヤ保有数0個」への遷移には、エピソード1のプロローグでキノピオ隊長を動かし始めてから、1-01でダイヤを全く取得しないでクリアするまでのタイムを付与します。実際には、自分自身のプレイ、あるいは、ほかのRTAプレイヤーの動画から、タイムを計測することになります。遷移元の状態における保有ダイヤ数によって推移先のステージのクリアタイムが変わることはないので、全ページについてダイヤを0個取得した場合のタイム~ダイヤを3個取得した場合のタイムを計測すれば、全ての遷移に対してタイムがひも付けられます。 状態と遷移、そして各推移にかかる時間をネットワークで表現することで、「進め!キノピオ隊長」のANY%カテゴリーのRTAを最短時間で実現し得るステージ(ページ)選択と進行アイテム(ダイヤ)取得は、数理最適化の中の「動的計画法」(リンク1、リンク2、リンク3)という手法、あるいは、「(非巡回有向ネットワークに対する)ダイクストラ法」という手法により、求めることができるようになります。本記事の説明は、どちらかというと後者寄りです。「ダイクストラ法」(リンク1、リンク2)は、カーナビが現在位置から目的地まで最短時間で到着できる道路の経路を求めたり、乗り換え案内サービスが鉄道である駅からある駅まで最短時間で移動する列車や乗り換えの経路を求めたりする際に使用できる手法です。本記事で表現したネットワークにダイクストラ法を適用して、「スタート」という状態から「3-28クリア時点、ダイヤ保有数155個」という状態までの累計タイムが最短になる遷移を求めることで、 「進め!キノピオ隊長」のANY%カテゴリーのRTAにおいて、どのページを訪れ、各ページではいくつのダイヤを取得することが最適なのかが、明らかになるわけです。 本来ならば、ここから動的計画法、ないし、(非巡回有向ネットワークに対する)ダイクストラ法の内容を紹介すべきなのですが、本記事の執筆者はここまでの記事執筆で力尽きてしまいました。申し訳ありません。本記事から貼られている各手法のリンクの先のウェブページは、理論的にやや厳密な記述が多いため、手法名を検索エンジンにかけて表示される別のウェブページをご覧になったほうが、理解しやすいかもしれません。 なお、本記事で紹介した手順は、「ナップサック問題」(リンク1、リンク2)を動的計画法で解くのと近いものがあります。 プログラミングコード 「進め!キノピオ隊長」のANY%カテゴリーで登場するステージ(ページ)数・進行アイテム(ダイヤ)数であれば、手計算でもできるのですが、面倒なのでプログラムを書いてコンピューターに計算させます。Python言語で実装したものを、以下に置いています。 動的計画法というよりかは非巡回有向ネットワークに対するダイクストラ法の実装になっております。また、Python/Codes/main.pyのnum_strategies: int = 1という変数の値を変えることで、1番目~num_strategies番目に最適なチャートまでを出力するようになっています(第num_strategies最適なチャートまで求める部分の実装には、ダイクストラ法で各頂点が保有するラベル数をnum_strategiesつにするという素朴な拡張を採用しています)。 計算結果 データ作成中です。データができしだい、計算結果をここに記載します。少々お待ちください。 発展 本記事では、各ステージについて、進行アイテムを0個、1個、…、最大個取得する場合のクリアタイムとして、それぞれ最短タイムだけを使用しましたが、代わりに数十回~数百回プレイした際のクリアタイムの分布を使用することが考えられます。クリアタイムの分布を離散確率分布にすることで、各ステージのクリアタイムのブレや難しい操作の成功率を考慮してステージ選択・アイテム取得を決定することができると予想します。 ほかのゲームソフトへの応用可能性 ほかのゲームソフトについて、「進め!キノピオ隊長」と同様に、ステージ選択と各ステージでのアイテム取得数の最適化を動的計画法(または、非巡回有向ネットワークに対するダイクストラ法)で求めることができるかを考えます。なお、世の中にはたくさんのゲームソフトがありますが、本記事の執筆者のゲーム知識の限界により、ここでは、最近のスーパーマリオシリーズ本編のみを考察の対象とします。 同じ手法が応用できそうなもの 最近の3Dマリオシリーズの中では、「スーパーマリオ 3Dランド」が、ゲームソフトの進行構成が「進め!キノピオ隊長」と近いので、同じ手法が適用できるのではないかと推測しています。ただし、番号の小さいワールドから大きいワールドへとワープが可能なシステムがあるので、ネットワークの構築方法を多少変更しなければならないのではないかと考えています。「スーパーマリオ 3Dワールド」も、キノピオ隊長ステージやミステリーハウスステージなど一部のステージはいつ訪れるかの自由度があるものの、全体としてはゲームソフトの進行構成が近いため、同じ手法が適用できる可能性はあるかもしれないと推測しています。 同じ手法が応用できなさそうなもの 一方、同じ最近の3Dマリオシリーズでも、「スーパーマリオ オデッセイ」や、「スーパーマリオ 3Dワールド + フューリーワールド」内の「フューリーワールド」は、ゲームソフトの進行構成として、進行アイテム(ムーン、ネコシャイン)の取得場所をステージと捉えた場合、ステージの訪問順に大きな自由度があるため、「進め!キノピオ隊長」と同じ手法は適用できません。これらのゲームで、どの場所の進行アイテムをどの順で取得すれば最短時間になり得るかは、「Prize Collecting Traveling Salesman Problem (PCTSP)」として捉えることができるのではないかと推測しています。PCTSPは、ステージ数が多ければ高速なコンピューターを用いても現実的な時間内に最短時間となるルートを導出できませんが、Speedrun.comで設定されているカテゴリーのうち、フューリーワールド(ANY%と100%の両方)はステージ(ネコシャイン)数が最大100であり、オデッセイのANY%はワールドごとに分割して考えることができ、各ワールドのステージ(ムーン)設置箇所数はANY%クリア前であれば少ないので、これらのカテゴリーについては最新のPCであれば現実的な時間内に最短時間となるルートを導出できるのではないかと予測しています。 最近の2Dマリオシリーズは、各ステージに進行アイテムとして「スターコイン」が複数ありますが、RTAのカテゴリーによって、スターコインを取らなくてもよいか、全て取らなければならないかのどちらかなので、本記事の手法は適さないのではないかと推測しています。 余談 「進め!キノピオ隊長」で、キノピオ隊長が各ステージをクリアしたときに発する音声が「バーボン!」に聞こえます?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SPSS Modelerのデータ検査ノード(目的変数がカテゴリ型)をPythonで書き換える。

SPSS Modelerのデータ検査ノードをPythonで書き換えます。データの傾向が一覧でつかめるのでとても人気のあるノードです。目的変数がカテゴリ型が連続型かによって動きが異なるところがありますが、今回は目的変数がカテゴリ型である場合の書き換えを行います。 0.データ 目的変数 Risk:信用リスク 説明変数 Age:年齢 Income:収入ランク Credit_cards:クレジットカード枚数 Education:学歴 Car_loans:車のローン数 年齢や収入ランクから信用リスクを判定する2値分類のモデルを評価します。 1m.①データ検査 Modeler版 まず、CSVの読み込み時にカテゴリ型のデータを文字列で読み込みます。これはデータに意図的に欠損値や空白データを混入させているためです。Car_loans、Credit_cards、Education、Income、Riskは文字列に上書きしています。 プレビューすると以下のように連続型の欠損値は\$null\$で表され、カテゴリ型の欠損値は空文字や空白文字で表示されます。 次に、データ型ノードで説明変数、目的変数の定義を行います。 「値の読み込み」ボタンをクリックします。その後、Riskのロールを「対象」(目的変数の意味)を設定します。 後はデータ検査ノードを実行するだけです。 サンプルグラフでは連続型のデータはヒストグラム、カテゴリ型のデータは度数の棒グラフを目的変数のRiskの値を元に色分けしてくれます。このグラフで各変数の分布と目的変数と各説明変数の関係性がぱっと見で把握できるのがとても良いところです。 また、連続型については最小、最大、平均値、標準偏差、歪度が集計されます。 そして、カテゴリ型についてはカテゴリ数が集計されます。 「有効」はNULLや空白値を除いた有効なデータ件数です。 次に欠損値検査タブを見てみます。 ここでは、連続型については外れ値(3シグマ以上)、極値(5シグマ以上)の件数、ヌル値の件数が出ます。 また、カテゴリ型では、空文字(’’)、空白文字(’ ’)の件数が出ます。なお、空白を別途定義している場合はその件数も出ます。 そして、ヌル値、空文字(’’)、空白文字(’ ’)を除いたレコード数の「有効なレコ―ド」数と「非欠損値の割合」(有効レコード数/全体の件数)が表示されます。 やはり欠損値の状況が一覧で分かりやすく理解できます。 なおこのタブには、今回は取り上げませんが、外れ値や欠損値を補完することもできる機能もついています。 1p.①データ検査 pandas版 設定 まず、カテゴリ型のデータをCategory型でCSVを読み込みます。 df = pd.read_csv('credit_risk_missing.csv',dtype = {'CustID':'int', 'Age':'float', 'Car_loans':'category','Credit_cards':'category', 'Education':'category','Income':'category','Risk':'category'} pandasではカテゴリ型もNaNで表示されました。空白文字(9行目のEducation)は空白で表示されます。 参考:pandasでデータを読み込むときに気を付けること(dtypeの指定) - Qiita グラフ表示 まず、複数のグラフを表示するためにnotebookの出力域を拡大します。 %%javascript IPython.OutputArea.auto_scroll_threshold = 9999; 参考:ipython-notebook — ipythonノートブック出力ウィンドウのサイズを変更します 連続型のデータはヒストグラム、カテゴリ型のデータは度数の棒グラフを目的変数のRiskの値を元に色分けして表示します。 df[col].dtype.nameでCatagory型や連続型を判定しています。 連続型はたくさんの型がありうるので正規表現re.sub(r'[0-9]+', '', df[col].dtype.name) in ['int','float']をつかって判定しています。 棒グラフとヒストグラムの書き方は以下の記事も参考にしてください。 グラフ表示 import re targetcol='Risk' for col in df.columns: if df[col].dtype.name=='category': dfcross=pd.crosstab(df[col],df[targetcol]) dfcross=dfcross.reindex(['1','0'],axis="columns") dfcross.plot.bar(stacked=True,title=col) elif re.sub(r'[0-9]+', '', df[col].dtype.name) in ['int','float']: nums=[] targets=df[targetcol].cat.categories.sort_values (ascending=False) for i,target in enumerate(targets): nums.append(df.query(targetcol+'==\'{0}\''.format(target))[col]) plt.title(col) plt.hist(nums,bins=25, stacked=True,label=targets.astype('str')) plt.legend() plt.figure() 参考:python - Check if dataframe column is Categorical - Stack Overflow 参考:Python で文字列から数字を削除する | Delft スタック 列の要約 連続型変数については最小、最大、平均値、標準偏差はdescribe()で計算できます。 連続型の要約 df.describe() Modelerと違って4分位のパーセンタイル値がでるのは便利です。ただし、歪度は別途計算する必要があります。 歪度はskew()で計算します。カテゴリ型の列についても、値としてはほとんど参考にはなりませんが、計算されています。 歪度 df.skew() カテゴリ型変数についてカテゴリ数を計算してみます。連続型の変数についてもカウントされています。Modelerは空文字をカテゴリとしてカウントしていますが、nunique()は空文字はNullとして無視していますので、Modelerのカテゴリ数とは異なります。後程欠損値のところで詳しく解説します。 カテゴリ数 df.nunique() 参考:pandasで歪度(Skewness)と尖度(Kurtosis)を計算 - Qiita 外れ値、極値 Modelerでは外れ値を3シグマ以上、極値を5シグマ以上で計算しているので同様に計算します。数値列だけが対象になるのでre.sub(r'[0-9]+', '', df[col].dtype.name) in ['int','float']で判定しています。 件数は式.sum()で計算しています。 数値列だけが対象になるので、データフレームの件数は可変です。そのため、Seriesオブジェクトをつくってからappendしています。 外れ値、極値 df1 = pd.DataFrame(index=[], columns=['外れ値','極値']) for col in df.columns: if re.sub(r'[0-9]+', '', df[col].dtype.name) in ['int','float']: limit_low3=df[col].mean()-df[col].std()*3 limit_high3=df[col].mean()+df[col].std()*3 limit_low5=df[col].mean()-df[col].std()*5 limit_high5=df[col].mean()+df[col].std()*5 hazurechi=((df[col] <limit_low3)|(df[col] >limit_high3)).sum() kyokuchi=((df[col] <limit_low5)|(df[col] >limit_high5)).sum() df1=df1.append(pd.Series([hazurechi-kyokuchi,kyokuchi],index=df1.columns,name=col)) 参考:Python初心者向け:四分位数/標準偏差を用いた外れ値の除外 | happy analysis 参考:Pandasにて条件にあったカウント(count)を行う方法【value_countsの使い方など】 | ウルトラフリーダム 参考:Pythonで、空のDataFrameにindexとセットで1行追加する | ITを使っていこう 欠損値 欠損値はisnull().sum()で数えることができます。 欠損値1 df.isnull().sum() しかしながら、Modelerとは欠損値の考え方が違っているのでカウント数が異なります。 Modelerはデータが存在しない場合、連続値についてはNULL値としてカウントし、カテゴリ型については空文字としてカウントします。pandas(というかnumpy)は空文字もNULLとして扱います。 Modelerは’ ’のような空白文字は欠損値として扱います。pandasはデータとして扱います。 上のModelerの仕様と同じようにpandasで欠損値を計算するには以下のようになります。 空白文字はdf[col].str.match(r'^ +$'))).sum()というように正規表現でカウントしています。 欠損値2 #欠損値2 colnames=['非欠損値の割合','有効なレコード','ヌル値','空文字','空白文字'] dfcol=pd.DataFrame(index=df.columns,columns=colnames) #ヌル for i,col in enumerate(dfcol.index): if df[col].dtype.name=='category': dfcol.iloc[i,colnames.index('ヌル値')]=0 elif re.sub(r'[0-9]+', '', df[col].dtype.name) in ['int','float']: dfcol.iloc[i,colnames.index('ヌル値')]=df[col].isnull().sum() #空文字列 for i,col in enumerate(dfcol.index): if df[col].dtype.name=='category': dfcol.iloc[i,colnames.index('空文字')]=df[col].isnull().sum() elif re.sub(r'[0-9]+', '', df[col].dtype.name) in ['int','float']: dfcol.iloc[i,colnames.index('空文字')]=0 #空白文字 for i,col in enumerate(dfcol.index): if df[col].dtype.name=='category': dfcol.iloc[i,colnames.index('空白文字')]=((df[col].str.match(r'^ +$'))).sum()+dfcol.iloc[i,colnames.index('空文字')] elif re.sub(r'[0-9]+', '', df[col].dtype.name) in ['int','float']: dfcol.iloc[i,colnames.index('空白文字')]=0 #有効なレコード dfcol['有効なレコード']=len(df)-(dfcol['ヌル値']+dfcol['空白文字']) dfcol['非欠損値の割合']=dfcol['有効なレコード']/len(df)*100``` 5. サンプル サンプルは以下に置きました。 ストリーム https://github.com/hkwd/200611Modeler2Python/blob/master/dataaudit/dataaudit.str?raw=true notebook https://github.com/hkwd/200611Modeler2Python/tree/master/dataaudit/dataaudit.ipynb データ https://raw.githubusercontent.com/hkwd/200611Modeler2Python/master/data/credit_risk_missing.csv ■テスト環境 Modeler 18.3 Windows 10 64bit Python 3.8.10 pandas 1.0.5 6. 参考情報 データ検査ノード
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

flask run が上手くできない場合の対処法

Python Flaskにてflask runでプレビュー画面を表示しようとした際、一部ターミナルでは機能しない場合があります。 ちなみに自分の場合はpowershellにて問題が起こりました。 (例)Dcument flask run (結果) + CategoryInfo : ObjectNotFound: (flask:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException (原因) 恐らくPathが通っていないことが原因でコマンドとして認識されていないと思われます。 (解決) 1,Pathを通す。 システム環境変数の設定にて設定しなおしてください。 2,pythonコマンドを使う。 pythonのversionを指定して、そこからflask runを割り当てます。 (書き方) python3 -m flask run  
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Progateの学習を終えたプログラム初心者が【テトリス】を作ってみた

Progateの学習を終えて、Pythonの理解を深めるためにテトリスをつくってみました。 アウトプットで書き留めようと思います。 プログラム初学者で初めて組んだプログラムです。 分からなかったところは参考サイトを参考に自分なりに書きました。 どうしても書けなかったところは、参考サイトを参考に自分なりに考えながら模写しています汗。 参考にさせてもらったサイト一覧 だえうホームページ SAMURAIENGINEER TKinterによる簡単なGUI kusakarism 環境 ・python: 3.10.1 使用したライブラリ ・TKinter ・random ・messagebox 初めにどういう感じで作るか決める。 ・ゲームのフィールドは二次元配列で作成 ・当たり判定は、配列中の数字で判定させる ・テトリスブロックは、1つのブロックを4つ並べたもの ・キー操作をTKinterを用いて、イベントとして受け取る。 ・ブロックの回転は座標でみた時に、90°回転させた時の値を変数の中に入れ直す。 ・テトリスブロックの落下はTKinterのafterメソッドを用いる ・横に一列積まれたらその列を消して上に積んであるブロックを下に落とす ・最上段までテトリスブロックが積まれたら、ゲーム終了。 after メソッドは、処理を遅らせて実行するメソッドです。 Tkinterの使い方:after で処理を「遅らせて」or 処理を「定期的」に実行する 実際に作ってみた tetris.py import tkinter import random from tkinter import messagebox size = 30 app = tkinter.Tk() app.title =("タイトル") can = tkinter.Canvas(app, width= size*12, height = size*21,) can.pack() app.mainloop() 初めにTKinterを使用するためにimportします。 そしてTKinterのTkクラスのインスタンスを生成 = appに格納 canvasの作成(テトリスは横10マス、縦20マスのゲーム。壁を配置したいため左右1マス、プラスしています) can.pack()にてCanvasを生成 次に再帰的処理を書く 再帰的処理とは 自分自身を呼び出す処理が書かれている関数を呼び出すこと tetris.py def game_loop(): can.delete("all") draw_tetris() can.after(50, game_loop) tetris.py app = tkinter.Tk() app.title =("タイトル") can = tkinter.Canvas(app, width= size*12, height = size*21,) can.pack() game_loop() #追加 app.mainloop() ブロックの作成 tetris.py tetroI = [0,0, 0,-1, 0,1, 0,2] tetroJ = [-1,1, 0,-1, 0,0, 0,1] tetroL = [0,0, 0,-1, 1,1, 0,1] tetroO = [0,0, 1,0, 0,-1, 1,-1,] tetroS = [-1,0, 0,-1, 1,-1, 0,0] tetroT = [-1,0, 0,0, 1,0, 0,1] tetroZ = [-1,-1, 0,-1, 1,0, 0,0] tetro = [tetroI, tetroJ, tetroL, tetroO, tetroS, tetroT, tetroZ] color = ["red", "yellow", "lime", "green", "blue", "navy", "fuchsia", "white", "black"] type = random.randint(0,6) 座標で入力してブロックを作っています。 (テトリスブロックは、1つのブロックを4つ並べたもの) tetroZを参考にすると 元の配列: [-1,-1, 0,-1, 1,0, 0,0] 解説:  [x-1,y-1][x0,y-1][x1,y0][x0,y0] 中心を[x0y0]とした時の座標に計4つのブロックを置いています y軸(マイナス方向)[x0,y-1]    |    | -----  -----x軸(プラス方向)[x1,y0]    |    | y軸(プラス方向)[x0,y1] テトリスブロックの描写 tetris.py moveX = 5 # 開始位置(X座標) moveY = 1 # 開始位置(Y座標) def draw_tetris(): i = 0 while i < 4: x = (tetro[type] [ i * 2 ] + moveX) * size y = (tetro[type] [ i * 2 + 1 ] + moveY) * size can.create_rectangle( x, y, x+size, y+size, fill=color[type]) i += 1 先程作成したブロックの配列からxとy座標を抜き出し、4つのブロックを描写しています。 四角形を書き表すときは、create_rectangle (始点x,始点y,終点x,終点y) で書き表すことができる。 実際にキー操作を入れてテトリスブロックを動かしてみる tetris.py app = tk.Tk() can = tk.Canvas(app, width= size*12, height = size*21, bg="#E5E5E5" ) can.pack() app.bind("<KeyPress>", keypress) #追加 game_loop() app.mainloop() tetris.py def keypress(event): global moveX, moveY if event.keysym == "Left": moveX -= 1 elif event.keysym == "Right": moveX += 1 elif event.keysym == "Up": moveY -= 1 elif event.keysym == "Down": moveY += 1 キー操作でmoveに加減してあげることで操作ができるように キャンバスにマス目を表示させる tetris.py def game_field(): i = 0 j = 0 while i < 22: field_y = i * size while j < 12: field_x = j * size can.create_rectangle( field_x, field_y, #始点 field_x + size, field_y + size, #終点 width=1, fill="white",outline="#ACACAC") #オプション j += 1 i += 1 j = 0 一個一個のブロックを上から順に並べて作成しました。 もっと良い書き方がありそう。。。 先程書いた再帰処理にも書いてあげる tetris.py def game_loop(): can.delete("all") game_field() #追加 draw_tetris() can.after(50, game_loop) フィールドの作成 tetris.py defence_field = [ [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,7,7,7,7,7,7,7,7,7,7,8], [8,8,8,8,8,8,8,8,8,8,8,8] ] 二次元配列でテトリスのフィールドに見立てて作成しました。 ちなみに7がブロックの動ける範囲(後ほど処理を書きます) (colorの要素を配列から取得させたいために7と8という数字を用いました) ついでにgame_fieldも変更しておきます tetris.py def game_field(): i = 0 j = 0 while i < 22: field_y = i * size j = 0 while j < 12: field_x = j * size can.create_rectangle( field_x, field_y, #始点 field_x + size, field_y + size, #終点 width=1, fill=color[defence_field[i][j]],outline="#ACACAC") if defence_field[i][j] == 8: can.create_rectangle( field_x, field_y, #始点 field_x + size, field_y + size, #終点 fill=color[8]) #オプション j += 1 i += 1 当たり判定の作成 tetris.py def judge(x, y): global moveX, moveY result = True i = 0 while i < 4: j = tetro[type] [i * 2] + x k = tetro[type] [i * 2 + 1] + y if defence_field[ k ] [ j ] != 7: result = False i += 1 if result == True: moveX = x moveY = y return result defence_fieldの配列内の"7"という数字以外の時に"False"を与えます。そして、移動させない為に数字を与えません。 また、キー操作にも変更を加えておきます tetris.py def keypress(event): global moveX, moveY judge_x = moveX judge_y = moveY if event.keysym == "Left": judge_x -= 1 elif event.keysym == "Right": judge_x += 1 elif event.keysym == "Up": judge_y -= 1 elif event.keysym == "Down": judge_y += 1 judge(judge_x, judge_y) 回転処理 tetris.py def keypress(event): global moveX, moveY judge_x = moveX judge_y = moveY rotation = [] rotation.extend(tetro[type]) if event.keysym == "Left": judge_x -= 1 elif event.keysym == "Right": judge_x += 1 elif event.keysym == "Down": judge_y += 1 elif event.keysym == "space": rotation.clear() i = 0 while i < 4: rotation.append(tetro[type][i*2+1]*(-1)) rotation.append(tetro[type][i*2]) i += 1 judge(judge_x, judge_y, rotation) judge関数にも変更を加えました tetris.py def judge(x, y, rotation): global moveX, moveY result = True i = 0 while i < 4: j = rotation [i * 2] + x k = rotation [i * 2 + 1] + y if defence_field[ k ] [ j ] != 7: result = False i += 1 if result == True: moveX = x moveY = y tetro[type].clear() tetro[type].extend(rotation) return result 新たに rotation関数を加え、回転する処理を施しています テトリスブロックが落ちる処理を作る tetris.py def drop_tetris(): global moveX, moveY, type rotation = [] rotation.extend(tetro[type]) result = judge(moveX, moveY +1, rotation) if result == False: i = 0 while i < 4: x = tetro[type][i*2] + moveX y = tetro[type][i*2+1] + moveY defence_field[y][x] = type i += 1 delete_line() #次で使います type = random.randint(0,6) moveX = 4 moveY = 1 can.after(1000, drop_tetris) tetris.py app = tkinter.Tk() can = tkinter.Canvas(app, width= size*12, height = size*21, bg="#E5E5E5" ) can.pack() #---処理--- app.bind("<KeyPress>", keypress) game_loop() drop_tetris() #追加 #----END--- app.mainloop() 一秒ごとに1マス下がるという処理をafterメゾットを用いて作っています。 一列揃ったら消す処理とゲームオーバーの処理 tetris.py def delete_line(): i = 1 while i < 21: if 7 not in defence_field[i]: j = 0 while j < i: k = 0 while k < 12: defence_field[i-j][k] = defence_field[i-j-1][k] k += 1 j += 1 i += 1 i = 1 #GAMEOVER while i < 11: if 7 != defence_field[1][i]: messagebox.showinfo("information", "GAMEOVER") exit() i += 1 defence_fieldには自由に動ける値として7を与えています。 その7という数字が列で見た時にひとつもない時、列を上の列から持ってくるという処理にしました。 まとめ 知識を定着させようと「テトリス」を作ってみよう!と思ったのですが、 とんでもなく難しかったです。 ですが、プログラムのどうやったら実装できるかという考え方や調べる能力。 他の人のコードを見て、テスト環境を作ってプログラムの動き方の深堀り。 エラーに苦しめられ何度もトライする精神。 とても時間がかかりましたが、いい勉強になったと思います。 以上、クソ記事ですがこれからレベルアップしてもっと質の良い記事を書くことができるよう誠心して行こうと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像処理】Numpyでハフ変換による直線検出

Numpyでハフ変換による直線検出を実装してみます。 直線を検出する画像を読み込みます。 import numpy as np import matplotlib.pyplot as plt original_image = plt.imread(image_name) if np.issubdtype(original_image.dtype, np.integer): original_image = original_image / np.iinfo(original_image.dtype).max gray_image = 0.2116 * original_image[:,:,0] + 0.7152 * original_image[:,:,1] + 0.0722 * original_image[:,:,2] plt.imshow(gray_image, cmap='gray') エッジ画像を作成します。ここでは、Cannyのエッジ検出を用います。Cannyのエッジ検出については以下の記事を参照してください。 【画像処理】NumpyでCannyエッジ検出 - Qiita def _convolve2d(image, kernel): shape = (image.shape[0] - kernel.shape[0] + 1, image.shape[1] - kernel.shape[1] + 1) + kernel.shape strides = image.strides * 2 strided_image = np.lib.stride_tricks.as_strided(image, shape, strides) return np.einsum('kl,ijkl->ij', kernel, strided_image) def _convolve2d_multichannel(image, kernel): convolved_image = np.empty((image.shape[0] - kernel.shape[0] + 1, image.shape[1] - kernel.shape[1] + 1, image.shape[2])) for i in range(image.shape[2]): convolved_image[:,:,i] = _convolve2d(image[:,:,i], kernel) return convolved_image def _pad_singlechannel_image(image, kernel_shape, boundary): return np.pad(image, ((int(kernel_shape[0] / 2),), (int(kernel_shape[1] / 2),)), boundary) def _pad_multichannel_image(image, kernel_shape, boundary): return np.pad(image, ((int(kernel_shape[0] / 2),), (int(kernel_shape[1] / 2),), (0,)), boundary) def convolve2d(image, kernel, boundary='edge'): if image.ndim == 2: pad_image = _pad_singlechannel_image(image, kernel.shape, boundary) if boundary is not None else image return _convolve2d(pad_image, kernel) elif image.ndim == 3: pad_image = _pad_multichannel_image(image, kernel.shape, boundary) if boundary is not None else image return _convolve2d_multichannel(pad_image, kernel) def create_gaussian_kernel(size=(3, 3), sigma=1): center = ((size[0] - 1) / 2, (size[1] - 1) / 2) sigma2 = 2 * sigma * sigma kernel = np.fromfunction(lambda y, x: np.exp(-((x - center[1]) ** 2 + (y - center[0]) ** 2) / sigma2), size) kernel = kernel / np.sum(kernel) return kernel def _suppress_nonmaximum(array): direction = array[9] if direction < 0: direction += np.pi if direction < 0.125 * np.pi: v0, v1, v2 = (array[3], array[4], array[5]) elif direction < 0.375 * np.pi: v0, v1, v2 = (array[2], array[4], array[6]) elif direction < 0.625 * np.pi: v0, v1, v2 = (array[1], array[4], array[7]) elif direction < 0.875 * np.pi: v0, v1, v2 = (array[0], array[4], array[8]) else: v0, v1, v2 = (array[3], array[4], array[5]) return v1 if v1 >= v0 and v1 >= v2 else 0 def suppress_nonmaximum(intensity_image, direction_image): pad_image = np.pad(intensity_image, 1, 'edge') shape = intensity_image.shape + (3, 3) strides = pad_image.strides * 2 strided_image = np.lib.stride_tricks.as_strided(pad_image, shape, strides).reshape(shape[0], shape[1], 9) return np.apply_along_axis(_suppress_nonmaximum, 2, np.dstack((strided_image, direction_image))) def histeresis_thresholding(image, threshold): edge_image = np.where(image >= threshold[1], 1.0, 0.0) while True: pad_edge_image = np.pad(edge_image, 1, 'edge') shape = image.shape + (3, 3) strides = pad_edge_image.strides * 2 neighbour_edge_image = np.lib.stride_tricks.as_strided(pad_edge_image, shape, strides) edge_candidate_index = np.where((image >= threshold[0]) & (edge_image != 1.0)) changed = False for index in list(zip(*edge_candidate_index)): if np.any(neighbour_edge_image[index] == 1.0): edge_image[index] = 1.0 changed = True if not changed: break; return edge_image def canny(image, gaussian_size=(3, 3), gaussian_sigma=1, threshold=(0.02, 0.05)): gaussian_kernel = create_gaussian_kernel(size=gaussian_size, sigma=gaussian_sigma) gaussian_image = convolve2d(gray_image, gaussian_kernel) sobel_x_kernel = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) sobel_x_image = convolve2d(gaussian_image, sobel_x_kernel) / 8 sobel_y_kernel = np.array(([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])) sobel_y_image = convolve2d(gaussian_image, sobel_y_kernel) / 8 sobel_intensity_image = np.sqrt(sobel_x_image ** 2 + sobel_y_image ** 2) sobel_direction_image = np.arctan(np.divide(sobel_y_image, sobel_x_image, out=np.zeros_like(sobel_y_image), where=sobel_x_image!=0)) nonmaximum_suppression_image = suppress_nonmaximum(sobel_intensity_image, sobel_direction_image) return histeresis_thresholding(nonmaximum_suppression_image, threshold) edge_image = canny(gray_image, gaussian_size=(3, 3), gaussian_sigma=0.2, threshold=(0.05, 0.2)) plt.imshow(edge_image, cmap='gray') 直線を次のように$(r, \theta)$で表します。$r$は原点から直線までの最短距離、$\theta$は原点からの直線までの最短地点を結んだ直線と原点がなす角度です。 r = x\cos\theta + y\sin\theta 上式よりエッジ画素$(x_e, y_e)$を通る直線は以下のようになります。 r = x_e\cos\theta + y_e\sin\theta $(r, \theta)$パラメータ空間を一定間隔で区切り、エッジ画素ごとに$\theta$を少しづつ変えながら$r$を求めます。求めた$(r, \theta)$の組み合わせをカウント(投票)していきます。 edges = np.where(edge_image == 1.0) r_max = np.sqrt(edge_image.shape[0] ** 2 + edge_image.shape[1] ** 2) votes = np.zeros((int(r_max), 180), dtype=int) for edge in list(zip(edges[1], edges[0])): for i in range(votes.shape[1]): ang = np.deg2rad(i) r = edge[0] * np.cos(ang) + edge[1] * np.sin(ang) votes[int(r)][i] += 1 plt.figure(figsize=(8,8)) plt.imshow(votes, aspect=0.5) plt.colorbar() 多数の投票が集まった$(r,\theta)$が検出された直線になります。検出された直線を画像上に描画してみます。直線の描画については以下の記事を参照してください。 【画像処理】Numpyで図形描画 - Qiita def is_local_maximum(image, size=(5, 5), boundary='edge'): pad_image = np.pad(image, ((int(size[0] / 2),), (int(size[1] / 2),)), boundary) areas = np.lib.stride_tricks.as_strided(pad_image, image.shape + size, pad_image.strides * 2) local_maximum = np.max(areas, axis=(2, 3)) return image == local_maximum def draw_line(image, color, weight, point1, point2): coord = np.fromfunction(lambda y, x: np.dstack((y + 0.5, x + 0.5)), image.shape[:2]) p12 = np.array([point2[1] - point1[1], point2[0] - point1[0]]) p12_norm = np.linalg.norm(p12) p12_dir = p12 / p12_norm p1c = np.dstack(([coord[:,:,0] - point1[1], coord[:,:,1] - point1[0]])) dot = np.einsum('ijk,k->ij', p1c, p12_dir) dist = np.sqrt(p1c[:,:,0] ** 2 + p1c[:,:,1] ** 2 - dot ** 2) condition = (dot >= 0) & (dot <= p12_norm) & (dist <= weight * 0.5) if image.ndim == 3: condition = np.tile(condition.reshape(condition.shape + (1,)), (1, 1, image.shape[2])) return np.where(condition, color, image) def draw_hough_lines(image, lines, color, weight): for line in lines: s = np.sin(line[1]) c = np.cos(line[1]) if s > 0.01: p1 = (0.0, line[0] / s) p2 = (image.shape[1], (line[0] - image.shape[1] * c) / s) image = draw_line(image, color, weight, p1, p2) else: p1 = (line[0] / c, 0.0) p2 = ((line[0] - image.shape[0] * s) / c, image.shape[0]) image = draw_line(image, color, weight, p1, p2) return image hough_lines = np.where(is_local_maximum(votes, size=(5, 5)) & (votes >= 50)) hough_line_image = draw_hough_lines(original_image, list(zip(hough_lines[0], np.deg2rad(hough_lines[1]))), np.array([1.0, 0.0, 1.0]), 2.0) plt.imshow(hough_line_image) これでハフ変換による直線の検出ができました。 ハフ変換による直線検出を1つの関数にまとめると次のようになります。 def detect_hough_lines(edge_image, vote_size, threshold, local_maximum_size): votes = np.zeros(vote_size, dtype=int) r_step = np.sqrt(edge_image.shape[0] ** 2 + edge_image.shape[1] ** 2) / vote_size[0] theta_step = np.pi / vote_size[1] edges = np.where(edge_image) for edge in list(zip(edges[1], edges[0])): for i in range(vote_size[1]): angle = i * theta_step r = edge[0] * np.cos(angle) + edge[1] * np.sin(angle) votes[int(r / r_step)][i] += 1 lines = np.where(is_local_maximum(votes) & (votes >= threshold)) return list(zip(lines[0] * r_step, lines[1] * theta_step)) r_max = np.sqrt(edge_image.shape[0] ** 2 + edge_image.shape[1] ** 2) hough_lines = detect_hough_lines(edge_image == 1.0, (int(r_max), 180), 50, (5, 5)) hough_line_image = draw_hough_lines(original_image, hough_lines, np.array([1.0, 0.0, 1.0]), 2.0) plt.imshow(hough_line_image) 実装したコードはGoogle Colaboratoryに置いてあります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

django_restframeworkでjwtトークン認証を実装したAPIをvueからaxiosで叩く

目的 django_restframeworkで実装したAPIをvueからaxiosで叩くことはしたが、実務のようにJWTを利用したトークン認証を行う 実施環境 ハードウェア環境 項目 情報 OS macOS Catalina(10.15.7) ハードウェア MacBook Air (11-inch, Early 2015) プロセッサ 1.6 GHz デュアルコアIntel Core i5 メモリ 4 GB 1600 MHz DDR3 グラフィックス intel HD Graphics 6000 1536 MB ソフトウェア環境 項目 情報 homebrew 3.3.8 mysql Ver 8.0.27 for macos10.15 on x86_64 @vue/cli 4.5.15 vue 2.6.14 前提 今回の記事はこれまでの【django/vue.js初学者向け】DjangoRestFrameworkで作成したAPIをVue.jsから叩いてみると【簡単】django_restframeworkのjwt認証を最低限実装していくの続きのため、これらの記事を読まないとわからない可能性がある。 概要 JWT認証のトークンを取得するAPIとJWTのアクセストークンがなければAPIを叩けないと言う処理の実装、APIを叩くためのフロント画面の実装が上記で完了しているため、これらを組み合わせていく。 詳細 ①v-modelで入力されたusernameとpasswordをscriptのプロパティに格納 { username: '', password: '', } ②ログインボタンを押すと@click(v-on:click)でloginメソッドが実行され、プロパティのusernameとpasswordを利用して【簡単】django_restframeworkのjwt認証を最低限実装していくで実装したAPIを「http://127.0.0.1:8000/api-auth/jwt/」 のエンドポイントから叩く。 login: function(){ // token取得のためのusernameとpasswordセット const data = {username: this.username, password: this.password} // access_token&refresh_tokenを取得 this.axios .post("http://127.0.0.1:8000/api-auth/jwt/",data) // レスポンスを一旦tokensプロパティに格納 .then(response => (this.tokens = response.data)); } ③レスポンスのアクセストークンとリフレッシュトークンをそれぞれプロパティに格納 .then(response => (this.tokens = response.data)); tokens: { "refresh":'', "access":'' }, ④メンバー情報取得ボタンを押すと@click(v-on:click)でgetMemberメソッドが実行され、http://127.0.0.1:8000/api/v1/member/"に対してgetメソッドで情報を取得する ⑤今回はサーバサイドでAPIにJWT認証が必要となる設定をしているため、Headerに {Authorization: アクセストークン}を含める必要がある。そうしなければ認証されていませんとエラーが出る。 ⑥そこで先ほど取得したアクセストークンをheaderに {Authorization: アクセストークン}を含める。 this.axios .get("http://127.0.0.1:8000/api/v1/member/",{headers: { // postmanでのAPIcal同様にJWTが必要 // 検証モードで確認できるヘッダー "Authorization": 'JWT ' + this.tokens.access, } }) ⑦Membersプロパティにレスポンスを格納し、それぞれのメンバーの特徴をループ処理で表示する <div v-for="(member, key) in Members" :key="key"> <hr> <p>{{ member.username }}</p> <p>{{ member.gender }}</p> <p>{{ member.introduction }}</p> <p>{{ member.job }}</p> <hr> </div> 大枠 <template> <div> <!-- Membersプロパティから --> <div v-for="(member, key) in Members" :key="key"> <hr> <p>{{ member.username }}</p> <p>{{ member.gender }}</p> <p>{{ member.introduction }}</p> <p>{{ member.job }}</p> <hr> </div> <!-- scriptのuserプロパティに入力値が格納される --> <input type="text" placeholder="username" v-model="username"> <!-- scriptのpasswordプロパティに入力値が格納される --> <input type="text" placeholder="password" v-model="password"> <!-- クリックするとloginメソッドを実行 --> <button @click="login">login</button><hr> <!-- クリックするとgetInfoメソッドを実行 --> <button @click="getInfo">メンバー情報を取得</button> </div> </template> <script> export default { data() { return { Members: [], tokens: { "refresh":'', "access":'' }, username: '', password: '', }; }, methods: { // メンバーの各情報を取得 getInfo: function(){ this.axios .get("http://127.0.0.1:8000/api/v1/member/",{headers: { // postmanでのAPIcal同様にJWTが必要 // この通信がうまくいかない時はchromeの検証モード/networkから確認できる "Authorization": 'JWT ' + this.tokens.access, } }) // レスポンスをMembersプロパティに格納 .then(response => (this.Members = response.data)); }, // usernameとpasswordからjwt認証を行いaccess_tokenとrefresh_tokenを取得 login: function(){ // token取得のためのusernameとpasswordセット const data = {username: this.username, password: this.password} // access_token&refresh_tokenを取得 this.axios .post("http://127.0.0.1:8000/api-auth/jwt/",data) // レスポンスを一旦tokensプロパティに格納 .then(response => (this.tokens = response.data)); } } }; </script> Vueの実装はこれで最低限の機能は揃ったのではないだろうか
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでアナグラムを判別するプログラムの作成

アナグラムとは 〘名〙 (anagram) ことばのつづりを換えることによる遊び。単語をばらばらに崩し、全く別の単語を作る。LIVE が EVIL に、TIME が EMIT にの類。 出典: 精選版 日本国語大辞典 例えば、evilとlive, eatとteaなどがアナグラムの例として挙げられます。 与えられた配列からアナグラムをグループ分けする いくつかの単語が格納されたリスト(strs)からアナグラムとなっている単語のグループを返す関数を作ります。 def groupingAnagram(strs): hashMap = {} #ハッシュマップを作成 for s in strs: key =''.join(sorted(s)) # 文字列をアルファベット順にソートする if key not in hashMap.keys(): hashMap[key] = [s] # ハッシュマップにキーを作成 else: hashMap[key].append(s) # 文字列sから作られたキーが既にハッシュマップにあるなら、そこにsを追加 return hashMap strs = ["eat","tea", "evil", "live"] print(groupingAnagram(strs)) 実行 {'aet': ['eat', 'tea'], 'eilv': ['evil', 'live']} アルファベット順に並べられた文字列をキーとして、アナグラムのグループ分けができました。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ツイキャスAPIv2でデータ収集

これは? データ収集用の目的でツイキャスAPI v2を呼ぶ方法、ドキュメントで言う所のアプリケーション単位でのAPI実行。ユーザ単位でのAPI実行(Webアプリ、クライアントアプリ等)は対象外。 アプリケーション登録 開発者向けページ(要ログイン)からCreate New AppしてClientIDとClientSecretをメモしておく。審査は無いので気が楽。 呼び出し例 Get User Infoを呼ぶ。ヘッダーに指定されたものを追加すればOK。 import requests client_id = 'xxxx.yyy...' client_secret = 'zzz' x_headers = {'X-Api-Version': '2.0'} twicas = requests.Session() twicas.auth = requests.auth.HTTPBasicAuth(client_id, client_secret) twicas.headers.update(x_headers) user_id = 'twitcasting_jp' user_info = twicas.get(f'https://apiv2.twitcasting.tv/users/{user_id}').json() print(user_info) 出力 {'supporter_count': 0, 'supporting_count': 0, 'user': {'id': '182224938', 'screen_id': 'twitcasting_jp', 'name': 'ツイキャス公式', 'image': 'http://imagegw02.twitcasting.tv/image3s/pbs.twimg.com/profile_images/1285073394094891008/127zPO4f_bigger.jpg', 'profile': 'ツイキャスの公式アカウントです。ツイキャスに関するお知らせなどを投稿します。お問い合わせは https://t.co/4gCf7XVm7N までお願いします。', 'level': 50, 'last_movie_id': '715753898', 'is_live': False, 'supporter_count': 0, 'supporting_count': 0, 'created': 0}} 公式アカウントはsupporter_countが0固定になっている… サポーターリスト取得例 例外とサポーター増減時の不整合には目をつむる。 import time from datetime import datetime import pandas as pd import matplotlib.pyplot as plt import matplotlib.dates as mdates target_id = 'twitcasting_PR' LIMIT = 20 para = {'offset': 0, 'limit': LIMIT, 'sort': 'new'} supporters = [] user_info = twicas.get(f'https://apiv2.twitcasting.tv/users/{user_id}').json() total_supporter = user_info['supporter_count'] while para['offset'] < total_supporter: # 取得 URL = f'https://apiv2.twitcasting.tv/users/{target_id}/supporters' res = twicas.get(URL,params=para) data = res.json() print('\rget_supporters: @%s %d/%d' % (target_id, para['offset'], total_supporter), end='') # 保存 if data['supporters']: supporters.extend(data['supporters']) else: # リソースが絞られて(?)空で返ってくることもあった気がするがスルー pass # リミット時待機 if res.headers['X-RateLimit-Remaining'] == '0': time_reset = int(res.headers['X-RateLimit-Reset']) time_now = datetime.now().timestamp() time_wait = (time_reset - time_now) + 3 if time_wait > 0: print(f' ...wait for {time_wait} [sec]', end='') time.sleep(time_wait) # パラメータ更新 para['offset'] = para['offset'] + LIMIT # dfに突っ込む df = pd.DataFrame.from_dict(supporters) df['supported'] = pd.to_datetime(df['supported']*10**9) df = df.set_index('supported') df.loc[:,('screen_id')].head() 出力 supported 2022-01-06 17:24:17 hikarin1130 2021-12-31 04:21:26 c:urutoranomamy 2021-12-25 01:27:54 c:aya_na327 2021-12-25 01:22:11 matutanikirito 2021-12-23 12:37:20 c:hitomi1514 Name: screen_id, dtype: object # サポーター増分を4半期ごとにプロット d = df.index.to_series().resample('Q').count() fig = plt.figure() ax = plt.plot(d.index, d) plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%y/%m/%d")) plt.xticks(rotation=30) plt.grid() plt.title(f'New Supporter Num @{target_id}') ドキュメント
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OpenVINOのモデルを使ってみた1

サークルでopenvinoのモデルを使った機械学習の推論をしているので、ちょっとづつ書いてみようと思います。 自分でも前に使ったモデルのことを忘れるので備忘録でもあります。 今回はopenvinoを使えるようにしていきます。 環境 windows10 Setup OpenVINO™ toolkit のダウンロード 最初に Intel® Distribution of OpenVINO™ toolkit をダウンロードします。 こちらのサイトにアクセスし、下の写真のようにしてメールアドレスと国を入力すればダウンロードができます。 .exeファイルを立ち上げてダウンロードを完了させてください。ダウンロード方法がわからないときはopenvino toolkit ダウンロード方法で検索!多分出てきます。 その際以下に関する警告文が出るかもしれませんがあまり気にしないでください。 mnaMicrosoft Visual Studio* with C++ 2019 or 2017 with MSBuild CMake 3.10 or higher 64-bit Python 3.6 – 3.8 64-bit 上二つは今回使いません。Pythonは今からインストールします。 Anacondaのインストール こちらからAnacondaをダウンロードします。 .exeファイルを開いてダウンロードを完了します。わからないときはこちらもAnaconda ダウンロード方法で検索!多分出てきます。 AnacondaのSetup Anaconda Prompt(anaconda3)を起動します。 初回だけ以下のコードを実行してください。 conda init 仮想環境の作成 仮想環境を作っていきます。仮想環境の作り方は conda create -n 仮想環境名 です。今回はopenvinoという仮想環境名で作成しましょう。 conda create -n openvino 仮想環境が作成出来たら仮想環境の有効化をします。 conda activate openvino 仮想環境の有効化ができたらパッケージをインストールします。以下の二つを実行してください。 conda install python=3.6.13 conda install opencv 仮想環境の作成は一旦ここまでにしておきます。 openvinoの実行 "C:\Program Files (x86)\IntelSWTools\openvino\bin\setupvars.bat" を実行します。これは環境変数への一時的な設定を行っています。環境変数への設定をしないとコマンドプロンプトを立ち上げるたびにこれを行わなければならないので、面倒な場合は環境変数への設定を行ってください。 ここまででopenvinoの実行ができるようになりました。 次回からopencvを使ってopenvinoのモデルの推論を行っていきます。 参考 最後に ご不明点、ご指摘ありましたらよろしくお願いします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む