Rails 5 Carrierwave and MiniMagick breaks gif at resizing for thumbnail - ruby-on-rails

I'm using carrierwave and mini_magick to resize my images, generate thumbnails. It works great for still images such as jpg and png, but when I try gif, it distorts it. You can see original vs the resized example below. Any idea how I make make it generate gif thumb without breaking it?
Original GIF :) https://imgur.com/oi1f8XT
Generated Thumbnail GIF :( https://imgur.com/a/PwAXv
ps how came the thumbnail's size is bigger than the original? Original is 800*600px and thumbnail is 400*300px. The whole point of generating thumbnail is having smaller file size anyways.
Thank you!
image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url(*args)
# # For Rails 3.1+ asset pipeline compatibility:
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
def default_url(*args)
ActionController::Base.helpers.asset_path("fallback/" + [version_name, "post.jpg"].compact.join('_'))
end
# Process files as they are uploaded:
# process scale: [800, 600]
# process :resize_to_fit => [800, 600]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process resize_to_fit: [50, 50]
# end
version :thumb do
process resize_to_fit: [400, 300]
end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_whitelist
%w(jpg jpeg gif png)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end

The gif you're trying to resize is optimized so that, after the first frame, each subsequent frame contains only the pixels that need to change. When the gif animates, the frames are stacked on top of one another creating the full animation.
Additionally, each frame is only large enough to contain the pixels that need to change. So, the size of each frame is different. When you resize the gif, Imagemagick is resizing each frame to be 400 by 300px regardless of its original size, which is causing the distortion you see.
You can fix this applying an Imagemagick command (with Minimagick bindings in Ruby) called coalesce. It de-optimizes the original image so that each frame is the full size of the canvas.
Calling coalesce makes the file size much larger, so it's necessary to re-optimize the gif once you're done resizing it.
Here's an example:
version :thumb do
process my_resize: [400, 300]
end
def my_resize(width, height)
if #file.content_type == "image/gif"
gif_safe_transform! do |image|
image.resize "#{width}x#{height}" # Perform any transformations here.
end
else
# Process other filetypes if necessary.
end
end
def gif_safe_transform!
MiniMagick::Tool::Convert.new do |image|
image << #file.path
image.coalesce # Remove optimizations so each layer shows the full image.
yield image
image.layers "Optimize" # Re-optimize the image.
image << #file.path
end
end
I wrote a more in-depth explanation with some examples here

Related

Minimagick not reducing png filesize instead its increasing

I want to compress my images only when the size is greater than 200kb before uploading them to s3 bucket,In my case images are not connected with any model, I am directly uploading it to s3 using storage as aws
My Uploader is
class CoverImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
#include CarrierWave::RMagick
include CarrierWave::MiniMagick
process :compress_image, if: :size_is_large?
# Choose what kind of storage to use for this uploader:
storage :aws
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def initialize(location_id)
#location_id = location_id
end
def store!(*args)
super
#file
end
def store_dir
"uploads/cover-images/#{#location_id}"
end
def compress_image
manipulate! do |img|
img.quality("60")
img
end
end
def size_is_large? picture
image = MiniMagick::Image.new(picture.path)
(image[:size]/1000) > 200
end
end
I am using following code to upload the image
image = MiniMagick::Image.open(https://abc.s3.amazon.com/uploads/image.png)
image_type = image.type.downcase
image_name = "#{location}.#{image_type}"
image_path = Rails.root.join("tmp", image_name)
file = File.open(image_path, 'wb') do |output|
output.write RestClient.get(cover_image["url"])
output.path
end
uploader = CoverImageUploader.new("test123")
s3_path = File.open(file) do |file|
uploaded_image = uploader.store!(file)
uploaded_image.url
end
For jpg and jpeg the above code is working fine but in case of png image, instead of decreasing the size its increasing the image size
My file size was 602 kb in png but it got increased to 1.3 mb after uploading it to s3
I want it to be compressed so the image can load faster on our webpages.
Can anyone please suggest what should I do for png image to be compressed ?

heroku with carrierwave dropbox storage and rails showing always same image

so I have a web application that runs fine in development, and with carrierwave and imagemagick I make some changes on the photos that I need to upload. The problem is that when on heroku when i ask for the main version of the photo it still gives me the thumb version
class BannerUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
if Rails.env.development?
storage :file
else
storage :dropbox
end
def store_dir
if version_name.nil?
"uploads/#{model.class.to_s.underscore}/#{mounted_as}"
else
"uploads/#{model.class.to_s.underscore}/#{mounted_as}_#{version_name}"
end
end
process convert: :jpg
process :crop
process resize_to_limit: [1200, 260]
def crop
if model.crop_x.present?
manipulate! do |img|
x = model.crop_x.to_i
y = model.crop_y.to_i
w = model.crop_w.to_i
h = model.crop_h.to_i
r = model.crop_r.to_i
img.rotate r
img.crop([[w, h].join('x'), [x, y].join('+')].join('+'))
end
end
end
version :thumb do
process resize_to_fill: [640, 320]
end
def extension_whitelist
%w(jpg jpeg png)
end
def filename
if original_filename
"#{secure_token}.#{file.extension}"
elsif file
file.filename
end
end
protected
def secure_token
var = :"##{mounted_as}_secure_token"
model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
end
end
this is my uploader
then in my views i have image_tag(model.banner_url(:thumb) to get the thumb version and image_tag #model.banner_url to get the large version. The problem is that the second one on my local machine runs just fine, but when on heroku it gives me the same image of the first one. It does create the right folders and files, and it does crop them right, but it doesn't retrieve the correct one. I am using the
gem 'dropbox-sdk-v2', '~> 0.0.3'
gem 'carrierwave-dropbox', '~> 2.0'
as heroku storage, with obviously a dropbox account
Often you'll want to add different versions of the same file. The
classic example is image thumbnails. There is built in support for
this*
I think the problem is the way you define the store_dir method.
Define it this way:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
And also CarrierWave documentation recommends to use the uploader this way:
uploader = AvatarUploader.new
uploader.store!(my_file) # size: 1024x768
uploader.url # => '/url/to/my_file.png' # size: 800x800
uploader.thumb.url # => '/url/to/thumb_my_file.png' # size: 200x200
Source: https://github.com/carrierwaveuploader/carrierwave

Image uploading issue with Carrierwave and Minimagick (Ruby on rails)

I am using Carrierwave to upload images and that works fine but I am now trying to use Minimagick to process these uploaded images.
What is going on?
The images still upload as well as their thumb version, however both images are exactly the same size.
Here is the uploader code:
class FileUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# Override the directory where uploaded files will be stored.
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Create different versions of your uploaded files:
version :thumb do
process :resize_to_fit => [100, 100]
end
# Add a white list of extensions which are allowed to be uploaded.
def extension_white_list
%w(jpg jpeg gif png)
end
end
And here is how I am calling it:
<%= image_tag example.my_file.url(:thumb).to_s %>
I dont think it is anything to do with the way I am calling it because the two files created by all this are the same, so sound slike its to do with processing it on upload.
Here is the folder created for each object:
Image
--- Fixed ---
The issue was ImageMagick. It was not properly configured on my machine so that means no gem that deals with image processing (Paperclip, Minimagick or Dragonfly) could complete any image manipulation.

Carrierwave undefined metho auto_orient

I've been trying to use RMagick auto_orient method to fix mobile uploads. Currently they are rotated 90 degrees. My uploader file currently looks like this.
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :fog
def root
Rails.root.join 'public/'
end
include CarrierWave::MimeTypes
process :set_content_type
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
process :resize_to_fill => [200, 200]
version :thumb do
process :resize_to_fill => [50, 50]
end
process :auto_orient
def extension_white_list
%w(jpg jpeg gif png)
end
end
This is giving me an error
undefined local variable or method auto_orient for AvatarUploader:Class (NameError)
I've tried several solutions,
exif image rotation issue using carrierwave and rmagick to upload to s3, https://github.com/minimagick/minimagick/issues/68
but no dice.
Anyone got a clue what I'm doing wrong?
Try adding the following:
def auto_orient
manipulate! do |img|
img.auto_orient!
end
end
As it stands now, the auto_orient process you're referencing doesn't exist in the context, hence the error.
Edit: according to the imagemagick github link you posted, auto_orient! might be broken. You could then use auto_orient instead in a similar way (it just creates a new image instead of modifying the one passed to the method). Refer to the links you posted for possible solutions using the auto_orient method.

CarrierWave: Uploads and saves original file...I don't want it to

I am using carrierwave_backgrounder to upload images to S3 in a background process with Sidekiq.
Here's my background_uploader.rb class...
class BackgroundUploader < CarrierWave::Uploader::Base
include ::CarrierWave::Backgrounder::Delay
include CarrierWave::RMagick
include CarrierWave::MimeTypes
process :set_content_type
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
storage :fog
def store_dir
"uploads/backgrounds/#{model.id}"
end
def default_url
"/assets/default.jpg"
end
process :resize_to_fit => [1024, 1024]
process :convert => 'jpg'
process :fix_exif_rotation
def extension_white_list
%w(jpg jpeg png)
end
def filename
#name ||= Digest::MD5.hexdigest(File.dirname(current_path.to_s))
"#{#name}.#{file.extension}" if original_filename
end
# Rotates the image based on the EXIF Orientation & applies gaussian blur
def fix_exif_rotation
manipulate! do |img|
img.auto_orient!
img = yield(img) if block_given?
img = img.gaussian_blur(0.0, 20.0)
img
end
end
end
carrierwave_backgrounder.rb:
CarrierWave::Backgrounder.configure do |c|
c.backend :sidekiq, queue: :carrierwave
end
background.rb contains:
mount_uploader :image, BackgroundUploader
process_in_background :image
I then run sidekiq -q carrierwave to fire up the background workers. All is working good! Upload a file, I see the queue accept it and start working...
If I immediately open up my AWS S3 Console I see the original file in there. Non-resized and un-blurred. Once the job is complete...I refresh S3 and there's the resized/blurred version. Now both images are in there, but I only want the blurred image to be there. In my view I use...
<%= image_tag(#background.image.to_s) %>
It displays the original file. If I check the checkbox to remove the file, it does so as it should (deletes the original from S3), but the blurred version stays there.
What's getting uploaded to S3...
original.jpg (immediate...I don't want this uploaded at all)
modified.jpg (after job completes)
Long story short: I don't want the original file to upload to S3.
I think your problem is the filename method, which carrierwave may rely on to find (and delete) the original file. Does the problem go away when you use a filename that doesn't change between when it's initially stored and when you process it?

Resources