20210511のPythonに関する記事は26件です。

Serverless Frameworkで環境変数を扱いたい

背景 Serverless Frameworkを使っていて秘匿情報どうしようって悩んだときに、公式でサポートされていたのでその設定方法を書く。 環境 Serverless Framework 2.41.0 設定方法 環境変数用のファイルを用意 .envや.env.devなど .gitignoreに追記するのも忘れずに AWS_ACCOUNT_ID=xxxxxxxxxxxx MY_SECRET_KEY=xxxxx serverless.ymlでの設定 useDotenv: true ... iam: role: statements: - Effect: "Allow" Action: - dynamodb:Query - dynamodb:Scan Resource: !Join - ":" - - "arn:aws:dynamodb:ap-northeast-1" - ${env:AWS_ACCOUNT_ID} - "table/myTable" ... functions: my-function: handler: src/my_function.lambda_handler # Lambdaの環境変数に渡したい場合 environment: MY_SECRET_KEY: ${env:MY_SECRET_KEY} useDotenv: trueを指定して ${env:変数名}で使うことができる バージョン3.0.0以降はデフォルトになり、useDotenv: trueの指定をする必要がなくなるらしい Links
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者日記〜じゃんけんプログラムを作る〜

はじめに 初投稿です。 今年入社した新卒エンジニアです。 今は研修中なのですが、今までアルバイトでもプログラムをしてきたことで、研修でつまづくことがほぼ無くなっています。 そんな時間がもったいないと思い、何か空いている時間で作ろうと思い立った時に、じゃんけんプログラムでも作るかとなったのがきっかけです。 めちゃくちゃ簡単なプログラムですが、備忘録的に何かに残しておきたいと思い、ここに投稿することにしました。 他の方に少しでも力になればと思い、かなり詳しく説明して残しておきたいと思います。 目次 次からちゃんと用意します。 環境 MacOS Python 3.9.1 ローカルにファイル(janken.py)を用意して、ソースコードをまるまるコピーします。 ターミナルを開いて以下のどちらかのコマンドを打てばできると思います。 $ python janken.py or $ python3 janken.py ソースコード janken.py import random import sys select = [[1,'グー'], [2, 'チョキ'], [3, 'パー']] for i in range(len(select)): print('%s:%s' %(i+1, select[i][1])) cpu = random.randint(1, 3) try: myslf = int(input('1~3を選択してください:')) except ValueError: print("1~3を入力してください") sys.exit() def prnt(): print('自分:' + select[myslf-1][1]) print('相手:' + select[cpu-1][1]) def win(): print('勝ちです') def lose(): print('負けです') if myslf == cpu: prnt() print('あいこです') else: if myslf == 1: if cpu == 2: prnt() win() else: prnt() lose() elif myslf == 2: if cpu == 1: prnt() lose() else: prnt() win() elif myslf == 3: if cpu == 1: prnt() win() else: prnt() lose() else: print('1~3を入力してください') 結果 $ python janken.py 1:グー 2:チョキ 3:パー 1〜3を選択してください:1 # => 自分:グー 相手:パー 負けです 詳しい解説 import random import sys randomはじゃんけん相手の手を決める際に使います。 sysは、inputでじゃんけんに対応する数字を入力する際に、こちらが望んだ数字以外が入力された時に終了するためにimportしました。 select = [[1,'グー'], [2, 'チョキ'], [3, 'パー']] for i in range(len(select)): print('%s:%s' %(i+1, select[i][1])) 次に、selectを2次元配列として1にグー、2にチョキ、3にパーを入れました。 今改めて見ると、2次元にする必要あったのかなって思いますね。 普通にリストとか辞書型として扱っても何も問題なさそうです。 何日か研修を受けただけですが、成長するものです。 次に、for文でselectの要素ぶん回してprintさせてます。 これも janken.py print(f'{str(i+1)}:{select[i][1]}') とかの方がわかりやすい気もします。 エンジニアの方、どっちの方がよく使うでしょうか?笑 cpu = random.randint(1, 3) try: myslf = int(input('1~3を選択してください:')) except ValueError: print("1~3を入力してください") sys.exit() ここでは、変数cpuに1から3のランダムな整数を代入します。 次に、変数myslfに、ターミナル上から入力した数字をint型に変換して代入を行います。 この時、int型に変換できない文字が入力された場合、エラーメッセージを入力して処理を終了する挙動になってます。 なぜここのメッセージ表記だけダブルクォーテーションが使用されているのでしょうか。 謎ですね。 def prnt(): print('自分:' + select[myslf-1][1]) print('相手:' + select[cpu-1][1]) def win(): print('勝ちです') def lose(): print('負けです') ここでは関数を定義してます。 prnt関数は、自分が出した手と相手が出した手を、selectから出力します。 win関数は勝ち、lose関数は負けを出力します。 if myslf == cpu: prnt() print('あいこです') else: if myslf == 1: if cpu == 2: prnt() win() else: prnt() lose() elif myslf == 2: if cpu == 1: prnt() lose() else: prnt() win() elif myslf == 3: if cpu == 1: prnt() win() else: prnt() lose() else: print('1~3を入力してください') これは、以下のようになります。 if 自分の手と相手の手が同じ場合: あいこ else: if 自分がグー: if 相手がチョキ: 勝ち else(相手がパー): 負け elif 自分がチョキ: if 相手がグー: 負け else(相手がパー): 勝ち elif 自分のパー: if 相手のグー: 勝ち else(相手がチョキ): 負け else: 1〜3が入力されていない場合の処理 以上です。 まとめ こんなめちゃくちゃ簡単なプログラムを、こんな詳しく解説して需要があるのかと思ったのが、記事を書いてみた正直な感想です。 でも自分の備忘録を兼ねているのでいいのです。なんでも。 次は、ヌメロンを作ったので、そのコードを記事にしたいします。 ヌメロンはこんな詳しく解説しないかもしれません。 あと、マークダウン形式で記事を書くのが初めてでした。 見づらい部分があったのは申し訳ないです。 これから、色々な人のqiitaの記事を見て、書き方を学んでもうちょっといい感じにしていきたいと思います。 最後まで読んでくださった方、本当にありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS LambdaとPythonでスラッシュコマンドで入力した文字をそのまま返すSlackBotを作成する

はじめに SlackBotを作る記事はいくつかあったのですが、記事が少し古かったり、複雑な記事が多かったため、シンプルに/hogeで入力した内容をそのまま返すSlackBotを作成します。Slackで匿名質問などをしたいときにも使えます。 Slackの設定 Botの設定 SlackAPIのサイトからCreate New Appで新しいBotを作ります。 その後、OAuth&PermissionsのScopesでBot Token Scopesというところがあるので、chat:writeとchat:write.publicとcommandをscopeに追加します。 その後install to Workspaceを選んでWorkspaceにBotをインストールしてください。 インストールした後に出てくるTokenが大事なので保管しておきましょう。 Botの設定の確認 Botの設定がうまく行っているかテストするにはこちらのサイトのtokenとchannelとtextを埋めてSendしてみてください。うまくいけば指定したチャンネルに入力したテキストが出力されるはずです。 channelはchannel IDを入れる必要があるのですが、こちらはWeb版のSlackチャンネルで出てくるURLに書いてあるので確認してみてください。 AWS側の設定 AWS Lambdaの設定 AWSを開いてAuthor from scratchのBasic infomationを埋めてください。 今回はPython3.6を使いました。 API Gatewayの設定 Lambdaの画面が出てきたら、+Add triggerを選択します。 Trigger configurationの画面のトリガー選択からAPI Gatewayを選択してください。 その後次のように埋めます。 できたあとは、こちらの画面のAPIエンドポイントにクリックしてアクセスしてみてください。"Hello from Lambda!"と出てきたら、API Gatewayの設定はうまくいってます。 AWS Lambdaのコード 次にAWS Lambdaのコードを修正しましょう。 lambda_function.py import urllib import base64 def send_text_response(event, response_text): SLACK_URL = "https://slack.com/api/chat.postMessage" channel_id = "<チャンネルIDを入力>" data = urllib.parse.urlencode( ( ("token", "xoxb-...<Bot User OAuth Tokenを入力>"), ("channel", channel_id), ("text", response_text) ) ) data = data.encode("ascii") request = urllib.request.Request(SLACK_URL, data=data, method="POST") request.add_header( "Content-Type", "application/x-www-form-urlencoded" ) urllib.request.urlopen(request).read() def lambda_handler(event, context): print(f"Received event:\n{event}\nWith context:\n{context}") # Bodyのbase64をデコードしてstr型にする body = base64.b64decode(event.get("body").encode()).decode() # BodyをDict型にparseする qs_d = urllib.parse.parse_qs(body) # parse_qsのdict型のvalueは配列で持っているため0番目を選択する response_text = qs_d["text"][0] send_text_response(event, response_text) # レスポンスをSlackbotが返す(なんでも良い) return "200 OK" 多分Pythonの書き方はもっといい書き方があると思うのですが、こちらでとりあえず動きます。 リクエストのログや出力などはCloudWatchコンソールのLogs > log groupsで見ることができます。 デバッグするときに使ってみてください。 スラッシュコマンドの設定とAWSとの接続 スラッシュコマンドでBotが動くようにするためには、slack apiの左タブのSlash Commandsを選択します。 Commandには実行時に使いたいコマンド、RequestURLにはAPI Gatewayで設定したAPI endpointのURLを入力してください。 以上で完成です。 Slackで/hoge "Botに話させたい文字列"を入力してあげればBotが返してくれます。 参考 こちらをかなり参考にしました。(英語) https://medium.com/glasswall-engineering/how-to-create-a-slack-bot-using-aws-lambda-in-1-hour-1dbc1b6f021c Node.jsで同様のものを作成しています。Requireの設定がうまく行かなかったため断念しました。 https://www.cview.co.jp/osakatech/2020.02.06.eSBhZVxH
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python boot camp by Dr.Angela day13

Debug 以下に示すプログラムのどこが問題なのか指摘していく。 #Describe_Problem def my_function(): for i in range(1, 20): #1から20まで連番で繰り返す if i == 20: #もしiが20だったら print("You got it") #printする my_function() #my_function()を呼び出す。 #出力結果 何も出力されない 原因>> for i in range(4)が0から数えて4回繰り返すことをふまえると、カウント変数は0ベース(0オリジン)でカウントされると分かる。range(開始数, 終了数)という書き方で、range(1, 20)と書くことで添え字(index)が1番(=1)から19番(=19)までの19個のオブジェクトを生成するので、i=20になることはありえないため、printされない。 解決策>> L2→ for i in range(1,21)にする [参考] https://techacademy.jp/magazine/15828 https://naruport.com/blog/2019/8/14/python-tutorial-for-statement-and-range/ #Reproduce_the_Bug from random import randint dice_imgs = ["❶", "❷", "❸", "❹", "❺", "❻"] dice_num = randint(1, 6) print(dice_imgs[dice_num]) #出力結果 (何回かrunするうちにたまにこうなる) Traceback (most recent call last): File "main.py", line 14, in <module> print(dice_imgs[dice_num]) IndexError: list index out of range 原因>> L3でdice_num = randint(1,6)にしているが、dice_imgsには①(index:0)~⑥(index:5)しかなく、仮にdice_num=6が選ばれてしまったらout of rangeになるのでErrorになる。さらに、index:0 (つまり"①")が出力されなくなっている。Errorにならない場合、出力される数としては②~⑥になっている。 解決策>> dice_num = randint(0,5)とする #Play_Computer year = int(input("What's your year of birth?")) if year > 1980 and year < 1994: print("You are a millenial.") elif year > 1994: print("You are a Gen Z.") #出力結果 What's your year of birth?1979 > 原因>> 1980年~1994年より前,1994年以降で場合分けをしているので、1994年丁度に生まれた人と1980年以前に生まれた人に対しては何のアクションも示さず、突然プログラム終了となっている。 解決策>> どちらかの「>,<」に等号(=)をつけるのと、else: でそれ以外の場合にどうするかを追記する #Print_is_Your_Friend 1 pages = 0 2 word_per_page = 0 3 pages = int(input("Number of pages: ")) 4 word_per_page == int(input("Number of words per page: ")) 5 total_words = pages * word_per_page 6 print(total_words) #出力結果 Number of pages: 2 Number of words per page: 89 0 原因>> L4→ 「==」になっていることで、左辺と右辺が等しいかどうかを見て、True or Falseを返している。runする中でこれは実際に何の影響も及ぼしていない(ただ0を入れない限りFalseがreturnされているだけ)が、word_per_pageには入力値が渡されていないのでL2で定義したままのword_per_page=0であるため常にtotal=0になっている。 解決策>> L4「==」を「=」に直す #Use_a_Debugger 1 def mutate(a_list): 2 b_list = [] 3 for item in a_list: 4 new_item = item * 2 5 b_list.append(new_item) 6 print(b_list) mutate([1,2,3,5,8,13]) #出力内容 [26] 原因>> L5→ b_list.appendがfor文の外にあることで、L3&L4で2倍にしたnew_itemが全くb_listに追加されず、最後にnew_itemに格納された26だけがL5のnew_itemに渡されてappendされているため、常に[26]のみが出力されている。本当はa_listの各項目をそれぞれ2倍した値をb_listに格納したいのだろう。 解決策>> L5→ インデントをfor文と揃えて、for文で毎回appendを回すようにする 続きはまた明日...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#01: AWS Cloud9のプレビューが上手く表示されない

