FF14の画像をバッチで画質処理+コピーライトの貼り付けしたい

文中の画像の権利表記:Copyright (C) 2010 – 2018 SQUARE ENIX CO., LTD. All Rights Reserved.

日記書くの二年ぶりだって。まじか。
ちょっと開発してるやつあるんで作業メモ。

やりたいこと:

  • たくさん画像あるやつを一律で補正して、固定イメージはっつけて別名で保存したい
  • 素材の画像をバッチに食わせて、別名で新しい画像を得たい
  • 要はFF14の画像を大量に画質処理+コピーライトの貼り付けしたい
  • 年が変わるとコピーライトの年表示が変わるから、繰り返し生成しなおすことを視野に入れたい

ふだんFireworksつかう(Photoshopはもう完全に使い方忘れた。かつては仕事道具だったのに)のだけど、こいつがバッチ処理みたいな機能ある。これつかって楽できんかと思ったけども、せいぜいサブネイル作るくらいのしょっぱい代物で話にならない。
もういい、画像ライブラリ振り回してなんかできるじゃろ、数学得意じゃないけどトーンカーブもなんとかなるじゃろということで。

Jupyter notebookで実験

とりあえずJupyterNotebook起動して、Pillowとかいろいろ読み込んどく

import os
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

はりさんの画像を読み込んでみる。

いいんじゃないすか。上のはりさんはスクリーンショットを切り取って小さくしただけのもので、補正やらは一切なし。暗い屋内だわくすんでるわ、ぱっとしない。

ほんではどっかから拾ってきたサインカーブ作るやつをplotで描かせてみる。
やっべグラフとか描いちゃってかっけえ。理数系ってこういうの鼻歌でやるんじゃろかっけえ。

ともかく、各ピクセルにこいつを食わせると暗い色をより暗く、明るい色をより明るくしてメリハリが付く。

ほんとはカーブの具合を上みたいな感じのカーブにいじりたいが、式の書き方がわかんないのでいったんこれで。拙速がだいじ。
さしあたり効率やらノウハウやらわかんないので、愚直に各ピクセルのRGB値を変換していく。

後ろの照明がぱっと明るくなってたり、はりさんの顔にあたってる明かりが明るくなってたり。フィルタはちゃんとかかったということで進める。

次、コピーライト画像を読み込む。
これを読み込んで、画像(今回ははりさんの画像)の、所定の位置にコピーライトを貼り付けたい。
ちょっと困ってるのが、元の画像ったらわりと大きめのマージンがあること。このマージンがだいぶ邪魔なので、ほんとは空白部分をトリムしたい。
cropでうまくいくとあったんだが、正確に0でないピクセルがありそうな気配でtrimかかんない。
一旦いいや。実際やるときはマージン分をいいぐあいに調整して貼り付けちゃおう。

はりさん画像とコピーライト画像のサイズを取得して、はりさん画像のどの座標にコピーライト画像を貼り付けたらいいかを調べる。テキトウにpaste_pos関数作っておこう。2つの画像をあげると、どの座標に描いたらいいかを返すやつ。横0縦184ピクセルのとこに貼り付けたらいいとわかった。

Image.pasteにコピーライト画像、貼り付け先の座標を渡す。引数のmaskにもコピーライト画像をわたしてるのは、これでアルファチャンネルをよしなにしてくれるとのことで。
上のサンプルではコピーライト画像のほうが横幅が大きいけれども、溢れたぶんはなくなる。
実際にこの仕掛けを利用するときはもっと大きな画像を使うことになるので、横幅あふれた―とかは実際には発生しないので、気にしないでよい。

だいたい実験が完了したので、これをバッチスクリプトとしてまとめたら終わり。
いままで画像処理やること少なかったわ、式わたして数値の変換するなんてこともやったことなかったわで、若干進歩。プログラミングのための線形代数買ってあるのに積んでるんだよなー

そのうち読もう。こういうのに役立つ何かが書いてあるかも。

Pythonでバッチ書いてcronで実行させると死ぬ

今回の解決方法はこちら。
export PYTHONIOENCODING=UTF-8
bash_profileかなんかに書いとく
おわり。
以下おまけ。

UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 38-41: ordinal not in range(128)

こんなこといわれる。Python書いてるとどっかで必ずぶつかって唸るやつ。
内部エンコードが云々とかはやっとこ理解してきて、中ではunicode、外に出すとき初めてencodeして、とかいうあたりは馴染んできた。
今日悩んでいたのは、自分で叩くと問題なく実行できる、文字列の出力を伴うプログラム。
実行状況をコンソールで見たかったので、あちこちにprintがどかどか入っているもの。
なんとなくうまくいくようなので、じゃあcrontab(これがまあ前時代的とかはあるんだけどrundeckだろうと根っこは同じだ)にセットすると、これがうまくいかない。
文字列を出力するところでいつものやつがでていて死んでる。
どういうこっちゃ。あたしゃ毎日0:30にリモートログインして手で叩かないといけないのか。

