Google CalendarとGoogle Keepを連携してPCで賢く予定管理したい
まえがき
古来より人間は道具を発明し,自らの能力の延長として使ってきました。 道具は能力を拡張するだけではなく補完的な意味合いも強くあり,例えば「眼鏡」があることにより近眼という致命的な能力欠如を持った個体でも社会においてそれほど不自由せずに生活を営むことができるようになりました。
眼鏡バンザイ。
ということは現代の情報社会の技術を使えば 「私生活がちゃんとしていない系の人」も「健常な人々」と同等な生活を営めるはずなのです。
締切や予定の把握・管理が苦手でもなんかうまくやる方法あるでしょ!助けてGoogle先生!ということです。
Google Keep と Google Calendar
今回の秘密道具はこちら。
- Google Keep:万能メモ帳
- Google Calendar:万能カレンダー
特に説明することはないですがGoogle Calendarは是非スマホにアプリを入れて使ってください。他の人との予定共有とかめっちゃ捗ります。 というかGoogleホニャララは情報共有において最強と勝手に思っております。Google Photoをみんな使おう。
Google KeepのCalendarリマインダ機能
Google Keep は普通に使えばただのメモ帳です。 しかし,メモをリマインダとして登録すればGoogle Calendarにも表示させることが可能です。 Calendarからは時刻などの情報を編集可能なようです。
詳しくは以下のサイトをば。 loumo.jp
健常者の人々には当たり前かもしれませんが,予定などのちょっとしたメモ書きというのは一般的にすぐにどっかいきがちです。 PCとスマホを持っている場合はその都度適当なファイルに書いて後々ファイル検索をかけて探すなんてこともよくあるわけです。
そういう片付けできない系ダメな奴がダメな理由は行動に一貫性・ルールがないからです。 メモはGoogle Keepだけを使う。少なくとも何か情報を紛失するリスクは減ります。
追記スペース
実はこの記事書いている段階ではあんまり活用法を絞れていません。
思いつきで書いているので。いざ変革。
便利な連携・使い方等見つけたら追記します。
PC版をデスクトップアプリ風に使う
とまぁGoogle系のサービスは便利で重宝するのですが,PC版はいちいちサイトを開く必要があります。
私はタブを片付けられないのでどうしても予定などは別窓で見たいという欲求が有り,デスクトップアプリ作ってほしーなーとは思ってます。
1.ショートカットの作成
以下の記事の手順でデスクトップ上にショートカットを作成することができます。
また,その後にタスクバー上で該当windowを右クリックし,「タスクバーにピン止め」をすることで速攻で起動できます。いいですね。
2.タスクバーのアイコンがChromeのものになる問題の解決
タスクバーにピンどめする時,タスクバーのアイコンがChromeのものになってしまうというバグ?を経験しました。
こういうときは一度タスクバーからピンどめを外して,ショートカットキーを右クリックして「タスクバーにピンどめ」を選択しなおします。
ここまで来ることでカレンダーやメモをアプリ感覚で起動することができて非常に快適になります。
参考になりそうな文献達
拡張機能はなかなか便利そうです。いちいちブックマークするよりも探しやすいと思います。(検索できるので)
ホモグラフィー行列を用いた鳥瞰変換(Bird's-eye view)
鳥瞰変換とは
一言で言うと,ある視点からとったカメラの画像を別の視点で撮ったように見せかける手法のことです。
他サイト様の説明がわかりやすいのでこちらに載せます。 daily-tech.hatenablog.com
より一般的には「斜めから撮った画像を真上から撮った画像のように写す」という場面で使われます。
鳥瞰変換とホモグラフィー変換
カメラの撮像の式
グローバル座標系にて座標にある点をカメラの位置①から撮像して画像上の点に写っているとします。 今,仮想的なカメラの回転を施した系②でこれが に移るとします。(この系②が上から見下ろす系になっていればいいわけですね。)
それぞれの場合でのカメラの撮像の式は以下のようになります。(Texで打ったので画像で失礼)
ホモグラフィー変換
一方,画像変換で用いられるホモグラフィー変換はホモグラフィー行列を用いて次のような形で表せます。
カメラの回転とホモグラフィー変換の関係性
上のニ式を見比べてみると,回転行列ととの間には以下のような直接対応関係があることがわかります。 当然ですが,系①と②の間に純粋な回転以外の並進成分などがあった場合はこんな簡単な関係にはなりません。
この関係を用いることでカメラを擬似的に真下向きに回したときの画像を生成することができるというわけです。
おまけ:手ぶれ補正
先程の式は裏を返せば,微小な並進成分を無視すれば画像内のホモグラフィー変換からカメラの回転を補正することができるということを表しています。 この手法を用いて手ぶれ補正をしたのが以下の記事だったりするわけです。
実践編
例のごとくOpenCVを使います。
下準備
先程の式を用いて鳥瞰変換を行う際には以下のパラメータが必要になります。
- カメラ行列K
- 系①におけるカメラ姿勢(R,t)(→ 実はRだけでいい)
これは一般にキャリブレーションと言われる作業です。
Ubuntu-ROS環境が整っている方にとっては造作もないと思いますが,Windowsなどでやる場合は下記のサイト等でやるのが良いかと思います。 ossyaritoori.hatenablog.com
また,カメラ姿勢(R,t)を測るのは結構大変で,ARマーカ等を用いるのが最も楽かなぁという気がします。 プログラムは適当に探してもらうとして,この辺りの記事なんかが説明が簡素でわかりやすいかも?
プログラムの流れ
カメラの姿勢Rの元となるrvec
から座標系①における回転を計算します。
ossyaritoori.hatenablog.com
また,変換したい座標系②での回転をcv2.Rodrigues
から生成しておきます。
この時,先程のと計算できます。
その後,Kをかけて作ったホモグラフィー行列を元にcv2.warpPerspective
に投げます。以上です。
略記プログラム
コードメモですがこのままでは動きませんよ~
# calculate bird view homography import math theta_below = np.array([0, math.pi, 0]) Rc_, _ = cv2.Rodrigues(theta_below)#変換先の回転 Rc,_ = cv2.Rodrigues(vm.rvecs)#オリジナル関数有 Homo = np.linalg.multi_dot([K,Rc_.T,Rc.T,np.linalg.inv(K)])#ホモグラフィー行列 nHomo = Homo/Homo[2,2]# 正規化 minx = -20000 miny = 0 # 移動量を調整しないと画面外に出ていってしまう… nHomo[0,2] -= minx nHomo[1,2] -= miny # 2000pix四方の画面を用意しないとどっか行っちゃうので… out=cv2.warpPerspective(img,nHomo,(2000,2000))
OpenCVで使われる座標系の作法メモ
OpenCVの座標系
座標系というやつはどう定義されているかが非常に重要です。 従って参照にすべきはQiitaでもなく,このブログでもなく公式APIリファレンスです。
これに書いてあることが全てです。でもなかなか探しづらいのでここにメモを書きます。
前提of前提
超前提知識ですが,OpenCVでは以下の前提があります。
- 右手系
- カメラの座標系において,左上が原点,横方向がX(右が正),画像の縦方向がY(下が正),奥行き方向がZ(奥が正)
OpenCVで使われる回転の作法
回転の記述法でメジャーどころと言えば以下の3つが挙げられます。
OpenCVではちょくちょく回転を表す3×1のベクトルを算出しますが,これは全てロドリゲスの回転公式に基づいています。
公式のcv2.Rodrigues()のレファレンスを覗くと
とあります。
日本語でいうと,「ベクトルのノルムが回転角,正規化されたベクトルが回転軸」を表します。
X軸[1,0,0]まわりに45度回転させたい場合は以下のように書けばいいわけです。
import cv2 import numy as np R, Jacob = cv2.Rodrigues(np.array([math.pi/4,0,0])) print(R) #R = array([[ 1. , 0. , 0. ], # [ 0. , 0.70710678, -0.70710678], # [ 0. , 0.70710678, 0.70710678]])
なお,cv2.Rodriguesは返り値を2つ返すので注意。
従って以下のようなオイラー角のDCM変換を手で書いている人はOpenCV上では使うのをやめた方がいいです。 ぱっと書きやすいので私は好きなんですが…
# Calculates Rotation Matrix given euler angles. def eulerAnglesToRotationMatrix(theta) : R_x = np.array([[1, 0, 0 ], [0, math.cos(theta[0]), -math.sin(theta[0]) ], [0, math.sin(theta[0]), math.cos(theta[0]) ] ]) R_y = np.array([[math.cos(theta[1]), 0, math.sin(theta[1]) ], [0, 1, 0 ], [-math.sin(theta[1]), 0, math.cos(theta[1]) ] ]) R_z = np.array([[math.cos(theta[2]), -math.sin(theta[2]), 0], [math.sin(theta[2]), math.cos(theta[2]), 0], [0, 0, 1] ]) R = np.dot(R_z, np.dot( R_y, R_x )) return R # Checks if a matrix is a valid rotation matrix. def isRotationMatrix(R) : Rt = np.transpose(R) shouldBeIdentity = np.dot(Rt, R) I = np.identity(3, dtype = R.dtype) n = np.linalg.norm(I - shouldBeIdentity) return n < 1e-6 # Calculates rotation matrix to euler angles # The result is the same as MATLAB except the order # of the euler angles ( x and z are swapped ). def rotationMatrixToEulerAngles(R) : assert(isRotationMatrix(R)) sy = math.sqrt(R[0,0] * R[0,0] + R[1,0] * R[1,0]) singular = sy < 1e-6 if not singular : x = math.atan2(R[2,1] , R[2,2]) y = math.atan2(-R[2,0], sy) z = math.atan2(R[1,0], R[0,0]) else : x = math.atan2(-R[1,2], R[1,1]) y = math.atan2(-R[2,0], sy) z = 0 return np.array([x, y, z])
OpenCVでの座標変換計算の数式
前節でOpenCVがどのような形で座標を扱うことを想定しているかがわかりました。次はそれがどのように使われることを想定されているかです。
例えばOpenCVではカメラの位置を図るような関数がいくつか存在します。既知の3次元点と投影された画像の点との対応からカメラの姿勢を計算するPNP問題を解く場合が特にそうです。
心当たりがあるのは
- SolvePNP系統
- arucoモジュールのEsitmatePose系
でしょうか。
rvecやtvecの扱い
これらでは得られたカメラの位置として返り値にrvecsとtvecsという3×1のベクトルを返してきます。 気になるのはこれらは果たしてどの座標系に属しているのかということです。(都合等をよく考えたらカメラ座標系にきまっているんですが)
以前の記事でもカメラ変位をカメラtoグローバルなのかグローバルtoカメラなのかで扱いが変わることを書いたと思います。 ossyaritoori.hatenablog.com
結論から言うとrvecsやtvecsはカメラのLocal座標系から見た値で表されています。 従って,
OpenCVで測ったカメラ姿勢から測った回転物の姿勢はグローバル座標系では
と計算できるわけです。
検証用投影プログラム
自分はちょっと疑い深いので実際に計算して確かめました。
三次元の点(グローバル座標)をカメラの視点に投影するcv2.projectPoints()
という関数があります。
imgpts, jac = cv2.projectPoints(3Dpoints, rvecs, tvecs, K, dist)
のように使えば3次元点を2次元に投影することができます。
この中身をDistortionを含めないで書くならこういう風になります。
tc=tvecs Rc,_=cv2.Rodrigues(rvecs) def camProjection(points,Rc,tc,K=np.eye(3),campose_is_global=0): ''' Input: CameraPose, Camera Matrix, Object Points in Global Coordinate Output: Projected Points ''' h,w = points.shape if h != 3: print("Input do not match points style!") if campose_is_global:#assume Rc,tc is from global frame Proj = K.dot(np.concatenate([Rc.T, -np.dot(Rc.T,tc)], axis=1)) else:#assume Rc,tc is from camera frame( opencv default) Proj = K.dot(np.concatenate([Rc, tc], axis=1)) pts = np.vstack((points,np.ones((1,w)))) converted_pts= Proj.dot(pts) return Projection(converted_pts) def Projection(points): h,w = points.shape if h != 3: print("Input do not match points style!") Dep = points[2,:] Depth = np.tile(Dep,(2,1)) return points[0:2,:]/Depth
これを使った検証の結果,やはりOpenCVではrvecsやtvecsはカメラのLocal座標系から見たものに適切に変換して使う必要があります。
3D座標変換の勘所メモ② - 異なる座標内での回転の変換 -
メモ第二弾です。
問題設定
- ローカル座標系Aとローカル座標系Bがあり,それぞれグローバル座標系に対しての回転を持つとする。
- グローバル座標にある点P をローカル座標系A内で回転を適用する。
- この時の点Pの回転をローカル座標系Bから見た回転[tex:R{PB}]とみなした時の[tex: R{PA},R_{PB}]の関係性は?
この問題はつまり,Aの座標系で動いた物体がBの座標系でどのように見えるかを表している。よくある問題です。
導出
まず点Pのグローバル座標ベクトルをとした時,系AとBから見た点Pのベクトルはそれぞれ
\begin{align} p_A &= R_A^\top p_G\\ p_B &= R_B^\top p_G \end{align}
です。ここで系Aのローカル座標内での回転を適用した時のベクトルは
となる。さらにこれを一旦グローバル座標にうつしその後座標Bに移した場合のベクトルは以下のようになる。
さて,これは座標系Bで見た移動後の点P'であるが最初の式を代入することでで表せる。
これが点Pを座標系Bにて回転させた
に等しくなることから,答えが導ける。
結論
との関係は,
逆の関係も
となります。Aが元からGlobal座標系だった場合は,
という式になります。
2次元回転との違い
回転まわりは2次元の図を書くとわかりやすいことが多々ありますが,今回のケースは2次元で考えると逆に混乱します。
2次元では回転の計算は可換(順番を変えても結果が同じ)であるため上記の式は2次元の場合
と同義であるからです。3次元以上での回転は非可換であるため上のようなややこしい式が登場するわけです。
参考URL
参考にした質疑
追記:以下の記事がわかりやすいですね。
3D座標変換の勘所メモ①
タイトルが非常に付けづらかったので埋もれないようにキーワードだけ並べました。
問題設定
2つの座標系があった時,1つの座標系から見て位置にある点Pがもう片方の座標系からはどういう値に見えるかという問題を考えることがままあります。 例えばカメラの撮像系等でこのような話はよくありますね。
図で示すと以下のような感じです。
毎回こんな図を書いて確認してしまうので記事として書いて答え合わせにしようと思います。
前提:ベクトルの記法について
どの座標系からみたベクトルかを左に添え字で書いています。そこそこPopularな記法だと思います。
例えばはベクトルを座標系2の上で解釈したものということにします。
また,,はそれぞれ座標系1から2への回転と並進の変位を表しています。教科書では1,2等の数字ではなく記号で書かれることが多いですね。
導出
まず座標系1からみた「座標系2to点P」のベクトルは
のようになります。 このベクトルを座標系2から見た時は,回転量の分だけ逆に回せばいいので(2次元座標で考えてみればすぐわかる)
\begin{align} ^2 t_{2p} = ^1_2 R^\top {}^1 t_{2p} \end{align}
これを先程の式と合わせると
\begin{align} ^2 t_{2p} = ^1_2 R^\top {}^1 t_{1p} - ^1_2 R^\top {}^1_2 t \end{align}
となります。
答え
結局これを以下のような同次表現に落としたとき,
\begin{align} ^2 t_{2p} = [R | t] \begin{bmatrix} {}^1 t_{1p} \\ 1 \end{bmatrix} \end{align}
R,tと「座標系1と2の関係を表すR,t」との関係は \begin{align} R = ^1_2 R^\top \\ t = - ^1_2 R^\top {}^1_2 t \end{align} という風になるわけです。この関係式はちょうど逆もしかりになります(当たり前)。
画像系でよくこのRとtを求めることがありますがGlobal座標でのカメラの位置に変換するにはこのような手順を踏む必要があるのを忘れないように。
余談:はてなブログでTex記法を書く方法
以下の呪文を冒頭で唱えた後に,
<p style="display: none;">[tex: ]</p>
\begin{align}
で囲むなどすれば通常とほぼ同様に書くことが出来ます。ただし,^や_などの文字には\でエスケープが必要になるらしく,特にt_{1p}
などの複数の文字を配置したい場合にはt\_{1p}
のように書いたほうが安定するようです。
[参考サイト]
はてなブログで数式が綺麗に書けるTeXの便利ワザ for 見たままモード - cBlog
追記: 最近同じことを書いているサイトを見つけました。No more 車輪の再発明。金言ですね。私はやってしまったわけですがw mem-archive.com
Travis CIを用いたPythonパッケージのテスト管理手順
本来は公式のチュートリアルに従うのが良いと思いますが,既存のコードにとりあえずTravisのマークを載せたいという方には参考になるかと思います。
Travis CIで何ができるのか
端的にはGithub等でコードを管理する際に自動でテストを走らせる事ができるツールです。
↓よく見るこれです。
公式の説明はこちら。
Core Concepts for Beginners - Travis CI
自分の行ったプログラムの変更がシステムに影響を及ぼさないか自動でチェックしてくれます。
今の所,チェックなんてローカルでやるし具体的な共有相手みたいなのも想定していないのでせいぜい環境を移行した時にどう動くかの簡易チェック的なものでしか運用しない予定です。 でもOSSの人たちはみなやっているようなので今はフリだけでもできるようにしておこうというやつです。
本記事では最小限Travis CIでbuildを通すやり方のみ記載します。
賢明な皆さんは是非きちんとしたサイトや資料を元に一から勉強して私に教えてください。
下準備:TravisCIへの登録とリポジトリのアクティベーション
Githubのアカウントを持った状態で以下のTravis.comのページに飛びます。
ここからGithubアカウントとの連携をぽちぽちと進めて登録は終了です。
その後右上のメニューからSettingを選んで以下の画像のように適用するリポジトリを選びます。
これで下準備は完了です。
.travis.ymlの作成
.travis.yml
というファイルを作成することで自動テストを実行することが出来ます。
例えばPython3.5のパッケージで何某かを実行させたい場合は以下のように書きます。
language: python python: - "3.5" install: - python setup.py install #- pip install yaml script: - python test.py
- 最初の行,
language:python
とあるのはどの言語でテストするかを示します。 python:
以下ではテストするバージョンを選択できます。install:
以降はどのモジュールが必要になるか記述します。setup.pyがある場合はそれを元にインストールすればいいですが,普通にpipから始まるコマンドも書けます。script:
以降では実際実行するコマンド群を表しています。この結果帰ってくる値が0であればテスト成功と判定されます。普通にディレクトリ移動などのシェル操作も出来ます。
結局やってることはリモートで仮想環境を立てて,該当する言語の環境を用意し,テストを実行・判定するという流れのようです。 先程のWebページからどのように処理が進んでいるか確認できます。
こんな感じ。
ハマったこと
いろいろくだらないミス等をたくさんしましたが,一番時間を取られたのは,
- GUIを表示するタイプ(cv2.imshowとか)のプログラムはテストを通すのが難しい
ということです。( X server うんたらとかいうErrorが出る)
dvfbというのを使ってGUIの設定などできるようですが,今の理解度ではGUIの有無をSwitchできるように調整する方が早かったです。
テストが通ったら
以下の画面の箇所をクリックするとこの画像のURLを入手できます。
これをマークダウンでREADMEに貼り付ければ自分のパッケージに反映されます
余談:リポジトリの構造について
継続的インテグレーションとありますが結局ちゃんとしたリポジトリの構造から用意するのが良いです。
リポジトリの構造については様々な意見を聞きますが以下の記事の通りにするのが妥当そうです。
しかし開発とかいろいろやってると変なフォルダたくさん作っちゃったりしてややこしくなるんですよね。 意図的に機能ごとにブランチを切る開発スタイルに自然と移行したいものです。
setup.pyについて
さっきの記事にもありましたが,setup.pyを置いておくといろいろ捗る傾向にあります。
説明は今回は省くとしてこんな感じに書いたりします。(ちょっと元ファイルから変えてる)
from setuptools import setup, find_packages import sys sys.path.append('./sample') setup( name = 'HOGE', version = '0.1', description='This is test codes for travis ci', install_requires=['numpy','opencv-contrib-python==3.4.0.12','docopt','matplotlib'], packages = find_packages(exclude=('sample', 'markers','trial')), license = 'MIT', author='Ossyaritoori', )
あとがき
この手のインテグレーションの話を見るとやはりはじめが肝心です。 きちんとした手法で最初から管理している場合はこんなことには悩みませんが,我流でいろいろコーディングしている所にこういうツールを導入するのは非常にコストが重いように思われます。
エンジニア個人としてはどこかのタイミングで導入に踏み切ってリポジトリ単位で慣れていくというのが良いように思いますが,組織としてこういうのを導入して管理するのは結構難しそうです。 大変なことをする割に単体では実務自体にはつながらないのが辛い所ですよね。 「N日人かけて自動でテストできるようにしました。」『それで次の機能はいつ作るの?』「…」
次のプロジェクトからはきちんと構造考えて作る…!
山手線車内に荷物を忘れてしまった件
はい。表題の通りです。 めっちゃ焦りました。その後風邪もひいて踏んだり蹴ったりだったのをよく覚えています。
初動:最初にすべきこと
忘れ物の確認方法には、対応の早い順に以下の3つが考えられます。
- 電車内を直接調べる
- 駅員さんに聞く
- 遺失物問い合わせセンターに電話
これを踏まえて荷物を忘れた人が最初にすべきことは,「乗っていた電車の発着時間や号車を確認する」ことです。
電車の特定の重要性
山手線には終点が存在せず,複数の電車が同じ経路をぐるぐる回っているため電車を特定しない限り,目的の車両にたどり着くのが困難になります。
電車の特定には,
- 出発駅と時間,到着駅と時間
- 内回りか外回り(反時計or時計回り)
の情報が最低限必要です。
駅に駆け込んだり,電話をする前に必ず乗っていた電車の情報をなにかにメモするようにしましょう。
車両ナンバーの特定方法
出発駅と到着駅とその時間さえ特定すれば駅員さんが車両を調べてくれますが,これを自分で予め調べておくことができます。
メリットとして,事前に車両ナンバーを抑えておけば,駅員さんに現在その車両がどこを走っているか聞くことができます。(時間外でしたが番号までわかっているということで特別に対応して貰った感があります。) 自力で探すつもりの人は調べ方を抑えておいても良いでしょう。
- えきから時刻表での探し方を示します。 www.ekikara.jp
-「品川駅」で検索した後,次の画面から山手線を選択します。
次のような時刻表が出るので乗った時刻または降りた時刻を選択します。(内回り・外回り・平日休日などの違いに注意)
次のような画面になります。この場合「1011G」というのが目的の車両番号です。
何時の~~という説明も必要になることが多いですが,車両番号を聞けば即対応してくれる駅は多かったです。 (なお,私のときは大崎の車庫に入ってしまったので延長戦に入ったわけですが。。。。)
号車をたどる
乗っていた号車を頑張って辿ったんですが正直あんまり聞かれなかったので何号車かまではガッツリ調べなくてもいい気がします。 駅構内の地図を駆使して頑張れば推定できます。不安ならどうぞ。
電車内を直接調べる,駅員さんに聞く
初動で時間があるor電車がまだ走っているならば,電車を直接調べることができます。 私は経験していないので他のサイトを参考にしてください。
延長戦:一日たったあとにすること
電車が車庫に入ったり,荷物が見つからなかったりした場合延長戦に入ります。 一日後の午後には車庫にあった忘れ物もシステムに登録されるため,検索をお願いすることが可能になります。
ここでは,電話でのお問い合わせセンターやお忘れ物承り所にアクセスすることになります。 www.jreast.co.jp
時間があるならお忘れ物承り所に行くべき
多くの方が電話で済ませているようですが,個人的には東京・上野・品川にあるお忘れ物承り所に行くのがおすすめです。
電話では,
- 待たされる
- 調べ方に柔軟性がない
- 意思疎通に問題がある
といったデメリットが存在します(4回電話して見つからず,承り所で一発で見つけた人並みの感想)。
承り所の方がきめ細やかな対応をしていただけたのでゆとりがある人は承り所に行ったほうが結果的に早く見つかる可能性が高いと思います。20時までやっているので勤め人でもなんとか間に合うのではないでしょうか。
見つからなかったら
電話などで「みつからない」と言われても絶望するには少し早いです。 車庫に行ってしまってまだ登録されていないのかもしれませんし,カバンと思っていたものが別のカテゴリで登録されていたり(僕はこれでした)というのがあるので時間を改めて何度か電話・承り所を訪れるのが良いと思います。
どうしても見つからなかったら…この記事を見ている場合ではありません。駅に電話して警察に届け出るのが最終解でしょう。
そもそもなくさないように
始末書の類はだいたい次回の防止策を要求してきます。一応今回の反省として,
電車に乗らない。網棚をなるべく使わないというのは結構有効な手段と思われます。立っているときでも足元に荷物を置くようにすればという風に考えています。
「よーし,もうこれで電車にものを忘れないよ! もし忘れたら木の下に埋めてもらっても構わないよ!」
(完)