Chapters 📕introduction: Web アプリ開発初心者、Django 触る 📕Error  📗 #01: AWS Cloud9のプレビューが上手く表示されない Windows プロジェクトを行った後に Mac でも動かしてみようと考えた。 エラー内容 $ python manage.py startapp hoge と入力してアプリケーションを作成した後に $ python manage.py runserver $IP:$PORT を入力してプレビューを見ようとしたらこんなエラーが。 VFS connection does not exist (直訳: VFSの接続が存在しない) ドユコト? VFS とは仮想ファイルシステム(英: Virtual File System) わからんから調べてみると、治し方を記事にしてくださってる方がいたので参考にさせていただきました! 答えはブラウザにあった...!! 記事を見ていると AWS Cloud9 は複数のブラウザでの動作をサポートしているのですが、SafariやFirefoxを用いると正常に動作しないことがユーザーによって報告されています。予期せぬエラーの原因となるので、もし他のブラウザをお使いの方は、Google Chromeに切り替えて開発を進めてください。 いやいやいやいや、そんなことで治るはずは... そう思いつつ Safari から Google Chrome に切り替えてみると...... うん、できちゃった いやこれで治るとは思わんやん。次からは Google Chrome で開発します。はい...以上です...... 参考記事 💻AWS Cloud9のプレビューが上手く表示されない時の対処法 - プログラミング入門ナビ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Web アプリ開発初心者、Django 触る

Chapters 📕introduction: Web アプリ開発初心者、Django 触る 📕Error  📗 #01: AWS Cloud9のプレビューが上手く表示されない 自己紹介 どうも。松本玲音奈です。 東京都立産業技術高等専門学校 電気電子工学コース 5 年生です。 Django を用いた Web アプリケーション開発で学んだことを Qiita にまとめよう!と考え、今日から Qiita に投稿をしていきたいと思います。 自分自身 Web アプリケーションを制作したことがないヨワヨワ人間ですが、皆さんと一緒に知識をつけて行けたらと考えてます!よろしくお願いします! 参考資料 📖プロフェッショナルWebプログラミング Django - 株式会社エムディエヌコーポレーション
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】logging の設定ファイル

はじめに python を使うおじさんですが、未だにlogging の使い方がこなれていない。 おじさんは モジュールとそれを呼び出すモジュールとで、両方に logger を設定して使いたい 出力の設定はまとめておきたい をしたいのだが、できていません。今回、設定をファイルから読み込むことで、少し進展しそうです。ので、メモ。 Configuration file を読む 設定ファイルは、logging.config.dictConfig() で読むことができます。 動作例 同じディレクトリに設定ファイルを置いてあります。 . ├── logging.yaml └── main.py 設定ファイル まず、設定ファイルの方です。最初は、最後の行にある disable_existing_loggers: False が無くて動きませんでした。大事です。 logging.yaml version: 1 formatters: simple_fmt: format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' handlers: console: class: logging.StreamHandler level: INFO formatter: simple_fmt stream: ext://sys.stdout console_AAA: class: logging.StreamHandler level: DEBUG formatter: simple_fmt stream: ext://sys.stdout loggers: AAA: level: DEBUG handlers: [console_AAA] propagate: no root: level: DEBUG handlers: [console] disable_existing_loggers: False 当然ながら、yamlファイルがきちんと書かれちないとload するときにエラーが出ます。class AAA では debug level で出力するようにしています。dictConfig() に渡される辞書については、下記に書かれています。version, formatters, handlers などのキーが必要です。 動作例 以下のプログラムを動かしてみます。 main.py from logging import getLogger,config import yaml logger = getLogger(__name__) class AAA(object): def __init__(self): self.logger = getLogger(self.__class__.__name__) self.logger.debug("class name is {}".format(self.__class__.__name__)) def do_something(self): self.logger.info("doing something") def main(): aaa = AAA() aaa.do_something() logger.debug('debug message') logger.info('info message') logger.warning('warn message') logger.error('error message') logger.critical('critical message') if __name__ == "__main__": config.dictConfig( yaml.load(open("logging1.yaml").read(), Loader=yaml.SafeLoader) ) main() 動かすと、class AAA にある logger では debug levelで出力されますが、main() なかでは info レベルです。 $ python main_yaml.py 2021-05-11 21:41:16,663 - AAA - DEBUG - class name is AAA 2021-05-11 21:41:16,663 - AAA - INFO - doing something 2021-05-11 21:41:16,664 - __main__ - INFO - info message 2021-05-11 21:41:16,664 - __main__ - WARNING - warn message 2021-05-11 21:41:16,664 - __main__ - ERROR - error message 2021-05-11 21:41:16,664 - __main__ - CRITICAL - critical message まとめ とりあえず動いた。もう少し調べてもよいかなと思うのは、 Formatter の使いこなし。。。 設定ファイルの書き方。Loggerのドキュメントに書かれていますが、json や独自形式?の .conf でも読むことができるようです。 https://docs.python.org/ja/3/howto/logging.html 別ファイルにあるlogger を出力する(本当はこれを確認したい) (2021/05/11)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【誰でもわかる正規表現!】 やってみたら超簡単だった

はじめに  みなさん、正規表現が便利なのは知っていますか? Qiitaをご覧になっている方であれば、多くの人が YES と答えているでしょう。 では、正規表現使ってますか? 今度は、... という微妙なところでしょうか。  それもそのはずで、正規表現は理解のとっかかりが掴みづらい。 見慣れない記号が並んでいて、意味を把握するのが難しい。 その上、解説しているサイトも難解なものが多い。 (平易に説明しているものが少ない。)  このような状況の中で正規表現を使いこなせるようになるには、 ...ズバリ 「用途を限定する」 ことでしょうか。 特定の用途なら割と簡単なものがあるので、1個でも組めれば、 それをきっかけにしてレパートリーが増えていくものかと。 そしていつの間にか、全体像を自然に把握しているようになります。 早い話がボトムアップ式で覚えていくのがベター!! と、いうわけでこの記事では「正規表現で整数(半角)にマッチさせる」 ことを紹介したいと思います。  とりあえずこの用途だけなら簡単にマスター可能でしょう。 対象となる読者  とりあえず、正規表現を使ってみたい人を対象としています。 簡単に説明してほしい 初心者で正規表現の予備知識が無い人 何らかのプログラミング言語を使っているが、正規表現は避けている テキスト処理で整数について処理したい オライリーの本は読む気がしない こういった方に向けて説明します。 参考にしたサイト  割と分かりやすく解説しているもの。 サルにもわかる正規表現入門 ゆびノート みやびのどっとぴーわい 動作環境  Python3系でいきましょう。 それでは以下から本編です。 やりたい事  整数(半角)にマッチさせる。 但し以下の条件を満たすのもとする。 条件その1:      0~59までの範囲の数字 条件その2:      1桁の場合はゼロ埋めしない(02などは除外する) メタキャラクタ  正規表現では「メタキャラクタ」という記号を用いる事で整数を表現できる。 \d や [0-9] などである。 しかし \d はPythonだと半角整数だけでなく、全角数字など余計なもの(ゆびノートさんによればジャワ数字もヒットしている)も含まれる。  そこで、\d ではなく [ ] のほうを使う。 この記号は [ ] の中に指定した文字のどれかにヒットする。 0 から 9 までの数字を指定したい場合には [0123456789] でも可能だが、サルにもわかる正規表現入門さんによると、面倒なので[0-9]でも大丈夫とのこと。  これで整数の表し方がわかったので、2桁の整数なら[0-9]を2つ並べて[0-9][0-9]とすればよさそうだ。 だがこれだと、整数が1桁の場合0埋め(03など)されてしまう。 ... これに対処するには 1桁の場合と2桁の場合で別々に処理する。 1桁の場合  条件は0~59までの数字なので、1桁のときは 0~9 までの数字を使う。 つまり、[0-9] でOKだ。 2桁の場合  2桁の数字で条件を満たすものは 10~59 だ。 そうすると2桁目は、[1-5] で1桁目は [0-9] である。 二つ並べて[1-5][0-9]とする。  後は1桁の場合か2桁の場合かの、どちらかを表すメタキャラクタである | を使う。 [0-9]|[1-5][0-9] これで完成と言いたいところだが、まだ問題が残っている。 それは、「数字の区切り」である。 区切り文字  例えば 15 の場合なら2桁の数字としてマッチさせたい。 しかし残酷にもこれは 15 のうち 1 の部分が1桁の場合である[0-9]のほう( | の左側)にマッチしてしまう。  これを回避するには、区切りを示すメタキャラクタである\bも付け加える。 \b[0-9]\b|\b[1-5][0-9]\b これで 15 のような場合は2桁の数字としてマッチさせることが出来るようになる。 ようやく正規表現が完成したので次は、Pythonで実装してみる。 Python! 対話型より #pythonから正規表現を使うには re をインポートする >>> import re #作成した正規表現を compile() の引数に渡す >>> pat = re.compile(r"\b[0-9]\b|\b[1-5][0-9]\b") #判定対象の文字列 >>> score = "15 54 9 60 123 03 30" #findall()はマッチがあるとリストを返す。(マッチしたもの全て) >>> pat.findall(score) ['15', '54', '9', '30'] まとめ この記事では、次のような事を理解しました。 正規表現ではメタ文字を使う。 メタ文字の [0-9] で整数を表せる。 A|B は AかBかを示す。 \b で区切りをつける。  今回は「整数」の取り扱いに的を絞りました。 簡単なものから実際に手を動かしてみる事で、徐々に理解が深まるのが実感できると思います。 正規表現はこの他にも多くの種類がありますが、本記事を契機としてレパートリーを増やしていきましょう!  なお、この記事を読んで簡単だと感じた頭がいい人は是非とも、オライリーの本や難解なサイトにも挑戦して下さい。 今回はこの辺で。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python 符号付き整数を取り扱う

Python 符号付き整数(bit数指定)を数値に変換する Arduinoから直接センサーデータを取得するとき、センサーデータが符号付きの数値(bit数指定)で戻ってきます。 組み込み用の言語であれば符号付き整数を簡単に取り扱う事ができますがPyhotnで計算する場合はそうもいきません。 そのため、符号付き数値(bit数指定)をPythonで扱える数値に変換する必要があります。 16bitの符号付き16進数の場合、下記のような変換結果を期待されます。 0x0001 -> 1 0x0000 -> 0 0xffff -> -1 符号付き数値の具体的なフォーマットは左側bitが符号、そのほかのbitは補数となります。 すなわち、0xffffをビット反転してあげます。 0xffff ^ 0xffff = 0x0000 = 0 その数値に-1してあげれば目的の数値となります 0xffff ^ 0xffff - 1 = 0x0000 -1 = -1 つまり先頭bitの符号を確認し、ビット反転すれば元の数値となる事がわかります。 Pythonのコードで書くとこのようになります。 signed_hex = 0xffff signed_int = (int(signed_hex^0xffff) * -1)-1 if (signed_hex & 0x8000) else int(signed_hex) print(signed_int) 実行結果 -1 ただ、このコードは16bitの符号付き整数のみ対応するため、汎用性を上げるためにbit数可変可能な下記のような関数を作ってみました。 def signed_hex2int( signed_hex, digit ): signed = 0x01 << (digit-1) mask = 0x00 for num in range(digit): mask = mask | (0x01<<num) signed_int = (int(signed_hex^mask)*-1)-1 if (signed_hex & signed) else int(signed_hex) return signed_int #test print (signed_hex2int( 0xf0, 8 )) #8bit print (signed_hex2int( 0xff00, 16 )) #16bit print (signed_hex2int( 0xfff000, 24 )) #24bit print (signed_hex2int( 0xffff0000, 32 )) #32bit print (signed_hex2int( 0x0f, 8 )) #8bit print (signed_hex2int( 0x00ff, 16 )) #16bit print (signed_hex2int( 0x000fff, 24 )) #24bit print (signed_hex2int( 0x000ffff, 32 )) #32bit 実行結果 -16 -256 -4096 -65536 15 255 4095 65535 挙動が怪しいところがありますので機種依存やPythonのバージョン依存があるかもしれません。 問題点あれば教えて頂ければ助かります。 また、関数名や変数名のつけ方に悩みました。いい命名方法があれば合わせてご意見お待ちしております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでwavファイルの再生時刻を取得する

