Saya menggunakan PIL untuk mengubah gambar PNG transparan yang diunggah dengan Django menjadi file JPG. Outputnya terlihat rusak.
Sumber data
Kode
Image.open(object.logo.path).save('/tmp/output.jpg', 'JPEG')
atau
Image.open(object.logo.path).convert('RGB').save('/tmp/output.png')
Hasil
Kedua cara tersebut, gambar yang dihasilkan terlihat seperti ini:
Apakah ada cara untuk memperbaikinya? Saya ingin memiliki latar belakang putih di mana latar belakang transparan dulu.
Larutan
Berkat jawaban yang bagus, saya telah menghasilkan koleksi fungsi berikut:
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
Performa
Fungsi non-pengomposisian sederhana alpha_to_color
adalah solusi tercepat, tetapi meninggalkan batas yang jelek karena tidak menangani area semi transparan.
Baik PIL murni maupun solusi pengomposisian numpy memberikan hasil yang bagus, tetapi alpha_composite_with_color
jauh lebih cepat (8,93 msec) daripada pure_pil_alpha_to_color
(79,6 msec).Jika numpy tersedia di sistem Anda, itulah caranya. (Pembaruan: Versi PIL murni yang baru adalah yang tercepat dari semua solusi yang disebutkan.)
$ 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
im = image.copy()
dapat dihapus daripure_pil_alpha_to_color_v2
tanpa mengubah hasilnya. (Setelah mengubah contoh berikutnyaim
menjadiimage
, tentu saja.)Jawaban:
Ini adalah versi yang jauh lebih sederhana - tidak yakin seberapa performanya. Sangat berdasarkan pada beberapa potongan django yang saya temukan saat membangun
RGBA -> JPG + BG
dukungan untuk thumbnail sorl.Hasil @ 80%
Hasil @ 50%
sumber
background = Image.new("RGB", png.size, (255, 255, 255))
.paste
perpaduan yang tepat.load
metode diperlukan untuksplit
metode tersebut. Dan itu luar biasa mendengarnya sebenarnya cepat / dan / sederhana!tuple index out of range
. Saya memperbaikinya dengan mengikuti pertanyaan lain ( stackoverflow.com/questions/1962795/… ). Saya harus mengonversi PNG ke RGBA terlebih dahulu dan kemudian mengirisnya:alpha = img.split()[-1]
kemudian menggunakannya pada background mask.Dengan menggunakan
Image.alpha_composite
, solusi dari Yuji 'Tomita' Tomita menjadi lebih sederhana. Kode ini dapat menghindarituple index out of range
kesalahan jika png tidak memiliki saluran alfa.sumber
.convert("RGB")
sebelum menyimpannyaBagian transparan sebagian besar memiliki nilai RGBA (0,0,0,0). Karena JPG tidak memiliki transparansi, nilai jpeg diatur ke (0,0,0), yang berwarna hitam.
Di sekitar ikon lingkaran, ada piksel dengan nilai RGB bukan nol di mana A = 0. Jadi, piksel terlihat transparan di PNG, tetapi berwarna lucu di JPG.
Anda dapat mengatur semua piksel di mana A == 0 memiliki R = G = B = 255 menggunakan numpy seperti ini:
Perhatikan bahwa logo juga memiliki beberapa piksel semi transparan yang digunakan untuk menghaluskan tepi di sekitar kata dan ikon. Menyimpan ke jpeg mengabaikan semi-transparansi, membuat jpeg yang dihasilkan terlihat cukup bergerigi.
Hasil kualitas yang lebih baik dapat dibuat dengan menggunakan
convert
perintah imagemagick :Untuk membuat perpaduan kualitas yang lebih bagus menggunakan numpy, Anda bisa menggunakan pengomposisian alfa :
sumber
Berikut solusi dalam PIL murni.
sumber
Itu tidak rusak. Ia melakukan persis seperti yang Anda perintahkan; piksel tersebut berwarna hitam dengan transparansi penuh. Anda perlu mengulang di semua piksel dan mengonversinya dengan transparansi penuh menjadi putih.
sumber
sumber
impor Gambar
def fig2img (fig): "" "@brief Mengonversi gambar Matplotlib menjadi Gambar PIL dalam format RGBA dan mengembalikannya @param fig gambar matplotlib @return gambar Python Imaging Library (PIL)" "" # letakkan gambar pixmap ke a numpy array buf = fig2data (fig) w, h, d = buf.shape return Image.frombytes ("RGBA", (w, h), buf.tostring ())
def fig2data (fig): "" "@brief Mengonversi gambar Matplotlib menjadi array numpy 4D dengan saluran RGBA dan mengembalikannya @param fig gambar matplotlib @kembalikan array 3D numpy nilai RGBA" "" # gambar gambar penyaji. canvas.draw ()
def rgba2rgb (img, c = (0, 0, 0), path = 'foo.jpg', is_already_saved = False, if_load = True): jika tidak is_already_saved: background = Image.new ("RGB", img.size, c) background.paste (img, mask = img.split () [3]) # 3 adalah saluran alfa
sumber