Validate Image Colorspace With Paperclip - ruby-on-rails

I'm using Paperclip to convert images. I have noticed a severe degradation of quality of the produced image if its colorspace isn't set to srgb.
Is there a way to validate the colorspace of the uploaded image?

Ive got half an answer that might help...
You will need to install: https://github.com/rmagick/rmagick
Add to your model:
attr_accessor :image_colorspace
validates_exclusion_of :image_colorspace, in: [Magick::CMYKColorspace], message: 'is not RGB'
before_logo_post_process :read_image_colorspace
def read_image_colorspace
self.image_colorspace = Magick::Image.from_blob(image.queued_for_write[:original].read).first.colorspace
true
end
(Replace image with your attachment name.)
...or, if you prefer to not use rmagick, and you have a 'nix system, you could do this:
attr_accessor :image_colorspace
validates_exclusion_of :image_colorspace, in: ['CMYK'], message: 'is not RGB'
before_logo_post_process :read_image_colorspace
def read_image_colorspace
self.logo_colorspace = `identify -verbose %m "#{image.queued_for_write[:original].path}" | grep 'Colorspace'`.to_s.upcase.strip.split(' ').last
true
end

Related

why Getting error on uploading the file with Webp-ffi gem?

Unsupported color conversion request
*** WebP::EncoderError Exception: Cannot read input picture file
got this error on certain jpg file. while converting into jpg again from google does the job but why is this?
'''webp_path = "#{filename.ext}.webp"
# Encode (convert) image to webp format with passed options
WebP.encode(path, webp_path, options)
# HACK: Changing of this two instance variables is the only way
# I found to make CarrierWave save new file that was created
# by encoding original image.
#filename = webp_path.split('/').pop
#file = CarrierWave::SanitizedFile.new(
tempfile: webp_path,
filename: webp_path,
content_type: 'image/webp'
)'''
This is what I use successfully, obviously need MiniMagick installed on the dev computer and server:
class ImagesUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
def store_dir
"uploads/story/#{model.created_at.strftime('%Y').to_s.underscore}/#{model.created_at.strftime('%m').to_s.underscore}/#{model.created_at.strftime('%d').to_s.underscore}"
end
def default_url(*args)
"/fallback.webp"
end
process :resize_to_fill => [1200, 677]
process convert_to_webp: [{ quality: 80 }]
version :preview do
process convert_to_webp: [{ quality: 80, resize_w: 470, resize_h: 265 }]
end
def extension_allowlist
%w(jpg jpeg gif png webp)
end
private
def convert_to_webp(options = {})
# Build path for new file
webp_path = "#{path}.webp"
# Encode (convert) image to webp format with passed options
WebP.encode(path, webp_path, options)
# HACK: Changing of this two instance variables is the only way
# I found to make CarrierWave save new file that was created
# by encoding original image.
#filename = webp_path.split('/').pop.split('.').first + '.webp'
#file = CarrierWave::SanitizedFile.new(
tempfile: webp_path,
filename: webp_path,
content_type: 'image/webp'
)
end
end

How to open .jpg file with Minimagick?

