17

これは、PIL の基本的な変換の質問です。これを正しく実装するために、過去数年間で少なくとも数回試しましたが、PIL の Image.transform についてよくわからないことがあるようです。画像の限界を明確に示すことができる相似変換 (またはアフィン変換) を実装したいと考えています。私のアプローチが機能することを確認するために、Matlab に実装しました。

Matlab の実装は次のとおりです。

im = imread('test.jpg');
y = size(im,1);
x = size(im,2);
angle = 45*3.14/180.0;
xextremes = [rot_x(angle,0,0),rot_x(angle,0,y-1),rot_x(angle,x-1,0),rot_x(angle,x-1,y-1)];
yextremes = [rot_y(angle,0,0),rot_y(angle,0,y-1),rot_y(angle,x-1,0),rot_y(angle,x-1,y-1)];
m = [cos(angle) sin(angle) -min(xextremes); -sin(angle) cos(angle) -min(yextremes); 0 0 1];
tform = maketform('affine',m')
round( [max(xextremes)-min(xextremes), max(yextremes)-min(yextremes)])
im = imtransform(im,tform,'bilinear','Size',round([max(xextremes)-min(xextremes), max(yextremes)-min(yextremes)]));
imwrite(im,'output.jpg');

function y = rot_x(angle,ptx,pty),
    y = cos(angle)*ptx + sin(angle)*pty

function y = rot_y(angle,ptx,pty),
    y = -sin(angle)*ptx + cos(angle)*pty

これは期待どおりに機能します。これは入力です:

ここに画像の説明を入力

これは出力です:

ここに画像の説明を入力

これは、同じ変換を実装する Python/PIL コードです。

import Image
import math

def rot_x(angle,ptx,pty):
    return math.cos(angle)*ptx + math.sin(angle)*pty

def rot_y(angle,ptx,pty):
    return -math.sin(angle)*ptx + math.cos(angle)*pty

angle = math.radians(45)
im = Image.open('test.jpg')
(x,y) = im.size
xextremes = [rot_x(angle,0,0),rot_x(angle,0,y-1),rot_x(angle,x-1,0),rot_x(angle,x-1,y-1)]
yextremes = [rot_y(angle,0,0),rot_y(angle,0,y-1),rot_y(angle,x-1,0),rot_y(angle,x-1,y-1)]
mnx = min(xextremes)
mxx = max(xextremes)
mny = min(yextremes)
mxy = max(yextremes)
im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,(math.cos(angle),math.sin(angle),-mnx,-math.sin(angle),math.cos(angle),-mny),resample=Image.BILINEAR)
im.save('outputpython.jpg')

これは Python からの出力です。

ここに画像の説明を入力

私は何年もの間、複数の OS で複数のバージョンの Python と PIL を使用してこれを試してきましたが、結果は常にほとんど同じです。

これは問題を説明する最も単純なケースです。必要な回転であれば、 im.rotate 呼び出しで回転を実行できますが、せん断とスケーリングも行いたいと考えています。これは、説明するための単なる例です。問題。すべてのアフィン変換で同じ出力を得たいと考えています。これを正しく取得できるようにしたいと思います。

編集:

変換行を次のように変更すると:

im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,(math.cos(angle),math.sin(angle),0,-math.sin(angle),math.cos(angle),0),resample=Image.BILINEAR)

これは私が得る出力です:

ここに画像の説明を入力

編集#2

-45 度回転し、オフセットを -0.5*mnx および -0.5*mny に変更すると、次のようになりました。

ここに画像の説明を入力

4

4 に答える 4

19

わかった!だから私は週末を通してこれを理解することに取り組んできました、そして私は私を満足させる答えを持っていると思います. コメントや提案をありがとうございました!

私はこれを見ることから始めます:

PIL pythonのアフィン変換?

著者は任意の相似変換を行うことができますが、コードが機能しなかった理由を説明していません。また、変換する必要がある画像の空間レイアウトについても説明しておらず、私の問題に対する線形代数解も提供していません。

しかし、彼のコードから、彼が行列の回転部分 (a、b、d、および e) をスケールに分割していることがわかります。私が引用したPILのドキュメントを読みに戻った:

"im.transform(size, AFFINE, data, filter) => 画像

画像にアフィン変換を適用し、結果を指定されたサイズの新しい画像に配置します。

データは、アフィン変換行列の最初の 2 行を含む 6 タプル (a、b、c、d、e、f) です。出力イメージの各ピクセル (x, y) について、入力イメージの位置 (ax + by + c, dx + ey + f) から新しい値が取得され、最も近いピクセルに丸められます。

この関数を使用して、元の画像の拡大縮小、変換、回転、せん断を行うことができます。」

したがって、パラメーター (a、b、c、d、e、f) は変換行列ですが、宛先画像の (x、y) を (ax + by + c、dx + ey + f) にマップするものです。ソース画像。ただし、適用する変換マトリックスのパラメーターではなく、その逆です。あれは:

  • Matlab とは異なる
  • しかし、幸いなことに、私は完全に理解しています

コードを添付しています:

import Image
import math
from numpy import matrix
from numpy import linalg

def rot_x(angle,ptx,pty):
    return math.cos(angle)*ptx + math.sin(angle)*pty

def rot_y(angle,ptx,pty):
    return -math.sin(angle)*ptx + math.cos(angle)*pty

angle = math.radians(45)
im = Image.open('test.jpg')
(x,y) = im.size
xextremes = [rot_x(angle,0,0),rot_x(angle,0,y-1),rot_x(angle,x-1,0),rot_x(angle,x-1,y-1)]
yextremes = [rot_y(angle,0,0),rot_y(angle,0,y-1),rot_y(angle,x-1,0),rot_y(angle,x-1,y-1)]
mnx = min(xextremes)
mxx = max(xextremes)
mny = min(yextremes)
mxy = max(yextremes)
print mnx,mny
T = matrix([[math.cos(angle),math.sin(angle),-mnx],[-math.sin(angle),math.cos(angle),-mny],[0,0,1]])
Tinv = linalg.inv(T);
print Tinv
Tinvtuple = (Tinv[0,0],Tinv[0,1], Tinv[0,2], Tinv[1,0],Tinv[1,1],Tinv[1,2])
print Tinvtuple
im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,Tinvtuple,resample=Image.BILINEAR)
im.save('outputpython2.jpg')

そしてpythonからの出力:

ここに画像の説明を入力

この質問に対する答えを、最後のまとめとしてもう一度述べさせてください。

PIL では、適用するアフィン変換の逆が必要です。

于 2013-06-17T07:00:27.500 に答える
1

これはあなたの質問に答えるべきだと思います。

そうでない場合は、アフィン変換を別の変換に連結できると考える必要があります。

したがって、目的の操作を次のように分割できます。

  1. 原点を画像の中央に移動

  2. 回転中

  3. 原点を戻す

  4. サイズ変更

これよりも、単一の変換を計算できます。

于 2013-06-14T06:37:31.117 に答える