add image cropping support
This commit is contained in:
@@ -5,6 +5,7 @@ module DanbooruImageResizer
|
||||
THUMBNAIL_OPTIONS = { size: :down, linear: false, auto_rotate: false, export_profile: SRGB_PROFILE }
|
||||
# http://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-jpegsave
|
||||
JPEG_OPTIONS = { background: 255, strip: true, interlace: true, optimize_coding: true }
|
||||
CROP_OPTIONS = { linear: false, auto_rotate: false, export_profile: SRGB_PROFILE, crop: :attention }
|
||||
|
||||
# XXX libvips-8.4 on Debian doesn't support the `Vips::Image.thumbnail` method.
|
||||
# On 8.4 we have to shell out to vipsthumbnail instead. Remove when Debian supports 8.5.
|
||||
@@ -16,6 +17,14 @@ module DanbooruImageResizer
|
||||
end
|
||||
end
|
||||
|
||||
def self.crop(file, length, quality = 90)
|
||||
if Vips.at_least_libvips?(8, 5)
|
||||
crop_ruby(file, length, quality)
|
||||
else
|
||||
crop_shell(file, length, quality)
|
||||
end
|
||||
end
|
||||
|
||||
# https://github.com/jcupitt/libvips/wiki/HOWTO----Image-shrinking
|
||||
# http://jcupitt.github.io/libvips/API/current/Using-vipsthumbnail.md.html
|
||||
def self.resize_ruby(file, width, height, resize_quality)
|
||||
@@ -26,6 +35,14 @@ module DanbooruImageResizer
|
||||
output_file
|
||||
end
|
||||
|
||||
def self.crop_ruby(file, length, resize_quality)
|
||||
output_file = Tempfile.new
|
||||
resized_image = Vips::Image.thumbnail(file.path, length, height: length, **CROP_OPTIONS)
|
||||
resized_image.jpegsave(output_file.path, Q: resize_quality, **JPEG_OPTIONS)
|
||||
|
||||
output_file
|
||||
end
|
||||
|
||||
def self.resize_shell(file, width, height, quality)
|
||||
output_file = Tempfile.new(["resize", ".jpg"])
|
||||
|
||||
@@ -47,4 +64,25 @@ module DanbooruImageResizer
|
||||
|
||||
output_file
|
||||
end
|
||||
|
||||
def self.crop_shell(file, length, quality)
|
||||
output_file = Tempfile.new(["crop", ".jpg"])
|
||||
|
||||
# --size=WxH will upscale if the image is smaller than the target size.
|
||||
# Fix the target size so that it's not bigger than the image.
|
||||
image = Vips::Image.new_from_file(file.path)
|
||||
|
||||
arguments = [
|
||||
file.path,
|
||||
"--eprofile=#{SRGB_PROFILE}",
|
||||
"--crop=none",
|
||||
"--size=#{length}",
|
||||
"--format=#{output_file.path}[Q=#{quality},background=255,strip,interlace,optimize_coding]"
|
||||
]
|
||||
|
||||
success = system("vipsthumbnail", *arguments)
|
||||
raise RuntimeError, "vipsthumbnail failed (exit status: #{$?.exitstatus})" if !success
|
||||
|
||||
output_file
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
class ImageCropper
|
||||
def self.enabled?
|
||||
Danbooru.config.aws_sqs_cropper_url.present?
|
||||
end
|
||||
|
||||
def self.notify(post)
|
||||
if post.is_image?
|
||||
sqs = SqsService.new(Danbooru.config.aws_sqs_cropper_url)
|
||||
sqs.send_message("#{post.id},https://#{Danbooru.config.hostname}/data/#{post.md5}.#{post.file_ext}")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -64,8 +64,18 @@ class PixivUgoiraConverter
|
||||
output_file
|
||||
end
|
||||
|
||||
def self.generate_crop(ugoira_file)
|
||||
file = Tempfile.new(["ugoira-crop", ".zip"], binmode: true)
|
||||
zipfile = Zip::File.new(ugoira_file.path)
|
||||
zipfile.entries.first.extract(file.path) { true } # 'true' means overwrite the existing tempfile.
|
||||
|
||||
DanbooruImageResizer.crop(file, Danbooru.config.small_image_width, 85)
|
||||
ensure
|
||||
file.close!
|
||||
end
|
||||
|
||||
def self.generate_preview(ugoira_file)
|
||||
file = Tempfile.new(binmode: true)
|
||||
file = Tempfile.new(["ugoira-preview", ".zip"], binmode: true)
|
||||
zipfile = Zip::File.new(ugoira_file.path)
|
||||
zipfile.entries.first.extract(file.path) { true } # 'true' means overwrite the existing tempfile.
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ class StorageManager
|
||||
"#{root_url}/images/download-preview.png"
|
||||
elsif type == :preview
|
||||
"#{base_url}/preview/#{subdir}#{file}"
|
||||
elsif type == :crop
|
||||
"#{base_url}/crop/#{subdir}#{file}"
|
||||
elsif type == :large && post.has_large?
|
||||
"#{base_url}/sample/#{subdir}#{seo_tags}#{file}"
|
||||
else
|
||||
@@ -77,6 +79,8 @@ class StorageManager
|
||||
case type
|
||||
when :preview
|
||||
"#{base_dir}/preview/#{subdir}#{file}"
|
||||
when :crop
|
||||
"#{base_dir}/crop/#{subdir}#{file}"
|
||||
when :large
|
||||
"#{base_dir}/sample/#{subdir}#{file}"
|
||||
when :original
|
||||
@@ -90,6 +94,8 @@ class StorageManager
|
||||
case type
|
||||
when :preview
|
||||
"#{md5}.jpg"
|
||||
when :crop
|
||||
"#{md5}.jpg"
|
||||
when :large
|
||||
"#{large_image_prefix}#{md5}.#{large_file_ext}"
|
||||
when :original
|
||||
|
||||
@@ -5,6 +5,7 @@ class StorageManager::Local < StorageManager
|
||||
temp_path = dest_path + "-" + SecureRandom.uuid + ".tmp"
|
||||
|
||||
FileUtils.mkdir_p(File.dirname(temp_path))
|
||||
io.rewind
|
||||
bytes_copied = IO.copy_stream(io, temp_path)
|
||||
raise Error, "store failed: #{bytes_copied}/#{io.size} bytes copied" if bytes_copied != io.size
|
||||
|
||||
|
||||
@@ -130,21 +130,23 @@ class UploadService
|
||||
def self.generate_resizes(file, upload)
|
||||
if upload.is_video?
|
||||
video = FFMPEG::Movie.new(file.path)
|
||||
crop_file = generate_video_preview_for(video, Danbooru.config.small_image_width, Danbooru.config.small_image_width)
|
||||
preview_file = generate_video_preview_for(video, Danbooru.config.small_image_width, Danbooru.config.small_image_width)
|
||||
|
||||
elsif upload.is_ugoira?
|
||||
preview_file = PixivUgoiraConverter.generate_preview(file)
|
||||
crop_file = PixivUgoiraConverter.generate_crop(file)
|
||||
sample_file = PixivUgoiraConverter.generate_webm(file, upload.context["ugoira"]["frame_data"])
|
||||
|
||||
elsif upload.is_image?
|
||||
preview_file = DanbooruImageResizer.resize(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 85)
|
||||
|
||||
crop_file = DanbooruImageResizer.crop(file, Danbooru.config.small_image_width, 85)
|
||||
if upload.image_width > Danbooru.config.large_image_width
|
||||
sample_file = DanbooruImageResizer.resize(file, Danbooru.config.large_image_width, upload.image_height, 90)
|
||||
end
|
||||
end
|
||||
|
||||
[preview_file, sample_file]
|
||||
[preview_file, crop_file, sample_file]
|
||||
end
|
||||
|
||||
def self.generate_video_preview_for(video, width, height)
|
||||
@@ -155,7 +157,7 @@ class UploadService
|
||||
width = (height * dimension_ratio).to_i
|
||||
end
|
||||
|
||||
output_file = Tempfile.new(binmode: true)
|
||||
output_file = Tempfile.new(["video-preview", ".jpg"], binmode: true)
|
||||
video.screenshot(output_file.path, {:seek_time => 0, :resolution => "#{width}x#{height}"})
|
||||
output_file
|
||||
end
|
||||
@@ -178,14 +180,16 @@ class UploadService
|
||||
|
||||
upload.tag_string = "#{upload.tag_string} #{Utils.automatic_tags(upload, file)}"
|
||||
|
||||
preview_file, sample_file = Utils.generate_resizes(file, upload)
|
||||
preview_file, crop_file, sample_file = Utils.generate_resizes(file, upload)
|
||||
|
||||
begin
|
||||
Utils.distribute_files(file, upload, :original)
|
||||
Utils.distribute_files(sample_file, upload, :large) if sample_file.present?
|
||||
Utils.distribute_files(preview_file, upload, :preview) if preview_file.present?
|
||||
Utils.distribute_files(crop_file, upload, :crop) if crop_file.present?
|
||||
ensure
|
||||
preview_file.try(:close!)
|
||||
crop_file.try(:close!)
|
||||
sample_file.try(:close!)
|
||||
end
|
||||
|
||||
@@ -583,13 +587,13 @@ class UploadService
|
||||
)
|
||||
end
|
||||
|
||||
notify_cropper(@post) if ImageCropper.enabled?
|
||||
upload.update(status: "completed", post_id: @post.id)
|
||||
@post
|
||||
end
|
||||
|
||||
def convert_to_post(upload)
|
||||
Post.new.tap do |p|
|
||||
p.has_cropped = true
|
||||
p.tag_string = upload.tag_string
|
||||
p.md5 = upload.md5
|
||||
p.file_ext = upload.file_ext
|
||||
@@ -607,8 +611,4 @@ class UploadService
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def notify_cropper(post)
|
||||
# ImageCropper.notify(post)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user