20180213

matplotlib floatingaxes と Affine2D で、axもimgも回転させ、colorも変えた。大変だった。

今回の目的

とりあえず、作ったものを載せる。

20180212211231.png

特徴は、

  1. 軸(subplot、axes)が回転している。
  2. 回転した軸の中身に、imshow(画像配列データの表示)を入れている。
  3. axの色、labelの色、tickの色、ticksの色、spinesの色、ticklabelsの色を、任意の色に変えている。

たったこれだけなのに、いつも通りにいかなかった。
いつものというのは、色の変更とか、軸の上限値とかをいじるコマンドのこと。
回転させると、いつものコマンドが効かなくなってしまう。
特に、3の、spines、ticklabelsの色を変える所が苦労した。
三連休つぶれた。

これをやろうと思った理由

動画で使うため。画像を回転させたくなった。
ここでやっている様に、行列の掛け算を、右側の行列をひっくり返して、上から移動させていくのをやりたい。
上記リンクの、画像バージョンを、次の動画の、1シーンでやろうとしている。
ここでは載せないが、その内、YouTubeにアップする。
とりあえず、回転だけさせたgifアニメは作った。

abcd.gif

floating_axesとは。

軸を回転させたかったので調べたら、この機能にたどり着いた。
このページの左端のサブプロットがやりたい。floating_axesで、軸を回転させることができることを知った。floating_axesが何をやっているのかは知らない。

単純なカーブのプロットの軸を回転させる。

figure_1-6.png

とりあえず、使ってみる。

pythonは2.7。
必要なライブラリ。

  • numpy
  • matplotlib
  • mpl_toolkits

バージョンは知らん。

このコードで、動くはず。

import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.transforms import Affine2D


#rotation angle
rads=45
#data
v0 = np.linspace(0,2*np.pi*7,100)
v1 = np.cos(v0)
# figure, subplots
fig = plt.figure()

#floatingaxes
plot_extents = min(v0)-1, max(v0), min(v1)-0.5, max(v1)+0.5#max and min of x and y
transform = Affine2D().rotate_deg(rads)
helper = floating_axes.GridHelperCurveLinear(transform, plot_extents)
ax1 = floating_axes.FloatingAxes(fig,[0.2,0.2,0.8,0.7], grid_helper=helper)
ax11=fig.add_axes(ax1)
ax111 = ax1.get_aux_axes(transform)

#plot img
curv1 = ax111.plot(v0, v1, clip_on=False)
#################################
#numbers of ticks and ticklabels#
#################################
helper.grid_finder.grid_locator1._nbins = 10 #horizontal ax
helper.grid_finder.grid_locator2._nbins = 2 #vertical ax

ax11.axis["left"].label.set_text("vertical ax")
ax11.axis["bottom"].label.set_text("horizontal ax")


plt.show()

get_aux_axesしないとだめ。やらなかった場合、カーブを手動で回転させるようなtransformをさせる必要がある。しかも、軸も回転しているので、それも考慮して。単純なプロットの場合は、おとなしくget_aux_axesされたし。
やらないとどうなるか。

figure_1-3.png

はみ出る。
回転させた軸でなく、元の軸を基準に、プロットされている。

引数に、clip_on=Falseしておけば、はみ出してもplotされるので、どうはみ出たのかが分かる。

「表示されない」、「部分的に表示される」などで、意図した画像が出てこない場合、
何が起きているのかが分からないと、解決に時間がかかる。

figure_1-4.png

はみ出ている様子を見れば、「あ、なんか変形してない…」と分かる。

imshowで画像を表示しようとしても何も出てこない。

単純にimshowをすると、画像がでてこない。軸すら出てこない。コードは最後のやつしか載せない。

figure_1-5.png

なにも表示されないの図。



