anti_avast
@anti_avast (兼蔵 山田)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

python pillowを使った画像加工

Q&A

Closed

解決したいこと

画像をを丸く切り抜きたい
別々に実行すると問題ないが
Bytes.IOを使用するとうまくいかない

発生している問題・エラー

File "c:\Users\tatsu\Desktop\xxxxx\discord\DIS\test3.py", line 8, in <module>
    img.save(img_bytes,format='png', optimize=True, quality=70)
  File "C:\Users\tatsu\AppData\Local\Programs\Python\Python39\lib\site-packages\PIL\Image.py", line 2235, in save
    save_handler(self, fp, filename)
  File "C:\Users\tatsu\AppData\Local\Programs\Python\Python39\lib\site-packages\PIL\PngImagePlugin.py", line 1237, in _save
    fp.write(_MAGIC)
TypeError: descriptor 'write' for '_io.BytesIO' objects doesn't apply to a 'bytes' object

①実行後:ダウンロードは問題なし
image.png
②実行後:綺麗に加工できてる
image.png
③動作しない
image.png

該当するソースコード①画像をダウンロードしてPNG形式で保存(GIF画像のまま加工ができなかったため)

from PIL import Image, ImageDraw, ImageFont
import requests
import io

gif=(io.BytesIO(requests.get("https://cdn.discordapp.com/avatars/889911438779490334/a_a2c4c790a280a1373ff4ff921edbc1d7.gif?size=1024").content))
img = Image.open(gif)
img.save("test.png",format='png', optimize=True, quality=95)

該当するソースコード②保存した画像を丸く切り抜く

from PIL import Image
from PIL import ImageDraw

path = "test.png"
offset = 20    
img = Image.open(path).convert('RGB')
mask = Image.new("L", img.size)
draw = ImageDraw.Draw(mask)
draw.ellipse([(offset, offset), (img.size[0] - offset, img.size[1] - offset)], 255)
del draw
img.putalpha(mask)
img.save("out.png")

該当するソースコード③ローカルに保存せず加工&送信したかったため、BytesIOを使用したが動作しない

from PIL import Image, ImageDraw, ImageFont
import requests
import io

gif = (io.BytesIO(requests.get("https://cdn.discordapp.com/avatars/889911438779490334/a_a2c4c790a280a1373ff4ff921edbc1d7.gif?size=1024").content))
img = Image.open(gif)
img_bytes = io.BytesIO
img.save(img_bytes,format='png', optimize=True, quality=70)

offset = 20 
img = Image.open(img_bytes).convert('RGB')
mask = Image.new("L", img.size)
draw = ImageDraw.Draw(mask)
draw.ellipse([(offset, offset), (img.size[0] - offset, img.size[1] - offset)], 255)
del draw

img.putalpha(mask)
img.save("out.png")

自分で試したこと

画像の形式(RGB,RGBA,P,PA)など複数の形式に変換して試したが
うまく動作しないです、別々だとうまく動作しているので
困って質問しました

0

2Answer

img_bytes = io.BytesIO()にすると動きます。

ただそもそも中間ファイルを作成する必要もなくて、

from PIL import Image, ImageDraw, ImageFont
import requests
import io

offset = 20
gif = io.BytesIO(requests.get("https://cdn.discordapp.com/avatars/889911438779490334/a_a2c4c790a280a1373ff4ff921edbc1d7.gif?size=1024").content)
img = Image.open(gif).convert('RGB')
mask = Image.new("L", img.size)
draw = ImageDraw.Draw(mask)
draw.ellipse([(offset, offset), (img.size[0] - offset, img.size[1] - offset)], 255)
del draw

img.putalpha(mask)
img.save("out.png")

これでも動きます。
convert('RGB')でgif(Pフォーマット)をRGBフォーマットに変換して、putalpha(mask)でAlphaを追加してRGBAフォーマットにしてくれます。

1Like

Comments

  1. @anti_avast

    Questioner

    ありがとうございます
    5時間ぐらいずっと悩んでいて
    うまく動作して感動です
    ありがとうございます
  2. うまく行ったようで良かったです👍
  3. @anti_avast

    Questioner

    すみません、このアイコン使って
    さらに加工をしたいのですが
    関数を定義してリターンを
    byte型で渡すことは可能ですか?

    def image_cut(icon_url):
    offset = 2
    gif = (io.BytesIO(requests.get(icon_url).content))
    img = Image.open(gif).convert('RGB')
    mask = Image.new("L", img.size)
    draw = ImageDraw.Draw(mask)
    draw.ellipse([(offset, offset), (img.size[0] - offset, img.size[1] - offset)], 255)
    del draw
    img.putalpha(mask)
    image_bytes = io.BytesIO
    img.save(image_bytes,format="png")
    return(image_bytes)

    エラーが出てしまって

    TypeError: descriptor 'write' for '_io.BytesIO' objects doesn't apply to a 'bytes' object

    このような感じです

コメントへの返信ですが、シンタックスハイライトが効かないのでこちらで。

io.BytesIOはクラスなので、使う時はio.BytesIO()としてインスタンス化する必要があります。

def image_cut(icon_url):
    offset = 2
    gif = io.BytesIO(requests.get(icon_url).content)
    img = Image.open(gif).convert('RGB')
    mask = Image.new("L", img.size)
    draw = ImageDraw.Draw(mask)
    draw.ellipse([(offset, offset), (img.size[0] - offset, img.size[1] - offset)], 255)
    del draw
    img.putalpha(mask)
    image_bytes = io.BytesIO()  # <-- ここを修正
    img.save(image_bytes, format="png")
    return image_bytes

byte型で渡すことは可能ですか?

bytes型のことでしょうか?
その場合は最後の行をreturn image_bytes.getvalue()に変更する必要があります。
BytesIO型が返るのでよければ上のコードでOKです。

1Like

Comments

  1. @anti_avast

    Questioner

    ありがとうございました。
    丁寧にありがとうございます

Your answer might help someone💌