らっちゃいブログ

日々の学びと気づきを発信するブログ

Pillowで画像を正方形に切り抜く方法

以前 Pillow を使ったサムネイルの作成方法について記事を書きましたが、今回はその続きです。

racchai.hatenablog.com

ユーザー画像等、サムネイルにした上で正方形に切り抜きたいケースが多々ありますよね。ではさっそくやり方を見ていきましょう。

事前準備

お決まりのインストール作業から。

$ pip install pillow

インストールができたら、以下のサムネイル生成コードをベースに解説していきます。

from PIL import Image

img = Image.open('original.jpg', 'r')
thumbnail_size = (100, 100)
img.thumbnail(thumbnail_size)
img.save('thumbnail.jpg', 'JPEG')

解説

Pillow では crop 関数というものが用意されていて、box と呼ばれる(left, top, right, bottom)のような座標を示すタプルを指定することで画像を切り取ることができます。

Image.crop(box=None)

つまり、box の指定を工夫すれば元画像を正方形に切り取ることができるわけですね。それでは実際の座標を計算していきます。

辺の長さを決定する

正方形に切り取るわけですから、まずは一辺の長さを決める必要がありますね。今回は、長方形の短い辺に合わせて長い方の辺を切り取る方式を取ることにします。

縦・横の短い方を採用するわけなので、以下のようにしてベースの長さを取得することができます。

img = Image.open('original.jpg', 'r')
square_size = min(img.size)

簡単ですね。

切り取る座標を決める

Pillow では画像の座標を指定して切り取ることになりますので、長さを取得しておきましょう。

width, height = img.size

次に座標位置を計算するわけですが、縦横どちらが長いかで処理が違ってきます。まずは width が大きい場合の座標を計算してみます。

(left, top, right, bottom)それぞれの座標を計算していくわけですが、width が大きい場合(横長)は縦方向の座標が計算しやすいので先に出してしまいます。

まずは top ですが、縦方向には切り取らないので 0 でOKです。そして bottom は top の位置から一辺の長さを加えた位置になりますので、以下のようになります。

top = 0
bottom = square_size

次は横方向ですが、こちらも複雑な計算は不要です。先に left を算出しますと、切り取る辺の長さから一辺の長さを引き、2で割った位置へ移動させればよいだけです。

left = (width - square_size) / 2

そして right の位置は left に一辺の長さを加えれば良いだけですね。

right = left + square_size

まとめると、以下のようになります。

img = Image.open('original.jpg', 'r')
square_size = min(img.size)

top = 0
bottom = square_size

left = (width - square_size) / 2
right = left + square_size

box = (left, top, right, bottom)

height が大きい場合も同様に考えれば、簡単に座標を決めることができますね。

img = Image.open('original.jpg', 'r')
square_size = min(img.size)

left = 0
right = square_size

top = (height - square_size) / 2
bottom = top + square_size

画像を切り取る

座標さえ決まってしまえば、あとは切り取るだけです。 縦横どちらが大きいかを分岐して座標を計算し、実際に切り取るまでのコードは以下のようになります。

from PIL import Image

img = Image.open('original.jpg', 'r')
square_size = min(img.size)

if width > height:
    top = 0
    bottom = square_size
    left = (width - square_size) / 2
    right = left + square_size
    box = (left, top, right, bottom)
else:
    left = 0
    right = square_size
    top = (height - square_size) / 2
    bottom = top + square_size
    box = (left, top, right, bottom)

img = img.crop(box)

thumbnail_size = (100, 100)
img.thumbnail(thumbnail_size)
img.save('thumbnail.jpg', 'JPEG')

ひとこと

画像を切り取るコードって意外とすっきり書けるものですね。

ただ、実のところ上記の計算方法だと1ピクセルほどずれてしまい、正確な正方形にならない場合がありますので、厳密な計算が必要な方は注意しましょう。

【Nginx】ガラケーからSSL接続できなくて原因を調べたときの話

経緯

ガラケーからアクセスすると

「ユーザ証明書がありません」

の表示が出てアクセスできない、というユーザーからの問い合わせで発覚。