結局どうも、シェルに諸々やらせるときには、エンコーディングについていい具合によろしくしてくれないので、わからんのでasciiでいいやと判断され、死ぬということがわかりました。
症状見ればそこまでは分かるんだけど、export LANG=ja_JP.UTF-8云々を書いてもうまくいかないし、なんなのと泣いていたところ、そういえばPYTHONIOENCODINGってあったよね何なのかよく知らんけどという着地をした次第。
新しくこさえたEC2に移行作業してるんですが、そういうまっさらのとこだと、設定の漏れがポコポコ出てくるんですね。
ansibleに追記して保存、一件落着。

参考りんく

Python 3の各種エンコーディングについて – Qiita http://qiita.com/methane/items/6e294ef5a1fad4afa843

Python2で文字列を処理する際の心掛け – Qiita http://qiita.com/FGtatsuro/items/cf178bc44ce7b068d233

FFXIVなんとかかんとか調査メモ

とある何かの中身を調べております。
Itemテーブル
legacy=1は「✝」マークついてたり、英語名にDated(時代遅れの,旧式の)とついたりしている。旧FF14のアイテムなのかな。見るべきものではなさそう。

なんかもーすごいきちんとデータが整理されてて、データ眺めていて気持ちがいい。

ほんとは馬とったとかいろいろ書くことあるんだけど、作業楽しいので後回し。

Rarityが1~7くらいまであるぽ。7はエーテリアル装備かなあ。

仮想環境構築:Vagrant + Docker