I have an error with MiniMagick during my "after_upload" in my image_concern.rb
My code makes the image that is poster be converted to jpg. Then it will take the file which is in / tmp and will modify this file to have it also in smaller to display it for later. But I have an error during this step:
`mogrify -resize 360x200^ -gravity Center -crop 360x200+0+0 /tmp/RackMultipart20200211-5215-1lxtqf1.png` failed with error: mogrify: unable to open image '/tmp/RackMultipart20200211-5215-1lxtqf1.png':
It tries to open this file in .png but it is in jpg in my tmp /. How to say that it opens this file but not in png but in jpg. Thank you
This is my after_upload code:
def #{field}_after_upload
path = #{field}_path
options = #{options}
if #{field}_file.respond_to? :path
dir = File.dirname(path)
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
image = MiniMagick::Image.new(#{field}_file.path) do |b|
if options[:resize].ends_with?('!')
b.resize '#{options[:resize].delete('!')}^'
b.gravity 'Center'
b.crop '#{options[:resize].delete('!')}+0+0'
else
b.resize '#{options[:resize].delete('!')}\>'
end
end
image.format 'jpg'
image.write path
if options[:formats]
options[:formats].each do |k, v|
image = MiniMagick::Image.new(#{field}_file.path) do |b|
b.resize "\#{v}^"
b.gravity 'Center'
b.crop "\#{v}+0+0"
end
image.format 'jpg'
image.write path.gsub('.jpg', "_\#{k}.jpg")
end
end
end
end
The bug starts from "if options[:formats]"
Thank you !

base 64 photo to paperclip rails image

I have a rails app and I am receiving a base64 encoded string that is an image from an ios app.
I am trying to convert it to a paperclip image.
So far I have:
module Api
module V1
class MyController < BaseController
def image_build
begin
token = request.headers["HTTP_TOKEN"]
#user = User.find_by(user_token: token)
#decoded_image = Base64.decode64(params[:image_data])
puts #decoded_image[0, 50]
#new_stand = Stand.create(:user_id => #user.id, :avatar => #decoded_image)
...
which outputs:
iVBORw0KGgoAAAANSUhEUgAAAPsAAACxCAYAAAACscusAAAAAX
�PNG
IHDR��ˬsRGB���
Completed 500 Internal Server Error in 90ms (Views: 3.1ms | ActiveRecord: 1.1ms)
How do I convert the base64 string to a proper image file in rails? I have paperclip running from this gem:
gem "paperclip", git: "git://github.com/thoughtbot/paperclip.git"
Notes
The images sent over can be any image from an ios phone so the type can change. I would like to support .gif, .png, .jpg, and jpeg.
In IOS I send over the information like this:
#IBOutlet var image: UIImage
#IBAction func Build(sender: AnyObject) {
let image_data = UIImagePNGRepresentation(image.image!)
self.service.createNewImage(notifier: notifier, image: image_data!)
}
then
let strBase64:String = image.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
let dataDictionary = ["image_data": strBase64]
self.post("image/build", data: dataDictionary).responseJSON
EDIT 1
I have started to attempt to use the paperclip io adapters but it still errors out. This is what I have so far:
#new_image = Stand.create(:user_id => #user.id)
encoded_picture = params[:image_data]
content_type = "image/jpg"
puts "HERE 1"
image = Paperclip.io_adapters.for("data:#{content_type};base64,#{encoded_picture}")
puts "HERE 2"
image.original_filename = "new_image.jpg"
puts "HERE 3"
#new_image.avatar = image
puts "HERE 4"
#new_image.cover_image_url = #new_image.avatar.url
puts "HERE 5"
#new_image.save
This code gives these errors:
HERE 1
HERE 2
HERE 3
Command :: file -b --mime '/tmp/082995477845923f853c5a48cc6e3cae20160712-26217-s1dngb.jpg'
Command :: identify -format '%wx%h,%[exif:orientation]' '/tmp/8d777f385d3dfec8815d20f7496026dc20160712-26217-1ht7ijm[0]' 2>/dev/null
Command :: identify -format %m '/tmp/8d777f385d3dfec8815d20f7496026dc20160712-26217-1ht7ijm[0]'
Command :: convert '/tmp/8d777f385d3dfec8815d20f7496026dc20160712-26217-1ht7ijm[0]' -auto-orient -resize "160x160>" '/tmp/4a98b13d8ce55141b173bb0ed5cb181220160712-26217-17sazsk'
Command :: identify -format '%wx%h,%[exif:orientation]' '/tmp/8d777f385d3dfec8815d20f7496026dc20160712-26217-1ht7ijm[0]' 2>/dev/null
Command :: identify -format %m '/tmp/8d777f385d3dfec8815d20f7496026dc20160712-26217-1ht7ijm[0]'
Command :: convert '/tmp/8d777f385d3dfec8815d20f7496026dc20160712-26217-1ht7ijm[0]' -auto-orient -resize "40x40>" '/tmp/4a98b13d8ce55141b173bb0ed5cb181220160712-26217-fhdb0w'
HERE 4
Completed 500 Internal Server Error in 654ms (Views: 2.5ms | Searchkick: 15.6ms | ActiveRecord: 7.3ms)
This seems to work, but I am still working on testing differing image types.
#new_image = Stand.create(:user_id => #user.id)
encoded_picture = params[:image_data]
content_type = "image/jpg"
image = Paperclip.io_adapters.for("data:#{content_type};base64,#{encoded_picture}")
image.original_filename = "new_image.jpg"
#new_image.avatar = image
#new_image.save
#new_image.cover_image_url = #new_image.avatar.url
#new_image.save
I have upvoted the previous answer because it helped me find other resources, but it does not contain an answer for my scenario.
A lot of useful information you can found in paperclip's repository, for your question i found this:
3.5.0:
Feature: Handle Base64-encoded data URIs as uploads
Paperclip::DataUriAdapter handles base64-encoded data.
Just works:
class Photo < ActiveRecord::Base
before_validation :set_image
has_attached_file :image
def set_image
StringIO.open(Base64.decode64(image_json)) do |str|
decorate_str(str)
str.original_filename = "THE_NAME_OF_FILE.jpg"
str.content_type = "image/jpeg"
self.image = data
end
end
def decorate_str(str)
str.class.class_eval { attr_accessor :original_filename, :content_type }
end
end
Similar way to solve your problem is here.
Paperclip's changelog

Watermark images with paperclip, rails 4

I've been attempting to add watermarks to my images, following the answer listed in watermark with paperclip :
Watermark.rb:
module Paperclip
class Watermark < Processor
# Handles watermarking of images that are uploaded.
attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options, :watermark_path, :watermark_offset, :overlay, :position
def initialize file, options = {}, attachment = nil
super
geometry = options[:geometry]
#file = file
#crop = geometry[-1,1] == '#'
#target_geometry = Geometry.parse geometry
#current_geometry = Geometry.from_file #file
#convert_options = options[:convert_options]
#whiny = options[:whiny].nil? ? true : options[:whiny]
#format = options[:format]
#watermark_path = options[:watermark_path]
#position = options[:position].nil? ? "SouthEast" : options[:position]
#watermark_offset = options[:watermark_offset]
#overlay = options[:overlay].nil? ? true : false
#current_format = File.extname(#file.path)
#basename = File.basename(#file.path, #current_format)
end
# TODO: extend watermark
# Returns true if the +target_geometry+ is meant to crop.
def crop?
#crop
end
# Returns true if the image is meant to make use of additional convert options.
def convert_options?
not #convert_options.blank?
end
# Performs the conversion of the +file+ into a watermark. Returns the Tempfile
# that contains the new image.
def make
dst = Tempfile.new([#basename, #format].compact.join("."))
dst.binmode
if watermark_path
command = "composite"
params = %W[-gravity #{#position}]
params += %W[-geometry '#{#watermark_offset}'] if #watermark_offset
params += %W['#{watermark_path}' '#{fromfile}']
params += transformation_command
params << "'#{tofile(dst)}'"
else
command = "convert"
params = ["'#{fromfile}'"]
params += transformation_command
params << "'#{tofile(dst)}'"
end
begin
Paperclip.run(command, params.join(' '))
rescue ArgumentError, Cocaine::CommandLineError
raise PaperclipError, "There was an error processing the watermark for #{#basename}" if #whiny
end
dst
end
def fromfile
File.expand_path(#file.path)
end
def tofile(destination)
File.expand_path(destination.path)
end
def transformation_command
scale, crop = #current_geometry.transformation_to(#target_geometry, crop?)
trans = %W[-resize '#{scale}']
trans += %W[-crop '#{crop}' +repage] if crop
trans << convert_options if convert_options?
trans
end
end
end
and model code:
has_attached_file :image,
:processors => [:watermark],
:styles => {
:large => "640x480",
:thumb => "100x100",
:medium => "300x300",
:content => {
:geometry => '150x153',
:watermark_path => Rails.root.join('app/assets/images/watermark.jpg'),
:position => 'SouthWest'
},
},
dependent: :allow_destroy
I've attempted to update this to work with Rails 4 (moving attr_accessor to params in the model), but I get the error:
uninitialized constant Paperclip::Watermark::PaperclipError
Any tips on how to get implement watermarks in a rails 4 app?
UPDATE: I was able to get past the uninitialized constant error with Graeme's suggestion below of changing:
raise PaperclipError, "There was an error processing the watermark for #{#basename}" if #whiny
to:
raise Paperclip::Error.new("There was an error processing the watermark for #{#basename}") if #whiny
I also had to remove the following from the model in order for the upload to process:
:url => "/images/:style/:id_:style.:extension",
:path => ":rails_root/app/assets/images/:style/:id_:style.:extension"
I don't understand what the purpose of :url and :path are in this scenario, when users are uploading images?
The problem is, even though the images now get uploaded, no watermark is being displayed. Thoughts?
Update 2:
In order to get the watermark displaying correctly, I had to change the model to:
has_attached_file :image,
:processors => [:watermark],
:url => "/system/:class/:attachment/:id_partition/:style/:filename",
:path => ":rails_root/public/system/:class/:attachment/:id_partition/:style/:filename",
:styles => {
:large => "640x480",
:thumb => "100x100",
:medium => {
:processors => [:watermark],
:geometry => '300x300',
:watermark_path => Rails.root.join('app/assets/images/icon.gif'),
:position => 'SouthWest'
},
},
dependent: :allow_destroy
The key piece was removing :content => . Only remaining issue is that the watermark is scaling up to fit the entire width of the image. Any recommendations on how to stop the watermark-scaling?
The problem with the watermark being stretched is the Imagemagick command which is combining the two images together and then resizing the result.
Effectively the command being run will be (I've abbreviated the actual filenames for clarity):
composite -gravity SouthWest icon.gif uploaded_image.gif -resize 300x300 output_image.gif
As you see the images are joined and then resized.
The command I believe you need is:
convert uploaded_image.gif -resize 300x300 icon.gif -gravity SouthWest -composite output_image.gif
i.e. resize the uploaded image and then add on the watermark.
I've tested this using composite and convert on the command line and it does what I believe you are looking for.
To achieve it in the code, you need to change if watermark_path statement in the make method:
def make
dst = Tempfile.new([#basename, #format].compact.join("."))
dst.binmode
if watermark_path
# -- original code --
# command = "composite"
# params = %W[-gravity #{#position}]
# params += %W[-geometry '#{#watermark_offset}'] if #watermark_offset
# params += %W['#{watermark_path}' '#{fromfile}']
# params += transformation_command
# params << "'#{tofile(dst)}'"
# -- new code --
command = "convert"
params = %W['#{fromfile}']
params += transformation_command
params += %W['#{watermark_path}' -gravity #{#position} -composite]
params << "'#{tofile(dst)}'"
else
command = "convert"
params = ["'#{fromfile}'"]
params += transformation_command
params << "'#{tofile(dst)}'"
end
begin
Paperclip.run(command, params.join(' '))
rescue ArgumentError, Cocaine::CommandLineError
raise PaperclipError, "There was an error processing the watermark for #{#basename}" if #whiny
end
dst
end
Disclaimer: I have not actually tested this so please forgive any errors.
PaperclipError doesn't exist.
Try changing:
raise PaperclipError, "There was an error processing the watermark for #{#basename}" if #whiny
to:
raise Paperclip::Error.new("There was an error processing the watermark for #{#basename}") if #whiny
Since you are now asking a different question I'll make a new answer.
:path is used to define where Paperclip will store the uploaded image. By default this will be :rails_root/public/system/:class/:attachment/:id_partition/:style/:filename.
:url is used to access the image later. By default this will be /system/:class/:attachment/:id_partition/:style/:filename.
(Actually, to save having to duplicate the url part, :path is really defined as :rails_root/public:url by default.)
By specifying them in the model you are changing the save location. I wouldn't recommend putting them in the assets directory as assets are really part of your application and you don't want user uploaded files to be going there.
As to why you are not seeing the watermark with the uploaded image, I guess the Imagemagick composite command is not being called correctly. Try running it on the command line, or adding the parameter -debug to see why it is failing.

How can I create thumbnail with carrierwave and a imagemagick script?

I'm using carrierwave to creating thumbnails, but I don't know how can -i use with this script.
mogrify -resize 246x246 -background none -gravity center -extent 246x246 -format png -quality 75 -path thumbs penguins.jpg
This script creating thumbnails and works good, but I would like to use this or similar in carrierwave versions.
The documentation on doing advanced configuration of image manipulation with carrierwave is here:
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Efficiently-converting-image-formats#full-example
If you look at the def mogrify section, you see that in the img.format("png") do |c| block is where the image manipulation options are passed.
That variable c is actually an instance of MiniMagick, which is a thin wrapper around mogrify.
https://github.com/minimagick/minimagick/
The full API for MiniMagick is not quite there, but if you dig into the source, you can find they have a list of all the possible methods they use here:
https://github.com/minimagick/minimagick/blob/master/lib/mini_magick.rb#L39
And those are all defined down below:
https://github.com/minimagick/minimagick/blob/master/lib/mini_magick.rb#L456
I would suggest adding the options you want to your own uploader:
def mogrify(options = {})
manipulate! do |img|
img.format("png") do |c|
# Add other options here:
c.gravity options[:gravity]
c.background options[:background]
c.extend options[:extend]
c.quality options[:quality]
# Original options follow:
c.fuzz "3%"
c.trim
c.rotate "#{options[:rotate]}" if options.has_key?(:rotate)
c.resize "#{options[:resolution]}>" if options.has_key?(:resolution)
c.resize "#{options[:resolution]}<" if options.has_key?(:resolution)
c.push '+profile'
c.+ "!xmp,*"
c.profile "#{Rails.root}/lib/color_profiles/sRGB_v4_ICC_preference_displayclass.icc"
c.colorspace "sRGB"
end
img
end
end
in your app/uploaders/image_uploader.rb
do something like this
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :thumb do
process :resize_to_limit => [246, 246]
end
end
Take a look at this rails cast 253-carrierwave-file-uploads

Resources