調査

まず設定内容を確認。

ガラケーとそれ以外でドメイン分けて名前ベースのバーチャルホストを切り、それぞれ異なる証明書を設定してました。

わざわざバーチャルホストを切ったのは、メインの証明書はワイルドカード証明書になっていて、ガラケーはそれに対応できないため。めんどくさいですよね。

次に状況を確認。手元にガラケーがないので Mac でアクセスしてみると、問題なくアクセスできてました。ひとまず証明書の期限が切れているわけではなさそう。

サーバー側にクロスルート証明書を仕込み忘れてたのかな?と思いきや、確認してみるとちゃんと入ってました。さすが自分。

となると、サーバー側の設定に問題はないように見える。こうなるとクライアント側の問題なのだろう。

そう思ってルート証明書が入ってないのか等々ガラケーについて調べていたところ、近くにいる人がたまたま docomoガラケー持っていたので、試させてもらうことができました。

すると「このサイトは安全でない可能性があるため接続できません」というエラーが。

これは何かおかしいと思い、サーバー側に tcpdump を仕込んでSSL通信を確認したところ、なんとガラケー用ではないワイルドカードの証明書が渡されているではないですか。なんてこったい。

ひとまずこれでサーバー側の設定がおかしいことが確定しました。

きっと正しいバーチャルホストへアクセスが行っていなそうだったので、ガラケー側バーチャルホストにだけデバッグログを仕込んでみると、正しくアクセスが来ている。うーん、しかしなんでか SSL のログが少ないような・・・

!!!

ここで閃きました。SSL だけバーチャルホストの設定が効いていないように見える。つまり、SSLだけが別バーチャルホスト(デフォルト設定)で動作している!

ここまでくるともうほぼ原因が見えていて、SNI が効いていない説が濃厚でした。なにしろガラケーは SNI に対応していないんですよね。すっかり忘れておりました。

というわけで、ガラケー用にIPを増やしてバーチャルホスト設定をIPベースにしてみたところ正しく動作するようになりました。(さらっと書いたけどこの作業が一番しんどかった)

ひとこと

運用が大変なので、ガラケー使ってる人はスマートフォンに変えてください!

Apache HTTPDに認証なしでコンテンツにアクセスできてしまう脆弱性

Apacheにクライアント認証を設定していても認証なしでアクセスできてしまう脆弱性が見つかりました。

JVNVU#97485903: Apache HTTPD の HTTP/2 通信における X.509 クライアント証明書の認証処理の問題

影響を受けるバージョンは2.4.18 から 2.4.20 までになります。

これだけだと非常に影響範囲が広い事案となりますが、本件は HTTP/2 でのアクセスに限られるそうです。というわけで、影響するバージョンでHTTP/2とクライアント認証をご利用の方は最新バージョンにアップデートしましょう。

racchai.hatenablog.com

racchai.hatenablog.com

racchai.hatenablog.com

Nginx1.11.2がリリースされました

変更点を見てみます。

https://nginx.org/en/CHANGES

*) Change: now nginx always uses internal MD5 and SHA1 implementations;
   the --with-md5 and --with-sha1 configure options were canceled.

*) Feature: variables support in the stream module.

*) Feature: the ngx_stream_map_module.

*) Feature: the ngx_stream_return_module.

*) Feature: a port can be specified in the "proxy_bind", "fastcgi_bind",
   "memcached_bind", "scgi_bind", and "uwsgi_bind" directives.

*) Feature: now nginx uses the IP_BIND_ADDRESS_NO_PORT socket option
   when available.

*) Bugfix: a segmentation fault might occur in a worker process when
   using HTTP/2 and the "proxy_request_buffering" directive.

*) Bugfix: the "Content-Length" request header line was always added to
   requests passed to backends, including requests without body, when
   using HTTP/2.

*) Bugfix: "http request count is zero" alerts might appear in logs when
   using HTTP/2.

*) Bugfix: unnecessary buffering might occur when using the "sub_filter"
   directive; the issue had appeared in 1.9.4.

stream モジュールと HTTP/2 関連の新機能とバグフィックスが目立ちますが、特段目立った変更はなさそうです。

