[解決済み] PILでRGBAのPNGをRGBに変換する
2022-08-15 05:25:38
質問
私はDjangoでアップロードされた透過PNG画像をJPGファイルに変換するためにPILを使用しています。出力は壊れたように見えます。
ソースファイル
コード
Image.open(object.logo.path).save('/tmp/output.jpg', 'JPEG')
または
Image.open(object.logo.path).convert('RGB').save('/tmp/output.png')
結果発表
どちらの方法でも、結果は次のようになります。
これを修正する方法はあるのでしょうか?背景が透明だったところを白にしたいのですが。
解決方法
素晴らしい回答のおかげで、次のような関数集を思いつきました。
import Image
import numpy as np
def alpha_to_color(image, color=(255, 255, 255)):
"""Set all fully transparent pixels of an RGBA image to the specified color.
This is a very simple solution that might leave over some ugly edges, due
to semi-transparent areas. You should use alpha_composite_with color instead.
Source: http://stackoverflow.com/a/9166671/284318
Keyword Arguments:
image -- PIL RGBA Image object
color -- Tuple r, g, b (default 255, 255, 255)
"""
x = np.array(image)
r, g, b, a = np.rollaxis(x, axis=-1)
r[a == 0] = color[0]
g[a == 0] = color[1]
b[a == 0] = color[2]
x = np.dstack([r, g, b, a])
return Image.fromarray(x, 'RGBA')
def alpha_composite(front, back):
"""Alpha composite two RGBA images.
Source: http://stackoverflow.com/a/9166671/284318
Keyword Arguments:
front -- PIL RGBA Image object
back -- PIL RGBA Image object
"""
front = np.asarray(front)
back = np.asarray(back)
result = np.empty(front.shape, dtype='float')
alpha = np.index_exp[:, :, 3:]
rgb = np.index_exp[:, :, :3]
falpha = front[alpha] / 255.0
balpha = back[alpha] / 255.0
result[alpha] = falpha + balpha * (1 - falpha)
old_setting = np.seterr(invalid='ignore')
result[rgb] = (front[rgb] * falpha + back[rgb] * balpha * (1 - falpha)) / result[alpha]
np.seterr(**old_setting)
result[alpha] *= 255
np.clip(result, 0, 255)
# astype('uint8') maps np.nan and np.inf to 0
result = result.astype('uint8')
result = Image.fromarray(result, 'RGBA')
return result
def alpha_composite_with_color(image, color=(255, 255, 255)):
"""Alpha composite an RGBA image with a single color image of the
specified color and the same size as the original image.
Keyword Arguments:
image -- PIL RGBA Image object
color -- Tuple r, g, b (default 255, 255, 255)
"""
back = Image.new('RGBA', size=image.size, color=color + (255,))
return alpha_composite(image, back)
def pure_pil_alpha_to_color_v1(image, color=(255, 255, 255)):
"""Alpha composite an RGBA Image with a specified color.
NOTE: This version is much slower than the
alpha_composite_with_color solution. Use it only if
numpy is not available.
Source: http://stackoverflow.com/a/9168169/284318
Keyword Arguments:
image -- PIL RGBA Image object
color -- Tuple r, g, b (default 255, 255, 255)
"""
def blend_value(back, front, a):
return (front * a + back * (255 - a)) / 255
def blend_rgba(back, front):
result = [blend_value(back[i], front[i], front[3]) for i in (0, 1, 2)]
return tuple(result + [255])
im = image.copy() # don't edit the reference directly
p = im.load() # load pixel array
for y in range(im.size[1]):
for x in range(im.size[0]):
p[x, y] = blend_rgba(color + (255,), p[x, y])
return im
def pure_pil_alpha_to_color_v2(image, color=(255, 255, 255)):
"""Alpha composite an RGBA Image with a specified color.
Simpler, faster version than the solutions above.
Source: http://stackoverflow.com/a/9459208/284318
Keyword Arguments:
image -- PIL RGBA Image object
color -- Tuple r, g, b (default 255, 255, 255)
"""
image.load() # needed for split()
background = Image.new('RGB', image.size, color)
background.paste(image, mask=image.split()[3]) # 3 is the alpha channel
return background
パフォーマンス
単純な非合成の
alpha_to_color
関数は最速のソリューションですが、半透明な領域を扱わないため、醜い境界線が残ります。
純粋なPILとnumpyの合成解はどちらも素晴らしい結果をもたらしますが
alpha_composite_with_color
の方がはるかに速い(8.93 msec)です。
pure_pil_alpha_to_color
(79.6 msec)よりもはるかに高速です。
もしnumpyがシステム上で利用可能であれば、そちらを利用した方が良いでしょう。
(更新: 新しい純粋なPILバージョンは、言及されたすべてのソリューションの中で最速です)。
$ python -m timeit "import Image; from apps.front import utils; i = Image.open(u'logo.png'); i2 = utils.alpha_to_color(i)"
10 loops, best of 3: 4.67 msec per loop
$ python -m timeit "import Image; from apps.front import utils; i = Image.open(u'logo.png'); i2 = utils.alpha_composite_with_color(i)"
10 loops, best of 3: 8.93 msec per loop
$ python -m timeit "import Image; from apps.front import utils; i = Image.open(u'logo.png'); i2 = utils.pure_pil_alpha_to_color(i)"
10 loops, best of 3: 79.6 msec per loop
$ python -m timeit "import Image; from apps.front import utils; i = Image.open(u'logo.png'); i2 = utils.pure_pil_alpha_to_color_v2(i)"
10 loops, best of 3: 1.1 msec per loop
どのように解決するのですか?
もっとシンプルなバージョンを紹介します。を構築しているときに見つけたいくつかの django のスニペットに重きを置いています。
RGBA -> JPG + BG
を構築しているときに見つけた Django のスニペットを参考にしています。
from PIL import Image
png = Image.open(object.logo.path)
png.load() # required for png.split()
background = Image.new("RGB", png.size, (255, 255, 255))
background.paste(png, mask=png.split()[3]) # 3 is the alpha channel
background.save('foo.jpg', 'JPEG', quality=80)
結果(80%)
50%での結果
関連
-
[解決済み] バイトを文字列に変換する
-
[解決済み] pipでPythonの全パッケージをアップグレードする方法
-
[解決済み] HTML Canvas を gif/jpg/png/pdf としてキャプチャしますか?
-
[解決済み] ImageMagickでSVGをPNGに変換する方法は?
-
[解決済み】Python Image Libraryが "decoder JPEG not available "というメッセージで失敗する件 - PIL
-
[解決済み】白の上のRGBをRGBAに変換する
-
[解決済み】PNG vs. GIF vs. JPEG vs. SVGの異なるユースケースとは?[クローズド]
-
[解決済み] SQLAlchemy: セッションの作成と再利用
-
[解決済み] django.db.migrations.exceptions.InconsistentMigrationHistory
-
[解決済み] 単純な文字列からtimedeltaオブジェクトを作成する方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] PILからopenCVフォーマットへの変換
-
[解決済み] Pythonでコード行間にかかる時間を測定するには?
-
[解決済み] Spyderを仮想環境で動作させるには?
-
[解決済み] Python 2.7サポート終了?
-
[解決済み] Pythonで0xを使わずにhex()を使うには?
-
[解決済み] subprocess.run()の出力を抑制またはキャプチャするには?
-
[解決済み] Pythonによる一対のクロスプロダクト [重複] (英語)
-
[解決済み] virtualenv の `--no-site-packages` オプションを元に戻す。
-
[解決済み] Pythonで、ウェブサイトが404か200かを確認するためにurllibをどのように使用しますか?
-
[解決済み] Pythonでランダムなファイル名を生成する最良の方法