回転させた軸の情報が必要。

  1. img = ax11.imshow(… する。extentでset_xlimなどの代わりにする。
  2. 回転させた軸のtransDataを取得。
  3. イメージデータのset_transformを使って、2を適用する。
figure_1-10.png
出た。しかし、正しく表示されていない。

affineで画像を回転させて、解決。

imshowした後、

  1. ゆがめて
  2. 回転+回転済みの軸情報を追加
    させる必要がある。

そこで、Affine2Dを使う。
このページを参考に、affineを適用してみた。

  1. img = ax11.imshow(… する。extentでset_xlimなどの代わりにする。
  2. img.get_extent()で、extentを得る。
  3. 2.で得たextentを、img._image_skew_coordinate = (x1,y2)
  4. 回転軸の変形情報(transData)に、rotate_deg(deg角度(ラジアンじゃない))で、回転行列情報を加えたものを作る。
  5. イメージデータのset_transformを使って、4を適用する。

できた。

figure_1-12.png

set_ylim, set_xlimを使ってはいけない。

ここで、いつも通り、set_ylimとか使うと、おかしなことになる。
理由は説明できない。
自分は、set_ylim, set_xlimを入れていたのが原因だと知らず、かなり時間を消費してしまった。

「もしかしてaffineの軸が、z軸と入れ替わってる?」とか思って、いろいろ手動で回転行列を試したものの、うまくいかず断念。
「もう諦めよう…」と思って、むしゃくしゃして色々消してたら、偶然set_ylim、set_xlim原因だと特定できた。

figure_1-16.png

こんな風になった。

軸の色の変更

ここから、あとちょっと色を変えるだけだと思っていたが、甘かった。
いつもの軸の色を変えるコマンドが効かない。
google検索して出てきた結果を使っても、効かない。

figure_1-15.png

こうしたいのに、

figure_1-14.png

こうなる。

何がうまくいかなかったのかは、下記コードのコメントアウトしてある所がそれにあたるので、そこを見て下さい。

ちなみに、下記コードでは、画像を変えてしまいました。理由は、縦方向への変化が乏しかったからです。そのほかの画像は、面倒くさいので、変えませんでした。

figure_1-18.png

import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.transforms import Affine2D
import matplotlib.transforms as mtransforms

#rotation angle
rads=45
#data
n=32
m=16

"""
v0 = np.linspace(0,2*np.pi*n)
v1 = np.cos(v0)
v2 = [v1 for i in range(m)]
"""
v0 = [np.linspace(0, 2*np.pi*i, n) for i in range(m)]
#v1 = np.cos(v0)
v2 = np.cos(v0)#[v1 for i in range(m)]


# figure, subplots
fig = plt.figure()


#floatingaxes
plot_extents = 0, n, 0, m#max and min of x and y
transform = Affine2D().rotate_deg(rads)
helper = floating_axes.GridHelperCurveLinear(transform, plot_extents)
ax1 = floating_axes.FloatingAxes(fig,[0.2,0.2,0.8,0.7], grid_helper=helper)
ax11=fig.add_axes(ax1)
#ax111 = ax1.get_aux_axes(transform)

#plot img
img = ax11.imshow(v2, extent=[0,n,0,m], interpolation='none', clip_on=False, cmap="Reds")

x1, x2, y1, y2 = img.get_extent()
img._image_skew_coordinate = (x2, y1)

transform2 = mtransforms.Affine2D().rotate_deg(rads) + ax11.transData
img.set_transform(transform2)


#################################
#numbers of ticks and ticklabels#
#################################

helper.grid_finder.grid_locator1._nbins = 10 #horizontal ax
helper.grid_finder.grid_locator2._nbins = 2 #vertical ax

ax11.axis["left"].label.set_text("vertical ax")
ax11.axis["bottom"].label.set_text("horizontal ax")

#############################
# Use "extent" not set_ylim #
#############################
"""
ax11.set_ylim(m,0)
ax11.set_xlim(0,n)
"""


#########
# spine #
#########
"""
# it does not work.
ax11.spines['bottom'].set_edgecolor([1,0,0])
ax11.spines['top'].set_edgecolor([1,0,0])
ax11.spines['left'].set_edgecolor([1,0,0])
ax11.spines['right'].set_edgecolor([1,0,0])
"""
# it works.
ax11.axis["left"].line.set_color([1,1,0])
ax11.axis["right"].line.set_color([1,1,0])
ax11.axis["top"].line.set_color([1,1,0])
ax11.axis["bottom"].line.set_color([1,1,0])

#########
# ticks #
#########
ax11.tick_params(axis='x', colors=[1,1,0])
ax11.tick_params(axis='y', colors=[1,1,0])

##########
# labels #
##########
ax11.xaxis.label.set_color([1,1,0])
ax11.yaxis.label.set_color([1,1,0])

##############
# ticklabels #
##############
ax11.axis["left"].major_ticklabels.set_color([1,1,0])
ax11.axis["bottom"].major_ticklabels.set_color([1,1,0])

plt.show()

figure_1-17.png

ダメ押しで最後の一枚

ax111 = ax1.get_aux_axes(transform)
ax111.plot(v0,v1+5,color=[1,1,1],lw=3)

を、plt.show()の前に入れれば、画像の上に、プロットできる事が分かる。
線積分の作図で役立つのではなかろうか…

いや、線積分を作図する際は、ぜひ回転していない軸でお願いします。

これまでにも何回かやろうとした

floating_axes自体には、1年前位から気付いてはいた。
が、その当時、画像の回転と、軸の色の問題で引っかかって諦めた。

これまでの自分と違った所は、

  1. インスタンスをしらみつぶしに試す。
  2. ライブラリの中身を覗く努力。
    をしたことだった。

…結局、壮大な時間の無駄使いをしました。
三連休全部つぶした。
こんなくだらない事で、時間を無駄にして欲しくないと思い記す。

このブログに関して

『ブログは1000文字以上でないと、クローラが「価値あるサイト」だと判断しない→検索上位に上がらない』とどこかで見たことがある。なので、頑張って1000文字以上になるようにいろいろ考えていた。

今現在、グダグダ書くと、1000文字を軽く超えてしまう。
これも、コード抜きで2000文字位いっている。
コード入れると5000文字いっている。

むしろ、1000文字以下の方が、まとまっていてよろしいのではないのか。クローラ云々ではなく。

グダグダ書くと、読みにくくなるディメリットがあるが、
その一方でメリットもある。それは、

  • 見直しに時間がかからない。(見直さない。)
  • 疲れない。
  • 長続きする!

このブログのモットーだ。

見直す必要なんかない。

正直、何度も見直す必要なんてない。
出力される絵と、プログラムとが大事なわけだから。
日本語がおかしくても、コードが動けばいいわけだし。
そもそも、コードだって動かなくったって、ネットに書いてある適当な情報なわけだし。

1000文字超えているが…

1000文字超えさせようとしたのは、アクセス数を稼ぐため。
アクセス数を稼ごうとしたのは、アフィのため。

しかし、アクセスカウンターが30000超えていますが、アフィリエイトでは1円も入ってきていません。
(かなり前の二百数十円が、延々繰り越されている。)

だれか、横の楽天のアドから業務用チーズとかチョコを買ってくれ。(PC版でしか見えないかもしれない)

posted by yuchan at 07:00 | Comment(0) | python
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: