0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pillowのcrop前にEXIF回転、左上の画素も変わる

0
Posted at

3×2のPNGが、Pillowで向きを補正すると2×3になった。左上の画素も10から40へ変わる。

開いた画像を画面どおりの座標で crop するとずれる。自分は切り出し位置を確認してて、左上の値の変化に引っかかった。

LLMへ送る画像と、切り出しに使う画像の向きはそろえたい。

PillowでEXIFの向きを再現する

CPython 3.14.8、Pillow 12.3.0で確認。次を orientation.py に保存し、python3 orientation.py で動かす。PNGならJPEGの圧縮で画素値が変わるのを避けられる。

from io import BytesIO
from PIL import Image, ImageOps

image = Image.new("L", (3, 2))
image.putdata([10, 20, 30, 40, 50, 60])
exif = Image.Exif()
exif[274] = 6
buf = BytesIO()
image.save(buf, format="PNG", exif=exif)
buf.seek(0)
with Image.open(buf) as raw:
    fixed = ImageOps.exif_transpose(raw)
    print(raw.size, fixed.size)
    print(raw.getpixel((0, 0)), fixed.getpixel((0, 0)))
    print(raw.getexif().get(274), fixed.getexif().get(274))
    again = ImageOps.exif_transpose(fixed)
    assert again.size == fixed.size
    assert again.tobytes() == fixed.tobytes()

出力はこうなる。assertも通った。

(3, 2) (2, 3)
10 40
6 None

cropに渡す座標はどちらの画像のものか

EXIFの274は向きのタグ。値6は表示時に時計回りへ90度回す指定で、保存された画素はそのまま残る。このPNGの raw では、左上は10だった。

ImageOps.pyの実装も確認した。exif_transpose は値6を ROTATE_270 に対応させ、画素を回してから向きのタグを削除する。Pillowの回転指定は反時計回りなので、この270度は時計回り90度と同じ。タグが消えるため、再適用しても回らない。

幅と高さだけ入れ替えても、左上の10は40にならない。

EXIFの向き6を反映し、3×2の画素配列が時計回りに回って2×3となる流れ。左上が10から40へ変わり、補正後の画像を送信と切り出しに使う。

LLMへ送る画像は、向きの補正を済ませてからリサイズする。切り出しにもその画像を使う。元の解像度で切り出すなら、補正後の幅と高さを使って座標を戻す。返ってきた座標がピクセル値か正規化値かも、呼び出す側の契約として決めておく。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?