あえて特筆するとすれば、IP_BIND_ADDRESS_NO_PORTオプションを指定することが可能になった点でしょうか。

NO_PORT と言っていますが、ポートを割り当てないという意味ではありません。ポートの割り当てタイミングをBIND時ではなく実際の接続時に変更するような動作となります。何がしたいかというと、dst ip が異なる場合には利用中のポートを再度割り当てることで、エフェメラルポートを節約することができる機能のようです。

私自身はエフェメラルポートが枯渇するような状況には陥ったことはないですが、必要な人には必要な機能なのでしょうね。

racchai.hatenablog.com

racchai.hatenablog.com

racchai.hatenablog.com

libcurlに任意のコードを実行される脆弱性

https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-4802

記事によると、SSPIまたは Telnet を使用してビルドされている場合に検索パスに関する脆弱性が複数存在するとのこと。この脆弱性を悪用することで、任意のコード実行が可能となるそうです。

影響を受けるバージョンは 7.49.1 未満になります。以下でパッチが公開されていますので、ご利用の方は忘れずに適用しましょう。

cURL - Windows DLL hijacking

racchai.hatenablog.com

racchai.hatenablog.com

racchai.hatenablog.com

Apache Commons FileUploadにDoS脆弱性

IPA から Apache commons FileUpload のDoS脆弱性についての情報が公開されました。

JVN#89379547: Apache Commons FileUpload におけるサービス運用妨害 (DoS) の脆弱性

影響は以下の通りです。

  • Commons FileUpload 1.3 から 1.3.1 まで
  • Commons FileUpload 1.2 から 1.2.2 まで
  • Tomcat 9.0.0.M1 から 9.0.0M6 まで
  • Tomcat 8.5.0 から 8.5.2 まで
  • Tomcat 8.0.0.RC1 から 8.0.35 まで
  • Tomcat 7.0.0 から 7.0.69 まで
  • Struts 2.5.1 およびそれ以前

上記に加え、現在ではもうサポート対象となっていない 1.0.x および 1.1.x も影響範囲とのことですので、すべてのバージョンが対象と思ってよさそうです。

ちなみに具体的な攻撃方法については何も記載がなく、マルチパートリクエスト処理の中に問題があるとのことです。

近年人気の Spring でも利用しているライブラリですので、影響する場合はアップデートしておきましょう。

Apache Httpd および Apache Tomcat は、それぞれ LimitRequestFileSize および maxHttpHeaderSize の設定により、HTTP リクエストヘッダの大きさを制限することが可能です。また、上限を 2048 バイトに設定することで本脆弱性の影響を受けないとしています。

とのことなので、HTTPヘッダーサイズを2048バイトに制限することでも回避することは可能みたいですね。アップデートするのが一番安全ではありますが、簡単に上げられないという方は設定変更で回避しましょう。

racchai.hatenablog.com

racchai.hatenablog.com

racchai.hatenablog.com

Error 'Table './mysql/proc' is marked as crashedが起きたときの対処についてメモ

windows のアップデートが走ってしまい、その際にMySQLが動作していた VM も道連れに死んでしまったためか、それ以降以下のエラーが出るようになってしまいました。バージョンは5.6です。

ERROR 145 (HY000): Table './mysql/proc' is marked as crashed and should be repaired

エラー内容を元に調べてみると、repairコマンドを使うと良いらしいことがわかったので試してみました。

database - Table './mysql/proc' is marked as crashed and should be repaired - Stack Overflow

mysql> repair table mysql.proc;
+------------+--------+----------+----------+
| Table      | Op     | Msg_type | Msg_text |
+------------+--------+----------+----------+
| mysql.proc | repair | status   | OK       |
+------------+--------+----------+----------+
1 row in set (0.00 sec)

一応これで現象自体は直ったわけですが、どういう状態になっていたのがどう直したのかがさっぱりわからず、もやもやしますね。

mysql.proc はプロシージャを管理するテーブルのはずなので、プロシージャを消してしまえばもう起きないということなら消してしまいたい衝動にかられています。