jarvis-bot/jarvis/cogs/image.py

113 lines
3.5 KiB
Python

import re
from io import BytesIO
import aiohttp
import cv2
import numpy as np
from discord import File
from discord.ext import commands
import jarvis
from jarvis.utils import build_embed, convert_bytesize, unconvert_bytesize
from jarvis.utils.field import Field
class ImageCog(commands.Cog):
"""
Image processing functions for J.A.R.V.I.S.
May be categorized under util later
"""
def __init__(self, bot):
self.bot = bot
self._session = aiohttp.ClientSession()
self.tgt_match = re.compile(
r"([0-9]*\.?[0-9]*?) ?([KMGTP]?B)", re.IGNORECASE
)
async def _resize(self, ctx, target: str, url: str = None):
if not target:
await ctx.send("Missing target size, i.e. 200KB.")
return
tgt = self.tgt_match.match(target)
if not tgt:
await ctx.send(
"Invalid target format ({}).".format(target)
+ " Expected format like 200KB"
)
return
tgt_size = unconvert_bytesize(float(tgt.groups()[0]), tgt.groups()[1])
if tgt_size > unconvert_bytesize(8, "MB"):
await ctx.send(
"Target too large to send. Please make target < 8MB"
)
return
file = None
filename = None
if (
ctx.message.attachments is not None
and len(ctx.message.attachments) > 0
):
file = await ctx.message.attachments[0].read()
filename = ctx.message.attachments[0].filename
elif url is not None:
async with self._session.get(url) as resp:
if resp.status == 200:
file = await resp.read()
filename = url.split("/")[-1]
else:
ctx.send("Missing file as either attachment or URL.")
size = len(file)
if size <= tgt_size:
await ctx.send("Image already meets target.")
return
ratio = max(tgt_size / size - 0.02, 0.50)
accuracy = 0.0
# TODO: Optimize to not run multiple times
while len(file) > tgt_size or (
len(file) <= tgt_size and accuracy < 0.65
):
old_file = file
buffer = np.frombuffer(file, dtype=np.uint8)
img = cv2.imdecode(buffer, flags=-1)
width = int(img.shape[1] * ratio)
height = int(img.shape[0] * ratio)
new_img = cv2.resize(img, (width, height))
file = cv2.imencode(".png", new_img)[1].tobytes()
accuracy = (len(file) / tgt_size) * 100
if accuracy <= 0.50:
file = old_file
ratio += 0.1
else:
ratio = max(tgt_size / len(file) - 0.02, 0.65)
bufio = BytesIO(file)
accuracy = (len(file) / tgt_size) * 100
fields = [
Field("Original Size", convert_bytesize(size), False),
Field("New Size", convert_bytesize(len(file)), False),
Field("Accuracy", f"{accuracy:.02f}%", False),
]
embed = build_embed(title=filename, description="", fields=fields)
embed.set_image(url=f"attachment://resized.png")
await ctx.send(
embed=embed,
file=File(bufio, filename="resized.png"),
)
@commands.command(name="resize", help="Resize an image")
async def _resize_pref(self, ctx, target: str, url: str = None):
await self._resize(ctx, target, url)
def setup(bot):
bot.add_cog(ImageCog(bot))