Kindle版の本を買って、日々vagrantとdockerについての理解を深めようと努力する日々です。
実のところインフラなんて興味なかった。結線だルーティングだミドルウェアがどうしたこうした。
「ぼかぁもっとクリエイティブな(略」
と思ってたんだけど、クリエイティブな何かをつくろうとするときの用意がめんどくさすぎる。手前の準備が煩雑すぎて、プログラム書くとこまで到達できない。
Vagrantはそもそも職場での開発環境の用意がめんどくさいことに加え、手順が口伝というのを打破するにあたり提案されていたツールで、名前しか知らなかった。
VMでマシン作るときの自動化がこいつで出きると。たしかにVirtualBoxで一個マシン作るのも面倒だった。
で、Dockerをその上に云々
vms
こういうのをやりたい。ずいぶん余計なのも色々あるんですが、Webサーバ幾つかのログをfluentdがかき集め、それをkibanaとかでビビっと束ねた表示とかをしちゃいたい。
この記事、下書きのままずいぶん放置してたんですが、そろそろやんないと。

参考書籍:Vagrant

参考書籍:Docker

SQLAlchemyとSQLiteで日付を取り扱う

あたくしの脳にぼんやりと刻まれている情報によれば、SQLiteは文字列と数値しか扱えないはず。
日付どうする。いやセンセ、日付ぐらいは扱ってくれてもいいのでは。
さておき、SQLAlchemyを通した場合に、なにか賢い仕組みがあるはず。

SQLAlchemyのマニュアルの当該ページには、

Date and Time Types

SQLite does not have built-in DATE, TIME, or DATETIME types, and pysqlite does not provide out of the box functionality for translating values between Python datetime objects and a SQLite-supported format. SQLAlchemy’s own DateTime and related types provide date formatting and parsing functionality when SQlite is used. The implementation classes are DATETIME, DATE and TIME. These types represent dates and times as ISO formatted strings, which also nicely support ordering. There’s no reliance on typical “libc” internals for these functions so historical dates are fully supported.

超訳:
SQLiteは日付型持ってない。SQLAlchemyが独自にこさえてる日付とかの型は、日付フォーマット+SQLiteで使った場合に動作するようになってる。その対象になってるのはDATETIME、DATEおよびTIMEだ。
これらを使う場合に限っては順番とかもちゃんと正しく動くし、ISOフォーマットされた文字列として日付と時刻を表現する。
で、こいつらはlibc内の関数に依存していないので、歴史的な日付が完全にサポートされている。

うわー自信ない。特に最後の一行。逆に読んでたらどうしよう。だって「libc非依存なのでヒストリカルデートが完全にサポートされてる」って、前後つながってる感がない。
だいたい、libcってなんだ。cのライブラリか。

ちょろっと見るとORACLE様の変なページが見つかり、「表 2–10 libc の日付と時間の処理関数」というところで以下の3つの関数の記述がある。

  • getdate() ユーザー形式の日付と時間を変換する
  • strftime() 日付と時間を文字列表現に変換する。
  • strptime() 日付と時間の変換

ははあ。こいつら完全にあたし見たことあるわ、phpで。phpのあの関数ってこいつらをラップしてたんだろか。
見てみる。
こんなページがあった。
PLEAC-PHP(http://pleac.sourceforge.net/pleac_php/datesandtimes.html)

これによると、phpはいろんな方法で日付のサポートをしていて、

  • UNIX/Cライブラリベース
    (localtime, gmtime, strftime, strptime, mktime, time, getdate, gettimeofday)
  • PHPのネイティブ関数(date,strtotime)
  • DateTimeクラスベース

日付の扱い方にこういう種類があるんだよ、とある。
phpのstrftimeとかははラッパーだったんだー、いやそういう感じのことなんだろうなとは思っていたがちゃんと理解した。
Library [libc]-based routinesってことは、Cのライブラリってのがlibcってことでいいんだな。なので、SQLAlchemy+SQLiteにおきましては、こいつらには関係ない作りになってます。ってことですね。
いやー勉強になりました。

pip install umysqldbからのerror: command ‘gcc’ failed with exit status 1

あとで自分が検索できるようにと、エラーメッセージそのものをタイトルにしちゃっています。
重宝します。

経緯はともかく解決方法

aptitude install python-dev

うわあ。
身も蓋もなかったなあ。
gccがどうだのって言われて怯んでたんですけどね。

んで

pip install MySQL-Python

おわり。

よくは知りませんが、PythonのモジュールMySQLdbとかいうのはは死に体なのか、uMySQLdbってモジュールがPyPiでは人気あるようです。スコアが高いのね。
あたしは呼べればなんでもいいですが、カッコつけてコンパイルとかしといて、結局aptさんに入れてもらって解決とか意味がわからない。
あー
コンパイルオプションなんか忘れたのかもしれないなあ。

そしてfuelphpやるかとおもいきやpyramidでセットアップ

phpめんどくさいよ。仕事で使って、なんで要件好きに決められる自分のプロジェクトでも自らphp選択するんだよ。いみがわからない。
ということで、前の記事でphpをアレしてたのは一旦放っておき、愛するPyramidの環境をつくろうと思う。
Nginx+Python+Mysql、Pyramid+Jinja2。個人的にはmaaDBはなんでもいいですけどこの構成が好きです。
まともにアプリ書き終えるとこまで行ったことないですけどね。

pcreateでざざっと作ったあとで思い出す。
昔の記事で、このへんの構成を好みで動かしたやつがあった気がする。プロジェクトテンプレートを作っておけばよかった。手作業はまたディレクトリ指定間違えたりする凡ミスの温床なのよ。

from ほにゃらら import うんたらかんたら

メモ書きエントリ。
呼び出し方で毎度書いて試すくせがあるので、パッとみてわかるサンプルを書いておきたい。

foo.py
    /models/__init__.py
            model.py

こんな構成
各スクリプトの中身(shebangとかは割愛)

models/__init__.py

class Wee(object):
    pass

def fnc_wee():
    pass

models/model.py

class Oyoyo(object):
    pass

def fnc_oyo():
    pass

で、__init__.pyの中の子を呼ぶときと、model.pyの中の子を呼ぶ時の記述まとめ。

其ノ一

import models
from models import model

こんな呼び方で、

print(models.Wee)
print(model.Oyoyo)

print(models.fnc_wee)
print(model.fnc_oyo)

こんなふう。

其ノ二

from models import Wee,fnc_wee
from models.model import Oyoyo,fnc_oyo

こちらはこうで、

print(Wee)
print(Oyoyo)

print(fnc_wee)
print(fnc_oyo)

こう。

「レンダリングされるビューは辞書を返すことができる」

Pyramid。
レンダリングされるビューは辞書を返すことができる
うんそれがどーしたんすか、くらいの感覚だったんですが、HTMLやその部品を返す代わりに辞書を返すことで、テストが楽になるんだよと。
あー。
いまさら納得。

 @view_config(renderer='myapp:templates/mytemplate.pt')
 def myview(request):
     return {'a':1}

デコレータだっけ。なんでこんな面倒くさい感じのことやるんだろうと思ってたんですが、コレやらないと面倒くさいからだったわけですね。
賢くなった。

あらためてPyramid

なんかPyConも間近なんですが今年は行かない予定。モチベーションが。

そんなアレなのも何なので、重い腰を上げて久々にPyramidやらをさわろうと一念発起。
もうなんにもおぼえてないくぽー

さしあたり昔に書き散らしたいろんなアプリ(たいていはPylons)の残骸をかき集めて、それっぽい骨組みの構築開始。
scaffoldでjinja2+alchemyとかのなんかいいのがあればいいんだが無いっぽいので、両scaffoldで生成したプロジェクトの中身を見ながら合成。手でちくちく。scaffold自分で書けるようにならないとこの馬鹿馬鹿しい作業が一生ついてまわるのな。
で、そろそろ目鼻ついてきたかなというところで困ったのがルーティング。
なんか様子が変だ。

map.connectからconfig.add_routeへ

なんかルーティングの登録も仕組みが変わってるのね。
route とビューのサンプル — The Pyramid Cookbook v0.1 (翻訳)

「3.可変のコントローラとアクション。」のあたり。

Pyramid ではルーティング変数によってビュークラスを選択することは できません。

まてw
なにさらっと重要なこと書いてんだ。どうすんのそれ。

# Pylons
map.connect(“/{controller}/{action}”)
map.connect(“/{controller/{action}/{id}”)

こういうのできないって。
えー?

一個ずつ

config.add_route(“help”, “/help/{action}”)

こう書けってことかなあ。正気とは思えんなあ。なんの不都合があってそうしたんだろう。

と思いきや

“pyramid_routehelper” は、 Pylons の map.resource のように動作する config.add_resource メソッドを提供します。

もう。
なんなの。