11

透明な画像を拡大縮小したいが、後で合成する背景の色がまだわからないとします。残念ながら、PIL は完全に透明なピクセルの色の値を組み込んでいるようで、悪い結果につながります。完全に透明なピクセルを無視するように PIL サイズ変更を指示する方法はありますか?

import PIL.Image

filename = "trans.png"  # http://qrc-designer.com/stuff/trans.png
size = (25,25)

im = PIL.Image.open(filename)
print im.mode  # RGBA

im = im.resize(size, PIL.Image.LINEAR)  # the same with CUBIC, ANTIALIAS, transform
# im.show()  # does not use alpha
im.save("resizelinear_"+filename)


# PIL scaled image has dark border

(0,0,0,0) 黒で完全に透明な背景の元の画像 黒いハローを含む出力画像 gimpでスケーリングされた適切な出力

(0,0,0,0) (黒だが完全に透明) の背景を持つ元の画像 (左)

黒い光輪のある出力画像 (中央)

gimpでスケーリングされた適切な出力(右)

編集:私が探しているものを達成するには、サイズ変更関数自体のサンプリングを変更して、完全に透明なピクセルを無視するようにする必要があります。

edit2:非常に醜い解決策を見つけました。完全に透明なピクセルの色の値を周囲の完全に透明でないピクセルの平均値に設定して、サイズ変更中の完全に透明なピクセルの色の影響を最小限に抑えます。単純な形では遅いですが、他に解決策がなければ投稿します。膨張操作を使用して必要なピクセルのみを処理することで、高速化できる可能性があります。

edit3:事前に乗算されたアルファが最適です-マークの回答を参照してください

4

4 に答える 4

5

PIL は、適切な結果を得るために必要な、サイズ変更の前にアルファ事前乗算を行わないようです。幸いなことに、力ずくで簡単に行うことができます。次に、サイズ変更された結果に対して逆の操作を行う必要があります。

def premultiply(im):
    pixels = im.load()
    for y in range(im.size[1]):
        for x in range(im.size[0]):
            r, g, b, a = pixels[x, y]
            if a != 255:
                r = r * a // 255
                g = g * a // 255
                b = b * a // 255
                pixels[x, y] = (r, g, b, a)

def unmultiply(im):
    pixels = im.load()
    for y in range(im.size[1]):
        for x in range(im.size[0]):
            r, g, b, a = pixels[x, y]
            if a != 255 and a != 0:
                r = 255 if r >= a else 255 * r // a
                g = 255 if g >= a else 255 * g // a
                b = 255 if b >= a else 255 * b // a
                pixels[x, y] = (r, g, b, a)

結果:premultiply、resize、unmultiply の結果

于 2012-11-02T18:29:12.623 に答える
3

各バンドを個別にリサンプリングできます。

im.load()
bands = im.split()
bands = [b.resize(size, Image.LINEAR) for b in bands]
im = Image.merge('RGBA', bands)

編集

たぶん、そのような高い透明度の値を避けることによって(numpyが必要です)

import numpy as np

# ...

im.load()
bands = list(im.split())
a = np.asarray(bands[-1])
a.flags.writeable = True
a[a != 0] = 1
bands[-1] = Image.fromarray(a)
bands = [b.resize(size, Image.LINEAR) for b in bands]
a = np.asarray(bands[-1])
a.flags.writeable = True
a[a != 0] = 255
bands[-1] = Image.fromarray(a)
im = Image.merge('RGBA', bands)
于 2012-10-23T09:44:16.747 に答える
0

画像全体を希望の色で塗りつぶし、アルファ チャネルでのみ形状を作成することはできますか?

于 2012-10-23T09:57:14.333 に答える
0

自分自身に答えて申し訳ありませんが、これが私が知っている唯一の有効な解決策です。完全に透明なピクセルの色の値を周囲の完全に透明でないピクセルの平均値に設定して、サイズ変更中の完全に透明なピクセルの色の影響を最小限に抑えます。適切な結果が得られない特殊なケースがあります。

それは非常に醜く遅いです。より良いものを考え出すことができれば、あなたの答えを喜んで受け入れます。

# might be possible to speed this up by only processing necessary pixels
#  using scipy dilate, numpy where

import PIL.Image

filename = "trans.png"  # http://qrc-designer.com/stuff/trans.png
size = (25,25)

import numpy as np

im = PIL.Image.open(filename)

npImRgba = np.asarray(im, dtype=np.uint8)
npImRgba2 = np.asarray(im, dtype=np.uint8)
npImRgba2.flags.writeable = True
lenY = npImRgba.shape[0]
lenX = npImRgba.shape[1]
for y in range(npImRgba.shape[0]):
    for x in range(npImRgba.shape[1]):
        if npImRgba[y, x, 3] != 0:  # only change completely transparent pixels
            continue        
        colSum = np.zeros((3), dtype=np.uint16)
        i = 0
        for oy in [-1, 0, 1]:
            for ox in [-1, 0, 1]:
                if not oy and not ox:
                    continue
                iy = y + oy
                if iy < 0:
                    continue
                if iy >= lenY:
                    continue
                ix = x + ox
                if ix < 0:
                    continue
                if ix >= lenX:
                    continue
                col = npImRgba[iy, ix]
                if not col[3]:
                    continue
                colSum += col[:3]
                i += 1
        npImRgba2[y, x, :3] = colSum / i

im = PIL.Image.fromarray(npImRgba2)
im = im.transform(size, PIL.Image.EXTENT, (0,0) + im.size, PIL.Image.LINEAR)
im.save("slime_"+filename)

結果: ここに画像の説明を入力

于 2012-11-01T13:53:53.310 に答える