jpeg-xl crops, previews, and samples

This commit is contained in:
edshot99 2024-10-26 16:56:17 -05:00
parent 645d0f0020
commit 7a4c9d44a1
5 changed files with 47 additions and 13 deletions

View File

@ -1,3 +1,15 @@
## JPEG XL PostThumbnailer
Experimental code that generates JPEG XL crops, previews, and samples instead of JPEG ones.
Kept for future reference.
### Notes
* APNG's will be non-animated JXL's because libvips and libspng is lacking APNG support
* JXL's will be non-animated unless libvips v8.16.0 is used
This can be worked around by doing something like:
$ cjxl XX/XX/md5.png sample/XX/XX/md5.jxl
<div align="center">
<img src="public/images/github-logo.svg" width="150" height="150" align="left">
<div align="left">

View File

@ -8,6 +8,8 @@ module DanbooruImageResizer
# https://www.libvips.org/API/current/VipsForeignSave.html#vips-jpegsave
JPEG_OPTIONS = { strip: true, interlace: true, optimize_coding: true }.freeze
CROP_OPTIONS = { linear: false, no_rotate: true, export_profile: "srgb", import_profile: "srgb", crop: :attention }.freeze
# https://www.libvips.org/API/current/VipsForeignSave.html#vips-jxlsave
JPEGXL_OPTIONS = { lossless: false }.freeze
def resize(file, width, height, resize_quality = 90, background_color: "000000")
r = background_color[0..1].to_i(16)
@ -20,6 +22,14 @@ module DanbooruImageResizer
output_file
end
def resize_jxl(file, width, height, distance = 1.0, effort = 7)
output_file = Tempfile.new
resized_image = thumbnail(file, width, height, THUMBNAIL_OPTIONS)
resized_image.jxlsave(output_file.path, distance: distance, effort: effort, **JPEGXL_OPTIONS)
output_file
end
def crop(file, width, height, resize_quality = 90, background_color: "000000")
return nil unless Danbooru.config.enable_image_cropping?
@ -33,6 +43,16 @@ module DanbooruImageResizer
output_file
end
def crop_jxl(file, width, height, distance = 1.0, effort = 7)
return nil unless Danbooru.config.enable_image_cropping?
output_file = Tempfile.new
resized_image = thumbnail(file, width, height, CROP_OPTIONS)
resized_image.jxlsave(output_file.path, distance: distance, effort: effort, **JPEGXL_OPTIONS)
output_file
end
# https://github.com/libvips/libvips/wiki/HOWTO----Image-shrinking
# https://www.libvips.org/API/current/Using-vipsthumbnail.md.html
def thumbnail(file, width, height, options)

View File

@ -7,12 +7,12 @@ module PostThumbnailer
video = FFMPEG::Movie.new(file.path)
crop_file = generate_video_crop_for(video, Danbooru.config.small_image_width)
preview_file = generate_video_preview_for(file.path, Danbooru.config.small_image_width)
sample_file = generate_video_sample_for(file.path)
sample_file = generate_video_sample_for(file.path, height)
elsif type == :image
preview_file = DanbooruImageResizer.resize(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 87, background_color: background_color)
crop_file = DanbooruImageResizer.crop(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 87, background_color: background_color)
preview_file = DanbooruImageResizer.resize_jxl(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 1.0, 9)
crop_file = DanbooruImageResizer.crop_jxl(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 1.0, 9)
if width > Danbooru.config.large_image_width
sample_file = DanbooruImageResizer.resize(file, Danbooru.config.large_image_width, height, 87, background_color: background_color)
sample_file = DanbooruImageResizer.resize_jxl(file, Danbooru.config.large_image_width, height, 1.0, 9)
end
end
@ -23,7 +23,7 @@ module PostThumbnailer
if type == :video
preview_file = generate_video_preview_for(file.path, Danbooru.config.small_image_width)
elsif type == :image
preview_file = DanbooruImageResizer.resize(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 87)
preview_file = DanbooruImageResizer.resize_jxl(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 1.0, 9)
end
preview_file
@ -32,7 +32,7 @@ module PostThumbnailer
def generate_video_crop_for(video, width)
vp = Tempfile.new(["video-preview", ".jpg"], binmode: true)
video.screenshot(vp.path, {:seek_time => 0, :resolution => "#{video.width}x#{video.height}"})
crop = DanbooruImageResizer.crop(vp, width, width, 87)
crop = DanbooruImageResizer.crop_jxl(vp, width, width, 1.0, 9)
vp.close
return crop
end
@ -46,10 +46,11 @@ module PostThumbnailer
Rails.logger.warn("[FFMPEG PREVIEW STDERR] #{stderr.chomp!}")
raise CorruptFileError.new("could not generate thumbnail")
end
output_file
preview_file = DanbooruImageResizer.resize_jxl(output_file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 1.0, 9)
preview_file
end
def generate_video_sample_for(video)
def generate_video_sample_for(video, height)
output_file = Tempfile.new(["video-sample", ".jpg"], binmode: true)
stdout, stderr, status = Open3.capture3(Danbooru.config.ffmpeg_path, '-y', '-i', video, '-vf', 'thumbnail', '-frames:v', '1', output_file.path)
@ -58,6 +59,7 @@ module PostThumbnailer
Rails.logger.warn("[FFMPEG SAMPLE STDERR] #{stderr.chomp!}")
raise CorruptFileError.new("could not generate sample")
end
output_file
sample_file = DanbooruImageResizer.resize_jxl(output_file, Danbooru.config.large_image_width, height, 1.0, 9)
sample_file
end
end

View File

@ -168,11 +168,11 @@ class StorageManager
def file_name(md5, file_ext, type, scale_factor: nil)
case type
when :preview
"#{md5}.jpg"
"#{md5}.jxl"
when :crop
"#{md5}.jpg"
"#{md5}.jxl"
when :large
"#{large_image_prefix}#{md5}.jpg"
"#{large_image_prefix}#{md5}.jxl"
when :original
"#{md5}.#{file_ext}"
when :scaled

View File

@ -253,7 +253,7 @@ class Post < ApplicationRecord
return true if is_video?
return false if is_gif?
return false if is_flash?
return false if has_tag?("animated_gif", "animated_png")
#return false if has_tag?("animated_gif", "animated_png")
is_image? && image_width.present? && image_width > Danbooru.config.large_image_width
end