背景 wavファイルの再生時間を取得したい 方法 import wave with wave.open('path/to/local/sample.wav', mode='rb') as wf: print('time(second): ', float(wf.getnframes() / wf.getframerate())) 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AtCoder Problems Hard No.71~80解説

Hard No.71 : C - Linear Approximation C - Linear Approximation 発想 とりあえず$A_i$を A_i' = A_i-i に変換します。このとき \sum|A_i'-b_{ \ }| を最小にする$b$は何か考えます。この$b$は中央値になります。$b$が中央値であれば、$b$を少し移動させたときに、増加する$|A_i'-b_{ \ }|$と減少する$|A_i'-b_{ \ }|$の個数が等しくなります。つまり微分が0になるので、$b$が中央値であることがわかります。 実装 データ数が偶数であるときは中央値はlow, highの2種類出てくるので、そのうち小さいほうを採用します。 import statistics N = int(input()) A = list(map(int, input().split())) Am = [] for i in range(1, N+1): Am.append(A[i-1]-i) Am = sorted(Am) mid_low = statistics.median_low(Am) mid_high = statistics.median_high(Am) ans_low = 0 for i in range(N): ans_low += abs(Am[i]-mid_low) ans_high = 0 for i in range(N): ans_high += abs(Am[i]-mid_high) ans = min(ans_low, ans_high) print(ans) Hard No.72 : C - 4/N C - 4/N 発想 何も考えずに考えると、$h, n, w$の3種類の文字がそれぞれ1~3500まで動くので、$3500^3$でTLEします。条件式が与えられたらその条件式を使って文字を1つ消去できるので、例えば$h, n$を動かしたとき$w$が整数になるか判定すれば良いです。 実装 w = \frac{Nhn}{4nh-N(h+n)} となるので分母、分子をそれぞれ計算します。0除算に注意します。 N = int(input()) for h in range(1, 3501): for n in range(1, 3501): w_above = N*h*n w_below = 4*h*n - N*(h+n) if w_below != 0: w = w_above // w_below if w > 0 and w_above % w_below == 0: print(h, n, w) exit() Hard No.73 : C - Strange Bank C - Strange Bank 発想 できるだけ大きい金額でお金を引き出したほうが、引き出す回数は少なくて済みます。引き出す回数は残りの金額によって刻々と変化しますので、動的計画法を使います。 実装 引き出す金額のリストを予め作っておきます。 $dp[i]$の定義は以下のようにします。 dp[i] : 残りの金額がiの時にお金を引き出す回数の最小値 N = int(input()) Six = [] six = 6 while six < 100000: Six.append(six) six *= 6 Nine = [] nine = 9 while nine < 100000: Nine.append(nine) nine *= 9 #1回で引き出す金額のリスト A = [1] + Six + Nine A = sorted(A, reverse=True) dp = [10**9+1]*(N+1) dp[0] = 0 for i in range(1, N+1): #1回で引き出す金額が大きい方から調べる for a in A: if i - a >= 0: dp[i] = min(dp[i], dp[i-a] + 1) print(dp[N]) Hard No.74 : D - Harlequin D - Harlequin 発想 思いついたら勝ち!思いつかなかったら負け!敗者に人権はない!って感じがしてつらい気持ちになります。とりあえずリンゴの数が少ない状況で実験してみます。 (1)リンゴが2つ(色は1, 1) 1つ自分がとった後、ルンルンが最後の1個を取るので負けです。 (2)リンゴが3つ(色は1, 1, 2) 自分が2のリンゴをとった後、(1)の状況になるので私の勝ちです。 以上より奇数個のリンゴがあれば、それらを1つずつ最初に取ることで私の勝ち、すべて偶数個であればルンルンの勝ちになります。 実装 N = int(input()) A = [] for i in range(N): A.append(int(input())) for i in range(N): if A[i] % 2 == 1: print('first') exit() print('second') Hard No.75 : D - Face Produces Unhappiness D - Face Produces Unhappiness 発想 $l,r$はどのように選べばよいか。例えば'LRRL'という並びがあったときは2つの'L'に挟まれた'RR'をまとめて回転させるように選べばよいです。「挟まれている文字列の個数」は'LR'と'RL'の個数を数えればよいです。 実装 N, K = map(int,input().split()) S = input() C = S.count('RL')+S.count('LR') #幸福な人数の最大値はN-1 #操作回数が余るときはC-2*K < 0となるので、その時は0にする print(N-1 - max(C - K*2, 0)) Hard No.76. : C - たくさんの数式 C - たくさんの数式 発想 $|S|$の最大値が10なので'+'を入れる箇所を全探索できそうです。 実装 $S$の末尾を最後に追加するのを忘れそうです。出てきた数式の計算はeval()を使います。文字列をそのまま数式として計算し、結果を返してくれます。 S = list(input()) N = len(S) - 1 ans = 0 #全探索 for i in range(2**N): #Sに'+'が追加されたS_plus S_plus = [] for j in range(N): S_plus.append(S[j]) if i >> j & 1 == 1: S_plus.append('+') #Sの末尾の文字を追加 S_plus.append(S[-1]) S_plus = ''.join(S_plus) ans += eval(S_plus) print(ans) Hard No.77 : A - 01 Matrix A - 01 Matrix 発想 とりあえず小さい状況で実験します。例えば3×3の行列で$A=1,B=1$の時を考えます。$A=1$だけ意識して行列を作ると、 \begin{bmatrix} 0 & 1 & 1\\ 0 & 1 & 1\\ 0 & 1 & 1 \end{bmatrix} という形になります。この形では$B=3$になってしまいます。$B=1$にするためにはどうすればよいでしょうか。例えば1行目をすべて0に変えた行列は \begin{bmatrix} 0 & 0 & 0\\ 0 & 1 & 1\\ 0 & 1 & 1 \end{bmatrix} になります。これだと2,3行目と2,3列目は$A=1,B=1$を満たしますが、1行目と1列目が満たすことができません。そこで$(1,1)$を変更すると、 \begin{bmatrix} 1 & 0 & 0\\ 0 & 1 & 1\\ 0 & 1 & 1 \end{bmatrix} となり、これですべての行、列で$A=1,B=1$を満たします。このように行列を4か所に分けた形にすれば良さそうです。 それらは (1)上から$B$個、左から$A$個は1 (2)下から$H-B$個、右から$W-A$個は1 (3)それ以外は0 とすれば良いです。 実装 H, W, A, B = map(int, input().split()) Ans = [] for i in range(H): Ans.append(['0']*W) for i in range(H): for j in range(W): if i < B and j > A-1: Ans[i][j] = '1' if i > B-1 and j < A: Ans[i][j] = '1' for i in range(H): print(''.join(Ans[i])) Hard No.78 : C - K-th Substring C - K-th Substring 発想 $K$は5以下であり、考えるべきsubstringのうち最大の文字数は$K$以下です。substringの文字数と$S$の位置で2重ループを組めばよいです。 実装 重複した文字は消したいのでset()を使います。 S = input() K = int(input()) Ans = set() # i:文字数 for i in range(1, K+1): # j:スタート位置 for j in range(len(S)-i+1): tmp = S[j:j+i] Ans.add(tmp) Ans = sorted(Ans) print(Ans[K-1]) Hard No.79 : D - Handstand D - Handstand 発想 変更を$K$回以下加えて'1'が連続で並ぶ長さの最大値を求めます。その連続部分列の左端は必ず'0'から'1'に切り替わったところの'1'です。この位置を$j$とします。次に連続部分列の右端を考えます。右端は必ず'1'から'0'に切り替わったところの'1'です。なおかつ'1'から'0'に切り替わる回数を数えて$K$回を初めて超えた場所です。この位置を$i$とします。連続部分列の長さは$i-j+1$になります。 実装 N, K = map(int, input().split()) S = list(input()) j = 0 ans = 0 cnt = 0 for i in range(N): #'10'という並びを探す if S[i] == '0': if i == 0 or S[i-1] == '1': cnt += 1 #'10'という並びの個数(=逆立ちに変更する箇所)がKを超えたら if cnt > K: while S[j] == '1': j += 1 while S[j] == '0': j += 1 cnt -= 1 ans = max(ans, i-j+1) print(ans) Hard No.80 : D - Remainder Reminder D - Remainder Reminder 発想 何も考えずにすべての組み合わせをとると$O(N^2)$でTLEします。$b$を1から$N$まで動かして、その時に$a$の場合の数が$O(1)$とか$O(logN)$で求められれば勝ちです。そのための条件が「$a$を$b$で割ったあまりが$K$以上」になります。 実装 $K=0$の場合は$N^2$通りになります。これは例外で処理しておきます。 $N$を$b$で割った商を$p$、$N$を$b$で割ったあまりを$r$とします。$b$を固定したとき、$a$の場合の数は$p×(b-K)+(r-K+1)$になりますが、$b-K<0,{ \ }r-K+1<0$になる場合があるので注意が必要です。 N, K = map(int, input().split()) if K == 0: print(N**2) else: ans = 0 for b in range(1, N+1): p = N // b r = N % b ans += p*max(0, b-K) + max(0, r-K+1) print(ans)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ImageDataGeneratorで画像を水増しする方法

