Skip to content

Commit

Permalink
Prioritize using pywebp if there is no duplicated frame, else use pillow
Browse files Browse the repository at this point in the history
  • Loading branch information
laggykiller committed Mar 28, 2024
1 parent 20d223e commit 492c6d0
Showing 1 changed file with 52 additions and 18 deletions.
70 changes: 52 additions & 18 deletions src/sticker_convert/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@
"[F] Failed Compression {} -> {}, "
"cannot get below limit {} with lowest quality under current settings (Best size: {})"
)
MSG_PYWEBP = (
"WARNING: System WebP>=0.5.0 was not found, hence Pillow cannot be used "
"for creating animated webp. Using pywebp instead, which is known to "
"collapse same frames into single frame, causing problem with animation timing."
MSG_PYWEBP_DUPFRAME = (
"[W] {} contains duplicated frame.\n"
" System WebP>=0.5.0 was not found, hence Pillow cannot be used\n"
" for creating animated webp. Using pywebp instead, which is known to\n"
" collapse same frames into single frame, causing problem with animation timing."
)
MSG_WEBP_PIL_DUPFRAME = (
"[W] {} contains duplicated frame.\n"
" Using Pillow to create animated webp to avoid same frames collapse\n"
" into single frame, but this is slower."
)

YUV_RGB_MATRIX = np.array(
Expand Down Expand Up @@ -189,7 +195,8 @@ def __init__(
self.result_step: Optional[int] = None

self.apngasm = None
self.pywebp_warning_displayed = False
self.msg_pywebp_dupframe_displayed = False
self.msg_webp_pil_dupframe_displayed = False

@staticmethod
def convert(
Expand Down Expand Up @@ -689,16 +696,51 @@ def frames_export(self) -> None:
self._frames_export_apng()
else:
self._frames_export_png()
elif self.out_f.suffix == ".gif" or (
self.out_f.suffix == ".webp" and (PIL_WEBP_ANIM or not is_animated)
):
self._frames_export_pil_anim()
elif self.out_f.suffix == ".webp":
self._frames_export_webp()
elif self.out_f.suffix == ".gif":
self._frames_export_pil_anim()
elif self.out_f.suffix in (".webm", ".mp4", ".mkv") or is_animated:
self._frames_export_pyav()
else:
self._frames_export_pil()

def _check_dup(self) -> bool:
if len(self.frames_processed) == 1:
return False

prev_frame = self.frames_processed[0]
for frame in self.frames_processed[1:]:
if np.array_equal(frame, prev_frame):
return True
prev_frame = frame

return False

def _frames_export_webp(self) -> None:
has_dup_frames = self._check_dup()
if self.fps:
# It was noted that pywebp would collapse all frames.
# aed005b attempted to fix this by creating webp with
# variable frame duration. However, the webp created would
# not be accepted by WhatsApp.
# Therefore, we are preferring Pillow over pywebp.
if has_dup_frames:
if PIL_WEBP_ANIM:
# Warn that using Pillow is slower
if not self.msg_webp_pil_dupframe_displayed:
self.cb.put(MSG_WEBP_PIL_DUPFRAME.format(self.in_f_name))
self.msg_webp_pil_dupframe_displayed = True
self._frames_export_pil_anim()
else:
if not self.msg_pywebp_dupframe_displayed:
self.cb.put(MSG_PYWEBP_DUPFRAME.format(self.in_f_name))
self.msg_pywebp_dupframe_displayed = True
self._frames_export_pywebp()
else:
self._frames_export_pywebp()
else:
self._frames_export_pil()

def _frames_export_pil(self) -> None:
with Image.fromarray(self.frames_processed[0]) as im: # type: ignore
Expand Down Expand Up @@ -799,17 +841,9 @@ def _frames_export_pil_anim(self) -> None:
**extra_kwargs,
)

def _frames_export_webp(self) -> None:
def _frames_export_pywebp(self) -> None:
import webp # type: ignore

# It was noted that pywebp would collapse all frames.
# aed005b attempted to fix this by creating webp with
# variable frame duration. However, the webp created would
# not be accepted by WhatsApp.
# Therefore, we are preferring Pillow over pywebp.
if not self.pywebp_warning_displayed:
self.cb.put(MSG_PYWEBP)

assert self.fps

config = webp.WebPConfig.new(quality=self.quality) # type: ignore
Expand Down

0 comments on commit 492c6d0

Please sign in to comment.