diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..81f4ea0 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +paperclip-facecrop \ No newline at end of file diff --git a/.idea/.rakeTasks b/.idea/.rakeTasks new file mode 100644 index 0000000..336edcd --- /dev/null +++ b/.idea/.rakeTasks @@ -0,0 +1,7 @@ + + diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f415bd5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,25 @@ + + + + http://www.w3.org/1999/xhtml + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b7cd248 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/paperclip-facecrop.iml b/.idea/paperclip-facecrop.iml new file mode 100644 index 0000000..82133fd --- /dev/null +++ b/.idea/paperclip-facecrop.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..c80f219 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..31b7098 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1347506012506 + 1347506012506 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index fb2bbc4..576e1d2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Paperclip::FaceCrop ==================== `Paperclip::FaceCrop` is a [Paperclip][paperclip] processor that is aware of the faces found on the image so that they aren't cropped or aren't shown too small while generating the thumbnails. -It can use the [OpenCV][opencv] library or the [Face.com][face_com] web service(or both at the same time) for the facial recognition. +It can use the [OpenCV][opencv] library, the [Face.com][face_com] or [LambdaLabs][lambda_labs] web service(or all three at the same time) for the facial recognition. ![](https://github.com/dagi3d/paperclip-facecrop/raw/master/README_example.jpg) @@ -25,6 +25,12 @@ checkout the OpenCV_2.2 branch or just remove the line 143 from `/ext/opencv/cve In order to use the Face.com service, you will also need to register in order to get your api key and api secret for your application. +### lambdal.com +- [rest-client][rest-client] + +In order to use the lambdal.com service, you will also need to register in order to get your api key for your application. + + Installation: ------------- @@ -85,6 +91,13 @@ only the found areas that contain parts like a mouth, an eye or a nose will be c 'FaceCom' => { :api_key => "", :api_secret => ""} } +### lambdal.com + + Paperclip::FaceCrop.detectors = { + 'LambdaLabs' => {:mashape_authorization => ""} + } + + Usage: ------ @@ -114,6 +127,7 @@ Copyright (c) 2011 Borja Martín Sánchez de Vivar The photo used as example belongs to [Jesper Rønn-Jensen](http://www.flickr.com/photos/jesper/) [face_com]: http://face.com +[lambda_labs]: http://lambdal.com [rest-client]: https://rubygems.org/gems/rest-client [paperclip]: https://github.com/thoughtbot/paperclip [opencv]: http://opencv.willowgarage.com/ diff --git a/lib/detectors/face_com.rb b/lib/detectors/face_com.rb index d70ba8a..8281f40 100755 --- a/lib/detectors/face_com.rb +++ b/lib/detectors/face_com.rb @@ -4,7 +4,7 @@ class FaceCrop::Detector::FaceCom < FaceCrop::Detector::Base URL = "http://api.face.com/faces/detect.json" def detect_faces(file) - query = @options.to_query + query = @options.to_query + "&detector=Aggressive" url = "#{URL}?#{query}" response = RestClient.post url, :file => File.new(file) diff --git a/lib/detectors/lambda_labs.rb b/lib/detectors/lambda_labs.rb new file mode 100644 index 0000000..e12a8b1 --- /dev/null +++ b/lib/detectors/lambda_labs.rb @@ -0,0 +1,27 @@ +require 'rest_client' + +class FaceCrop::Detector::LambdaLabs < FaceCrop::Detector::Base + URL = "https://lambda-face-recognition.p.mashape.com/detect" + + def detect_faces(file) + response = RestClient.post(URL, + {:files => File.new(file)}, + {:content_type => 'json', "X-Mashape-Authorization" => @options[:mashape_authorization]}) + + response = JSON.parse(response) + + photo = response['photos'].first + photo['tags'].map do |tag| + # values are returned as percentual values + + x = tag['center']['x'].to_i + y = tag['center']['y'].to_i + w = tag['width'].to_i + h = tag['height'].to_i + + region = FaceCrop::Detector::Region.new(x, y, w, h) + region.color = "blue" + region + end + end +end \ No newline at end of file diff --git a/lib/face_crop.rb b/lib/face_crop.rb index dff20d9..4012357 100755 --- a/lib/face_crop.rb +++ b/lib/face_crop.rb @@ -5,6 +5,7 @@ module FaceCrop module Detector autoload :FaceCom, File.expand_path('../detectors/face_com', __FILE__) + autoload :LambdaLabs, File.expand_path('../detectors/lambda_labs', __FILE__) autoload :OpenCV, File.expand_path('../detectors/opencv', __FILE__) # Base diff --git a/lib/processor.rb b/lib/processor.rb index 5816c40..16d56eb 100755 --- a/lib/processor.rb +++ b/lib/processor.rb @@ -4,10 +4,10 @@ module Paperclip class FaceCrop < Paperclip::Thumbnail @@debug = false - + #cattr_accessor :classifiers cattr_accessor :debug - + def self.detectors=(detectors) @@detectors = detectors.map do |name, options| #require File.expand_path("../detectors/#{name}", __FILE__) @@ -15,16 +15,16 @@ def self.detectors=(detectors) detector = detector_class.new(options) end end - + def initialize(file, options = {}, attachment = nil) super(file, options, attachment) - - + + raise "No detectors were defined" if @@detectors.nil? - + faces_regions = [] faces_parts_regions = [] - + @@detectors.each do |detector| begin faces_regions += detector.detect(file.path) @@ -33,82 +33,92 @@ def initialize(file, options = {}, attachment = nil) Rails.logger.error(e) end end - - + + x_coords, y_coords, widths, heights = [], [], [], [] - + faces_regions.each do |region| x_coords << region.top_left.x << region.bottom_right.x y_coords << region.top_left.y << region.bottom_right.y widths << region.width heights << region.height end - - + + @has_faces = faces_regions.size > 0 - + if @has_faces @top_left_x = x_coords.min @top_left_y = y_coords.min @bottom_right_x = x_coords.max @bottom_right_y = y_coords.max - - - + + + #puts @top_left_x.to_s - + # average faces areas average_face_width = widths.sum / widths.size average_face_height = heights.sum / heights.size - + # calculating the surrounding margin of the area that covers all the found faces # - + # new width @top_left_x -= average_face_width / 1.2 @bottom_right_x += average_face_width / 1.2 - - - + + # new height #puts ":::#{@top_left_x}---#{average_face_width}" #return - + @top_left_y -= average_face_height / 1.2 - @bottom_right_y += average_face_height / 1.6 + @bottom_right_y += average_face_height / 1.2 + calculate_bounds - - - + + + # if the new area is smaller than the target geometry, it's scaled so the final image isn't resampled # - if @faces_width < @target_geometry.width + if @faces_width < @target_geometry.width delta_width = (@target_geometry.width - @faces_width) / 2 @top_left_x -= delta_width @bottom_right_x += delta_width calculate_bounds end - + # scale the image so the cropped image still displays the faces if @faces_width > @target_geometry.width && crop? ratio = @faces_width / @target_geometry.width @faces_height = @target_geometry.height * ratio end - + #raise (@target_geometry.height > 0 and @faces_height < @target_geometry.height).to_s - + if (@target_geometry.height > 0 and @faces_height < @target_geometry.height) delta_height = (@target_geometry.height - @faces_height) / 2 @top_left_y -= delta_height @bottom_right_y += delta_height calculate_bounds end - + + + #fix image position for extrem aspect ratios + if @target_geometry.width / @target_geometry.height < 0.6 + @top_left_x = x_coords.min * 1.2 + end + + if @target_geometry.width / @target_geometry.height > 1.4 + @top_left_y = y_coords.min * 1.2 + end + @faces_height = @faces_width if @target_geometry.height == 0 - + @current_geometry = Paperclip::Geometry.new(@faces_width, @faces_height) - + if @@debug parameters = [] parameters << "-stroke" << "green" @@ -117,43 +127,43 @@ def initialize(file, options = {}, attachment = nil) parameters << ":source" parameters << ":dest" parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ") - + Paperclip.run("convert", parameters, :source => "#{File.expand_path(file.path)}", :dest => "#{File.expand_path(file.path)}") end - - + + end end - - + + def transformation_command return super unless @has_faces - + scale, crop = @current_geometry.transformation_to(@target_geometry, crop?) faces_crop = "%dx%d+%d+%d" % [@faces_width, @faces_height, @top_left_x, @top_left_y] - + trans = [] trans << "-crop" << %["#{faces_crop}"] << "+repage" trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty? trans << "-crop" << %["#{crop}"] << "+repage" if crop - + trans end - + private - + # calculate_bounds # def calculate_bounds - @top_left_x = 0 if @top_left_x < 0 + @top_left_x = 0 if @top_left_x < 0 @bottom_right_x = @current_geometry.width if @bottom_right_x > @current_geometry.width @top_left_y = 0 if @top_left_y < 0 @bottom_right_y = @current_geometry.height if @bottom_right_y > @current_geometry.height - + @faces_width = @bottom_right_x - @top_left_x @faces_height = @bottom_right_y - @top_left_y end - + end end \ No newline at end of file