はじめに  DeepLearningを活用するにあたり,ImageDataGeneratorクラスを利用して画像の水増しを行い、フォルダごとのラベル付けから水増しまでを行う方法をメモしました。また、画像データをnpzファイルにバイナリデータとして保存する方法も示しました。 ファイル・フォルダの位置関係  今回は、ネットからリンゴ、バナナ、グレープの画像をそれぞれ3枚適当に持ってきて、それらをフォルダで分類しました。 $ tree . |-- img_increase.ipynb `-- img_original |-- apple | |-- apple1.jpg | |-- apple2.jpg | `-- apple3.jpg |-- banana | |-- banana1.jpg | |-- banana2.jpg | `-- banana3.jpg `-- grape |-- grape1.jpg |-- grape2.jpg `-- grape3.jpg インポートするライブラリ import numpy as np import glob import random import cv2 import keras import matplotlib.pyplot as plt from keras.preprocessing.image import ImageDataGenerator %matplotlib inline 画像の読み込み  以下に、上記の原画像を読み込み、フォルダごとにラベル付けするプログラムを示しました。このプログラムを実行すると、原画像データ及びそれに対するラベルデータがnpzファイルに保存されます。 # npzファイル:Numpy配列 ndarray を保存するバイナリファイル outfile="fruits_org_data.npz"#保存ファイル名 max_photo=3 #各フォルダ内の枚数 photo_size=80 #画素数(80*80) x=[]#画像データ y=[]#ラベルデータ #フォルダ名 categories = ["apple","banana","grape"] #path以下の画像を読み込む def glob_files(path,label): files=glob.glob(path+"/*.jpg") #フォルダ内の画像ファイル名をリストで返す random.shuffle(files) #フォルダごとに画像をシャッフル #各ファイルを処理 num=0 for f in files: if num >=max_photo:break #フォルダ内の写真を全て読み込んだら終了 num+=1 #画像ファイルを読む img=cv2.imread(f) img=cv2.resize(img, (photo_size,photo_size )) img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB) img=np.asarray(img) x.append(img) y.append(label) print(num) def main(): for cat, label in zip(categories, range(len(categories))): #各画像のフォルダーを読む(./は現在のフォルダ) glob_files("./img_original/" + cat, label) #x(学習データ),y(ラベル)の対応は維持したまま全画像データをシャッフル for l in [x, y]: np.random.seed(1) np.random.shuffle(l) #ファイルへ保存 np.savez(outfile,x=x,y=y)#xとyがnumpyのリストとして与えられる print("保存しました:"+outfile,len(x)) main()  プログラムの説明はコメント文で行っているので,細かいことは省略しますが,ここでは13行目のglob関数について説明します. glob関数の実行例  試しに、以下のプログラムを実行すると print(glob.glob("./img_original/apple/*.jpg")) このような結果が出力されます。 ['./img_original/apple\\apple1.jpg', './img_original/apple\\apple2.jpg', './img_original/apple\\apple3.jpg'] このように、glob関数は、指定したファイルまでのpathをリストとして返すような関数となっています。 ImageDataGeneratorによる画像水増し  原画像の読み込みができたので、次は水増しを行っていきます。以下のコードを実行することで、90枚(=3×3×10)に増えます。 # ImageDataGeneratorクラスのオブジェクト生成 datagen = ImageDataGenerator( rotation_range=45, #±45°でランダムに回転 vertical_flip=True, #垂直方向にランダムで反転 horizontal_flip=True) #水平方向にランダムで反転 def images_gen(x_list,y_list): x_list_add=[] y_list_add=[] for x ,y in zip(x_list,y_list): x = x.reshape((1,) + x.shape) batch_list=[] i = 0 # flowメソッド:numpyデータとラベルの配列を受け取り、拡張/正規化したデータのバッチを生成 for batch in datagen.flow(x, batch_size=1): batch=batch.astype(np.uint8)#データ型を揃える batch=batch.reshape((photo_size, photo_size, 3)) x_list_add.append(batch) y_list_add.append(y) i += 1 if i > 9:#1枚から10枚作る(計90=3*3*10) break x_np_add=np.array(x_list_add) y_np_add=np.array(y_list_add) #x(学習データ),y(ラベル)の対応は維持したままシャッフル for l in [x_np_add, y_np_add]: np.random.seed(1) np.random.shuffle(l) return x_np_add,y_np_add #画像データを読み込み images=np.load("./fruits_org_data.npz") x=images["x"] #画像データ y=images["y"] #ラベルデータ #読み込んだデータを三次元配列に変換 x=x.reshape(-1,photo_size,photo_size,3) #(サンプル数、行数、列数、RGB)の形に #水増し x_add,y_add=images_gen(x,y)  ImageDataGeneratorクラスの詳しい内容は、こちらに載っています。⇒KerasDocumentation 上記のプログラムの3~5行目で、±45°の回転、垂直・水平方向の反転を行うことを指定しています。他にも、画像のシフト、ズーム、明暗を変える引数が存在します。 水増しされているか確認 plt.imshow(x_add[60]) print("Label: {}".format(y_add[60]))  これを実行してみると、次のようにきちんと表示されたことから、水増しできていることが分かりますね。 最後に注意 ・画像の水増しは、原画像をtrainとtestデータに分けてから行う! ・上記のプログラムではyのLabelデータは0~2のlistとなっているので、one-hot エンコーディングを忘れずに! 終わりに  このように、ImageDataGeneratorクラスを用いれば、簡単に水増しを行うことができます。また、ImageDataGeneratorクラスに含まれていないノイズなどは、クラスの継承により、ノイズをつける関数を加えることで解決できます。 参考文献 Keras の ImageDataGenerator を使って学習画像を増やす Keras Documentation ラーメンを本気で分類してみた
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Feature Scaling(標準化・正規化)とは

はじめに  機械学習では,変数の「標準化」というものが出てきますが,この標準化についてよく理解できなかったので調べてみました.また,実際に標準化がある時とない時で比較してみました. Feature Scalingについて  Feature Scaling とは,特徴量の取りうる値のスケールを変えることであり,その種類として「標準化」と「正規化」があります.データセットの特徴量間でスケールが異なることは多くあります.例えば,体重と慎重,家の価格と部屋数ではその単位と値の範囲が異なります.そのような異なるスケールのデータセットを学習させると,うまく学習できなくなるので,学習前の前処理で特徴量間のスケールを揃える必要があります. 標準化とは  標準化とは,特徴量を標準正規分布(平均を0,分散を1)のスケーリングに合わせる処理のことです.変換の式は,次式となります. $$x_{std, i} = \frac{x_i-\mu}{\sigma}$$ $$(x_i: 元データ, \mu:平均, \sigma:標準偏差)$$ 正規化とは  正規化とは,特徴量の値の範囲を一定の範囲に収める変換で,その範囲は主に[0, 1],または [-1, 1]となります.範囲を[0, 1]にするときの変換式は,次式となります. $$x_{norm, i}=\frac{x_i - x_{min}}{x_{max}-x_{min}}$$ $$(x_i:元データ, x_{min}:最小値, x_{max}:最大値)$$ 標準化と正規化の使い分け  正規化は外れ値の影響が大きいので,基本は標準化を使います.以下が使い分けの例です. 標準化 ロジスティック回帰、SVM、NNなど勾配法を用いたモデル kNN, k-meansなどの距離を用いるモデル PCA, LDA(潜在的ディリクレ配分法), kernel PCA などのfeature extractionの手法 正規化 画像処理における RGBの強さ [0, 255] sigmoid, tanhなどの活性化関数を用いる,NNのいくつかのモデル 使わない時 決定木,ランダムフォレスト 標準化した時としてない時の比較  最後に,irisデータのSVMによる分類において,標準化した時としてない時の正答率を比較していきます. 実行したプログラム import numpy as np import pandas as pd import matplotlib.pyplot as plt % matplotlib inline # irisデータセットの読み込み from sklearn.datasets import load_iris iris = load_iris() X = iris.data[50:150, [2,3]] y = iris.target[50:150] # データの標準化 from sklearn.preprocessing import StandardScaler scaler = StandardScaler() scaler.fit(X) X_std = scaler.transform(X) from sklearn.model_selection import train_test_split X_1_train, X_1_test, y_1_train, y_1_test = train_test_split(X, y, test_size=0.3, random_state=0) X_2_train, X_2_test, y_2_train, y_2_test = train_test_split(X_std, y, test_size=0.3, random_state=0) # 線形SVCの学習 from sklearn.svm import SVC svc_1_slack = SVC(kernel='linear', C=1.0) svc_2_slack = SVC(kernel='linear', C=1.0) svc_1_slack.fit(X_1_train, y_1_train) svc_2_slack.fit(X_2_train, y_2_train) # 正答率を出力 print('標準化なしの場合: %f' % svc_slack.score(X_1_test, y_1_test)) print('標準化ありの場合: %f' % svc_slack.score(X_2_test, y_2_test)) 実行結果 標準化なしの場合: 0.500000 標準化ありの場合: 0.966667 以上のように,特徴量を標準化した時の方が,実際に正答率が高くなることを確認することができました. 参考文献 「Feature Scalingはなぜ必要か」 「標準化と正規化の違い」
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python 3.8.5でpyatsのインストールにエラーになる

概要 インストールメモです。 Python 3.8.5でpip install pyats[full」を実行したら次のようなエラーになりました。 ERROR: Could not find a version that satisfies the requirement pyats[full] (from versions: none) ERROR: No matching distribution found for pyats[full] 解決 問題はpipのバージョンが古いからでした。 手順: 既存のpipバージョンを確認pip --versionpip 20.0.2であることを確認 pipをアップグレードpip install -U pip pipのバージョンを再確認pip --versionpip 21.1.1であることを確認 pyatsをインストールpip install pyats[full]無事にインストール 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像処理初心者がOpenCVを使ってみた(1)

この記事は、画像処理エンジニア検定を受験するにあたり、OpenCVを実際に利用して画像処理について学ぶことを目的としています。勉強した内容をメモします。 ※画像処理でできることをメインに書いていくので、Pythonや使用する各関数の説明などは省略しています。 使用環境 windows10 Anaconda: 1.7.2 python: 3.7.9 Jupyter Notebook 前提 画像処理とは 画像はパターンを表す数値(画素値)の集まり 0から255で表現する RGBの3色から成る。 例 (10, 20, 30) ※順番がR(Red)G(Green)B(Blue)ではないことに注意 B G R 10 20 30 画像を解析しやすくする、情報を抽出するための処理 OpenCVとは オープンソースの画像処理ライブラリ マルチプラットフォーム:windows, mac, linux 多言語対応:C++, Pyhton, Java... 画像処理以外にも機械学習などのコンテンツも含む 目次 環境構築 画像の表示・出力 ウィンドウの調整 色変換 γ(ガンマ)変換で画像を明るくする、暗くする 表示した画像に図形を描写する 画像の二値化 透視変換 畳み込み 環境構築 AnacondaのインストールとOpenCVのインストール Anacondaはすでにインストール済みだったので、上記のブログを参考にOpenCVをインストールしました。 画像の表示・出力 import cv2 #画像を保存するためにimport import os img = cv2.imread("data/src/score.jpg") #画像の表示 cv2.imshow("img",img) #何かしらのキーが入力されるとウィンドウが閉じられる cv2.waitKey(0) cv2.destroyAllWindows() #outputディレクトリを作成 os.mkdir("./output") #読み込んだimgをtest.jpgとして保存 cv2.imwrite("output/test.jpg", img) 出力された画像は以下。左上のtitleが「img」となっている。 ウィンドウの調整 前項の画像の表示で表示させたウィンドウは固定サイズでした。 以下のようにコードを書くと、表示されるウィンドウのサイズを調整することができます。 import cv2 img = cv2.imread("data/src/favorite.jpg") #第二引数にcv2.WINDOW_NORMALを指定するとウィンドウの調整ができるようになる cv2.namedWindow("window", cv2.WINDOW_NORMAL) cv2.imshow("window", img) cv2.waitKey(0) cv2.destroyAllWindows() 表示される画像は以下のような感じです。 ウィンドウを縮小させるとこんな感じになります。 また、表示させる画像のリサイズを行う方法もあります。 import cv2 img = cv2.imread("data/src/score.jpg") cv2.imshow("img",img) cv2.waitKey(0) cv2.destroyAllWindows() img.shape #出力(3672, 4896, 3) #高さ、幅 size = (300, 200) img_resize = cv2.resize(img,size) img_resize.shape #出力(200, 300, 3) cv2.imshow("resize",img_resize) cv2.waitKey(0) cv2.destroyAllWindows() 表示される画像は、以下のようになりました。 上記とは別の方法で画像をリサイズする方法もあります。 img_area = cv2.resize(img, size, interpolation = cv2.INTER_AREA) img_linear = cv2.resize(img, size, interpolation = cv2.INTER_LINEAR) cv2.imshow("area", img_area) cv2.imshow("linear", img_linear) cv2.waitKey(0) cv2.destroyAllWindows() 出力結果は以下のようになります。 2つの画像を比べると、titleが「linear」となっている画像の方がギザギザ?になっていることが分かります。 色変換 カラー画像を白黒画像にする方法を以下のコードで書きます。 import cv2 img = cv2.imread("data/src/favorite.jpg") #用意した画像が大きいためリサイズして縮小させてます size = (300, 200) img = cv2.resize(img,size) img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img_gray.shape #出力は解像度の情報だけ残る -> (200, 300) cv2.imshow("img", img) cv2.imshow("gray", img_gray) cv2.waitKey(0) cv2.destroyAllWindows() 上記コードを実行した結果、以下のように画像が表示されました。 また、以下の方法で画像を白黒にすることもできます。 import cv2 # imread()の第二引数に0を設定することで白黒画像として読み込まれる。 img_gray2 = cv2.imread("data/src/favorite.jpg", 0) #用意した画像が大きいためリサイズして縮小させてます size = (300, 200) img_gray2 = cv2.resize(img_gray2,size) #白黒画像になっているか確認するコード cv2.imshow("gray", img_gray) cv2.waitKey(0) cv2.destroyAllWindows() γ(ガンマ)変換で画像を明るくする、暗くする こちらの記事にγ変換についてまとまっているので、勉強させていただきました。 実際にγ変換を用いて画像の明るさを変換するコードを実行しました。 import cv2 import numpy as np #元画像より明るい画像を表示させます gamma = 2.0 img = cv2.imread("data/src/favorite.jpg") #元画像が大きいためリサイズしています size = (300, 200) img = cv2.resize(img,size) gamma_cvt = np.zeros((256,1), dtype = np.uint8) for i in range(256): gamma_cvt[i][0] = 255 * (float(i) / 255) ** (1.0 / gamma) img_gamma = cv2.LUT(img, gamma_cvt) cv2.imshow("img",img) cv2.imshow("gamma",img_gamma) cv2.waitKey(0) cv2.destroyAllWindows() 実行結果は以下のようになりました。画像が明るくなっているのが分かると思います。 また、変数gammaを1より小さくすることで、元画像より暗い画像へ変換することもできます。 import cv2 import numpy as np #元画像より暗い画像を表示させます gamma = 0.5 #元画像が大きいためリサイズしています img = cv2.imread("data/src/favorite.jpg") size = (300, 200) img = cv2.resize(img,size) gamma_cvt = np.zeros((256,1), dtype = np.uint8) for i in range(256): gamma_cvt[i][0] = 255 * (float(i) / 255) ** (1.0 / gamma) img_gamma = cv2.LUT(img, gamma_cvt) cv2.imshow("img",img) cv2.imshow("gamma",img_gamma) cv2.waitKey(0) cv2.destroyAllWindows() 上記のコードを実行した結果がこちらです。元画像よりも暗い画像に変換されていることが分かります。 表示した画像に図形を描写する 読み込んだ画像に対して、図形などを表示させることができます。 以下にサンプルを作成しました。座標軸などは任意の値を設定しています。 import cv2 import numpy as np #白い画像を生成 img = np.ones((1000, 1000, 3)) * 255 #青い直線を描写 cv2.line(img, (0, 0), (150,300),(255, 0, 0,), 2) #緑四角形を描写 cv2.rectangle(img, (100, 100), (200, 200),(0, 255, 0), 4) #赤い円を描写 cv2.circle(img, (250,100), 50, (0, 0, 255), -1) #青い楕円を描写 cv2.ellipse(img,(250, 250), (100, 50), 20, 0, 360, (255, 0, 0), 1) #水色の多角形(今回は三角形)を描写 pts = np.array([[100, 30], [200, 30], [200, 80], [100, 30]]) cv2.polylines(img, [pts], False,(100, 255, 0), 3) #テキストを描写 font = cv2.FONT_HERSHEY_SIMPLEX cv2.putText(img, "text!!!", (400, 400), font, 1, (0, 255, 0),3, cv2.LINE_AA) cv2.imshow("img", img) cv2.waitKey(0) cv2.destroyAllWindows() 上記コードを実行した結果がこちらです。白い画像にそれぞれ図形が描写されていることが分かります。 画像の二値化 画像の二値化とは、画像を白と黒の2色に変換する処理のことで、ある値(閾値)以上の画素値を白、ある値未満の画素値を黒に変換することを指します。 以下のコードで画像の二値化をしてみます。 import cv2 import matplotlib.pyplot as plt %matplotlib inline img = cv2.imread("data/src/favorite.jpg", 0) #画像サイズが大きいためリサイズしています size = (300, 200) img = cv2.resize(img,size) #閾値を50に設定 threshold = 50 #retは閾値が返ってくる ret, img_th = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY) cv2.imshow("img_th", img_th) cv2.waitKey(0) cv2.destroyAllWindows() 上記のコードを実行した結果が以下です。 また、以下のように適応に閾値を設定して、二値化することも可能です。 import cv2 import matplotlib.pyplot as plt %matplotlib inline img = cv2.imread("data/src/favorite.jpg", 0) #画像サイズが大きいためリサイズしています size = (300, 200) img = cv2.resize(img,size) img_ada = cv2.adaptiveThreshold(img, 255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 3, 1) cv2.imshow("img_ada",img_ada) cv2.waitKey(0) cv2.destroyAllWindows() 上記のコードを実行した結果が以下です。 また、トラックバーを使用して、閾値を変化させることも可能です。 import cv2 img = cv2.imread("data/src/favorite.jpg", 0) #画像サイズが大きいためリサイズしています size = (300, 200) img = cv2.resize(img,size) #トラックバーを用いて、二値化の閾値を変化させる def onTrackbar(position): global threshold threshold = position cv2.namedWindow("img") threshold = 100 cv2.createTrackbar("track","img",threshold, 255, onTrackbar) while True: ret, img_th = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY) cv2.imshow("img",img_th) #Escapeキーでウィンドウを閉じます if cv2.waitKey(10) == 27: break cv2.destroyAllWindows() 上記コードでは、閾値を0~255まで変化させることができます。 以下の画像は閾値を0, 77, 255に変化させたときの画像です。 透視変換 透視変換とは、任意の視点から3次元空間を眺めて遠近感を出す変換です。 以下のサンプルで透視変換がどういう変換か確認します。 import cv2 import numpy as np img = cv2.imread("data/src/score.jpg", 0) #画像サイズが大きいためリサイズしています size = (500, 500) img = cv2.resize(img,size) h, w = img.shape[:2] p1 = np.float32([[100, 500], [300, 500], [300, 100],[100,100]]) p2 = np.float32([[100, 500], [300, 500], [200, 200],[100,200]]) p_matrix = cv2.getPerspectiveTransform(p1, p2) img_p = cv2.warpPerspective(img, p_matrix, (w, h)) cv2.imshow("img",img_p) cv2.waitKey(0) cv2.destroyAllWindows() 実行結果は以下のような画像です。 畳み込み 以下のページに畳み込みについてまとまっているので、こちらで勉強させていただきました。 フィルターを用いて、画像の平滑化(画素値の変化を滑らかにすること)を行いました。 以下がそのコードです。 import cv2 import numpy as np kernel = np.ones((5,5)) / 25.0 img = cv2.imread("data/src/score.jpg", 0) #画像サイズが大きいためリサイズしています size = (500, 500) img = cv2.resize(img,size) img_2 = cv2.filter2D(img, -1, kernel) cv2.imshow("img_2",img_2) cv2.imshow("img",img) cv2.waitKey(0) cv2.destroyAllWindows() 上記を実行すると次のような画像が表示されます。「img」が元画像です。 楽譜の周りや音符などがぼやけて見えると思います。 また、画像の平滑化にはGaussianBlurを用いた方法もあります。 import cv2 img = cv2.imread("data/src/score.jpg", 0) #画像サイズが大きいためリサイズしています size = (500, 500) img = cv2.resize(img,size) img_gaussian = cv2.GaussianBlur(img,(9,9),2) cv2.imshow("img_gaussian",img_gaussian) cv2.imshow("img",img) cv2.waitKey(0) cv2.destroyAllWindows() 上記コードの実行結果は以下です。 こちらの画像も平滑化されています。 medianBlurを用いた場合のコードと実行結果も以下に書きます。 import cv2 img = cv2.imread("data/src/score.jpg", 0) #画像サイズが大きいためリサイズしています size = (500, 500) img = cv2.resize(img,size) #第二引数の値が大きいほど画像がぼやけます img_median = cv2.medianBlur(img, 5) cv2.imshow("img_median",img_median) cv2.imshow("img",img) cv2.waitKey(0) cv2.destroyAllWindows() 上記の実行結果は以下のようになります。他の方法よりもぼやけた結果となりました。 bilateralFilterを用いた方法も書きます。 import cv2 img = cv2.imread("data/src/score.jpg", 0) #画像サイズが大きいためリサイズしています size = (500, 500) img = cv2.resize(img,size) img_bi = cv2.bilateralFilter(img, 20, 30, 30) cv2.imshow("img_bi",img_bi) cv2.imshow("img",img) cv2.waitKey(0) cv2.destroyAllWindows() 実行結果は以下のようになりました。 最後に まだまだ画像処理を理解するには道のりが長そうです。 引き続き勉強します。 間違いなどありましたら、ご指摘いただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

巡回セールスマン問題としての「道の駅スタンプラリー」

地点数128の巡回セールスマン問題 北海道にある道の駅を巡る最短ルートを探してみました.いわゆる巡回セールスマン問題. Python使って解かせてみたけど, PuLP → 計算終わらず OR-Tools → 非現実的なルート選択 深層強化学習 → 学習が進まずお手上げ という結果になったので,その失敗談を残しておきます. 128を分割して幾つかのグループに分けてPuLPを適用することで,一応はそれらしいルートを得ましたけど...悔しいです! 北海道 道の駅スタンプラリーを最速で制覇するには  「北海道はでっかいどう!」よく耳にするフレーズです.私の息子も幼少時よく大きな声で言ってました.どれだけ北海道がデッカいのか,それを身をもって体験できるのが「北海道 道の駅スタンプラリー」です.私は,北海道に来る前は東北に住んでまして,東北 道の駅スタンプラリーを制覇した経験があります.その自信もあって,北海道でも制覇してやる!と意気込んで参加しましたが,結果は….はい,無理でした(涙).北の大地は広すぎました.そこで考えました.神様にお願いしても北海道のサイズは変わりません.せめてラリーの走行距離を短くできないだろうか.これは…最短ルートを見つけるしかないっ!  前置きが長くなりましたが,このような理由から「北海道内にある全ての道の駅1を巡る最短ルート」を見つけ出すことに取り組みました.そして,なんとか得られたルートを以下に示します.ここでは「なとわ・えさん」から出発した場合を示していますが,巡回路(一筆書きで書けるルート)ですから,どこの道の駅をスタートにしても,このルートに沿って走れば最短距離でスタートした駅まで戻ってこれます.総移動距離は3917.4 kmです. Start↓ つづき1 つづき2 つづき3 つづき4 なとわ・えさん 自然体感しむかっぷ 流氷街道網走 てしお 北欧の風 道の駅とうべつ 縄文ロマン 南かやべ 南ふらの メルヘンの丘めまんべつ なかがわ 望羊中山 しかべ間歇泉公園 しかおい ノンキーランド ひがしもこと えんべつ富士見 名水の郷きょうごく つど〜る・プラザ・さわら うりまく ぐるっとパノラマ美幌峠 ☆ロマン街道しょさんべつ あかいがわ YOU・遊・もり ピア21しほろ 摩周温泉 ほっと♡はぼろ スペース・アップルよいち くろまつない しほろ温泉 あいおい 風Wとままえ オスコイ!かもえない らんこし・ふるさとの丘 かみしほろ オーロラタウン93りくべつ るもい いわない ニセコビュープラザ 足寄湖 おんねゆ温泉 おびら鰊番屋 シェルプラザ・港 真狩フラワーセンター あしょろ銀河ホール21 サロマ湖 森と湖の里ほろかない みなとま〜れ寿都 230ルスツ ステラ★ほんべつ 愛ランド湧別 絵本の里けんぶち よってけ!島牧 とうや湖 ガーデンスパ十勝川温泉 かみゆうべつ温泉チューリップの湯 とうま てっくいランド大成 とようら おとふけ 遠軽 森のオホーツク ひがしかわ「道草館」 ルート229元和台 あぷた なかさつない まるせっぷ びえい「丘のくら」 あっさぶ みたら室蘭 さらべつ しらたき びえい「白金ビルケ」 江差 だて歴史の杜 忠類 香りの里たきのうえ あさひかわ 上ノ国もんじゅ そうべつ情報館i コスモール大樹 オホーツク紋別 ライスランドふかがわ 北前船 松前 フォーレスト276大滝 うらほろ おこっぺ 鐘のなるまち・ちっぷべつ 横綱の里ふくしま サーモンパーク千歳 しらぬか恋問 おうむ サンフラワー北竜 しりうち 花ロードえにわ 阿寒丹頂の里 にしおこっぺ花夢 田園の里うりゅう みそぎの郷 きこない マオいの丘公園 厚岸グルメパーク もち米の里☆なよろ たきかわ なないろ・ななえ ウトナイ湖 スワン44ねむろ びふか スタープラザ芦別 なとわ・えさん サラブレッドロード新冠 おだいとう おといねっぷ うたしないチロルの湯 Goal ! みついし 知床・らうす ピンネシリ つるぬま - むかわ四季の館 うとろ・シリエトク マリーンアイランド岡島 ハウスヤルビ奈井江 - あびらD51ステーション しゃり 北オホーツクはまとんべつ 三笠 - 夕張メロード パパスランドさっつる さるふつ公園 しんしのつ - 樹海ロード日高 はなやか(葉菜野花)小清水 わっかない 石狩「あいろーど厚田」 - つづき1へ ↗ つづき2へ ↗ つづき3へ ↗ つづき4へ ↗ - Googleマップの著作権の関係から,ここに最短ルートを示した北海道全体のマップは載せられませんので,数が多くなりますが分割して示します.なお,有料道路は避けています. 「知床・らうす」~「うとろ・シリエスク」間は要注意です! 最短距離である国道334号線経由は4月下旬~10月しか通れません. ところどころ,来た道を引き返していますが,「最短距離」にこだわると致し方ない結果でしょう. このルートを見つけるまでの試行錯誤  今回の「こだわり」は,Google mapを使って得た正確な距離情報を基に,128という多くの訪問地点を対象に解析したことです.10~30の訪問地点間を単純に直線でつないで最短巡回ルートを求めた例2はインターネット上で散見されますが,実際の道に沿った距離を使って北海道の全ての道の駅を対象にした巡回セールスマン問題を解きたかったのです.  まず,Google mapを使って各道の駅間の距離を調べて距離行列をつくります.ただ,これを素直に作るとなると ${}_{128}\mathrm{C}_2 $ 通りの距離を調べる必要があるため,めんどくさい.そこで閾値を設けて,明らかにその値以上の距離にある場合は1000kmという大きな値を入れました. 1. Pythonライブラリ PuLP  PythonのライブラリであるPuLPを使って最短ルートを求める計算プログラムを作ります. 参考にしたページはこちら このページ下部に訪問地点数と求解に要した時間の関係図が載っており,それを見ると地点数128の場合は途方もない時間がかかるようです.試しに,ノートPCのCPUで計算してみると案の定,1ヶ月かかっても計算が終わらない.そこで,北海道全体を幾つかのサブエリアに分けて,各サブエリアの中でPuLPを使って最短距離を見つけて,最後にそれらを連結する!という工夫をしました.この記事を書いている時に偶然見つけた論文 Alhanjouri (2018) も同じような考え方してますね. ということで,地道に作った距離行列を入力データとして,コンピュータにせっせと計算してもらった結果,上で示したような巡回ルートをゲットできました. 2. Pythonライブラリ OR-Tools  有名な数理最適化ライブラリであるOR-Toolsを使ってもトライしてみました. 参考にしたページはこちら このアルゴリズムは近似解法です.limit_time=10として計算しました. 結果は...うまく行きませんでした.どうも十勝から亀田半島に飛びたがるのです.つまり非現実的なルートを選択します.OR-Toolsでは日高ロード沿いにある「むかわ」「新冠」「みついし」をUターンしてくるルートを捻り出すことができない模様.距離行列を作る際に,全ての距離を正確に入力しないとダメなのかもしれません(「遠すぎる場合は一様に1000km」ではダメということ). 3. 深層強化学習  深層強化学習を使った求解にもトライしてみました. 参考にしたページはこちら. 関連する書籍も出ています → 現場で使える!Python深層強化学習入門 この書籍のwebサイトから,深層強化学習の実装例がダウンロードできます3.アルゴリズムに関しては,どうやらarXivの論文 Belloら (2017) を参考にしているようです.Pointer Networkを利用した方策勾配ベース(Actor-Critic)のアルゴリズムとのこと.  私はディープラーニングの初学者かつ組合せ最適化問題の非専門家(いわゆる素人さん)のため,「巡回セールスマン問題に深層強化学習を適用するのは得策ではない」と仰る専門家の先生を尻目に,あえて興味本位でトライしてみました. その結果は...やはり訪問地点数128ではダメですね(涙). 下記の通り,(効率的に解を算出できるかは別として)地点数42だと正解にたどり着くことを確認しましたが,さすがに100オーバーだとキビしいようです. 地点数42の例題はこちら ページ中程にある"DANTZIG42" 距離行列が既に準備されています. 訪問地点の分布状態はこんな感じ↓ 深層強化学習により得られた最短巡回ルート↓ 上記の例題ページに "The minimal tour has length 699" と書いてあるので,おそらくこれで合っていると思います.  Belloさん達の原著論文では「訪問地点数100でも上手くいったよ」的な書きっぷりだったので,その論文からハイパーパラメタを拝借してミニバッチ数128,学習係数0.0001の5000ステップ毎に0.96倍,重みの初期値は[-0.08, 0.08]の一様乱数,閾値1の勾配クリッピングの導入,オプティマイザをAdamに変更....などを採用して計算を走らせるも,学習が途中で立ち往生して進まない. このゴールデンウィーク中ずっと「ああでもないこうでもない」といろいろ試してみましたが,力尽きて諦めました. Pythonを勉強して1年,Pytorch歴3ヶ月,Tensorflowに関しては「何ですか?それ」レベルの私にとっては,この取り組み自体が自分の勉強のために良い教材になりました. あとがき  得られた最短ルートのマップを見て,「こんなの当たり前じゃ~ん」と言う人がいるかもしれません.しかし,北海道の白地図上に道の駅を点で示したものを我々人間が見て,パッと最短ルートを見つけることは難しいです.いや,無理です(実際にやってみればわかります).いわゆる天才ならできるのかな? 北海道開発局 ←ここに載っています. 令和2年7月末日時点の地図データを基にしているため,道の駅の数は128です. ↩ たとえば https://qiita.com/Kanahiro/items/08df9b18d471c0ba678e ↩ フレームワークがTensorflowの1系のため,初めてTensorflowを触る私にはコードの改変が大変でした.特に,for構文がサポートされていないため,二重ループをtf.whileで表現する方法を考えるのに苦労しました. ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gspreadでGoogleスプレッドシートを更新する際にセルの表示形式をきれいにしたい

概要 gspreadでスプレッドシートに値を張り付けるときに、表示形式も意識すると幸せになるので、その方法をまとめました。 結論 ファイルから読み込んだものを張り付ける場合は値を適切に変換する(変換にやや考慮点がある) update_cells のオプションにUSER_ENTEREDを入れる 貼り付ける値の例 例えば以下を張り付けていくとします。 実際にはファイルだったり、BigQueryだったりAthenaだったりから読み込むと思います。 ケース1 特に何も考えず、縦に張り付けていきます。 case1.py # 前略 wkb = gc.open_by_key("xxxxxxxxxxx") wks = wkb.worksheet("data1") wks.clear() cell_list = wks.range('A1:A10') vlist=[] vlist.append("12039") vlist.append("1111222233334444") vlist.append("3.141592") vlist.append("2021-01-14") idx=0 for val in vlist : cell_list[idx].value = val idx+=1 wks.update_cells(cell_list) ケース1の結果 一瞬、ん?いいんじゃない?と思いますが、すべて文字列なので計算などが行えません。 しかも=sum()を文字列に実行すると0になるようで、想像しただけで恐ろしいことになりそうです。 ケース2 gspreadはpythonの型を適切に変換して渡せば、セルもよい感じにしてくれるようです。 なので case2.py #前略 idx=0 for val in vlist : try : val = int(val) except : pass cell_list[idx].value = val idx+=1 wks.update_cells(cell_list) としてみます。 ケース2の結果 やったか!? はい、以下がダメです。 - 長い数値が壊れる - 少数が文字列のまま これらも考慮していきます。 ケース3 ロジックの組み方はほかにもあると思いますが、変換の順序だったりを考慮する必要があり、少し煩雑にはなるかと思います。今回は ドットが含まれていたらfloatへ変換 一定文字数以下の場合にintへ変換 といった判定をしています。 case3.py #前略 vlist=[] vlist.append("12039") vlist.append("1111222233334444") vlist.append("3.141592") vlist.append("2021-01-14") vlist.append("2021-01-15") idx=0 for val in vlist : try : if len(val) <= 10: val = int(val) except : pass try : if "." in val: val = float(val) except : pass cell_list[idx].value = val idx+=1 wks.update_cells(cell_list) ケース3の結果 良い感じです。 日付があるので、最新データを別シートに表示したいな、、、と=max( ああっ 日付型が文字列のままになってしまいました。 ケース4 よしじゃあ日付型に変換して渡す、、、と途中でJSONを介すためか、エラーになってしまいます。 これは、文字列のままにして update_cellsのオプションとして USER_ENTERED を渡すと解決します。 case4.py #前略 idx=0 for val in vlist : try : if len(val) <= 10: val = int(val) except : pass try : if "." in val: val = float(val) except : pass cell_list[idx].value = val idx+=1 wks.update_cells(cell_list,value_input_option="USER_ENTERED") ケース4の結果 これでようやく良い感じになりました! 雑感 貼れるか貼れないかではなく「貼れたけどなんかおかしい」となってしまうのが厄介ですね。 なお適切に型が設定されたBigQueryのテーブルに、ライブラリで取得したデータをそのまま上記のようなことをする場合は、変換は不要となります。 が、ファイルを読み込んで、、、というのもRPAとしては結構あるケースなのではと思いまとめてみました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

boto3でDynamoDBテーブルの属性(attribute)を削除する

ググってもすぐ見つからなくて難儀したのでここに残します。 remove_attribute.py import boto3 dynamodb = boto3.resource('dynamodb') my_table = dynamodb.Table('MyTable') partition_key = 'hoge' sort_key = 123 # 取得 result = my_table.get_item( Key= { 'pk': partition_key, 'sk': sort_key } ) print(result['Item']) # 属性の削除 my_table.update_item( Key= { 'pk': partition_key, 'sk': sort_key }, UpdateExpression='remove foo' ) # 再取得 result = my_table.get_item( Key= { 'pk': partition_key, 'sk': sort_key } ) print(result['Item']) $ python remove_attribute.py {'pk': 'hoge', 'sk': Decimal('123'), 'foo': 'bar'} {'pk': 'hoge', 'sk': Decimal('123')}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Atcoderアルゴリズム勉強会第1回(色んな全探索)

はじめに こんにちは 現在京都に住んでいる修士2年の大学院生です.自己紹介だけの記事はかけないとのことで、ここで自己紹介について書こうと思ったものの、長くなりそうなのでやはり短めにいきたいと思います. S台で浪人するも第1志望に落ち、関西圏公立大学に入学しましたが、仮面浪人して今の大学の学部に入りなおした多浪大学生です. さて、今回はatcoderで高橋さんが行った講義、最強のアルゴリズム勉強会の第1回について取り組んでいきたいと思います.スライドにのっている問題を中心に解いていきます. 全探索なので難しい問題はないはずです(多分) スライドはこちらになります.[https://www.slideshare.net/chokudai/wap-atcoder1] 全探索問題 天下一プログラマーコンテスト2012 予選B A問題 孫子算経 これは特にコメントないです.恐らく全探索の導入的な問題です. スライドでは簡単にforループが列挙できる条件として、「分岐の回数が少なく、回数が決まっている」「ループごとに、選択肢が有限個で決まっている」とのことです. a,b,c=map(int,input().split()) for i in range(1,128) : if i%3==a and i%5==b and i%7==c : print(i) ARC001 A問題 センター採点 1,2,3,4の選択肢の個数を数えました.なんかこんな感じのコード昨日のABC200のC問題でも書いた気がする. どんどん次の問題にいきましょう. n=int(input()) c=list(input()) count=[0]*4 for i in c : count[int(i)-1]+=1 print(max(count),min(count)) ARC004 A問題 2点間距離の最大値 特に意識したことはないですが、根号は最後に付けました. この種類の問題は難しくなってくると誤差が生じてきて厄介ですよね n=int(input()) x=[] for i in range(n) : x.append(list(map(int,input().split()))) ans=0 for i in range(n-1) : for j in range(i+1,n) : distance=(x[j][0]-x[i][0])**2+(x[j][1]-x[i][1])**2 ans=max(ans,distance) print(ans**0.5) ARC018 B問題 格子点と整数 for文3つ書いて愚直に計算しました.個人的にはfor文3個以上になるのは好きではないので他に良い解法があれば知りたいですね. 3つの組み合わせを出してくれるようなitertoolあったような気がするけど,リスト内の要素の組み合わせはどうするんだっけ. n=int(input()) x=[] for i in range(n) : x.append(list(map(int,input().split()))) count=0 for i in range(n-2) : for j in range(i+1,n-1) : for k in range(j+1,n) : ans=abs((x[j][0]-x[i][0])*(x[k][1]-x[i][1])-(x[k][0]-x[i][0])*(x[j][1]-x[i][1])) if ans%2==0 and ans!=0: count+=1 print(count) ABC004 D問題 マーブル これはこの中では一番面倒な問題でした. 中央に位置する緑色の配置を決めてから、左に位置する赤色、そして右に位置する青色の配置を決めました.結論(最後の配置)から遡る問題でした. 各マーブルの移動関数については、マーブル全体が初期中央位置よりも左側にある場合、右側にある場合、そして初期中央位置が含まれている場合の3通り考えられますが、1つの関数にまとめることができました. 例)緑のマーブル7個が座標-8から-2に配置された場合の移動回数 (0-(-8))\times(0-(-8)+1)\div2-(-2-0)\times(-2-0+1)\div2=35\\ 左辺については、 (マーブル8個の座標-8から-1までの移動総回数)ー(マーブル1個の座標-1までの移動総回数) となっている. これはマーブル全体が初期中央よりも左側に位置する場合の計算である.右側の時も同様に計算すれば求まる. この式を一般化する. (マーブル移動後の左端)=l、(マーブル移動後の右端)=r、(初期中央位置)=cとおくと (-1)^{c<l}(c-l)\times(c-l+1)\div2+(-1)^{r<c}(r-c)\times(r-c+1)\div2=ans\\ となる. ここでc<lおよびr<cがでてくるが、これは各々の不等式を満たすと1となり、満たさない場合は-1となる.これによって、3通りの場合すべてを1つの関数で表すことができる. これはこの問題で初知りでした、、、不等式満たさないと0になると思ってました 後は、for文で緑左端の位置を回していき、赤の右端、青の左端の位置を決めていくだけでした. r,g,b=map(int,input().split()) ans=10**10 #----各マーブルの移動回数の関数----# #----l=left,r=right,center=c,r<cが成り立てば1,不成立なら-1----# def cost(l,r,c) : return (-1)**(r<c)*(r-c)*(r-c+1)//2+(-1)**(c<l)*(c-l)*(c-l+1)//2 #----緑色のマーブルの左端の位置から決めていく----# for l in range(-500,201) : rR=min(l-1,-100+r//2) #min(緑色マーブル左端からひとつ左,赤色マーブル右端) lB=max(l+g,100-b//2) #max(緑色マーブル右端からひとつ右,青色マーブル左端) ans=min(ans,cost(rR-r+1,rR,-100)+cost(l,l+g-1,0)+cost(lB,lB+b-1,100)) print(ans) 最後に 最後の問題だけは結構難しかったです.関数を作るところが悩みました. こんな感じでアルゴリズムについてスライド配布してくださるのでatcoderには感謝しかありません. 次は,第2回アルゴリズムのスライドを扱っていきたいと思います.なんの範囲かは忘れてしまいましたが、恐らくまた全探索だったような気がする.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pyenvのインストール周りの仕様が変わっていたので

出会ったエラー いつものDockerfileを新しいサーバーでbuildしていたら、pipがnot foundになってこけた。 こんなwarningも出ていた。 WARNING: `pyenv init -` no longer sets PATH. Dockerfileの仕様 ubuntu18.04, cuda10.2, zsh pyenv 1.2.27 fix 旧仕様でのDockerfile(pyenv部分の一部) RUN curl https://pyenv.run | zsh && \ echo '' >> /root/.zshrc && \ echo 'export PATH="/root/.pyenv/bin:$PATH"' >> /root/.zshrc && \ echo 'eval "$(pyenv init -)"' >> /root/.zshrc && \ echo 'eval "$(pyenv virtualenv-init -)"' >> /root/.zshrc 新しい仕様 実質 pyenv init - ⇨ pyenv init --path ? (.profileをいじれ的なメッセージが出ていたけど従っても動かなかったので、メンテが行き届いてない感がある。) RUN curl https://pyenv.run | zsh && \ echo '' >> /root/.zshrc && \ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> /root/.zshrc && \ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> /root/.zshrc && \ echo 'eval "$(pyenv init --path)"' >> /root/.zshrc && \ echo 'eval "$(pyenv virtualenv-init -)"' >> /root/.zshrc 新しい仕様でのpyenv部分の全体的には RUN curl https://pyenv.run | zsh && \ echo '' >> /root/.zshrc && \ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> /root/.zshrc && \ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> /root/.zshrc && \ echo 'eval "$(pyenv init --path)"' >> /root/.zshrc && \ echo 'eval "$(pyenv virtualenv-init -)"' >> /root/.zshrc RUN source /root/.zshrc && \ pyenv install 3.8.0 && \ pyenv global 3.8.0 && \ pip install -U pip (ところで毎回 source /root/.zshrc 書かずに済む方法はないのかな…?) 参考 (つい2,3日前の記事)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[python] 関数内でexecを使ってglobal変数を定義する方法

追記2 コメントで@shiracamusさんが綺麗なコードを書いてくださいました。 この記事の内容は全部これでOKそうです。 def hoge(): globals()['a'] = 1 hoge() print(a) OK def hoge(): # execで定義した変数は、第3引数のdictに格納されるみたいなので、現在のグローバル変数(globals())を指定 # 第2引数には、とりあえず {} を入れておく exec('a=1', locals(), globals()) hoge() a output 1 NG def hoge(): global a exec('a=1') hoge() a output NameError: name 'a' is not defined 追記1 exec の第1引数の中で関数内で定義された辞書を使う方法。色々試したら出来たので、結果だけ。 ポイント: ・グローバル変数に同名の変数があるとそちらが参照されるので、被らないような変数名に置き換えてから実行する。 ・第2引数にlocals()を指定する。(これをしないと、NameError: name 'fagragfaa' is not definedになる。) def hoge(): dic = {'a': 1, 'b': 2} for key in dic: # グローバル変数の dic と被らないように、変数名を変える fagragfaa = dic exec(f'{key} = fagragfaa["{key}"]', locals(), globals()) dic = {} hoge() a, b output (1, 2)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonで高速フーリエ変換(FFT)

やること ある地震波を入力とした加速度(下の図)から、加速度応答スペクトル(スペクトル密度関数)を求める fftは複雑なことが多く理解しにくいため、最低限必要なところだけ説明する 補足 fft(高速フーリエ変換)をするなら、scipy.fftかnumpy.fftが主流 公式によるとscipy.fftpackはLegacyとなっており、推奨されていない scipyはドキュメントが非常にわかりやすかった モジュールのインポート numpyもscipyも違いはありません。 # numpyを使うとき from numpy.fft import fft, fftfreq # scipyを使う時 from scipy.fft import fft, fftfreq 高速フーリエ変換 yは分析したい時系列データです # サンプル数 N = len(y) # サンプリング間隔 T = 0.01 # フーリエ変換する yf = fft(y) # 周波数軸の作成 xf = fftfreq(N, T)[:N//2] 描画 plt.plot(xf, 2.0/N * np.abs(yf[0:N//2])) 2つの山が見て取れる T=0.01としたので、サンプリング周波数はその逆数で $f_s=100Hz$となる。つまりナイキスト周波数は50Hzなので、描画したグラフは周波数が0から50の範囲となっている。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows で python から ShellExecute を利用する

意外にはまってしまったのでメモしておきます。 背景 Windows において、python を1つ上げたら芋ずる式に他のプロセスを上げていきたい。 起動数は固定なので、shell32.dll の ShellExecute を使うことを考えた。 しかし python からこの DLL を利用するにはいくつか tips があるようだ。 問題のあるプログラム 最初は単純に ShellExecute を呼び出せばよいと思い、以下のようなプログラムを書いていました。 from ctypes import windll shellexecute = windll.shell32.ShellExecuteA err = shellexecute(None, "open", "./test.exe", None, None, 1) print(err) しかし結果は 31 エラーで撃沈。仕方がないので python の ctypes の項を読むと、ASCII 文字列は c_char_p で変換して渡す必要がありそう。さっそく実行。 from ctypes import windll, c_char_p shellexecute = windll.shell32.ShellExecuteA err = shellexecute(None, c_char_p(b"open"), c_char_p(b"./test.exe"), None, None, 1) print(err) が、これも 31 エラーで弾かれる。 もしかして、区切り記号? 途方に暮れていると、Windows ではフォルダの区切り記号が \ であることに気づいた。やってみよう! from ctypes import windll, c_char_p shellexecute = windll.shell32.ShellExecuteA err = shellexecute(None, c_char_p(b"open"), c_char_p(b".\\test.exe"), None, None, 1) print(err) 今度はうまく動きました。ShellExecuteW を使いたい場合は、 from ctypes import windll, c_wchar_p shellexecute = windll.shell32.ShellExecuteW err = shellexecute(None, c_wchar_p("open"), c_wchar_p(".\\test.exe"), None, None, 1) print(err) とすれば OK。 DLL 化して呼び出しを簡略化 でも一々 c_wchar_p を指定するのは面倒なので、DLL に組み込んでしまった。 mycommon.cc #include <windows.h> extern "C" __declspec(dllexport) int launch(LPCWSTR file, int mode) { HINSTANCE hInst = ShellExecuteW(NULL, L"open", file, NULL, NULL, mode); return GetLastError(); } mycommon.cc をコンパイルし、mycommon.dll を作成すると、 from ctypes import cdll launch = cdll["./mycommon"].launch err = launch(".\\test.exe", 1) という形で呼び出すことができた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonを用いたWebScraping(歌詞の抜き出し)

星野源さんの歌詞はどういった単語が多く使われているのかを調べるために、Webスクレイピングを行った。 Webスクレイピングは、Webサイトから情報を抽出する技術のこと。 *ウェブサイトによってスクレイピングを拒否している場合があり、そういったサイトにスクレイピングを行うと処罰の対象となるため注意!アクセスしすぎも迷惑をかけるのでダメ! 今回の目標 Webスクレイピングで、星野源さん全曲の歌詞を取得し、シルエットに使用頻度の高い単語を当てはめる。 参考サイト 米津玄師の歌詞をWordCloudで可視化してみた。(qiita) ソースコードと雑談 昨年春に大学の先輩から作ってみればと言われ、作ってみた。1年経ってから備忘録を書くというなんともマヌケなものだが、折角の成果だから載せておく。 1.サイトから星野源作詞・作曲の曲を全て抜き出す import os import re import bs4 import time import requests import pprint def load(url): res = requests.get(url) res.raise_for_status() return res.text def pickup_tag(html, find_tag): soup = bs4.BeautifulSoup(str(html), 'html.parser') paragraphs = soup.find_all(find_tag) return paragraphs def parse(html): soup = bs4.BeautifulSoup(str(html), 'html.parser') # htmlタグの排除 kashi_row = soup.getText() kashi_row = kashi_row.replace('\n', '') kashi_row = kashi_row.replace(' ', '') # 英数字の排除 kashi_row = re.sub(r'[a-zA-Z0-9]', '', kashi_row) # 記号の排除 kashi_row = re.sub(r'[ <>♪`‘’“”・…_!?!-/:-@[-`{-~]', '', kashi_row) # 注意書きの排除 kashi = re.sub(r'注意:.+', '', kashi_row) return kashi def main(): with open('hosino_kashi.txt', 'a') as f: # アーティストページのアドレス url = f'https://www.uta-net.com/artist/9867/' # 曲ページの先頭アドレス base_url = f'https://www.uta-net.com' # ページの取得 html = load(url) # 曲ごとのurlを格納 musics_url = [] # 歌詞を格納 kashis = '' """ 曲のurlを取得 """ # td要素の取り出し for td in pickup_tag(html, 'td'): # a要素の取り出し for a in pickup_tag(td, 'a'): # href属性にsongを含むか if 'song' in a.get('href'): # urlを配列に追加 musics_url.append(base_url + a.get('href')) """ 歌詞の取得 """ for i, page in enumerate(musics_url): print('{}曲目:{}'.format(i + 1, page)) html = load(page) for div in pickup_tag(html, 'div'): # id検索がうまく行えなかった為、一度strにキャスト div = str(div) # 歌詞が格納されているdiv要素か if r'itemprop="text"' in div: # 不要なデータを取り除く kashi = parse(div) print(kashi, end = '\n\n') # 歌詞を1つにまとめる kashis += kashi + '\n' # 1秒待機 time.sleep(1) break # 歌詞の書き込み f.write(kashis) if __name__ == '__main__': main() 2. WordCloudを使用して、出てくる回数の多い単語を大きくして表示 import MeCab from matplotlib import pyplot as plt from wordcloud import WordCloud # テキストファイル読み込み with open('hoshino_kashi.txt', mode='rt', encoding='utf-8') as fi: source_text = fi.read() # MeCabの準備 tagger = MeCab.Tagger() tagger.parse('') node = tagger.parseToNode(source_text) # 名詞を取り出す word_list = [] while node: word_type = node.feature.split(',')[0] if word_type == '名詞': word_list.append(node.surface) node = node.next # リストを文字列に変換 word_chain = ' '.join(word_list) #無意味そうな単語除去 stop_words = ['そう', 'よう', 'もの', 'こと', 'まま'] # ワードクラウド作成 W = WordCloud(font_path='/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', width=640, height=480, background_color='white', colormap='bone', stopwords=set(stop_words)).generate(word_chain) plt.imshow(W) plt.axis('off') plt.show() 3.写真から白抜きした星野源さんに2の手法を適用 写真から白抜き(シルエット)にする方法は忘れた....。恐らくネットで調べれば出てくる。 from PIL import Image import numpy as np import MeCab from matplotlib import pyplot as plt from wordcloud import WordCloud # テキストファイル読み込み with open('hoshino_kashi.txt', mode='rt', encoding='utf-8') as fi: source_text = fi.read() # MeCabの準備 tagger = MeCab.Tagger() tagger.parse('') node = tagger.parseToNode(source_text) # 名詞を取り出す word_list = [] while node: word_type = node.feature.split(',')[0] if word_type == '名詞': word_list.append(node.surface) node = node.next # リストを文字列に変換 word_chain = ' '.join(word_list) #無意味そうな単語除去 stop_words = ['そう', 'よう', 'もの', 'こと', 'まま', 'これ', 'それ'] def get_wordcrowd_mask( word_chain, imgpath ): img_color = np.array(Image.open( imgpath )) wc = WordCloud(font_path='/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', mask=img_color, collocations=False, # 単語の重複しないように stopwords=set(stop_words), background_color='white', contour_width=3, contour_color='black' ).generate( word_chain ) # show plt.figure(figsize=(6,6), dpi=200, facecolor='white') plt.imshow(wc) plt.axis("off") plt.show() 星野源さんの曲は、日常や身近な人を歌っていることが多いのかなと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonを用いたWebScrapingとWordCloudを用いた可視化〜星野源さんを添えて〜

星野源さんの歌詞はどういった単語が多く使われているのかを調べるために、Webスクレイピングを行った。 Webスクレイピングは、Webサイトから情報を抽出する技術のこと。 *ウェブサイトによってスクレイピングを拒否している場合があり、そういったサイトにスクレイピングを行うと処罰の対象となるため注意!アクセスしすぎも迷惑をかけるのでダメ! 今回の目標 Webスクレイピングで、星野源さん全曲の歌詞を取得し、シルエットに使用頻度の高い単語を当てはめる。 顔の輪郭では分からないかもしれないが、星野源さん 参考サイト 米津玄師の歌詞をWordCloudで可視化してみた。(Qiita) かなり参考にさせてもらいました。 ソースコードと雑談 昨年春に大学の先輩から作ってみればと言われた。 当時はPythonに初めて触れたので、見よう見まねで作成した。 1年経ってから備忘録を書くというなんともマヌケなものだが、折角の成果だから載せておく。 1.サイトから星野源作詞・作曲の曲を全て抜き出す import os import re import bs4 import time import requests import pprint def load(url): res = requests.get(url) res.raise_for_status() return res.text def pickup_tag(html, find_tag): soup = bs4.BeautifulSoup(str(html), 'html.parser') paragraphs = soup.find_all(find_tag) return paragraphs def parse(html): soup = bs4.BeautifulSoup(str(html), 'html.parser') # htmlタグの排除 kashi_row = soup.getText() kashi_row = kashi_row.replace('\n', '') kashi_row = kashi_row.replace(' ', '') # 英数字の排除 kashi_row = re.sub(r'[a-zA-Z0-9]', '', kashi_row) # 記号の排除 kashi_row = re.sub(r'[ <>♪`‘’“”・…_!?!-/:-@[-`{-~]', '', kashi_row) # 注意書きの排除 kashi = re.sub(r'注意:.+', '', kashi_row) return kashi def main(): with open('hosino_kashi.txt', 'a') as f: # アーティストページのアドレス url = f'https://www.uta-net.com/artist/9867/' # 曲ページの先頭アドレス base_url = f'https://www.uta-net.com' # ページの取得 html = load(url) # 曲ごとのurlを格納 musics_url = [] # 歌詞を格納 kashis = '' """ 曲のurlを取得 """ # td要素の取り出し for td in pickup_tag(html, 'td'): # a要素の取り出し for a in pickup_tag(td, 'a'): # href属性にsongを含むか if 'song' in a.get('href'): # urlを配列に追加 musics_url.append(base_url + a.get('href')) """ 歌詞の取得 """ for i, page in enumerate(musics_url): print('{}曲目:{}'.format(i + 1, page)) html = load(page) for div in pickup_tag(html, 'div'): # id検索がうまく行えなかった為、一度strにキャスト div = str(div) # 歌詞が格納されているdiv要素か if r'itemprop="text"' in div: # 不要なデータを取り除く kashi = parse(div) print(kashi, end = '\n\n') # 歌詞を1つにまとめる kashis += kashi + '\n' # 1秒待機 time.sleep(1) break # 歌詞の書き込み f.write(kashis) if __name__ == '__main__': main() 1では、歌ネットからデータを取得し、歌詞データをテキストファイルに保存する。 2. WordCloudを使用して、出てくる回数の多い単語を大きくして表示 import MeCab from matplotlib import pyplot as plt from wordcloud import WordCloud # テキストファイル読み込み with open('hoshino_kashi.txt', mode='rt', encoding='utf-8') as fi: source_text = fi.read() # MeCabの準備 tagger = MeCab.Tagger() tagger.parse('') node = tagger.parseToNode(source_text) # 名詞を取り出す word_list = [] while node: word_type = node.feature.split(',')[0] if word_type == '名詞': word_list.append(node.surface) node = node.next # リストを文字列に変換 word_chain = ' '.join(word_list) #無意味そうな単語除去 stop_words = ['そう', 'よう', 'もの', 'こと', 'まま'] # ワードクラウド作成 W = WordCloud(font_path='/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', width=640, height=480, background_color='white', colormap='bone', stopwords=set(stop_words)).generate(word_chain) plt.imshow(W) plt.axis('off') plt.show() ここまできたら、四角よりもシルエットに単語を並べた方が良いかなと思い・・・。 写真からシルエットを出す方法を忘れた 3.写真から顔面が青白い星野源さんを作成 OpenCVを用いた画像処理を施し、写真を青白くさせる。ここの調整が難しかった。青白くならないと、綺麗なシルエットが出ない。写真は肖像権に引っかかるかもしれないので、一応載せない。(すみません・・・)上半身がしっかり写っているものが良いと思われる。 import cv2 import numpy as np import matplotlib.pyplot as plt BLUR = 21 CANNY_THRESH_1 = 10 CANNY_THRESH_2 = 100 MASK_DILATE_ITER = 10 MASK_ERODE_ITER = 10 MASK_COLOR = (1.0,0.0,0.0) img = cv2.imread('hoshino2.jpg') #青白くしたい画像 img1 = img[0 : 2100, 0: 2650] gray = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, CANNY_THRESH_1, CANNY_THRESH_2) edges = cv2.dilate(edges, None) edges = cv2.erode(edges, None) contour_info = [] contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) for c in contours: contour_info.append(( c, cv2.isContourConvex(c), cv2.contourArea(c), )) contour_info = sorted(contour_info, key=lambda c: c[2], reverse=True) max_contour = contour_info[0] mask = np.zeros(edges.shape) cv2.fillConvexPoly(mask, max_contour[0], (255)) mask = cv2.dilate(mask, None, iterations=MASK_DILATE_ITER) mask = cv2.erode(mask, None, iterations=MASK_ERODE_ITER) mask = cv2.GaussianBlur(mask, (BLUR, BLUR), 0) mask_stack = np.dstack([mask]*3) mask_stack = mask_stack.astype('float32') / 255.0 img1 = img1.astype('float32') / 255.0 masked = (mask_stack * img1) + ((1-mask_stack) * MASK_COLOR) masked = (masked*255).astype('uint8') plt.imshow(img_a) plt.axis("off") plt.show() スクリプトにコメントが無くてごめんなさい そのうち書きます。 4.顔面が青白い星野源さんにWordCloudを適用 from PIL import Image import numpy as np import MeCab from matplotlib import pyplot as plt from wordcloud import WordCloud # テキストファイル読み込み with open('hoshino_kashi.txt', mode='rt', encoding='utf-8') as fi: source_text = fi.read() # MeCabの準備 tagger = MeCab.Tagger() tagger.parse('') node = tagger.parseToNode(source_text) # 名詞を取り出す word_list = [] while node: word_type = node.feature.split(',')[0] if word_type == '名詞': word_list.append(node.surface) node = node.next # リストを文字列に変換 word_chain = ' '.join(word_list) #無意味そうな単語除去 stop_words = ['そう', 'よう', 'もの', 'こと', 'まま', 'これ', 'それ'] def get_wordcrowd_mask( word_chain, imgpath ): img_color = np.array(Image.open( imgpath )) wc = WordCloud(font_path='/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', mask=img_color, collocations=False, # 単語の重複しないように stopwords=set(stop_words), background_color='white', contour_width=3, contour_color='black' ).generate( word_chain ) # show plt.figure(figsize=(6,6), dpi=200, facecolor='white') plt.imshow(wc) plt.axis("off") plt.show() 顔面が青白いとWordCloudを適用した際に、顔の輪郭がしっかり出る 星野源さんの曲は、日常や身近な人を歌っていることが多いのかなと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

motpyのROS実装(高速オブジェクトトラッキング)

 高速オブジェクトトラッキングアルゴリズムのmotpyをROS1/ROS2に実装したので投稿します。この記事には個人ブログやROS Japan UGでも発表したものも含まれます。 motpyとは?  motpyとは、カルマンフィルタを用いてバウンディングボックスを追跡するオブジェクトトラッキングアルゴリズムです。デプスイメージを使わずともWebカメラのみの画像でもトラッキングが可能で、矩形のみの情報で追跡するため、Raspberry Piなどでも高速オブジェクトトラッキングを実現することができます。  この手法自体はそれほど珍しくはないですが、motpyは、他と異なりPythonライブラリとして提供されているため、あらゆるPython実装の物体検出アルゴリズムに組み込むことができます。 私は、このアルゴリズムをDarknet_rosに組み込みたいと思っていましたが、darknet自体はC++とCで実装されているのでイマイチ使いにくい…  そこで、motpy自体をROS1/ROS2ノードにしました。 処理フロー  追跡方法について処理フローを以下に示します。 Webカメラから画像トピックを送信します(usb_cam) darknet_rosによって物体検出を行います(darknet_ros) motpy_rosによってdarknet_ros由来のBoundingBoxesを入力からトラッキングID付きのBoundingBoxesに変換します(motpy_ros) 表示専用ノードで表示します(ROS1の場合、mosaic_bbox)  現時点では、motpy自体にクラス名が実装されていないため、motpy_rosの出力には「Person」や「Bus」などの区別が付けられません。 インストール  ここでは、ROS1での環境構築と実行について示します。依存環境は以下のとおりです。 ROS1 Noetic OpenCV4 darknet_ros_msgs ros-noetic-usb_cam motpy # Create workspace $ source /opt/ros/noetic/setup.bash $ cd ~ $ mkdir -p ros1_ws/src $ cd ros1_ws/src # Clone repository $ git clone --branch ros1-noetic https://github.com/Ar-Ray-code/motpy_ros.git $ cd motpy_ros $ pip3 install -r requirements.txt $ cd ~/ros1_ws/src $ catkin_make 実行  WebカメラをPCに接続して、以下のコマンドを入力します。 $ source /opt/ros/noetic/setup.bash $ source ~/ros1_ws/devel/setup.bash $ roslaunch motpy_ros example_mosaic.launch  トラッキングの様子です。物体を移動させたあとも認識した顔に対してIDを追跡しています。  Rqt-graphの表示は次のようになっているはずです。  ノートPCのCPU(Core i5 10210U)でも十分に追跡できるため、かなり実用的なトラッキングノードだと思います。  バグなど気づいた点があれば、ぜひissueやPRを送ってもらえると嬉しいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む